# 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=<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, 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 <JWT>`)
- `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: "<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), 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`).
