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.
237 lines
9.5 KiB
Markdown
237 lines
9.5 KiB
Markdown
# Arkitektur — Synops
|
|
|
|
## Visjon
|
|
|
|
Synops er en plattform for redaksjonelt arbeid og podcast-produksjon.
|
|
Ikke en webapp med features — en plattform med primitiver som kan bli
|
|
hva som helst. Sidelinja (podcastredaksjonen) er en tenant som bruker
|
|
Synops.
|
|
|
|
Alt er noder og edges i en graf. En bruker er en node. Et team er en
|
|
node. En mediefil er en node. Hva noe "er" bestemmes av edges, ikke
|
|
av noden selv. Maskinrommet eier alle skrivinger. Frontend leser
|
|
sanntidsoppdateringer via WebSocket (PG LISTEN/NOTIFY).
|
|
|
|
## Lagmodell
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ GUI (SvelteKit) — spatial canvas │
|
|
│ Verktøy-paneler i BlockShell │
|
|
│ Drag-and-drop mellom paneler │
|
|
└────────┬──────────────────┬─────────────┘
|
|
│ intensjoner │ les (sanntid)
|
|
│ │ WebSocket
|
|
┌────────▼──────────────────┘
|
|
│ Maskinrommet (Rust)
|
|
│ Orkestrerer + WebSocket-hub
|
|
│ PG LISTEN/NOTIFY → WS → GUI
|
|
└──┬───────────┬──┘
|
|
│ spawner │
|
|
▼ ▼
|
|
┌─────────┐ ┌─────┐┌─────┐┌─────────────┐
|
|
│ CLI- │ │ PG ││ CAS ││ Whisper, │
|
|
│ verktøy │ │ ││ ││ LiteLLM, │
|
|
│ (tools/)│ │ ││ ││ LiveKit ... │
|
|
└─────────┘ └─────┘└─────┘└─────────────┘
|
|
▲ Claude bruker de samme
|
|
```
|
|
|
|
### Skrivestien
|
|
GUI → intensjon → Maskinrommet (Rust) → PG → NOTIFY → WebSocket → GUI
|
|
|
|
Frontend sender intensjoner (ikke data). Maskinrommet validerer og
|
|
skriver til PG. PG NOTIFY-triggere sender endringer via WebSocket
|
|
til alle tilkoblede klienter i sanntid. Maskinrommet leser edges
|
|
og bestemmer hvilke tjenester som trigges.
|
|
|
|
### Lesestien (sanntid)
|
|
PG NOTIFY → Maskinrommet (WebSocket-hub) → GUI
|
|
|
|
Maskinrommet lytter på PG LISTEN/NOTIFY-kanaler og videresender
|
|
relevante endringer via WebSocket til tilkoblede klienter, filtrert
|
|
på tilgangsmatrisen.
|
|
|
|
### Lesestien (tunge spørringer)
|
|
GUI → Maskinrommet (Rust) → PG
|
|
|
|
Søk, statistikk, semantisk søk (pgvector), graftraversering
|
|
(AGE/Cypher).
|
|
|
|
## Datamodell
|
|
|
|
### Alt er noder
|
|
Én `nodes`-tabell. Alt er noder: brukere, team, meldinger, oppgaver,
|
|
notater, mediefiler, kommunikasjonsrom, samlings-noder. En bruker er
|
|
en node som tilfeldigvis kan logge inn.
|
|
|
|
Felles skjema: id, innhold, created_at, content_hash (→ CAS).
|
|
Modalitetsspesifikk metadata i JSONB.
|
|
|
|
### Edges definerer alt
|
|
Én `edges`-tabell. Edge-typer er frie strenger. Hva en node "er"
|
|
bestemmes utelukkende av dens edges:
|
|
|
|
- Node + edge til kanal = chatmelding
|
|
- Node + edge til board + status-edge = kanban-kort
|
|
- Node + edge til dato = kalenderoppføring
|
|
- Node + edge til kun bruker = privat notat
|
|
- Node uten edges = løs tanke
|
|
|
|
### Visninger er spørringer
|
|
Visninger er spørringer mot node-/edge-stores med edge-filtre:
|
|
- Chat = noder med kanal-edge, sortert på tid
|
|
- Kanban = noder med board-edge, gruppert på status
|
|
- Kalender = noder med dato-edge, på tidslinje
|
|
- Mottaksflate = noder med edge til deg, vektet på relevans
|
|
|
|
Ingen forhåndsdefinerte visningstyper. Nye visninger er nye filtre.
|
|
|
|
## Arbeidsflaten — spatial canvas
|
|
|
|
Brukergrensesnittet er et **spatial canvas** der verktøy (chat, kanban,
|
|
kalender, editor, studio) plasseres som paneler i en fri flate.
|
|
|
|
### Tre lag
|
|
1. **Personlig flate** — brukerens standard arbeidsflate
|
|
2. **Node-flate** — default-oppsett fra node-eier/traits
|
|
3. **Brukerens tilpasning** — overrides lagret per bruker per node
|
|
|
|
### Drag-and-drop som primær interaksjon
|
|
To retninger, to semantikker:
|
|
- **Dra ut av kontekst → ny node.** Innhold som dras fra et verktøy
|
|
til et annet skaper en ny node med `source_material`-edge tilbake
|
|
til originalen. Originalen er uendret.
|
|
- **Dra verktøy inn i kontekst → transformer.** Et verktøy (AI, editor)
|
|
som mottar en node anvender sin funksjon direkte på originalen.
|
|
|
|
Kompatibilitetsmatrisen bestemmer hva som kan dras hvor.
|
|
Ref: `docs/retninger/arbeidsflaten.md`, `docs/features/universell_overfoering.md`
|
|
|
|
### Arbeidsflaten er en node
|
|
Canvaset er selv en node med edges til panelene det viser. Layout
|
|
(posisjon, størrelse, z-index) er metadata på edgen. Arbeidsflater
|
|
kan nestes — en flate kan vises som panel i en annen.
|
|
|
|
## Input
|
|
|
|
Én universell input-komponent, gjenbrukt overalt. Fanger tekst, lyd,
|
|
bilde, AI, URL. Konteksten (hvor du er) bestemmer hvilke edges som
|
|
legges til. Output er alltid en node.
|
|
|
|
## Struktur uten workspaces
|
|
|
|
Ingen workspace-velger. Ingen `workspace_id`. Samlings-noder gir
|
|
struktur: et team er en samlings-node, et prosjekt er en samlings-node.
|
|
Du ser noder du har tilgang til via dine edges.
|
|
|
|
### Aliaser
|
|
En bruker kan ha alias-noder (f.eks. for ulike roller). Koblet med
|
|
system-edges som er usynlige for traversering.
|
|
|
|
## Maskinrommet
|
|
|
|
Rust-tjeneste som **orkestrerer**, ikke gjør arbeid selv.
|
|
|
|
Eier alle skrivinger. Frontend sender intensjoner, maskinrommet
|
|
validerer og utfører. Selve prosesseringen delegeres til CLI-verktøy
|
|
i `tools/` — maskinrommet spawner riktig verktøy fra jobbkøen.
|
|
|
|
Kjerneansvar (forblir i maskinrommet):
|
|
- Auth + tilgangskontroll
|
|
- Intentions (validering, edge-logikk, PG-skriving)
|
|
- Jobbkø (polling, retry, dead letter)
|
|
- CAS-forvaltning og pruning
|
|
|
|
Alt annet (transkribering, rendering, AI, lyd, RSS) er CLI-verktøy.
|
|
Claude bruker de samme verktøyene direkte fra terminalen — nyttig
|
|
for debugging, testing og utvikling.
|
|
|
|
Ref: `docs/retninger/unix_filosofi.md`
|
|
|
|
## Sikkerhet
|
|
|
|
### Synlighet (visibility)
|
|
Noder har en visibility-egenskap med fire nivåer:
|
|
- **hidden** — usynlig, kun tilgjengelig via system-edges
|
|
- **discoverable** — kan finnes, men innhold skjult
|
|
- **readable** — innhold lesbart for de med tilgang
|
|
- **open** — tilgjengelig for alle med traverseringssti
|
|
|
|
Traversering respekterer visibility. Du kan ikke følge edges gjennom
|
|
noder du ikke har lov til å se.
|
|
|
|
### Materialisert tilgangsmatrise
|
|
`node_access`-tabell som cacher beregnet tilgang fra edge-grafen.
|
|
Oppdateres ved edge-endring, ikke ved lesing. Rask oppslag — ingen
|
|
rekursiv graftraversering per forespørsel.
|
|
|
|
### Privat er default
|
|
Input uten mottaker-edge er automatisk privat. Ingen ser det.
|
|
Deling er å legge til edges.
|
|
|
|
## Datalag
|
|
|
|
### PostgreSQL
|
|
Eneste datakilde. Alle noder og edges. Fulltekstsøk,
|
|
pgvector (semantisk søk), JSONB. Apache AGE for Cypher ved behov.
|
|
PG LISTEN/NOTIFY-triggere sender sanntidsoppdateringer.
|
|
|
|
### CAS (Content-Addressable Store)
|
|
Binærdata (lyd, bilde, video) lagret med hash. TTL basert på
|
|
modalitet, edges og aksesslog. Generert innhold (TTS, thumbnails)
|
|
er en cache som regenereres on-demand.
|
|
|
|
## Driftsmodell: hybrid native + Docker
|
|
|
|
Egenutviklet kode kjører **native på hosten** via systemd.
|
|
Tredjepartstjenester kjører i **Docker**. Prinsipp: Docker for det vi
|
|
ikke bygger selv, native for det vi har full kontroll over.
|
|
|
|
**Native (systemd):** Caddy, maskinrommet (Rust), SvelteKit.
|
|
**Docker:** PostgreSQL, Authentik, LiteLLM, faster-whisper.
|
|
|
|
Maskinrommet og Caddy kjører native fordi de trenger direkte tilgang
|
|
til host-ressurser (CLI-verktøy, TLS-konfig). Docker-tjenester
|
|
eksponerer porter på localhost.
|
|
|
|
## Teknologivalg
|
|
|
|
| Rolle | Teknologi | Kjøremodus | Begrunnelse |
|
|
|-------|-----------|------------|-------------|
|
|
| Orkestrator | Rust | Native (systemd) | Orkestrerer CLI-verktøy, eier auth+edges, trenger host-tilgang |
|
|
| CLI-verktøy | Rust/Shell | Native | Prosessering (transcribe, render, audio, AI). Delt mellom maskinrommet og Claude |
|
|
| Frontend | SvelteKit | Native (systemd) | PWA, SSR, spatial canvas med verktøy-paneler |
|
|
| Database | PostgreSQL | Docker | Versjonsstyring, enkel oppgradering |
|
|
| Sanntid | PG LISTEN/NOTIFY + WebSocket | Native (i maskinrommet) | Ingen ekstra avhengighet, sanntid fra PG |
|
|
| Binærlagring | CAS (filsystem) | Native | Enkel, deduplisering, ingen ekstern avhengighet |
|
|
| AI Gateway | LiteLLM | Docker | Ferdig image, sjelden oppdatering |
|
|
| AI Agent | Claude Code CLI | Native | Chat-deltaker, spawnes av maskinrommet |
|
|
| STT | faster-whisper | Docker | Modellhåndtering, ferdig image |
|
|
| TTS | ElevenLabs (→ lokal) | — | Kommersiell start, lokal når kvaliteten holder |
|
|
| Auth | Authentik | Docker | Kompleks stack (server + worker + Redis) |
|
|
| Reverse proxy | Caddy | Native (systemd) | Auto-TLS, direkte tilgang til konfig |
|
|
| Lyd/video | LiveKit | Docker | WebRTC, self-hosted |
|
|
|
|
## Traits — samlingsnoder med evner
|
|
|
|
Samlingsnoder berikes med **traits** — navngitte evner som aktiverer
|
|
spesifikk funksjonalitet i frontend og backend. En samling med
|
|
`publishing`-trait blir et nettmagasin. Legg til `podcast` og den
|
|
blir et podcaststudio. Fjern `chat` og diskusjonsfunksjonen forsvinner.
|
|
|
|
Traits er komposisjon, ikke typer. Forhåndsdefinerte pakker
|
|
(nettmagasin, podcaststudio, redaksjon osv.) er bare snarveier for
|
|
vanlige kombinasjoner — brukeren kan tilpasse fritt etterpå.
|
|
|
|
Rendret innhold (HTML for publiserte artikler, feeds, OG-bilder)
|
|
lagres i CAS som avledede representasjoner. Caddy serverer direkte
|
|
fra disk uten å treffe applikasjonslagene.
|
|
|
|
Full spesifikasjon: `docs/primitiver/traits.md`
|
|
Publiseringsflyt: `docs/concepts/publisering.md`
|
|
|
|
## Retninger
|
|
|
|
Arkitekturen er basert på vedtatte retninger dokumentert i
|
|
`docs/retninger/`. Se `docs/retninger/README.md` for oversikt.
|