synops/docs/arkitektur.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

172 lines
6.6 KiB
Markdown

# Arkitektur — Synops
## Visjon
Synops er en plattform for redaksjonelt arbeid og podcast-produksjon.
Ikke en webapp med features — en plattform med primitiver som kan bli
hva som helst. Sidelinja (podcastredaksjonen) er en tenant som bruker
Synops.
Alt er noder og edges i en graf. En bruker er en node. Et team er en
node. En mediefil er en node. Hva noe "er" bestemmes av edges, ikke
av noden selv. Maskinrommet eier alle skrivinger. Frontend er et tynt
lag som leser grafen fra SpacetimeDB.
## Lagmodell
```
┌─────────────────────────────────────┐
│ GUI (SvelteKit) │
│ Visninger: spørringer mot STDB │
└────────┬──────────────────┬─────────┘
│ intensjoner │ les (sanntid)
│ │ direkte WebSocket
┌────────▼────────┐ ┌──────▼──────────┐
│ Maskinrommet │ │ SpacetimeDB │
│ (Rust) │ │ Hele grafen │
│ Eier alle │ │ (noder+edges) │
│ skrivinger │ └─────────────────┘
└──┬─────┬─────┬──┘
│ │ │
▼ ▼ ▼
┌─────┐┌─────┐┌─────┐┌─────────────┐
│ PG ││STDB ││ CAS ││ Whisper, │
│(bak)││(skr)││ ││ LiteLLM, │
│ ││ ││ ││ LiveKit ... │
└─────┘└─────┘└─────┘└─────────────┘
```
### Skrivestien
GUI → intensjon → Maskinrommet (Rust) → SpacetimeDB (instant) → PG (async)
Frontend sender intensjoner (ikke data). Maskinrommet validerer,
skriver til SpacetimeDB først for umiddelbar oppdatering, deretter
persisterer til PG asynkront. Maskinrommet leser edges og bestemmer
hvilke tjenester som trigges.
### Lesestien (sanntid)
SpacetimeDB → GUI (direkte WebSocket)
SpacetimeDB holder hele grafen — alle noder og edges. Frontend
abonnerer via WebSocket med edge-filtre. Visninger er spørringer
mot STDB, ikke forhåndsdefinerte API-endepunkter.
### Lesestien (tunge spørringer)
GUI → Maskinrommet (Rust) → PG
Søk, statistikk, semantisk søk (pgvector), graftraversering
(AGE/Cypher). For operasjoner der STDB ikke er egnet.
## Datamodell
### Alt er noder
Én `nodes`-tabell. Alt er noder: brukere, team, meldinger, oppgaver,
notater, mediefiler, kommunikasjonsrom, samlings-noder. En bruker er
en node som tilfeldigvis kan logge inn.
Felles skjema: id, innhold, created_at, content_hash (→ CAS).
Modalitetsspesifikk metadata i JSONB.
### Edges definerer alt
Én `edges`-tabell. Edge-typer er frie strenger. Hva en node "er"
bestemmes utelukkende av dens edges:
- Node + edge til kanal = chatmelding
- Node + edge til board + status-edge = kanban-kort
- Node + edge til dato = kalenderoppføring
- Node + edge til kun bruker = privat notat
- Node uten edges = løs tanke
### Visninger er spørringer
Visninger er spørringer mot SpacetimeDB med edge-filtre:
- Chat = noder med kanal-edge, sortert på tid
- Kanban = noder med board-edge, gruppert på status
- Kalender = noder med dato-edge, på tidslinje
- Mottaksflate = noder med edge til deg, vektet på relevans
Ingen forhåndsdefinerte visningstyper. Nye visninger er nye filtre.
## Input
Én universell input-komponent, gjenbrukt overalt. Fanger tekst, lyd,
bilde, AI, URL. Konteksten (hvor du er) bestemmer hvilke edges som
legges til. Output er alltid en node.
## Struktur uten workspaces
Ingen workspace-velger. Ingen `workspace_id`. Samlings-noder gir
struktur: et team er en samlings-node, et prosjekt er en samlings-node.
Du ser noder du har tilgang til via dine edges.
### Aliaser
En bruker kan ha alias-noder (f.eks. for ulike roller). Koblet med
system-edges som er usynlige for traversering.
## Maskinrommet
Rust-tjeneste med tre operasjoner: **fang**, **prosesser**, **lever**.
Eier alle skrivinger. Frontend sender intensjoner, maskinrommet
validerer og utfører. Edge-drevet ressursorkestrering: maskinrommet
leser edges og bestemmer hvilke tjenester som spinnes opp.
Forvalter også CAS (binærlagring) med intelligent pruning basert
på modalitet, edges og aksessmønstre.
## Sikkerhet
### Synlighet (visibility)
Noder har en visibility-egenskap med fire nivåer:
- **hidden** — usynlig, kun tilgjengelig via system-edges
- **discoverable** — kan finnes, men innhold skjult
- **readable** — innhold lesbart for de med tilgang
- **open** — tilgjengelig for alle med traverseringssti
Traversering respekterer visibility. Du kan ikke følge edges gjennom
noder du ikke har lov til å se.
### Materialisert tilgangsmatrise
`node_access`-tabell som cacher beregnet tilgang fra edge-grafen.
Oppdateres ved edge-endring, ikke ved lesing. Rask oppslag — ingen
rekursiv graftraversering per forespørsel.
### Privat er default
Input uten mottaker-edge er automatisk privat. Ingen ser det.
Deling er å legge til edges.
## Datalag
### PostgreSQL
Persistent backup og arkiv. Alle noder og edges. Fulltekstsøk,
pgvector (semantisk søk), JSONB. Apache AGE for Cypher ved behov.
### SpacetimeDB
Holder hele grafen — alle noder og edges. Frontend abonnerer via
WebSocket. Maskinrommet skriver hit først for umiddelbar oppdatering,
PG synkroniseres asynkront.
### CAS (Content-Addressable Store)
Binærdata (lyd, bilde, video) lagret med hash. TTL basert på
modalitet, edges og aksesslog. Generert innhold (TTS, thumbnails)
er en cache som regenereres on-demand.
## Teknologivalg
| Rolle | Teknologi | Begrunnelse |
|-------|-----------|-------------|
| Orkestrator | Rust | Ytelse, typesikkerhet, eier alle skrivinger |
| Frontend | SvelteKit | PWA, SSR, tynt lag mot STDB |
| Database | PostgreSQL | Persistent backup, pgvector, fulltekstsøk, AGE |
| Sanntid | SpacetimeDB | Hele grafen, WebSocket-subscriptions, ~10μs |
| Binærlagring | CAS (filsystem) | Enkel, deduplisering, ingen ekstern avhengighet |
| AI Gateway | LiteLLM | Multi-provider, BYOK, OpenRouter fallback |
| STT | faster-whisper | Lokal, god norsk kvalitet |
| TTS | ElevenLabs (→ lokal) | Kommersiell start, lokal når kvaliteten holder |
| Auth | Authentik | SSO, OIDC, self-hosted |
| Reverse proxy | Caddy | Auto-TLS, enkel config |
| Lyd/video | LiveKit | WebRTC, self-hosted |
## Retninger
Arkitekturen er basert på vedtatte retninger dokumentert i
`docs/retninger/`. Se `docs/retninger/README.md` for oversikt.