synops/docs/infra/api_grensesnitt.md
vegard d7a9f3816d Fullfør oppgave 22.1: WebSocket-lag med PG LISTEN/NOTIFY og frontend dual-tilkobling
Backend:
- ws.rs: Fikset WS auth via query-param (browser kan ikke sende headers ved WS upgrade)
- auth.rs: Gjort decoding_key pub for gjenbruk i ws-modulen

Frontend:
- pg-ws.svelte.ts: Ny PG WebSocket-klient med auto-reconnect og event-logging
- index.ts: Eksporterer pgWsConnect/pgWsDisconnect/pgWsState
- +layout.svelte: Kobler til PG WS i parallell med STDB for verifisering

Docs:
- api_grensesnitt.md: Dokumentert /ws endepunkt og sanntidsarkitektur
- tasks.md: Merket 22.1 som ferdig

Deploy: Krever restart av maskinrommet + rebuild av frontend.
2026-03-18 12:01:10 +00:00

141 lines
7.7 KiB
Markdown

# Infrastruktur: API-grensesnitt og Tjenesteansvar
## 1. Konsept
Frontend sender **intensjoner** til maskinrommet (Rust/axum HTTP API).
Maskinrommet eier alle skrivinger: det validerer, skriver til PG,
og orkestrerer konsekvenser.
Sanntid: PG LISTEN/NOTIFY → maskinrommet → WebSocket `/ws` → frontend.
SpacetimeDB er under utfasing — frontend kobler til begge i parallell
under verifiseringsfasen (Fase M1).
Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet → PG.
## 2. Kommunikasjonskart
```
┌─────────────────────────────────────────────────────────────┐
│ Brukerens nettleser (SvelteKit klient) │
└──────────┬──────────────────────┬───────────────────────────┘
│ │
│ HTTP (intensjoner) │ WebSocket (sanntid)
▼ ▼
┌──────────────────────┐ ┌──────────────────┐
│ Maskinrommet (Rust) │ │ SpacetimeDB │
│ axum HTTP API │ │ │
│ │ │ - Hele grafen │
│ Ansvar: │ │ - Push til │
│ - Validere │ │ klienter │
│ - Skrive STDB+PG │ │ │
│ - Orkestrere │ └──────────────────┘
│ - Tunge spørringer │
│ - Bakgrunnsjobber │
└──┬─────┬─────┬───────┘
│ │ │
▼ ▼ ▼
┌─────┐┌─────┐┌─────────────┐
│ PG ││STDB ││ Whisper, │
│ ││ ││ LiteLLM, │
│ ││ ││ LiveKit ... │
└─────┘└─────┘└─────────────┘
```
## 3. Ansvarsfordeling
| Komponent | Rolle | Snakker med |
|---|---|---|
| **SvelteKit (klient)** | UI, brukerinteraksjon | Maskinrommet (HTTP), SpacetimeDB (WS) |
| **Maskinrommet (Rust)** | Intensjons-API, orkestrering, tunge spørringer | PG, SpacetimeDB, CAS, Whisper, LiteLLM |
| **SpacetimeDB** | Sanntids state, push til klienter | Klienter (WS), maskinrommet (skriver) |
| **PostgreSQL** | Persistent arkiv, søk, statistikk | Maskinrommet (SQL) |
## 4. Viktige avklaringer
- **Maskinrommet er en HTTP API-server** (axum). Frontend sender intensjoner hit.
- **Maskinrommet eier alle skrivinger.** Frontend skriver aldri direkte til PG eller STDB.
- **SpacetimeDB nås direkte** fra klienten via WebSocket for lesing (sanntid).
- **SvelteKit er et rent frontend-prosjekt.** Ingen server-side PG-tilgang.
- **Bakgrunnsjobber** (Whisper, LLM, TTS) orkestreres av maskinrommet, aldri direkte fra frontend.
## 5. Implementerte endepunkter
### Offentlige
- `GET /health` — Helsesjekk. Verifiserer PG- og STDB-tilkobling.
### WebSocket (sanntid, oppgave 22.1)
- `GET /ws?token=<JWT>` — WebSocket-oppgradering for sanntidsstrøm.
Token sendes som query-parameter (nettlesere støtter ikke custom headers ved WS upgrade).
- Ved tilkobling: sender `initial_sync` med alle noder, edges og access brukeren kan se.
- Deretter: strømmer `node_changed`, `edge_changed` og `access_changed` events,
filtrert på brukerens tilgangsmatrise (node_access).
- Kilder: PG LISTEN/NOTIFY-triggere på nodes, edges og node_access-tabellene.
- Events er JSON med `{ "type": "node_changed"|"edge_changed"|"access_changed", ... }`.
### Autentiserte (krever `Authorization: Bearer <JWT>`)
- `GET /me` — Returnerer autentisert brukers `node_id` og `authentik_sub`.
- `POST /intentions/create_node` — Opprett node. Skriv til STDB (instant),
spawn async PG-skriving, returner `node_id` umiddelbart.
- Body (JSON): `{ node_kind?, title?, content?, visibility?, metadata? }`
- Defaults: `node_kind="content"`, `visibility="hidden"`, andre felter tomme
- Respons: `{ node_id: "<uuid>" }`
- `POST /intentions/create_edge` — Opprett edge mellom to noder.
Validerer at begge nodene eksisterer og at edge_type ikke er tom.
- Body (JSON): `{ source_id, target_id, edge_type, metadata?, system? }`
- Defaults: `metadata={}`, `system=false`
- Respons: `{ edge_id: "<uuid>" }`
- `POST /intentions/update_node` — Oppdater eksisterende node (partial update).
Krever tilgang: created_by eller owner/admin-edge.
- Body (JSON): `{ node_id, node_kind?, title?, content?, visibility?, metadata? }`
- Kun oppgitte felter endres, resten beholdes
- Respons: `{ node_id: "<uuid>" }`
- `POST /intentions/delete_node` — Slett node og tilhørende edges.
Krever tilgang: created_by eller owner/admin-edge.
- Body (JSON): `{ node_id }`
- Respons: `{ deleted: true }`
- `POST /intentions/update_edge` — Oppdater eksisterende edge (partial update).
Krever tilgang: created_by eller owner/admin-edge til source-noden.
- Body (JSON): `{ edge_id, edge_type?, metadata? }`
- Kun oppgitte felter endres, resten beholdes
- Respons: `{ edge_id: "<uuid>" }`
- `POST /intentions/delete_edge` — Slett en edge. Brukes bl.a. for avpublisering.
Krever tilgang: created_by eller owner/admin-edge til source-noden.
Ved fjerning av belongs_to-edge til publiseringssamling invalideres forside-cache.
- Body (JSON): `{ edge_id }`
- Respons: `{ deleted: true }`
- `POST /intentions/set_slot` — Sett slot-metadata (hero/featured/strøm) på
belongs_to-edge i publiseringssamling. Håndterer hero-erstatning og featured-overflow.
- Body (JSON): `{ edge_id, slot, slot_order?, pinned? }`
- Respons: `{ edge_id, displaced[] }`
### LiveKit / Sanntidslyd (oppgave 11.2)
- `POST /intentions/join_communication` — Koble til sanntidslyd i en kommunikasjonsnode.
Validerer deltaker-tilgang (owner/member_of/host_of-edge eller via alias).
Genererer LiveKit access token (JWT), oppretter rom i STDB, oppdaterer node-metadata.
- Body (JSON): `{ communication_id, role? }` (role: "publisher" | "subscriber", default "publisher")
- Respons: `{ livekit_room_name, livekit_token, livekit_url, identity, participants[] }`
- Frontend bruker `livekit_token` + `livekit_url` til å koble livekit-client SDK.
- `POST /intentions/leave_communication` — Forlat sanntidsrom.
Fjerner deltaker fra STDB live-rom.
- Body (JSON): `{ communication_id }`
- Respons: `{ status: "left" }`
- `POST /intentions/close_communication` — Steng sanntidsrom (krever owner/admin).
Fjerner alle deltakere, setter `live_status=ended` i metadata.
- Body (JSON): `{ communication_id }`
- Respons: `{ status: "closed" }`
### SpacetimeDB sanntidstabeller (LiveKit)
- `live_room` — Aktive rom. Felt: `room_id`, `communication_id`, `is_active`, `started_at`, `participant_count`.
- `room_participant` — Deltakere i rom. Felt: `id`, `room_id`, `user_id`, `display_name`, `role`, `joined_at`.
Frontend abonnerer på disse via SpacetimeDB WebSocket for sanntids deltakerliste.
## 6. Instruks for Claude Code
- Maskinrommet (`maskinrommet/`) er Rust-prosjektet med axum, tokio, sqlx.
- Intensjoner fra frontend → `POST /intentions/*` endepunkter i maskinrommet.
- Tunge spørringer fra frontend → `GET /query/*` endepunkter i maskinrommet.
- Frontend (SvelteKit) har ingen direkte databasetilgang.
- Bakgrunnsjobber trigges av maskinrommet, ikke via en separat jobbkø-tabell.
> **Historisk merknad:** Tidligere beskrev dette dokumentet en arkitektur
> der SvelteKit var web-API og Rust kun var workers. Denne ble erstattet
> av maskinrommet-arkitekturen (se `docs/retninger/maskinrommet.md`).