# 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. Frontend bruker portvokterens WebSocket for sanntid. Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet → PG. ## 2. Kommunikasjonskart ``` ┌─────────────────────────────────────────────────────────────┐ │ Brukerens nettleser (SvelteKit klient) │ └──────────┬──────────────────────┬───────────────────────────┘ │ │ │ HTTP (intensjoner) │ WebSocket (sanntid) ▼ ▼ ┌──────────────────────────────────────┐ │ Maskinrommet / Portvokteren (Rust) │ │ axum HTTP API + WebSocket │ │ │ │ Ansvar: │ │ - Validere intensjoner │ │ - Skrive til PG │ │ - Orkestrere bakgrunnsjobber │ │ - Tunge spørringer │ │ - Sanntid via PG LISTEN → WS │ └──┬─────────────┬─────────────────────┘ │ │ ▼ ▼ ┌─────┐ ┌─────────────┐ │ PG │ │ Whisper, │ │ │ │ LiteLLM, │ │ │ │ LiveKit ... │ └─────┘ └─────────────┘ ``` ## 3. Ansvarsfordeling | Komponent | Rolle | Snakker med | |---|---|---| | **SvelteKit (klient)** | UI, brukerinteraksjon | Maskinrommet (HTTP + WS) | | **Maskinrommet (Rust)** | Intensjons-API, orkestrering, sanntid, tunge spørringer | PG, CAS, Whisper, LiteLLM | | **PostgreSQL** | Eneste datakilde, sanntid via LISTEN/NOTIFY | Maskinrommet (SQL) | ## 4. Viktige avklaringer - **Maskinrommet er HTTP API + WebSocket-server** (axum). Frontend sender intensjoner via HTTP, mottar sanntid via WebSocket. - **Maskinrommet eier alle skrivinger.** Frontend skriver aldri direkte til PG. - **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-tilkobling. ### WebSocket (sanntid, oppgave 22.1–22.2) - `GET /ws?token=` — 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, access og mixer_channels brukeren kan se. - Deretter: strømmer berikede events (`node_changed`, `edge_changed`, `access_changed`, `mixer_channel_changed`), filtrert på brukerens tilgangsmatrise (node_access). - Berikede events: INSERT/UPDATE-events inneholder full raddata fra PG (ikke bare ID), slik at frontend kan oppdatere stores direkte uten ekstra API-kall. - Kilder: PG LISTEN/NOTIFY-triggere på nodes, edges, node_access og mixer_channels. - Ved lag (klient sakker etter): sender full resync (ny initial_sync). ### Autentiserte (krever `Authorization: Bearer `) - `GET /me` — Returnerer autentisert brukers `node_id` og `authentik_sub`. - `POST /intentions/create_node` — Opprett node. Skriv til PG, returner `node_id` umiddelbart. - Body (JSON): `{ node_kind?, title?, content?, visibility?, metadata? }` - Defaults: `node_kind="content"`, `visibility="hidden"`, andre felter tomme - Respons: `{ node_id: "" }` - `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: "" }` - `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: "" }` - `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: "" }` - `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), 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 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" }` ### Mixer-kanaler (oppgave 22.2) Delt mixer-tilstand i LiveKit-rom. Skriver til PG `mixer_channels`-tabell; NOTIFY-trigger propagerer til WS. - `POST /intentions/create_mixer_channel` — Opprett mixer-kanal for deltaker i rom. - Body: `{ room_id, target_user_id }` - `POST /intentions/set_gain` — Sett gain (0.0–1.5) for en kanal. - Body: `{ room_id, target_user_id, gain }` - `POST /intentions/set_mute` — Mute/unmute en kanal. - Body: `{ room_id, target_user_id, is_muted }` - `POST /intentions/toggle_effect` — Toggle en lydeffekt. - Body: `{ room_id, target_user_id, effect_name }` - `POST /intentions/set_mixer_role` — Sett rolle (editor/viewer) for deltaker. - Body: `{ room_id, target_user_id, role }` ## 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 via `job_queue`-tabellen (se `docs/infra/jobbkø.md`). > **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`).