# Synops — Claude Code Prosjektguide

## Prosjektoversikt
Synops er en plattform for redaksjonelt arbeid og podcast-produksjon.
Self-hosted på Hetzner VPS med full datakontroll.

Synops er plattformen. Sidelinja (podcastredaksjonen) er en tenant —
en organisasjon som bruker Synops. Denne distinksjonen er bevisst:
plattformkode og infrastruktur er skilt fra tenant-data og -innhold.

## Arbeidsflyt
- **Standard arbeidsmodus:** Start i planleggingsmodus. Lag en grundig plan,
  få godkjenning, deretter implementer. Jobbene er ment å kunne kjøre
  lenge autonomt uten input underveis.
- **Alt skjer på serveren.** Claude Code kjører direkte på produksjonsserveren
  (`/home/vegard/synops/`). Rust, Node.js og Docker er tilgjengelig.
  Bygging, testing og deploy skjer her. Vegard kobler til via SSH/tmux.
- **Browser-testing:** Claude har ikke tilgang til browser. Visuell testing
  gjøres av Vegard. Claude verifiserer backend (kompilering, API, DB-state).
- **Commit og push:** Bruk egen vurdering. Trygt og reverserbart.
- **Deploy til produksjon:** Krever alltid eksplisitt godkjenning fra Vegard.
- **Diskusjon:** Forklar og diskuter før arkitekturendringer.
  For implementering innenfor eksisterende spec — bare kjør.
- **Defensiv skriving:** Skriv planer, arbeidsstatus og mellomresultater
  til disk underveis (f.eks. `docs/fikseliste.md` eller en dedikert fil
  i `docs/`). Kontekst kan forsvinne ved planmodus-reset eller krasj.
  Neste instans skal kunne lese seg opp fra filene og fortsette.
- **Oppgaver:** Oppgaver er noder i grafen (`node_kind: 'task'`).
  Plukkes av agenter via PG-query, ikke filer. Se `docs/infra/oppgaver.md`.

## Dokumentasjonstre
CLAUDE.md er eneste startdokument. Oppgavelister i roten, docs under `docs/`:

- `docs/infra/oppgaver.md` — Oppgaver som noder i grafen

- `docs/arkitektur.md` — Overordnet arkitektur, lagmodell, teknologivalg
- `docs/retninger/` — Arkitektoniske teser og vedtatte retninger:
  - `README.md` — Oversikt og status
  - `status_quo.md` — Hva v1 var: ambisiøse primitiver, tradisjonell overflate
  - `rom_ikke_forum.md` — Opplevelse-først, to-lags-modell, administrativ opplevelse
  - `universell_input.md` — Tre primitiver (input, mottak, kommunikasjon), noder+edges
  - `maskinrommet.md` — Rust-orkestrator: fang, prosesser, lever
  - `bruker_ikke_workspace.md` — Noder er sentrum: brukere, team, innhold er noder. Tilgangsmatrise fra edges.
  - `datalaget.md` — PG(+AGE) som graf og arkiv, PG LISTEN/NOTIFY + WebSocket som sanntidslag
  - `arbeidsflaten.md` — Spatial canvas med verktøy-paneler, drag-and-drop, kompatibilitetsmatrise
  - `unix_filosofi.md` — Maskinrommet som orkestrator, arbeid i CLI-verktøy, Claude og maskinrommet deler verktøykasse
  - `kvalitetsprinsipper.md` — Gjør det riktig, én gang. Ikke hardkod, forstå hvorfor, én fiks ikke workarounds
- `docs/primitiver/` — Spesifikasjoner for kjerneprimitivene:
  - `nodes.md` — Node-skjema, node_kind, visibility, CAS-noder, eierskap
  - `edges.md` — Edge-skjema, typer, metadata, systemedges
  - `traits.md` — Trait-system: evner/funksjonalitet for samlingsnoder, katalog, pakker
  - (kommer: input, mottak, kommunikasjonsnode)
