# Datalaget

**Status: Besluttet. Revidert mars 2026 — SpacetimeDB fjernet.**

> PostgreSQL er eneste datakilde. Sanntid via PG `LISTEN/NOTIFY`
> og WebSocket i vaktmesteren. CAS lagrer binærdata.
> Apache AGE legges til ved behov for Cypher-traverseringer.

## Lagmodell

```
GUI (SvelteKit)
  │ skriv                    │ les (sanntid, WebSocket)
  ▼                          ▼
Vaktmesteren (Rust)         Vaktmesteren ──WebSocket──→ GUI
  │ validering                 ▲
  └──→ PostgreSQL ──NOTIFY──→──┘
```

### Skrivestien
GUI → vaktmesteren → validering → PG. Frontend oppdateres via
WebSocket-push utløst av PG NOTIFY.

### Lesestien (sanntid)
PG → vaktmesteren → WebSocket → GUI.
Vaktmesteren holder en in-memory cache av aktive subscriptions
og pusher relevante endringer til tilkoblede klienter.

### Lesestien (tunge spørringer)
GUI → vaktmesteren → PG.
Fulltekstsøk, pgvector, statistikk, AGE-traverseringer.

## PostgreSQL — eneste datakilde

Én sannhetskilde. Ingen synk, ingen konsistensproblemer:

- **Sanntid:** `LISTEN/NOTIFY` → vaktmesteren → 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. Vaktmesteren lytter og videresender:

```
PG: NOTIFY node_changed, '{"id":"abc","kind":"content"}'
  → Vaktmesteren 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 vaktmesteren

Vaktmesteren 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

Én retning (PG → klient), ingen reducer-logikk, ingen
konsistensproblemer.

## Historikk: SpacetimeDB (fjernet mars 2026)

SpacetimeDB ble brukt som sanntidslag i v1/prototype. Det løste
sanntid elegant, men for produksjon på én server skapte det
unødvendig kompleksitet:

- **Synk-kompleksitet.** PG ↔ STDB synk var en egen feilkategori.
- **Dobbelt vedlikehold.** STDB-modul med reducers måtte holdes i
  synk med PG-skjema.
- **Ekstra SPOF.** Enda en tjeneste å overvåke og restarte.
- **Unødvendig for skalaen.** PG LISTEN/NOTIFY + WebSocket gir
  ~5ms latency — umerkelig forskjell for brukere.

SpacetimeDB ble faset ut i fire steg: WebSocket-lag, frontend-
migrering, fjern skrivestien, fjern alt. Se erfaringsdocs for
lærdommer: `docs/erfaringer/spacetimedb_integrasjon.md`.

## 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 / Vaktmesteren](maskinrommet.md) — CAS-pruning,
  edge-drevet ressursorkestrering, validering før skriving,
  WebSocket-endepunkt for sanntid
