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

7.9 KiB

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:

  • Input-primitiven → fang() + prosesser()

  • Mottak-primitiven → lever()

  • Kommunikasjonsnoden → alle tre

  • Noder er sentrum — maskinrommet eier tilgangsmatrise-oppdatering

  • Datalaget — maskinrommet skriver PG, sanntid via LISTEN/NOTIFY + WebSocket