This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/docs/live-intent.md
pijukka c8cfdcd88e host(pinix): add live intent and ereader viewer services
Both run on pinix, published only through the pinix Tailnet namespace and
Traefik's web-internal entrypoint (unbound local-data + Headscale split
DNS for intent./ereader.domain2). Live Intent reads its OpenAI key from
the new pinix-only agenix secret.

ereaderViewer is a path: flake input for now; the repo split plan moves
it into its own repository on cgit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:18:55 +02:00

105 lines
5.2 KiB
Markdown

# Live Transkript auf pinix
`intent.<domain2>` ist ein kleiner, ausschließlich im pinix-Tailnet erreichbarer
Sprachchat für Planungs-Gespräche. Er erzeugt keinen Intent, erstellt keine
Tickets und führt weder Codex-Aufträge noch andere Aktionen aus. Ein Gespräch
wird nach dem Auflegen als lokales Markdown-Transkript gespeichert; die spätere
Weiterverwendung im Codex-Abo ist bewusst ein manueller Schritt außerhalb dieser
Anwendung.
## Ablauf und Kosten
Der Browser verbindet sich per WebRTC direkt mit GPT-Live. Pinix tauscht nur das
SDP-Angebot mit `POST /v1/live/sessions` aus; der OpenAI-Schlüssel bleibt als
systemd-Credential auf dem Server. Der Browser wartet auf `session.started` und
sendet beim Beenden `session.close`. Nach `session.closed` speichert er die
empfangenen Transkriptabschnitte direkt lokal. Es gibt keinen separaten
`/api/draft`- oder Responses-Aufruf beim Abschluss und keinen Intent-Formatzwang.
Audio wird nicht aufgezeichnet.
Die Oberfläche schließt eine Session standardmäßig nach zehn Minuten. Das ist
eine Begrenzung für normale Browsernutzung, aber keine serverseitige
Abrechnungsgarantie bei manipulierten Clients. GPT-Live wird nach Dauer
abgerechnet. Die weiterhin aktivierte Responses-Delegation kann **während des
Gesprächs** separat kostenpflichtige API-Nutzung auslösen; das Speichern des
Transkripts selbst ruft kein Modell auf.
Der Live-Default ist `gpt-live-1`. `chatgpt-live-1` wurde in den überprüften
offiziellen Referenzen nicht als Live-Modell gefunden und wird deshalb nicht
still ersetzt. Beide Modelle bleiben über `services.liveIntent.liveModel` und
`services.liveIntent.backendModel` konfigurierbar. Der Default
`gpt-5.6-luna` für die in-session Responses-Delegation hat keine Tools und kann
keine Aktion ausführen.
## Speicherung und Fehlerverhalten
Neue Gespräche liegen mit restriktiven Rechten unter:
```
/var/lib/live-intent/transcripts/<YYYY-MM-DD-UUID>/transcript.md
```
Der Browser erzeugt die Gesprächs-ID beim Start und behält sie für einen
Speicherwiederholungsversuch. Der Server validiert ID und Inhalt, schreibt in
einen neuen Ordner atomar und gibt bei exakt demselben Retry dieselbe Datei
zurück, ohne eine zweite Datei anzulegen. Abweichender Inhalt für dieselbe ID
ergibt einen Konflikt statt eines Überschreibens. Der Download-Link ruft genau
diese `transcript.md` ab.
Bei normal bestätigtem `session.closed` steht `Session-Abschluss: bestätigt` in
der Datei. Endet der Datenkanal ohne diese Bestätigung oder läuft der
15-Sekunden-Abschluss-Fallback ab, versucht die UI trotzdem die bis dahin
empfangenen Abschnitte zu speichern und kennzeichnet die Datei ehrlich als
`unbestätigt`. Ohne empfangene Textabschnitte kann keine Datei erzeugt werden;
die UI zeigt dies als Fehler an. Eine vollständige Disconnect- oder
Kostenabsicherung ist nicht Teil dieses Prototyps.
Der bestehende StateDirectory bleibt erhalten. Alte Dateien unter
`/var/lib/live-intent/intents/` werden weder gelesen, verändert noch gelöscht.
## Schutzmaßnahmen und Schlüssel
Der Dienst verwendet einen `DynamicUser`, `StateDirectory`, `UMask=0077`,
begrenzte Anfragekörper, atomisches Schreiben sowie strikte ID-/Pfadvalidierung.
Schreibende Endpunkte prüfen den exakten HTTPS-Origin. Die Traefik-Route nutzt
nur `web-internal`; der Split-DNS-Eintrag verweist nur auf die private
Tailnet-Adresse. Es gibt keinen öffentlichen Router und keine zusätzliche
Firewall-Freigabe. Server-Logs enthalten weder SDP, Transkripte noch Schlüssel.
Die statische UI und die lokale Transkript-API funktionieren ohne Credential;
nur das Starten einer Live-Session antwortet dann kontrolliert mit HTTP 503.
Der OpenAI-Schlüssel gehört ausschließlich als agenix-Datei
`secrets/liveIntentOpenaiApiKey.age` in eine einzelne
`OPENAI_API_KEY=…`-Zeile. Das Modul reicht sie als systemd-
`LoadCredential` weiter; sie erscheint weder im Browser noch im Nix-Store oder
in einer HTTP-Antwort.
## Prüfung und Aktivierung
Für Quellcode-Tests:
```bash
node --test services/live-intent/tests/server.test.mjs
```
Der dokumentierte pinix-Weg nutzt einen selektiven, nicht löschenden rsync in
`/home/drone/nixos-config-build` und baut anschließend auf pinix lokal:
```bash
ssh pinix 'cd /home/drone/nixos-config-build && sudo nixos-rebuild switch --flake path:.#pinix --no-reexec'
```
Danach im Tailnet `https://intent.<domain2>` öffnen, Mikrofon freigeben, eine
Session starten, einige Transkriptzeilen erzeugen, regulär auflegen und den
Download prüfen. Ein echter Browser-/Audio-Test benötigt ein geeignetes
Mikrofon und verursacht GPT-Live-Kosten; er ist nicht durch die Offline-Tests
abgedeckt.
## Offizielle OpenAI-Referenzen
- [Getting started with GPT-Live](https://developers.openai.com/api/docs/guides/live)
- [WebRTC für GPT-Live](https://developers.openai.com/api/docs/guides/voice-webrtc?api=live)
- [Delegation und Tools](https://developers.openai.com/api/docs/guides/live-delegation)
- [Live-Sessions verwalten](https://developers.openai.com/api/docs/guides/live-conversations)
- [GPT-Live prompten](https://developers.openai.com/api/docs/guides/live-prompting)
- [TypeScript-Referenz: Live-Session erstellen](https://developers.openai.com/api/reference/typescript/resources/live/methods/create)