Oppdaterer dokumentasjon basert på tre eksterne arkitekturvurderinger: - RLS Leak Hunter med CI-test og audit-trigger (migration_safety.md) - pgvector-migrasjon flyttet til Lag 2, WAL-arkivering med pgBackRest (ARCHITECTURE.md, produksjon.md) - Off-site backup med rclone, Docker cgroups for workers (ARCHITECTURE.md, produksjon.md) - Kostnadskontroll i AI Gateway: workspace-budsjett, auto-fallback (ai_gateway.md) - Gjeste-token sikkerhetsdybde: ClamAV, rate limiting, auto-revoke (den_asynkrone_gjesten.md) - SpacetimeDB fase 1-vurdering: PG LISTEN/NOTIFY som mellomsteg (synkronisering.md) - Kritiske events (Aha-markører) flushes umiddelbart (synkronisering.md) - Ekstern helsesjekk, observability-utvidelser (ARCHITECTURE.md) - Tre nye forslag: Contradiction Detector, Auto-Highlight Reel, Audience Voice Memo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
26 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, 320 GB SSD). 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.
- CPU-ressursstyring:
faster-whisper(medium) bruker ~18 min på 30 min lyd og kan stjele CPU fra LiveKit under live-innspilling (risiko for audio glitches). To-lags beskyttelse:- Docker cgroups (harde grenser):
docker-compose.ymlskal settedeploy.resources.limitspå worker-containere: maks 4 CPU og 8 GB RAM for Whisper-workers, slik at LiveKit og PostgreSQL alltid har garantert kapasitet. - Applikasjonsnivå (dynamisk): Rust-workeren implementerer en "Resource Governor" som reduserer Whisper-tråder ytterligere (f.eks.
--threads 2) når et LiveKit-rom er aktivt. Sjekkes via LiveKit room-status i jobbkøen.
- Docker cgroups (harde grenser):
- Diskstrategi: 320 GB SSD fylles raskt med råopptak og MP3-er. Tre tiltak:
- Block Storage: Mediafiler serveres fra en separat Hetzner Block Storage-volum montert på
/srv/sidelinja/media/, skalerbart uavhengig av OS-disken. - S3-abstraksjon: SvelteKit sin filopplasting bør abstrahere lagring bak et S3-kompatibelt grensesnitt (Hetzner Object Storage eller Cloudflare R2), slik at vi kan flytte til ekstern lagring uten å endre applikasjonskode. Caddy kan proxy-e eller redirecte til S3 for servering.
- Arkiveringspolicy: Råopptak eldre enn 6 mnd flyttes automatisk til Object Storage via nattlig jobb. Kun ferdig-redigerte MP3-er beholdes lokalt for rask servering.
- Block Storage: Mediafiler serveres fra en separat Hetzner Block Storage-volum montert på
- 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— Forgejovegard.info— Separat nettsted, deler SSO med Sidelinja
- Kildekode og CI/CD: Forgejo (Selv-hostet Git). To repos:
sidelinja/server— app-kode, infra, arkitektur, configsidelinja/sidelinja— podcastinnhold (transkripsjoner, show notes, research)
- Utvikling og Utrulling: Kode skrives og testes lokalt i WSL2. Infrastruktur-config (docker-compose, Caddy, Authentik) endres direkte i prod. Deploy: push til Forgejo → SSH pull på server →
docker compose up -d --build.
2.1 Serverstruktur (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)
├── media/ # Lydfiler (podcast, råopptak)
└── logs/ # Caddy access-logger, app-logger
2.2 Dataklassifisering og backup-strategi
Alle data i Sidelinja faller i én av fire kategorier. Nye komponenter og features MÅ klassifisere sin data etter dette skjemaet.
Kategori 1: Kritisk — krever backup
Data som ikke kan gjenskapes. Tap = permanent informasjonstap.
| Data | Lagring | Backup |
|---|---|---|
| PostgreSQL (kunnskapsgraf, brukere, metadata, episoder) | data/postgres/ |
Daglig pg_dump + WAL-arkivering (PITR) |
| Lydfiler (MP3, råopptak) | media/ |
Daglig fil-backup |
.env (hemmeligheter) |
/srv/sidelinja/.env |
Manuell kopi, ikke i Git |
Kategori 2: Gjenskapbar fra Git
Data som lever i Forgejo og kan klones/pulles på nytt.
| Data | Lagring | Restore |
|---|---|---|
| Kildekode | Git (Forgejo) | git clone |
| Transkripsjoner (SRT master) | Git (Forgejo) | git clone |
| PG-skjema + migrasjoner | Git (Forgejo) | Kjør migrasjoner |
| Config-filer (Caddyfile, etc.) | Git (Forgejo) | git clone |
| Forgejo-data (repos, issues) | data/forgejo/ |
Daglig backup som sikkerhetsnett — kan gjenskapes men tidkrevende |
Kategori 3: Avledet — kan regenereres
Data som er deterministisk avledet fra kategori 1 eller 2. Tåler tap — regenereres automatisk.
| Data | Kilde | Regenerering |
|---|---|---|
| Ren tekst (transkripsjoner) | SRT i Git | Rust-worker parser SRT |
| Segmenter (tidsstemplet, grafkoblet) | SRT i Git | Rust-worker parser SRT |
| Full-text søkeindeks | SRT i Git | Rebuild fra Git |
| SRT → PG-cache | SRT i Git | Reimport via webhook |
| Redis-cache | Applikasjonsdata | Regenereres automatisk |
| Caddy-sertifikater | Let's Encrypt | Regenereres automatisk |
Kategori 4: Flyktig — tåler tap, TTL-styrt
Arbeidsdata med begrenset levetid. Ryddes automatisk.
| Data | Lagring | TTL | Formål |
|---|---|---|---|
| Live-transkripsjonslogg | PostgreSQL | 30 dager | Feilsøking av live-assistent |
| Caddy access-logger | logs/caddy/ |
90 dager | Podcast-statistikk (batch-prosesseres først) |
| Jobbkø-historikk (fullførte jobber) | PostgreSQL | 30 dager | Feilsøking |
| Whisper-modeller | .docker-data/ (lokal) |
Ingen TTL | Re-download fra HuggingFace ved behov |
Off-site backup (kritisk)
Lokal backup på samme server beskytter kun mot logiske feil (slettet fil, korrupt dump). Ved fysisk diskfeil eller nodefeil hos Hetzner tapes både produksjon og backup. Kategori 1-data må pushes ut av serveren:
| Data | Mål | Verktøy | Frekvens |
|---|---|---|---|
| PostgreSQL-dumper | Hetzner Object Storage (S3-kompatibel) | rclone sync |
Daglig etter pg_dump |
| Lydfiler (media/) | Hetzner Object Storage | rclone sync (inkrementell) |
Daglig |
.env |
Kryptert kopi i Object Storage | gpg -c + rclone |
Ved endring |
Retensjon off-site: 90 dager for PG-dumper, ubegrenset for media. Kostnad: ~€5/mnd for 100 GB på Hetzner Object Storage.
PostgreSQL WAL-arkivering (Point-In-Time Recovery)
Daglig pg_dump kl. 03:00 betyr opptil 24 timers datatap ved korrupsjon midt på dagen. For å redusere dette til minutter, settes opp kontinuerlig WAL-arkivering:
- Verktøy: pgBackRest eller WAL-G (foretrukket for S3-kompatibel lagring)
- Flyt: PostgreSQL streamer WAL-segmenter kontinuerlig til Hetzner Object Storage. Ved behov kan databasen gjenopprettes til et vilkårlig tidspunkt (PITR).
- Konfigurasjon:
archive_mode = on,archive_commandpeker på pgBackRest/WAL-G som pusher til S3. - Full backup: Ukentlig full backup via pgBackRest, daglige inkrementelle. WAL-segmenter fyller gapet.
- Recovery:
pgbackrest restore --target-time="2026-03-15 13:59:00"gjenoppretter til minuttet før krasjet. - Kostnad: Minimal — WAL-segmenter er komprimerte og kompakte. ~1-5 GB/mnd avhengig av skriveaktivitet.
Retningslinjer for nye komponenter
Når en ny feature eller komponent introduserer data:
- Klassifiser — hvilken kategori faller dataen i?
- Dokumenter — legg til i tabellen over
- Implementer TTL for kategori 4 — aldri la flyktig data vokse ubegrenset
- Aldri dupliser kilde — avledede data (kategori 3) skal kunne slettes og regenereres
- Aldri backup avledet data — det er bortkastet plass og skaper falsk trygghet
2.3 Lokalt Utviklingsmiljø
Det lokale miljøet (WSL2) er et kodeutviklingsmiljø, ikke en replika av prod. Infrastruktur-config (docker-compose, Caddy, Authentik) testes direkte i prod. Komplett oppsett: docs/setup/lokal.md.
- Docker Compose Dev:
docker-compose.dev.ymlspinner opp PostgreSQL, Redis, SpacetimeDB, Caddy, Whisper og AI Gateway lokalt. Volumene er flyktige (.docker-data/, gitignored). - Docker Compose Prod:
/srv/sidelinja/docker-compose.ymlkjører PostgreSQL, Redis, Caddy, Authentik, Forgejo og SvelteKit (web-container bygget fraweb/Dockerfile). - SvelteKit HMR: Kjøres utenfor Docker lokalt for rask iterasjon. I prod bygges som Docker-container med adapter-node.
- Rust Workers: Kompileres og kjøres lokalt med
cargo run. - AI Gateway / Whisper: Lokale instanser for eksperimentering og prompt-testing.
- Forgejo/Authentik: Kjører IKKE lokalt — push direkte til prod-Forgejo.
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/infra/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 LiteLLM (AI Gateway — sentralisert ruting til Gemini, Claude, Grok, OpenRouter). All AI-kode peker påhttp://ai-gateway:4000/v1, aldri direkte til leverandører. Sedocs/infra/ai_gateway.md. - SSO / Autentisering: Authentik (Selv-hostet). Sentralisert rollestyring.
4. Den To-delte Databasestrategien
- PostgreSQL (Historikk & Kunnskapsgraf): Én sentralisert instans i Docker for brukerkontoer, Git-metadata, aggregert statistikk og Kunnskapsgrafen (Artikler, Faktoider).
- SpacetimeDB (Sanntidsbuffer): In-memory database for live chat, status på episoder, og live-oppdateringer i studio. Klienten (Svelte) lytter direkte på SpacetimeDB. SpacetimeDB er en ren sanntidsbuffer — all data synkes til PostgreSQL innen ~5 sek. PostgreSQL-skjemaet dekker alle SpacetimeDB-tabeller, slik at SpacetimeDB kan erstattes med PG
LISTEN/NOTIFY+ SSE uten arkitekturendring. Sedocs/infra/synkronisering.md§1.1–1.2. - Synkronisering: Event-drevet med ~5 sek forsinkelse. SpacetimeDB er autoritativ for sanntidsdata (chat, kanban), PostgreSQL for persistent data (kunnskapsgraf, metadata). Detaljer i
docs/infra/synkronisering.md.
4.1 Workspace-modellen (Multi-tenancy)
Sidelinja er designet for multi-tenancy fra dag én. Hver organisasjon/podcast opererer i sin egen workspace — en streng datasilo som sikrer full isolasjon av kunnskap og arbeidsflyt.
Prinsipp: Ingenting deles på tvers
- PostgreSQL: Supertabellen
nodesoggraph_edgeshar obligatoriskworkspace_id. Row-Level Security (RLS) sikrer at spørringer aldri lekker data mellom workspaces. SvelteKit setterSET app.current_workspace_idved tilkobling. - SpacetimeDB: WebSocket-tilkoblinger bærer et
workspace_id-token. Modulen partisjonerer minnet og kringkaster kun til klienter i samme workspace. - Mediefiler: Lagres i
/srv/sidelinja/media/{workspace_slug}/. Caddy ruter basert på domene. - Transkripsjoner: Ett Forgejo-repo per workspace for SRT-filer.
- Jobbkø: Alle jobber i
job_queuemerkes medworkspace_id. Rust-workers kjører som superuser (bypasser RLS) og isolerer via applikasjonskode. - AI-prompts: Whisper
initial_promptog LLM system-prompts lagres iworkspaces.settings(JSONB) per workspace.
Tilgangsstyring
Authentik styrer gruppetilhørighet. SvelteKit mapper innlogget brukers Authentik-grupper til tilgjengelige workspaces via workspace_members-tabellen. UI-et har en global "Workspace-switcher" som tømmer lokal state (hard reset) ved bytte for å forhindre visuell datalekkasje.
Globale ressurser (unntak fra silo)
relation_types— deles på tvers (systemdefinerte relasjonstyper).users— Authentik-IDer er globale, workspace-tilhørighet styres viaworkspace_members.- Valgomat-modulen er publikumsrettet og opererer potensielt på tvers av workspaces. Eierskapsmodellen avklares ved implementering (se
docs/concepts/valgomaten.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 — eierskapsmodell:
- Git (Forgejo) er kilde til sannhet. Master-formatet er SRT (SubRip) — et etablert undertekstformat med tidsstempler som er redigerbart, diffbart og lett å parse. Whisper leverer SRT direkte (
response_format=srt). - PostgreSQL lagrer alt avledet fra SRT-kilden:
- Ren tekst — strippes fra SRT for lesbart publiseringsdokument
- Segmenter — tidsstemplede utdrag koblet til Aktører/Temaer i kunnskapsgrafen
- Full-text søkeindeks — for oppslag på tvers av episoder
- Flyt: Whisper → SRT → Git commit → Forgejo webhook → Rust-worker parser SRT → avledede formater i PG. SvelteKit serverer SRT fra Git og avledet innhold fra PG.
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: bytesaktivert for podcast-streaming. - RSS-Feed: Genereres av SvelteKit og leveres statisk eller dynamisk med aggressiv caching.
7. Planlagte Funksjoner
Detaljerte spesifikasjoner ligger i docs/concepts/ (brukeropplevelser) og docs/features/ (byggeklosser).
Konsepter (brukeropplevelser)
- Studioet: Podcast-innspilling med LiveKit, live AI faktoid-oppslag og Aha-markør.
- Møterommet: LiveKit-basert møterom med AI-referent, off-the-record, whiteboard, scratchpad og søkbar historikk.
- Redaksjonen: Daglig arbeidsflate med trådet chat (channels), Kanban, show notes og AI research-klipper.
- Podcastfabrikken: Automatisert publiseringspipeline — Whisper, AI-metadata, RSS, cache-busting.
- Kunnskapsgrafen: Visuell utforsking og redigering av kunnskapsnettverk (D3.js/Vis.js).
- Valgomaten: Publikumsrettet, crowdsourced valgomat med PCA og Sidelinja Explorer.
- Den Asynkrone Gjesten: Tidsbegrenset lenke til gjester for asynkrone lydopptak som lander i redaksjonens arbeidsflyt.
Features (byggeklosser)
Chat (channels), Kanban, Kalender, Notater/Scratchpad, Whiteboard, Live transkripsjon, Live AI (faktoid + referent), Visuell graf, AI Research-Klipper, Lydmeldinger & Diktering, Podcast-statistikk, Kunnskaps-Bridge (cross-workspace), Prompt-Laboratorium, Graf-vedlikehold (nattlig jobb som finner isolerte noder og foreslår koblinger basert på co-occurrence i transkripsjoner).
8. Bygge-rekkefølge (Avhengighetskart)
Lag 0 — Infrastruktur
- Produksjonsserver: PostgreSQL, Caddy, Authentik, Forgejo, Redis
- Lokalt utviklingsmiljo: docker-compose.dev.yml (PostgreSQL, Redis, Caddy)
- Rust toolchain (lokal + server)
- faster-whisper-server (lokal, testet med medium + prompt)
Lag 1 — Fundament (ingen avhengigheter)
- Workspace-modell (workspaces, workspace_members, RLS-policies)
- PostgreSQL-skjema (nodes m/workspace_id, graph_edges, job_queue, messages, channels, media_files)
- SpacetimeDB grunnoppsett (Docker, Rust WASM-modul, TypeScript-bindings)
- SvelteKit skjelett med Authentik-integrasjon + Workspace-switcher
- AI Gateway (LiteLLM) oppsett + config
- Git-repostruktur for transkripsjoner (ett repo per workspace)
Lag 2 — Kjernekomponenter (krever Lag 1)
- Jobbkø-worker (Rust)
- Kunnskapsgraf CRUD (SvelteKit server-side)
- pgvector-migrasjon (0005):
CREATE EXTENSION vector;+ embedding-kolonner på nodes — gjøres tidlig for å unngå smertefull migrasjon i Lag 4 - RLS Leak Hunter i CI (se
docs/setup/migration_safety.md) - [~] Chat med channels (PG-adapter + SpacetimeDB hybrid-adapter ferdig, sync-worker gjenstår)
- [~] Kanban (PG-adapter ferdig med drag & drop, redigeringsmodal, CRUD API. SpacetimeDB-sync gjenstår)
- [~] Kalender (PG-adapter ferdig med månedsvisning, fargekoder, heldags/tidshendelser. SpacetimeDB-sync gjenstår)
- [~] Notater/Scratchpad (PG-adapter ferdig med auto-save, debounce, tittel+innhold. Rich text og SpacetimeDB-sync gjenstår)
- Lydmeldinger & Diktering (opptak + Whisper + AI-opprydding)
- Prompt-Laboratorium (prompt-testing mot egne data)
- Promptfoo testsett for første jobbtyper (norsk testdata)
Lag 3 — Features (krever Lag 2)
- Podcastfabrikken (Whisper SRT → Git → PG-avledede formater + episodeside)
- AI Research-Klipper (kunnskapsgraf + jobbkø + AI Gateway)
- Podcast-Statistikk (jobbkø + episoder)
- Whiteboard (sanntids frihåndstavle i SpacetimeDB)
- Den Asynkrone Gjesten (gjeste-tokens + lydmeldinger)
Lag 4 — Avansert (krever Lag 3)
- Studioet: Live AI-Assistent (fylt kunnskapsgraf + LiveKit + Whisper small)
- Møterommet: AI-Referent (LiveKit + Whisper + møte-oppsummering)
- Visuell Kunnskapsgraf (D3.js/Vis.js graf-visning)
- Kunnskaps-Bridge (pgvector, cross-workspace discovery)
- Graf-vedlikehold (nattlig jobb: finn isolerte noder, foreslå koblinger basert på co-occurrence)
- 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(enworker_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 Redaksjonens chat, slik at redaksjonen ser det i sin daglige arbeidsflate
9.4 Observability Dashboard
SvelteKit-appen inkluderer en intern admin-side (/admin/observability) som samler:
- Container-status: Healthcheck-resultater fra Docker (via
docker compose ps/ Docker socket) - Jobbkø: Pending/running/error-count med sparkline-grafer (siste 24t)
- AI Gateway: Token-bruk per jobbtype, kostnad per workspace, failover-hendelser (fra LiteLLMs innebygde logging). Inkluderer workspace-budsjett status (se
docs/infra/ai_gateway.md§6). - Disk/Minne: Mediamappe-størrelse per workspace, PG-størrelse, SpacetimeDB-minnebruk (med graf over tid)
- Sikkerhet: Siste secret-rotasjon timestamp (
.env-endringer), RLS Leak Hunter siste kjøring, antall aktive guest-tokens - SpacetimeDB: Minnebruk-graf,
sync_outbox-størrelse (indikerer sync-etterslep), tilkoblede klienter per workspace
Ingen eksterne tjenester (Prometheus, Grafana) — alt bygges som SvelteKit-sider med data hentet server-side fra PG, Docker og LiteLLM. Konsistent med self-hosted-filosofien.
9.5 Ekstern helsesjekk (utenfor stacken)
Intern overvåking er verdiløs hvis hele serveren er nede. En ekstern uptime-monitor utenfor Hetzner-stacken skal polle følgende endepunkter og varsle ved feil:
| Endepunkt | Sjekk | Varsel |
|---|---|---|
https://sidelinja.org/api/health |
HTTP 200 | E-post/push ved 2 min nedetid |
https://auth.sidelinja.org |
HTTP 200 | E-post/push ved 2 min nedetid |
sidelinja.org:443 |
SSL-utløp < 7 dager | E-post |
Implementering: Bruk en gratis/billig ekstern tjeneste (UptimeRobot, Hetrixtools, eller lignende) — dette er det eneste unntaket fra self-hosted-filosofien, da en helsesjekk per definisjon må leve utenfor systemet den overvåker.
9.6 Ingen andre eksterne observability-tjenester
Utover ekstern helsesjekk (§9.5) skjer all overvåking og varsling internt i Sidelinja-suiten. Ingen avhengighet til Discord, Slack eller andre tredjepartstjenester.
10. Erfaringslogg
Mappen docs/erfaringer/ samler praktiske lærdommer fra implementering — ikke hva vi valgte, men hva vi lærte som ikke er åpenbart fra koden. Formålet er å treffe raskere blink med neste komponent. Nye komponenter BØR legge til erfaringer etter ferdig implementering.
Innhold per mars 2025:
svelte5_reaktivitet.md— $state-getters, SSR-feller, polling-mønsterspacetimedb_integrasjon.md— SDK-konvensjoner, BigInt, Rust borrow-felleradapter_moenster.md— Hybrid PG+SpacetimeDB, anti-patterns, anbefaling for neste komponentauthentik_oidc.md— Sub-claim er SHA256, @auth/sveltekit JWT-quirks, redirect URI
11. Testing og Utrulling
11.1 Teststrategi
Sidelinja har tre testnivåer. Alle nye features MÅ dekke de relevante nivåene.
| Nivå | Hva | Verktøy | Kjøres |
|---|---|---|---|
| Enhetstester | Rust-logikk (jobbkø, sync, parsing) | cargo test |
Lokalt + CI |
| Migrasjonstester | PG-skjema: opp-migrering mot tom DB + seed + spørringer | psql + testskript |
Lokalt + CI |
| Prompt-regresjon | LLM-prompts mot norske testsett | Promptfoo | Lokalt + CI (månedlig cron) |
E2E-tester (SvelteKit → PG → SpacetimeDB) innføres når Lag 2 er stabilt. Inntil da er manuell testing i dev-miljøet tilstrekkelig — kodebasen er liten nok til at det er effektivt.
11.2 Migrasjonstesting
Migrasjonsfiler i migrations/ testes automatisk:
- Spin opp en tom PostgreSQL-container
- Kjør alle migrasjoner sekvensielt
- Kjør seed-data (fixture) med testdata
- Verifiser: RLS-policies blokkerer cross-workspace-tilgang, indekser eksisterer, constraints holder
- Kjør
pg_dump --schema-onlyog diff mot forrige kjente state
11.3 Utrullingsprosedyre (Deployment Checklist)
1. [ ] Alle tester grønne lokalt (cargo test, migrasjonstester, promptfoo)
2. [ ] Push til Forgejo (main branch)
3. [ ] SSH til prod: ssh sidelinja@157.180.81.26
4. [ ] cd /srv/sidelinja && git pull
5. [ ] Nye migrasjoner? → Kjør mot prod-PG (manuelt, verifiser først)
6. [ ] docker compose up -d --build
7. [ ] Verifiser healthchecks: docker compose ps (alle "healthy")
8. [ ] Smoke-test: curl https://sidelinja.org/health
9. [ ] Sjekk logs: docker compose logs --tail=50 <tjeneste>
Rollback: git revert + ny deploy. Migrasjoner som endrer skjema MÅ ha en tilhørende down-migrering.
11.4 Rate Limiting
Ressurskrevende endepunkter beskyttes mot overbruk:
| Endepunkt | Begrensning | Mekanisme |
|---|---|---|
| LiveKit (romopprettelse) | Maks 5 aktive rom per workspace | Applikasjonslogikk i SvelteKit |
| Whisper (transkripsjon) | Maks 3 samtidige jobber per workspace | Jobbkø-constraint (max_concurrent_per_workspace) |
| AI Gateway (LLM-kall) | LiteLLMs innebygde rate limiting | max_parallel_requests i config.yaml |
| Filopplasting (media) | Maks 500 MB per fil, 5 GB per workspace/dag | SvelteKit middleware |
Caddy håndterer generell rate limiting (DDoS-beskyttelse) via rate_limit-direktivet.
12. 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>.mdførst. Oppdater disse dokumentene hvis arkitekturen for funksjonen endres. - Ingen "gh" CLI: Vi bruker Forgejo. For Pull Requests/Issues, bruk
teaCLI. - 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/infra/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.