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

7.7 KiB

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).