- `docs/concepts/` — Brukeropplevelser/produktområder:
  - `studioet.md`, `møterommet.md`, `redaksjonen.md`, `podcastfabrikken.md`,
    `kunnskapsgrafen.md`, `valgomaten.md`, `den_asynkrone_gjesten.md`,
    `publisering.md`, `adminpanelet.md`, `arbeidstavlen.md`,
    `selvdokumenterende_system.md`
- `docs/features/` — Tekniske byggeklosser:
  - Se individuelle filer for chat, kanban, kalender, meldingsboks,
    kunnskapsgraf, whiteboard, live transkripsjon, ressursforbruk, m.fl.
  - `lydstudio.md` — Lydredigering via FFmpeg, EDL, ikke-destruktiv prosessering
  - `artikkelverktoy.md` — Langform TipTap-editor, drag-and-drop mottak, source_material-edges
  - `universell_overfoering.md` — Drag-and-drop mellom verktøy-paneler, kompatibilitetsmatrise
- `docs/fikseliste.md` — UI-bugs og forbedringer (levende jobbliste for grensesnitt)
- `docs/proposals/` — Idébank med 32+ uimplementerte forslag (se README.md)
- `docs/setup/` — Oppsett og drift:
  - `produksjon.md` — Steg-for-steg oppsett av Hetzner VPS fra scratch
  - `lokal.md` — Historisk: lokalt utviklingsmiljø (WSL2). Utdatert — all utvikling skjer nå på serveren.
  - `migration_safety.md` — Sjekkliste for PostgreSQL-migrasjoner (v1 workspace-RLS, trenger omskriving til node_access)
- `docs/infra/` — Infrastruktur og drift:
  - `ai_gateway.md` — LiteLLM som sentralisert AI-ruter (BYOK + fallback)
  - `api_grensesnitt.md` — Kommunikasjonskart: SvelteKit er web-API, Rust er worker
  - `jobbkø.md` — PostgreSQL-basert køsystem for bakgrunnsjobber
  - `synkronisering.md` — Historisk: PG ↔ SpacetimeDB dataflyt (arkivdokument, SpacetimeDB fjernet mars 2026)
  - `claude_agent.md` — Claude som chat-deltaker: arkitektur, triggere, sikkerhet
  - `observerbarhet.md` — Strukturert logging, metrikk-endepunkt (/metrics), AI-kostnad
  - `oppgaver.md` — Oppgaver som noder: prioritet, status, agent-plukking, krasj-deteksjon
- `docs/erfaringer/` — Lærdommer fra v1 (Svelte 5, Authentik). STDB-docs arkivert i `arkiv/`
- `reference/` — Kode fra v1 med gjenbruksverdi (Editor.svelte)
- `ops/` — Repeterbare vedlikeholdsjobber (ryddejobb, doc-audit, drift-sjekk)

## Aktører
- **Vegard** — serveradmin, utvikler, bruker. Kobler til via SSH/tmux.
- **Claude** — AI-agent, utvikler. Kjører direkte på serveren (`/home/vegard/synops/`).
- Begge har sudo + docker-tilgang på serveren.

## Stack
- **Orkestrator/Backend:** Rust (maskinrommet) — kjører native på hosten
- **Frontend:** SvelteKit (TypeScript, PWA)
- **Sanntid:** PG LISTEN/NOTIFY + WebSocket (portvokteren)
- **Database/Graf:** PostgreSQL (+Apache AGE ved behov)
- **Binærlagring:** CAS (content-addressable store)
- **AI:** Claude Code (chat-agent), LiteLLM (AI Gateway), faster-whisper (STT)
- **Infra:** Docker (tredjepart), systemd (egenutviklet), Caddy, Authentik (SSO)

## Driftsmodell: hybrid native + Docker
Egenutviklet kode kjører **native på hosten** (systemd). Tredjepartstjenester
kjører i **Docker**. Prinsipp: Docker for det vi ikke bygger selv, native
for det vi har full kontroll over.

