- ARCHITECTURE.md: SvelteKit+Authentik merket ferdig i Lag 1, prod docker-compose dokumentert - CLAUDE.md: Oppdatert prod-status - produksjon.md: Lag B-C verifiseringsliste oppdatert med deploy-dato Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
299 lines
No EOL
21 KiB
Markdown
299 lines
No EOL
21 KiB
Markdown
# 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). To repos:
|
||
- `sidelinja/server` — app-kode, infra, arkitektur, config
|
||
- `sidelinja/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 + fil-backup |
|
||
| 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 |
|
||
|
||
#### Retningslinjer for nye komponenter
|
||
Når en ny feature eller komponent introduserer data:
|
||
1. **Klassifiser** — hvilken kategori faller dataen i?
|
||
2. **Dokumenter** — legg til i tabellen over
|
||
3. **Implementer TTL** for kategori 4 — aldri la flyktig data vokse ubegrenset
|
||
4. **Aldri dupliser kilde** — avledede data (kategori 3) skal kunne slettes og regenereres
|
||
5. **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.yml` spinner opp PostgreSQL, Redis, SpacetimeDB, Caddy, Whisper og AI Gateway lokalt. Volumene er flyktige (`.docker-data/`, gitignored).
|
||
* **Docker Compose Prod:** `/srv/sidelinja/docker-compose.yml` kjører PostgreSQL, Redis, Caddy, Authentik, Forgejo og SvelteKit (`web`-container bygget fra `web/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. Se `docs/infra/ai_gateway.md`.
|
||
* **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 (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. Se `docs/infra/synkronisering.md` §1.1–1.2.
|
||
3. **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 `nodes` og `graph_edges` har obligatorisk `workspace_id`. Row-Level Security (RLS) sikrer at spørringer aldri lekker data mellom workspaces. SvelteKit setter `SET app.current_workspace_id` ved 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_queue` merkes med `workspace_id`. Rust-workers kjører som superuser (bypasser RLS) og isolerer via applikasjonskode.
|
||
* **AI-prompts:** Whisper `initial_prompt` og LLM system-prompts lagres i `workspaces.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 via `workspace_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: bytes` aktivert 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, Whiteboard, Live transkripsjon, Live AI (faktoid + referent), Visuell graf, AI Research-Klipper, Lydmeldinger & Diktering, Podcast-statistikk, Kunnskaps-Bridge (cross-workspace), Prompt-Laboratorium.
|
||
|
||
## 8. Bygge-rekkefølge (Avhengighetskart)
|
||
|
||
### Lag 0 — Infrastruktur
|
||
- [x] Produksjonsserver: PostgreSQL, Caddy, Authentik, Forgejo, Redis
|
||
- [x] Lokalt utviklingsmiljo: docker-compose.dev.yml (PostgreSQL, Redis, Caddy)
|
||
- [x] Rust toolchain (lokal + server)
|
||
- [x] faster-whisper-server (lokal, testet med medium + prompt)
|
||
|
||
### Lag 1 — Fundament (ingen avhengigheter)
|
||
- [x] Workspace-modell (workspaces, workspace_members, RLS-policies)
|
||
- [x] PostgreSQL-skjema (nodes m/workspace_id, graph_edges, job_queue, messages, channels, media_files)
|
||
- [x] SpacetimeDB grunnoppsett (Docker, Rust WASM-modul, TypeScript-bindings)
|
||
- [x] SvelteKit skjelett med Authentik-integrasjon + Workspace-switcher
|
||
- [x] 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)
|
||
- [~] Chat med channels (PG-adapter + SpacetimeDB hybrid-adapter ferdig, sync-worker gjenstår)
|
||
- [ ] Kanban (SpacetimeDB ↔ PG synk)
|
||
- [ ] 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)
|
||
- [ ] 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 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)
|
||
- **Disk/Minne:** Mediamappe-størrelse per workspace, PG-størrelse, SpacetimeDB-minnebruk
|
||
|
||
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 Ingen eksterne observability-tjenester
|
||
All overvåking og varsling skjer 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ønster
|
||
- `spacetimedb_integrasjon.md` — SDK-konvensjoner, BigInt, Rust borrow-feller
|
||
- `adapter_moenster.md` — Hybrid PG+SpacetimeDB, anti-patterns, anbefaling for neste komponent
|
||
|
||
## 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:
|
||
1. Spin opp en tom PostgreSQL-container
|
||
2. Kjør alle migrasjoner sekvensielt
|
||
3. Kjør seed-data (fixture) med testdata
|
||
4. Verifiser: RLS-policies blokkerer cross-workspace-tilgang, indekser eksisterer, constraints holder
|
||
5. Kjør `pg_dump --schema-only` og 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>.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/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. |