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>
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:
- Auth — JWT-validering, "hvem er denne requesten?"
- HTTP-ruting — frontend → riktig CLI-verktøy
- Sanntid — PG LISTEN/NOTIFY → WebSocket til frontend
- 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