### Native (systemd)
| Tjeneste | Beskrivelse | Deploy |
|----------|-------------|--------|
| **Caddy** | Reverse proxy + TLS | `sudo systemctl reload caddy` (config: `/srv/synops/config/caddy/Caddyfile`) |
| **maskinrommet** | Rust API + jobbkø | `cargo build --release` → `sudo systemctl restart maskinrommet` |
| **SvelteKit** | Frontend (når klar) | `npm run build` → systemd |

Maskinrommet kjører native fordi det trenger tilgang til `claude` CLI
og hele hosten. Env-filen genereres dynamisk av `scripts/maskinrommet-env.sh`
med Docker container-IPs.

### Docker (docker-compose)
| Tjeneste | Begrunnelse |
|----------|-------------|
| **PostgreSQL** | Versjonsstyring, enkel oppgradering |
| **Authentik** | Kompleks stack (server + worker + Redis) |
| **LiteLLM** | Ferdig image, sjelden oppdatering |
| **faster-whisper** | Modellhåndtering, ferdig image |

### Kommunikasjon mellom lagene
- Caddy (native) → Docker-tjenester: via localhost-porter
  (Authentik:9000, Forgejo:3000)
- Caddy (native) → native tjenester: direkte localhost
  (maskinrommet:3100, SvelteKit:3200)
- Maskinrommet (host) → Docker-tjenester: via container-IP
  (løses dynamisk i `maskinrommet-env.sh`)

## CLI-verktøy (`tools/`)
Trenger du et verktøy — lag det. Gjør det generisk, robust og stabilt,
så blir det tilgjengelig for maskinrommet (jobbkø) også. Claude og
maskinrommet deler verktøykasse.

- Navnekonvensjon: `synops-<verb>` (f.eks. `synops-transcribe`, `synops-render`)
- Delt lib: `synops-common` crate (PG-tilkobling, CAS, node/edge-typer)
- Dokumentert i `tools/README.md`
- Ref: `docs/retninger/unix_filosofi.md`, `docs/infra/agent_api.md`

## Claude som chat-deltaker
- **Agent-node:** `d3eebc99-9c0b-4ef8-bb6d-6bb9bd380a44` (node_kind: `agent`)
- **Trigger:** Melding i kommunikasjonsnode der Claude er `member_of`
  → `agent_respond`-jobb → `claude -p` → svar tilbake i chatten
- **Kill switch:** `UPDATE agent_identities SET is_active = false WHERE agent_key = 'claude-main'`
- **Detaljer:** Se `docs/infra/claude_agent.md`

