server/ARCHITECTURE.md

12 KiB

Sidelinja - Architecture Decision Record & System Overview

Dette dokumentet definerer den overordnede arkitekturen, teknologistacken og datamodellen for Sidelinja-suiten. AI-agenter (som Claude Code) SKAL lese og forstå dette dokumentet før de foreslår endringer, skriver kode eller gjør arkitektoniske valg.

1. Visjon og Konsept

Sidelinja er ikke bare en podcast-host; det er et redaksjonelt operativsystem og en kunnskapsgraf. Målet er å bygge en plattform som sømløst integrerer research, asynkron kommunikasjon (chat), sanntids innspilling (Lyd/Video) og automatisert publisering. Visjonen inkluderer også at plattformen skal fungere som en "live co-host" (virtuell assistent) under innspilling ved å boble opp relevant informasjon fra kunnskapsgrafen i sanntid. Systemet er bygget for full datakontroll, eierskap og minimal bruk av lukkede tredjepartstjenester.

2. Infrastruktur og DevOps

  • Produksjonsserver: Hetzner VPS (Ubuntu, 8 vCPU, 16 GB RAM). Kapasiteten er tilstrekkelig for nåværende behov. Ved behov kan VPS-en dobles (16 vCPU, 32 GB). Mest CPU-krevende tjenester er faster-whisper og LiveKit under samtidig bruk — disse bør overvåkes først ved kapasitetsproblemer.
  • Orkestrering: Docker / Docker Compose. Alle tjenester kjører i isolerte containere på et internt Docker-nettverk.
  • Reverse Proxy & Webserver: Caddy. Håndterer all innkommende trafikk for flere domener, automatisk HTTPS (Let's Encrypt), og ruting til interne containere. Port 80/443 er de eneste portene som er eksponert mot internett.
  • Domener:
    • sidelinja.org — Hovedapplikasjon (SvelteKit, media, SpacetimeDB, LiveKit)
    • auth.sidelinja.org — Authentik SSO (felles for alle domener)
    • git.sidelinja.org — Forgejo
    • vegard.info — Separat nettsted, deler SSO med Sidelinja
  • Kildekode og CI/CD: Forgejo (Selv-hostet Git). All kode og konfigurasjon lever her.
  • Utvikling og Utrulling (Claude Code Workflow): All prototyping og koding skjer lokalt i WSL2 (Ubuntu) på en lokal Windows 11-maskin. Når koden er testet og klar, pusher AI-agenten (Claude Code) til Forgejo. Deretter logger agenten seg på produksjonsserveren via SSH for å hente koden, trigge kompilering og starte tjenestene på nytt. Regelen "ikke programmere i produksjon" betyr utelukkende at redigering av kildekode og "prøving og feiling" hører hjemme lokalt, ikke live på serveren.

2.1 Serverstruktur og Backup-strategi (Produksjon)

All persistent data, konfigurasjon og kildekode monteres via Docker Bind Mounts til en fast struktur på vertssystemet, typisk /srv/sidelinja/. Dette muliggjør granulert backup.

/srv/sidelinja/
├── docker-compose.yml       # Orkestrering
├── .env                     # Miljøvariabler (IKKE i Git)
├── config/                  # Konfigurasjonsfiler (Caddy, Authentik, etc.)
├── data/                    # Databaser (Postgres, SpacetimeDB, Forgejo) -> KRITISK BACKUP
├── media/                   # Lydfiler (podcast, råopptak) -> KRITISK BACKUP
└── logs/                    # Caddy access-logger, app-logger -> SEPARAT/LANGTIDS BACKUP

Målrettet backup: Mappen logs/ ekskluderes fra den daglige snapshot-backupen for å spare plass, men rulleres og arkiveres separat for fremtidig dataanalyse.

2.2 Lokalt Utviklingsmiljø (Dev Replika)

For å sikre smidig lokal utvikling i WSL2, bygger vi en nøyaktig replika av produksjonsmiljøet, men optimalisert for utviklingshastighet (Hot Reloading). Komplett steg-for-steg oppsett finnes i docs/setup/lokal.md, produksjonsoppsett i docs/setup/produksjon.md.

  • Docker Compose Dev: Vi bruker en egen docker-compose.dev.yml som spinner opp lokale instanser av databasene (PostgreSQL, SpacetimeDB) og LiveKit. Volumene for disse er lokale og flyktige/seedede.
  • SvelteKit HMR: SvelteKit-klienten kjøres utenfor Docker under aktiv utvikling (ved bruk av npm run dev i WSL2). Dette sikrer at Hot Module Replacement (HMR) fungerer lynraskt når kode endres.
  • Lokal Ruting: En lokal Caddy-instans ruter trafikk fra localhost til SvelteKit, SpacetimeDB og LiveKit, med self-signed sertifikater (local_certs) for sikker kontekst (WebRTC).
  • Forgejo: Kjører ikke lokalt. Push direkte til produksjons-Forgejo fra WSL2.

3. Teknologistack

Vi følger et "Best tool for the job"-prinsipp, med en sterk preferanse for minnesikkerhet, ytelse og rene grensesnitt.

  • Backend/Automasjon: Rust. Brukes som bakgrunnsworkers (jobbkø), logg-parsing og SpacetimeDB-moduler. Rust er ikke en API-server — SvelteKit server-side håndterer all HTTP-kommunikasjon og PG-tilgang direkte (se docs/features/api_grensesnitt.md).
  • Frontend / UI: SvelteKit (med TypeScript). Bygges som en PWA. Valgt for ytelse og enkel integrasjon med WebRTC og vanilla JS-biblioteker.
  • Sanntids Lyd/Video: LiveKit (Selv-hostet). Håndterer WebRTC, fler-bruker videochat og opptak i det virtuelle "studioet".
  • AI / Prosessering: faster-whisper (lokal transkripsjon) og OpenRouter (Claude-modeller for tekstanalyse).
  • SSO / Autentisering: Authentik (Selv-hostet). Sentralisert rollestyring.

4. Den To-delte Databasestrategien

  1. PostgreSQL (Historikk & Kunnskapsgraf): Én sentralisert instans i Docker for brukerkontoer, Git-metadata, aggregert statistikk og Kunnskapsgrafen (Artikler, Faktoider).
  2. SpacetimeDB (Sanntid & Arbeidsflyt): In-memory database for live chat, status på episoder, og live-oppdateringer i studio. Klienten (Svelte) lytter direkte på SpacetimeDB. Strategisk avhengighet, men all persistent data synkes til PostgreSQL — ved eventuelt bortfall kan sanntidslaget erstattes uten tap av data.
  3. Synkronisering: Event-drevet med ~5 sek forsinkelse. SpacetimeDB er autoritativ for sanntidsdata (chat, kanban), PostgreSQL for persistent data (kunnskapsgraf, metadata). Detaljer i docs/features/synkronisering.md.

5. Datamodell: Kunnskapsgrafen

Systemet er bygget rundt Temaer og Aktører, ikke episoder. Dette bygger et asynkront research-arkiv. Alle entiteter arver UUID fra en felles nodes-supertabell som gir ekte FK-integritet i grafmodellen (detaljer i docs/features/kunnskapsgraf_og_relasjoner.md).

  • Tema (Saker): Levende konsepter ("Skolepolitikk").
  • Aktør (Entity): Personer eller organisasjoner ("Jonas Gahr Støre").
  • Faktoide (Factoid): En atomisk bit med informasjon koblet til Aktører/Temaer ("Søkte jobb i AP i 2011").
  • Episode: Et tidsbegrenset prosjekt ("Episode 42") som samler et utvalg av aktuelle Temaer.
  • Segment: En tidsavgrenset del av en episode med egen transkripsjon, koblet til Temaer/Aktører i grafen. Muliggjør presise oppslag ("hva sa vi om X i Episode 42?").
  • Research-klipp: Råtekst renset av AI, koblet til Temaer/Aktører.

Transkripsjoner: Git (Forgejo) er kilde til sannhet (redigerbar, sporbar). PostgreSQL er søkeindeks (full-text, koblet til grafen). Endringer i Git reimporteres automatisk via Forgejo webhook.

6. Podcast Hosting og Distribusjon

  • Lagring: MP3-filer lagres flatt i /srv/sidelinja/media/. Ingen lydfiler i databaser.
  • Servering: Caddy serverer media-mappen. MÅ ha Accept-Ranges: bytes aktivert for podcast-streaming.
  • RSS-Feed: Genereres av SvelteKit og leveres statisk eller dynamisk med aggressiv caching.

7. Planlagte Funksjoner (Feature Ideas)

Dette er hovedkonseptene plattformen skal støtte. Merk: Detaljerte tekniske spesifikasjoner, flytskjemaer og datastrukturer for hver av disse ligger i mappen docs/features/.

  • Live AI-Assistent i Studio: Sanntidstranskripsjon via mikrofonene som lytter etter nøkkelord (Named Entity Recognition). Gjør asynkrone oppslag i PostgreSQL og dytter relevante "Faktoider" live til Svelte-grensesnittet via SpacetimeDB mens programlederne snakker.
  • AI Research-Klipper ("Ctrl+A workflow"): Et verktøy der redaksjonen limer inn rotete nyhetsartikler. AI-en (OpenRouter) renser, oppsummerer, og trekker ut Aktører og Faktoider som lagres i Kunnskapsgrafen.
  • Produktivitetssuiten: En Svelte/SpacetimeDB-basert flate for Kanban-styring av episoder, trådet chat knyttet til Temaer, og kollaborative show notes.
  • Valgomat: En publikumsrettet, avansert og interaktiv valgomat drevet av SpacetimeDB for umiddelbar respons og vekting av svar.
  • Podcast-Statistikk (Privacy First): Batch-prosessering i Rust som tygger Caddy JSON-logger, dedupliserer lyttere, fjerner bots og lagrer ferdig statistikk i PostgreSQL.
  • Podcastfabrikken: System for automatisk og manuell publisering (Whisper transkripsjon, metadata via OpenRouter) og versjonshåndtering/cache-busting ved oppdatering av eksisterende episoder.

8. Bygge-rekkefølge (Avhengighetskart)

Lag 1 — Fundament (ingen avhengigheter):
  ├── PostgreSQL-skjema (nodes, graph_edges, job_queue)
  ├── SpacetimeDB grunnoppsett
  └── SvelteKit skjelett med Authentik-integrasjon

Lag 2 — Kjernekomponenter (krever Lag 1):
  ├── Jobbkø-worker (Rust)
  ├── Kunnskapsgraf CRUD (SvelteKit server-side)
  └── Produktivitetssuiten: Chat + Kanban (SpacetimeDB ↔ PG synk)

Lag 3 — Features (krever Lag 2):
  ├── AI Research-Klipper (kunnskapsgraf + jobbkø)
  ├── Podcastfabrikken (jobbkø + episoder/segmenter)
  └── Podcast-Statistikk (jobbkø + episoder)

Lag 4 — Avansert (krever Lag 3):
  ├── Live AI-Assistent (fylt kunnskapsgraf + LiveKit + Whisper)
  └── Valgomat (selvstendig, lav prioritet)

9. Observabilitet

9.1 Helse

Alle Docker-containere skal ha healthcheck definert i docker-compose.yml:

  • PostgreSQL: pg_isready
  • SpacetimeDB: TCP-sjekk mot intern port
  • Caddy: curl -f http://localhost/health
  • SvelteKit: curl -f http://localhost:3000/health
  • Rust Workers: Heartbeat-rad i job_queue (en worker_heartbeat-jobb som re-enqueuer seg selv hvert minutt — fravær betyr død worker)

9.2 Logging

  • Format: Strukturert JSON fra alle komponenter (Rust, SvelteKit, Caddy)
  • Plassering: /srv/sidelinja/logs/ med undermapper per tjeneste
  • Rotasjon: Standard Linux logrotate, daglig rotasjon, 30 dagers retensjon
  • Caddy podcast-logger behandles separat av statistikk-workeren (se docs/features/podcast_statistikk.md)

9.3 Jobbkø-overvåking

  • Admin-visning i SvelteKit som viser job_queue-status (pending, running, error-count)
  • Feilede jobber (status = 'error') poster automatisk en varslingsmelding til et dedikert system-tema i Produktivitetssuiten (intern chat), slik at redaksjonen ser det i sin daglige arbeidsflate

9.4 Ingen eksterne tjenester

All overvåking og varsling skjer internt i Sidelinja-suiten. Ingen avhengighet til Discord, Slack eller andre tredjepartstjenester.

10. AI Agent Guidelines (Instrukser for Claude Code)

  • Start her: Når du settes til å bygge en ny komponent, sikre alltid at det lokale utviklingsmiljøet (docker-compose.dev.yml) kjører først.
  • Dokumentasjonsstandard: Når du skal implementere en ny funksjon (feature), sjekk ALLTID om det finnes et dokument i docs/features/<feature-navn>.md først. Oppdater disse dokumentene hvis arkitekturen for funksjonen endres.
  • Ingen "gh" CLI: Vi bruker Forgejo. For Pull Requests/Issues, bruk tea CLI.
  • Deployment: Kod og test lokalt i WSL. Push til Forgejo, logg inn via SSH for å pulle kode og restarte containere/tjenester (docker compose up -d).
  • Asynkron AI: Tyngre jobber (Whisper, OpenRouter) skal aldri blokkere web-forespørsler. Alle bakgrunnsjobber kjøres via den felles PostgreSQL-baserte jobbkøen (se docs/features/jobbkø.md).
  • Sikkerhet: Forsøk aldri å eksponere databaseporter ut mot internett i Docker Compose-filer (hverken lokalt eller i prod). Port 80/443 (Caddy) er de eneste inngangsportene.