server/docs/retninger/datalaget.md
vegard c5de6086ca Innarbeid ekstern review: RLS-siloer, AGE-pragmatisme, compute-separasjon
- bruker_ikke_workspace: RLS-advarsel tatt på alvor — samlings-noder er
  harde sikkerhetsiloer under panseret, edge-basert tilgang er UX-lag
  innenfor siloene
- datalaget: AGE moderert fra "beslutning" til "planlagt utvidelse" —
  start med CTEs, legg til AGE når det faktisk trengs
- maskinrommet: compute-separasjon dokumentert — tunge workers kan
  flyttes til egen node, maskinrommet ruter transparent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:04:09 +01:00

182 lines
8.3 KiB
Markdown

# Datalaget — PG + AGE som enhetlig graf og arkiv
> PostgreSQL er den eneste databasen. Apache AGE gir Cypher-semantikk
> for graftraverseringer. SpacetimeDB er sanntidslaget over samme data.
> Én database, to tilgangsmønstre, ingen eierskapskonflikt.
## Observasjoner
Hele retningsarbeidet har bygget seg mot "alt er noder og edges."
Tilgang er edges, visninger er spørringer, administrasjon er
traversering. Spørsmålet er: hvilken teknologi støtter dette best?
### Neo4j ble vurdert og forkastet
Neo4j er best-in-class for dype graftraverseringer, men for dette
prosjektet gir den mer problemer enn den løser:
- **Tredje database.** PG + SpacetimeDB + Neo4j = tre systemer å
drifte, sikkerhetskopiere og overvåke. På én Hetzner VPS.
- **Mister PG-økosystemet.** pgvector (semantisk søk), fulltekstsøk,
JSONB, pg_cron, statistikk-aggregeringer — alt dette trenger vi
uansett, og det lever i PG. Med Neo4j trenger vi PG *i tillegg*.
- **Minnehungrig.** Neo4j anbefaler 8-16GB heap. På en delt VPS med
PG, SpacetimeDB, Whisper og LiteLLM er det for mye.
- **Lisens.** Community er AGPLv3. Enterprise-features (clustering,
avansert sikkerhet) krever betalt lisens.
### Apache AGE — Cypher i PG
Apache AGE er en PG-extension som legger til openCypher-støtte. Noder
og edges lagres i PG-tabeller, men spørres med Cypher-semantikk.
**Oppsider:**
- **Én database.** Ingen ny infrastruktur. Samme backup, connection
pooling, tooling, monitoring.
- **SQL + Cypher i samme spørring.** "Finn alle noder brukeren har
tilgang til via team-traversering, JOINet med fulltekstsøk og
pgvector." Det kan du ikke gjøre med en separat grafdatabase.
- **Null migrasjonskostnad.** Eksisterende nodes/edges-tabeller kan
eksponeres som graf. Legg til extension, ikke bytt database.
- **Økosystemet bevares.** pgvector, fulltekstsøk, JSONB, pg_cron —
alt fungerer side om side med Cypher-spørringer.
- **Produksjonsbruk.** Azure og EDB tilbyr AGE som managed extension.
**Nedsider:**
- **Ikke native graf.** Under panseret er det PG-tabeller. For svært
dype traverseringer (10+ hopp over millioner av noder) er Neo4j
raskere. Men de fleste spørringene våre er 1-5 hopp.
- **Yngre prosjekt.** Mindre community, tynnere dokumentasjon enn Neo4j.
- **Quirks.** Spørringer må være enten read-only eller write-only
(WITH-clause for overgang). Noen pg_upgrade-begrensninger.
## Beslutning
**PostgreSQL** som enhetlig datalager for noder og edges.
**Apache AGE** som planlagt utvidelse — ikke forpliktet fra dag én.
De fleste spørringer i dette systemet er grunne:
- "Vis noder med edge til denne brukeren" — 1 hopp
- "Sjekk tilgang via team" — 2 hopp
- "Finn alle kommunikasjonsnoder brukeren har tilgang til" — 2-3 hopp
- "Traverser kunnskapsgrafen mellom to emner" — 3-5 hopp
PG med rekursive CTEs håndterer 1-3 hopp utmerket. AGE legges til
når graftraversering faktisk blir en målbar flaskehals — ikke før.
AGE er en extension, ikke en migrering, så den kan boltes på uten
å endre eksisterende kode.
Pragmatisk rekkefølge:
1. **Nå:** PG med nodes/edges-tabeller og CTEs
2. **Når CTEs blir smertefulle:** Legg til AGE for Cypher-spørringer
3. **Usannsynlig:** Evaluer Neo4j hvis AGE ikke holder
## Lagmodell
```
┌─────────────────────────────────────┐
│ GUI (SvelteKit) │
│ Primitiver, visninger │
└────────┬──────────────────┬─────────┘
│ skriv │ les (sanntid)
│ │ direkte WebSocket
┌────────▼────────┐ ┌─────▼─────────┐
│ Maskinrommet │ │ SpacetimeDB │──┐
│ (Rust) │ │ (STDB) │ │
│ Orkestrering │ └───────────────┘ │
└──┬─────┬─────┬──┘ │
│ │ │ sync ↕ │
▼ ▼ ▼ │
┌─────┐┌─────┐┌─────┐┌─────────────┐ │
│ PG ││STDB ││ CAS ││ Whisper, │ │
│+AGE ││(skr)││ ││ LiteLLM, │ │
│ ││ ││ ││ LiveKit ... │ │
└─────┘└─────┘└─────┘└─────────────┘ │
```
### Skriv vs les — to stier med god grunn
**Skriv:** GUI → Maskinrommet (Rust) → tjenester (PG, STDB, CAS, ...)
All orkestrering, edge-logikk, ressursallokering og validering går
gjennom Rust. Maskinrommet bestemmer hva som skjer: hva som skrives
til PG, hva som speiles til SpacetimeDB, hva som lagres i CAS,
hvilke tjenester som trigges.
**Les (sanntid):** SpacetimeDB → GUI (direkte WebSocket)
SpacetimeDB sitt klient-SDK kobler seg direkte via WebSocket,
definerer SQL-subscriptions, og synkroniserer automatisk med lokal
cache — uten network round-trips for lesing (~10μs per transaksjon).
Å proxy dette gjennom Rust ville lagt til et hopp, serialisering og
kontekstbytte — en dårligere reimplementering av noe STDB gjør
optimalt. Den direkte lese-stien er en bevisst arkitekturbeslutning,
ikke et hull i lagmodellen.
**Les (tradisjonelt):** GUI → Maskinrommet (Rust) → PG
Søk, historikk, statistikk, arkiv — alt som ikke er sanntid går
gjennom Rust og PG. AGE-spørringer for graftraversering, pgvector
for semantisk søk, SQL for aggregeringer.
### PG er arkivet og grafen
Alle noder og edges lever i PG. AGE gir Cypher-spørringer for
traversering. Standard SQL for alt annet (aggregeringer, fulltekstsøk,
JOINs mot pgvector). Én database, to spørrespråk som utfyller
hverandre.
### SpacetimeDB er sanntidslaget
Aktive noder og edges speilet til SpacetimeDB for live-oppdateringer.
Ting som er "nå" lever i SpacetimeDB. Ting som er "ferdig" lever kun
i PG. Ingen eierskapskonflikt — SpacetimeDB er en live-visning av
en delmengde av PG-grafen.
### CAS er binærlageret
Lyd, bilde, video lagres content-addressable utenfor PG. Noder i
PG peker på CAS-hasher. Pruning-regler basert på edges, aksesslog
og modalitet (se maskinrommet).
## Migreringsstrategi
### Fase 1: AGE på eksisterende PG
Installer Apache AGE extension. Eksponer eksisterende nodes/edges-
tabeller som graf. Ingen endring for resten av systemet — bare en
ny måte å spørre på.
### Fase 2: Konsolider til node+edge-modellen
Migrer meldingsboks, kanban, kalender etc. til den universelle
node+edge-modellen gradvis. Gamle tabeller kan leve som views
over grafen i overgangsperioden.
### Fase 3: Parallell prototype
Det gamle systemet kjører uforstyrret. En ny frontend-prototype
bygges ved siden av som bruker AGE-grafen direkte. Live sync fra
gammelt til nytt via enveis oppdateringer. Testbrukere leker i
det nye, produksjon er trygt i det gamle.
### Fase 4: Cutover
Når det nye er godt nok, flyttes trafikk over. Det gamle systemet
kan kjøre som read-only fallback en periode.
## Spenninger og åpne spørsmål
- **AGE-modenhet.** Prosjektet er aktivt og støttet av Azure/EDB,
men community er mindre enn Neo4j. Risikoen er håndterbar fordi
dataene alltid er PG-tabeller — AGE er bare et spørrelag.
- **SpacetimeDB-synk.** Hvordan synkes noder/edges mellom PG (AGE)
og SpacetimeDB effektivt? Trenger en sync-mekanisme som forstår
"aktiv delmengde."
- **Skjemadesign.** Én node-tabell med alle typer innhold — hva er
felles kolonner vs edge-metadata vs JSONB-payload? Trenger
gjennomtenkt skjema som balanserer fleksibilitet og spørrbarhet.
## Forhold til andre retninger
- [Universell input og mottak](universell_input.md) — noder og edges
er den underliggende datamodellen for alle tre primitiver
- [Maskinrommet](maskinrommet.md) — CAS og pruning lever her, AGE
brukes for edge-drevet ressursorkestrering
- [Bruker, ikke workspace](bruker_ikke_workspace.md) — tilgang via
graf-traversering (AGE) i stedet for workspace-membership-tabeller
- [Rom, ikke forum](rom_ikke_forum.md) — to-lags-modellen:
SpacetimeDB for nåtid, PG+AGE for arkiv og graf