synops/docs/retninger/maskinrommet.md
vegard 00bf5d27ce Arkitekturbeslutninger: noder er sentrum, edges definerer alt
Grunnleggende arkitekturbeslutninger tatt og dokumentert:

- Alt er noder (brukere, team, innhold, mediefiler, samlings-noder)
- Edges definerer hva en node er (freeform typer, metadata i JSONB)
- Materialisert tilgangsmatrise (node_access) erstatter workspace-RLS
- Visibility (hidden/discoverable/readable/open) på noder
- Aliaser via usynlige system-edges
- Maskinrommet eier all skriving (SpacetimeDB først, PG asynk)
- SpacetimeDB holder hele grafen, PG er persistent backup
- Node- og edge-skjema spesifisert (docs/primitiver/)

Fjernet workspace-konseptet fra hele dokumentasjonen (~40 filer).
Fem retninger besluttet, én åpen (rom, ikke forum).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 10:29:54 +01:00

196 lines
6.8 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 (SpacetimeDB-reducer)
## Maskinrommet eier all skriving
Frontend sender intensjoner. Maskinrommet utfører.
```
Frontend: "legg til Trond i møtet"
→ Maskinrommet validerer
→ Skriver edge til SpacetimeDB (instant)
→ Oppdaterer tilgangsmatrise
→ Persisterer til PG (asynk)
→ 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 → SpacetimeDB (instant) → PG (asynk).
Se [synkronisering](../infra/synkronisering.md).
## 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.
## 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 SpacetimeDB
først, PG asynk