# Datalaget **Status: Besluttet. Revidert mars 2026 — SpacetimeDB fases ut.** > PostgreSQL er eneste datakilde. Sanntid via PG `LISTEN/NOTIFY` > og WebSocket i portvokteren. CAS lagrer binærdata. > Apache AGE legges til ved behov for Cypher-traverseringer. > SpacetimeDB fases ut — se migrasjonsplan. ## Lagmodell ``` GUI (SvelteKit) │ skriv │ les (sanntid, WebSocket) ▼ ▼ Portvokteren (Rust) Portvokteren ──WebSocket──→ GUI │ validering ▲ └──→ PostgreSQL ──NOTIFY──→──┘ ``` ### Skrivestien GUI → portvokteren → validering → PG. Frontend oppdateres via WebSocket-push utløst av PG NOTIFY. ### Lesestien (sanntid) PG → portvokteren → WebSocket → GUI. Portvokteren holder en in-memory cache av aktive subscriptions og pusher relevante endringer til tilkoblede klienter. ### Lesestien (tunge spørringer) GUI → portvokteren → PG. Fulltekstsøk, pgvector, statistikk, AGE-traverseringer. ## PostgreSQL — eneste datakilde Én sannhetskilde. Ingen synk, ingen konsistensproblemer: - **Sanntid:** `LISTEN/NOTIFY` → portvokteren → WebSocket - **Fulltekstsøk:** `tsvector` på `nodes.content` og `nodes.title` - **Semantisk søk:** pgvector for embedding-basert likhet - **Graftraversering:** rekursive CTEs, Apache AGE ved behov - **Statistikk:** aggregeringer, tidsserier - **Tilgangsmatrise:** `node_access` beregnet fra edges ### Apache AGE — ved behov De fleste spørringer er grunne (1-3 hopp) og håndteres av CTEs. AGE legges til som PG-extension når Cypher-semantikk faktisk trengs: 1. **Nå:** PG med nodes/edges-tabeller og CTEs 2. **Når CTEs blir smertefulle:** Legg til AGE 3. **Usannsynlig:** Evaluer Neo4j hvis AGE ikke holder AGE er en extension, ikke en migrering. ## CAS — binærlagring Lyd, bilde, video lagres content-addressable på disk. CAS-noder i grafen bærer metadata (`cas_hash`, `mime`, `size_bytes`). Selve biten lever utenfor PG. Pruning-regler basert på modalitet, edges og aksessmønstre. Se [maskinrommet](maskinrommet.md). ## Sanntid via PG LISTEN/NOTIFY PG har innebygd pub/sub. Portvokteren lytter og videresender: ``` PG: NOTIFY node_changed, '{"id":"abc","kind":"content"}' → Portvokteren mottar → Sjekker tilgangsmatrise: hvem skal se denne endringen? → Pusher til relevante WebSocket-tilkoblinger → Frontend oppdaterer reaktivt ``` ### Trigger i PG ```sql CREATE OR REPLACE FUNCTION notify_node_change() RETURNS trigger AS $$ BEGIN PERFORM pg_notify('node_changed', json_build_object( 'op', TG_OP, 'id', NEW.id, 'kind', NEW.node_kind )::text ); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER nodes_notify AFTER INSERT OR UPDATE ON nodes FOR EACH ROW EXECUTE FUNCTION notify_node_change(); ``` Tilsvarende for edges. ### WebSocket i portvokteren Portvokteren holder: - Map av tilkoblede klienter → brukerens node_id - Map av node_id → synlige noder (fra node_access) - Ved NOTIFY: filtrer på tilgang, push til relevante klienter Enklere enn STDB-synk: én retning (PG → klient), ingen reducer-logikk, ingen konsistensproblemer. ## Hvorfor SpacetimeDB fases ut SpacetimeDB var et godt eksperiment. Det løste sanntid elegant i prototype-fasen. Men for produksjon på én server: - **Synk-kompleksitet.** PG ↔ STDB synk er en egen feilkategori. Erfaringsdocs (adapter_moenster.md, spacetimedb_integrasjon.md) dokumenterer smerten. - **Dobbelt vedlikehold.** STDB-modul med reducers må holdes i synk med PG-skjema. Endring i nodes-tabellen → to steder. - **Ekstra SPOF.** Enda en tjeneste å overvåke, restarte, debugge. - **Unødvendig for skalaen.** PG LISTEN/NOTIFY + WebSocket gir ~5ms latency. STDB ga ~0.01ms. Forskjellen er umerkelig for brukere. - **CLI-verktøy forenkles.** Bare PG-tilkobling, ingen STDB-klient. ## Migrasjonsplan: STDB → PG LISTEN/NOTIFY ### Fase M1: WebSocket-lag i portvokteren ✅ Implementert LISTEN/NOTIFY-lytter og WebSocket-endepunkt i portvokteren. PG-triggers for nodes, edges og access. Frontend koblet til begge (STDB + nytt WS) i parallell. ### Fase M2: Frontend-migrering ✅ Frontend bruker nå kun portvokterens WebSocket. SpacetimeDB-klient fjernet. Reactive stores oppdateres direkte fra WS-meldinger. Berikede events: portvokteren henter full raddata fra PG etter NOTIFY (ikke bare ID) slik at stores kan oppdateres uten ekstra API-kall. Mixer-kanaler migrert fra STDB til PG-tabell med tilhørende NOTIFY-trigger og HTTP API-endepunkter. ### Fase M3: Fjern skrivestien til STDB Portvokteren slutter å skrive til SpacetimeDB. All skriving går kun til PG. NOTIFY-triggere er eneste push-mekanisme. ### Fase M4: Fjern STDB - Stopp SpacetimeDB Docker-container - Fjern STDB-modul (spacetimedb/) - Fjern STDB-klient fra portvokteren - Fjern STDB-avhengigheter fra frontend - Fjern synkroniserings-kode - Oppdater docs (synkronisering.md → arkiver) - Oppdater CLAUDE.md ### Fase M5: Opprydding - Slett erfaringsdocs som kun gjelder STDB - Oppdater alle docs-referanser til STDB - Fjern Docker-konfig for SpacetimeDB ## Forhold til andre retninger - [Noder er sentrum](bruker_ikke_workspace.md) — tilgangsmatrise beregnet fra edge-grafen, brukes for WebSocket-filtrering - [Universell input og mottak](universell_input.md) — noder og edges er datamodellen for alle tre primitiver - [Maskinrommet / Portvokteren](maskinrommet.md) — CAS-pruning, edge-drevet ressursorkestrering, validering før skriving, WebSocket-endepunkt for sanntid