SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet). Kode fjernet: - spacetimedb/ Rust-modul og spacetime.json - maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers) - frontend module_bindings/ (23 auto-genererte filer) - spacetimedb npm-avhengighet fra package.json - scripts/test-sanntid.sh (testet STDB-flyt) Infrastruktur: - Docker-container stoppet og fjernet fra docker-compose.yml - Caddy: fjernet /spacetime/* reverse proxy - maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler - .env.example: fjernet SpacetimeDB-seksjoner Dokumentasjon oppdatert: - CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell - docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg - docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet" - 37 andre docs oppdatert (features, concepts, infra, ops, retninger) - Alle kode-kommentarer med STDB-referanser oppdatert Verifisert: maskinrommet bygger og starter OK, frontend bygger OK, helsesjekk returnerer 200. Caddy reloadet.
7.9 KiB
7.9 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.
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_syncmed 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).
- Ved tilkobling: sender
Autentiserte (krever Authorization: Bearer <JWT>)
GET /me— Returnerer autentisert brukersnode_idogauthentik_sub.POST /intentions/create_node— Opprett node. Skriv til PG, returnernode_idumiddelbart.- Body (JSON):
{ node_kind?, title?, content?, visibility?, metadata? } - Defaults:
node_kind="content",visibility="hidden", andre felter tomme - Respons:
{ node_id: "<uuid>" }
- Body (JSON):
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>" }
- Body (JSON):
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>" }
- Body (JSON):
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 }
- Body (JSON):
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>" }
- Body (JSON):
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 }
- Body (JSON):
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[] }
- Body (JSON):
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_urltil å koble livekit-client SDK.
- Body (JSON):
POST /intentions/leave_communication— Forlat sanntidsrom. Fjerner deltaker fra live-rom.- Body (JSON):
{ communication_id } - Respons:
{ status: "left" }
- Body (JSON):
POST /intentions/close_communication— Steng sanntidsrom (krever owner/admin). Fjerner alle deltakere, setterlive_status=endedi metadata.- Body (JSON):
{ communication_id } - Respons:
{ status: "closed" }
- Body (JSON):
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 }
- Body:
POST /intentions/set_gain— Sett gain (0.0–1.5) for en kanal.- Body:
{ room_id, target_user_id, gain }
- Body:
POST /intentions/set_mute— Mute/unmute en kanal.- Body:
{ room_id, target_user_id, is_muted }
- Body:
POST /intentions/toggle_effect— Toggle en lydeffekt.- Body:
{ room_id, target_user_id, effect_name }
- Body:
POST /intentions/set_mixer_role— Sett rolle (editor/viewer) for deltaker.- Body:
{ room_id, target_user_id, role }
- Body:
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).