## Produksjonsserver
- **IP:** 157.180.81.26
- **SSH:** `ssh vegard@157.180.81.26`
- **Root-login:** Deaktivert
- **Server-filer:** `/srv/synops/` (docker-compose.yml, .env, config/, data/)
- **Domener (synops.no er primær):**
  - `synops.no` — Offentlig landingsside + /pub/* + /media/* (Caddy → statisk + maskinrommet)
  - `ws.synops.no` — Appen (SvelteKit + /api/* → maskinrommet). Krever login.
  - `auth.synops.no` — Authentik SSO
  - `api.synops.no` — Maskinrommet direkte (CLI, webhooks, eksternt)
  - `git.synops.no` — Forgejo (SSH port 222)
  - `vegard.info` — Personlig nettsted (statisk)
  - `sidelinja.org` — Redirect til synops.no
  - `workspace.synops.no` — Legacy redirect til ws.synops.no
  - `*.sidelinja.org` — Legacy aliaser (auth, api, git)

## Git
- **Repos i Forgejo:**
  - `vegard/synops` — plattformkode og arkitektur: `ssh://git@git.synops.no:222/vegard/synops.git`
  - `sidelinja/sidelinja` — podcastinnhold: `ssh://git@git.synops.no:222/sidelinja/sidelinja.git`
- **Git-identitet:** vegard / vnotnes@pm.me
- **Forgejo-bruker:** vegard (admin)
- **CLI:** Bruk `tea`, ikke `gh` (vi bruker Forgejo, ikke GitHub)

## Viktige regler
- Aldri eksponere databaseporter mot internett
- All AI-trafikk via maskinrommet, aldri direkte til leverandør-APIer
- Tunge jobber (Whisper, LLM, TTS) blokkerer aldri brukerforespørsler
- Sjekk alltid relevant doc i `docs/` før implementering
- Dokumenter lærdommer i `docs/erfaringer/`

## Lagmodell
```
GUI (SvelteKit) — spatial canvas med verktøy-paneler
  │ skriv              │ les (sanntid via WebSocket)
  ▼                    ▲
Maskinrommet (Rust) ───┘ PG LISTEN/NOTIFY → WebSocket → GUI
  │ orkestrerer
  ▼
CLI-verktøy (tools/) ←── Claude bruker de samme
  │
  ▼
Tjenester: PG+AGE, CAS, Whisper, LiteLLM, LiveKit ...
```

## Kjerneprinsipper
1. **Alt er noder og edges.** Ingen separate tabeller for chat, kanban,
   kalender, notater. Visninger er spørringer mot grafen.
2. **Tre primitiver:** Input (fanger), Mottak (presenterer), Kommunikasjon
   (samler folk). Alt annet er visninger og edges.
3. **Maskinrommet orkestrerer, CLI-verktøy gjør jobben.** Maskinrommet eier
   auth, intentions, jobbkø og edge-logikk. All prosessering (transkribering,
   rendering, AI, lyd) skjer i CLI-verktøy som maskinrommet spawner.
   Claude bruker de samme verktøyene direkte. Ny funksjonalitet starter
   som CLI-verktøy, absorberes i orkestratoren, dokumenteres nøye.
   Ref: `docs/retninger/unix_filosofi.md`.
4. **Noder er sentrum.** Brukere, team, innhold — alt er noder.
   Du ser dine edges. Tilgang via materialisert tilgangsmatrise.
5. **Privat er default.** Input uten mottaker-edge er privat. Security
   by design, ikke konfigurasjon.
6. **PG er sannhetskilden.** All data lever i PostgreSQL.
   Sanntid via PG LISTEN/NOTIFY + WebSocket i portvokteren.
7. **Alt er paneler i en arbeidsflate.** Brukergrensesnittet er et spatial
   canvas der verktøy plasseres, sizes og arrangeres fritt. Hver feature
   er et panel i BlockShell. Interaksjon mellom paneler via drag-and-drop:
   - **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.
   - **Dra verktøy inn i kontekst → transformer.** Et verktøy (AI, editor,
     studio) som mottar en node anvender sin funksjon på originalen.
   Ref: `docs/retninger/arbeidsflaten.md`, `docs/features/universell_overfoering.md`.

## Utviklingsfilosofi
Disse prinsippene styrer *hvordan* vi bygger, ikke *hva*.
Ref: `docs/retninger/kvalitetsprinsipper.md` for utdypning.

1. **Forstå hvorfor, ikke bare hva.** Ikke sett en konfigverdi fordi
   det "fikser" problemet. Forstå mekanismen. Ellers vet du ikke hva
   som knekker når konteksten endres.
2. **Ikke hardkod det dynamiske.** Hvis en verdi kan leses fra request,
   miljø eller database — ikke lås den i en konfigurasjonsfil. Spør:
   "Hva går i stykker om vi legger til et subdomain i morgen?"
3. **Én fiks, ikke to workarounds.** Finn rotårsaken. To workarounds
   som kompenserer for hverandre er verre enn én ufikset bug.
4. **Preferer fjerning over tillegg.** Kan du løse problemet ved å
   fjerne kode/konfig? Gjør det. Mindre = færre feil = enklere.
5. **Én mekanisme per problem.** Ikke to overlappende løsninger.
   Det skaper forvirring om hvilken som gjør jobben.
6. **Bedre med én ting som fungerer perfekt enn ti som fungerer
   halvveis.** Gaffatape-løsninger skaper teknisk gjeld som vokser
   eksponentielt. Gjør det riktig, én gang.
