synops/docs/retninger/maskinrommet.md
vegard fd0b75ee13 Orkestrering: tre nivåer (script/fritekst/drøm) + rename portvokter→vaktmester
Orkestrering restrukturert med deklarativt script som primærnivå:
- Nivå 1: eksakte CLI-kall med {event.*}-variabler, ingen AI
- Nivå 2: fritekst tolket av bot med function calling
- Nivå 3: drømmemodus — bruker skriver fritt, mangler→work_items
- Auto-eskalering: script→bot ved uventet feil

Rename portvokter→vaktmester i alle docs — bedre navn for en
tjeneste som gjør ting, ikke bare sjekker legitimasjon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:59:29 +00:00

222 lines
7.9 KiB
Markdown

# Maskinrommet
**Status: Besluttet.**
> Én Rust-tjeneste med et fast grensesnitt. Alt som krever tunge
> ressurser eller eksterne tjenester går gjennom dette laget.
> Fang, prosesser, lever. Maskinrommet er det eneste som skriver
> noder og edges.
## Tre operasjoner
### 1. Fang (input-absorpsjon)
Ta imot råmateriale i alle modaliteter:
- Tekst (melding, URL, dokument)
- Lyd (voice memo, live stream, filopplasting)
- Bilde (foto, skjermbilde, tegning)
- Video (stream, opptak)
- Strukturert data (JSON, metadata, edges)
### 2. Prosesser (transformasjon)
Analyser, transformer, berik og systematiser:
- **STT** — lyd → tekst (Whisper)
- **TTS** — tekst → lyd (ElevenLabs / lokal modell)
- **AI-analyse** — oppsummering, klassifisering, edge-forslag
- **Beriking** — URL → metadata, bilde → beskrivelse
- **Søk** — fulltekst, semantisk (pgvector), graftraversering
- **Mediaprosessering** — transcode, thumbnail, waveform
### 3. Lever (output-distribusjon)
Lever resultat i riktig modalitet til riktig mottaker:
- Tekst (melding, notifikasjon, digest)
- Lyd (TTS-opplesning, lydstream)
- Video/bilde (stream, thumbnail)
- Strukturert data (noder, edges tilbake i grafen)
- Push (WebSocket via PG LISTEN/NOTIFY)
## Maskinrommet eier all skriving
Frontend sender intensjoner. Maskinrommet utfører.
```
Frontend: "legg til Trond i møtet"
→ Maskinrommet validerer
→ Skriver edge til PG
→ Oppdaterer tilgangsmatrise
→ PG NOTIFY → WebSocket → frontend oppdateres i sanntid
→ Reagerer på konsekvensene (koble inn LiveKit, starte transkripsjon)
```
Alt i én operasjon. Maskinrommet er ikke reaktivt i en pub/sub-
forstand — det orkestrerer hele sekvensen. Enklere å forstå,
enklere å debugge.
Skrivestien: validering → PG → NOTIFY → WebSocket (sanntid).
## Edge-drevet ressursorkestrering
Maskinrommet leser edges for å vite hva det skal gjøre. Noden
er alltid enkel. Edges bestemmer hvilke ressurser som spinnes opp.
### Privat er default
Input uten mottaker-edge er automatisk privat. Ingen ressurser
kobles inn utover det grunnleggende (fang + transkriber).
### Ressurser er proporsjonale med edges
```
Dagboknotat (privat voice memo):
node → fang lyd → transkriber (Whisper) → lagre
Ressurser: minimal
Samtale med Trond:
node + mottaker-edge(Trond)
→ fang lyd → transkriber → lever tekst/lyd til Trond
Ressurser: STT + levering til én
Redaksjonsmøte (5 deltakere):
node + mottaker-edges(5) + rolle-edges
→ fang lyd fra alle → transkriber → lever til alle → AI-referent
Ressurser: STT + levering til 5 + LLM
Livesending (1000 lyttere):
node + mottaker-edges(∞) + stream-edge
→ fang lyd → transkriber → stream via LiveKit → distribuer
→ generer segmenter → kjør live AI → publiser
Ressurser: STT + LiveKit + LLM + mediaprosessering
```
### Naturlig eskalering
Du starter en privat voice-note. Deler den med Trond → legg til
edge, maskinrommet begynner å levere. Trond foreslår møte → flere
edges, maskinrommet kobler inn sanntidsstrømming. Møtet blir
innspilling → publiserings-edge, maskinrommet aktiverer
produksjonspipeline. Hvert steg er bare å legge til edges.
## Grensesnittet
```
fang(input: RåInput) → NodeId
prosesser(node: NodeId, operasjon: Operasjon) → Resultat
lever(node: NodeId, mottaker: Mottaker, format: Format) → Status
```
I praksis er mye av dette implisitt: maskinrommet ser hvilke edges
som ble skrevet og handler deretter. Primitivene manipulerer edges
via maskinrommet, og maskinrommet kobler inn riktige ressurser.
## CAS og intelligent pruning
### CAS som lagringsprimitiv
All binærdata (lyd, bilde, video) lagres content-addressable.
CAS-noder i grafen bærer metadata (`cas_hash`, `mime`, `size`).
Selve biten lever på disk.
- **Deduplisering gratis** — samme fil delt i tre kontekster = én kopi
- **Separasjon** — "innholdet eksisterer" er adskilt fra "innholdet
er tilgjengelig"
- **Enkel opprydning** — slett filen fra CAS, noden beholder metadata
### Lagringsregler per modalitet
| Modalitet | Default levetid | Begrunnelse |
|-----------|----------------|-------------|
| Tekst | Evig | Billig, essensen av innholdet |
| Transkripsjon | Evig | Tekstlig representasjon — bevarer meningen |
| Lyd | 30 dager | Transkripsjon bevarer innholdet |
| Bilde | 30 dager | Beskrivelse/metadata bevarer kontekst |
| Video | 14 dager | Dyrest, transkripsjon + thumbnail bevarer det meste |
### Signaler som forlenger levetid
- **Edges.** Lydfil med publiserings-edge = beholdes. Privat
voice-memo uten edges = 30-dagers TTL.
- **Aksesslog.** Avspilt i løpet av TTL-perioden = forlenges.
- **Transkripsjonsstatus.** Utranskribert lyd kan trenge lengre TTL.
- **Edge-type.** Publisert = behold. Arkivert møte = transkripsjon
holder.
### Generert innhold er en cache
TTS, thumbnails, AI-oppsummeringer, waveforms — alt som kan
regenereres er en cache i CAS med samme TTL-mekanisme.
### Samlings-node-styrt aggressivitet
Hver samlings-node kan justere sin pruning-profil:
- **Konservativt** — behold alt lenge (arkiv-node)
- **Aggressivt** — tekst bevares, binærdata prunes raskt
- **Tilpasset** — egne regler per modalitet og edge-type
### Disk-nødventil
Maskinrommet overvåker diskbruk:
- **>85 %:** Genererte filer slettes (kan regenereres)
- **>90 %:** Aggressiv pruning for alle samlings-noder
- **>95 %:** Kritisk alarm. Alt uten publiserings-edge slettes.
Tekst og transkripsjoner bevares alltid.
## Isolasjon og observerbarhet
### Isolasjon
Bytt Whisper med noe annet? Endre maskinrommet. Frontend vet
ingenting. Legg til bildegenerering? Ny operasjon. Primitivene
kaller den uten å vite hva som skjer under.
### Observerbarhet
Alt går gjennom ett punkt. Logging, metrikker, kostnadsrapportering
— alt på ett sted. "Hva bruker vi AI-ressurser på?" har ett svar.
### Kapasitetsstyring
Prioritering, kø, rate limiting, fallback mellom leverandører.
Live transkripsjon prioriteres over bakgrunns-oppsummering.
## Compute-separasjon
Maskinrommet orkestrerer — tunge jobber trenger ikke kjøre på
samme maskin.
- **Nå:** Alt på én VPS. Jobbkøen prioriterer sanntid over batch.
- **Snart:** Trekk ut tunge workers til separat node (billig
ARM-instans) som poller jobbkøen. Maskinrommet ruter transparent.
- **Kildevern-modus:** Lokal LLM krever dedikert compute med
fysisk isolasjon.
Compute-separasjon er en konfigurasjon, ikke en arkitekturendring.
## Evolusjon: Maskinrommet → Vaktmesteren
Maskinrommet ble bygget som en monolitt — auth, validering,
prosessering, jobbkø i én binær. Med unix-filosofi-
retningen (se `docs/retninger/unix_filosofi.md`) flyttes all
prosessering til CLI-verktøy. Det som blir igjen er:
1. **Auth** — JWT-validering, "hvem er denne requesten?"
2. **HTTP-ruting** — frontend → riktig CLI-verktøy
3. **Sanntid** — PG LISTEN/NOTIFY → WebSocket til frontend
4. **Jobbkø-dispatch** — poll PG, spawn CLI-verktøy
Dette er en **vaktmester**, ikke et maskinrom. Når uttynningen er
ferdig, renames `maskinrommet/` til `vaktmesteren/` og systemd-
tjenesten oppdateres. Navnet skal reflektere rollen: vokter porten,
gjør ikke jobben.
**Hva dette gir:**
- Vaktmesteren dør → frontend stopper, men CLI-verktøy fungerer.
Claude jobber videre, scripts kjører, terminalen funker.
- Et CLI-verktøy dør → bare den funksjonen stopper. Alt annet
er upåvirket.
- Vaktmesteren blir så enkel at den nesten aldri feiler.
**Edge-validering og tilgangskontroll** flyttes til `synops-common`
(delt lib) — brukes av både vaktmesteren og CLI-verktøy.
## Forhold til andre retninger
Maskinrommet er infrastrukturen *under* de tre primitivene i
[universell input og mottak](universell_input.md):
- Input-primitiven → `fang()` + `prosesser()`
- Mottak-primitiven → `lever()`
- Kommunikasjonsnoden → alle tre
- [Noder er sentrum](bruker_ikke_workspace.md) — maskinrommet
eier tilgangsmatrise-oppdatering
- [Datalaget](datalaget.md) — maskinrommet skriver PG,
sanntid via LISTEN/NOTIFY + WebSocket