Grunnleggende arkitekturbeslutninger tatt og dokumentert: - Alt er noder (brukere, team, innhold, mediefiler, samlings-noder) - Edges definerer hva en node er (freeform typer, metadata i JSONB) - Materialisert tilgangsmatrise (node_access) erstatter workspace-RLS - Visibility (hidden/discoverable/readable/open) på noder - Aliaser via usynlige system-edges - Maskinrommet eier all skriving (SpacetimeDB først, PG asynk) - SpacetimeDB holder hele grafen, PG er persistent backup - Node- og edge-skjema spesifisert (docs/primitiver/) Fjernet workspace-konseptet fra hele dokumentasjonen (~40 filer). Fem retninger besluttet, én åpen (rom, ikke forum). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.3 KiB
Konsept: Den Asynkrone Gjesten
Filsti: docs/concepts/den_asynkrone_gjesten.md
1. Konsept
Mange interessante gjester har ikke tid til å stille i studio. Den Asynkrone Gjesten lar redaksjonen sende en unik lenke til en gjest som kan svare på spørsmål via tale — fra mobilen, når det passer dem. Svarene lander direkte i redaksjonens arbeidsflyt, transkriberes automatisk, og kan brukes i podcasten.
2. Brukeropplevelse
2.1 Redaksjonens side
- Redaksjonen oppretter en "Gjestesesjon" knyttet til et Tema.
- Legger inn spørsmål (tekst) som gjesten skal svare på.
- Systemet genererer en unik, tidsbegrenset URL.
- URL-en sendes til gjesten via e-post, SMS eller chat.
- Gjestens svar (lydmeldinger) dukker opp i Tema-chatten som
voice_memo-meldinger, automatisk transkribert. - Redaksjonen triagerer svarene — kan tagge, klippe inn i episode, eller bruke som research.
2.2 Gjestens side
- Gjesten åpner lenken i mobilnettleseren. Ingen app, ingen konto, ingen registrering.
- Ser en enkel, ren flate: podcast-logo, spørsmålene fra redaksjonen, og en opptaksknapp per spørsmål.
- Trykker record, snakker, trykker stopp. Kan lytte tilbake og ta om igjen.
- Ved innsending lastes lydfilene opp og gjesten ser en bekreftelse.
- Lenken utløper etter gitt tid eller antall besøk.
2.3 Minimal friksjon
- Ingen Authentik-innlogging — tilgang via signert token
- Ingen app — ren PWA/nettleser
- Ingen redigering — gjesten snakker bare
- Responsivt, mobil-first design
3. Komponenter
| Feature | Rolle |
|---|---|
| Lydmeldinger | Opptakskomponent gjenbrukes (se docs/features/lydmeldinger.md) |
| Chat (channels) | Svarene lander i en channel knyttet til Temaet |
| Live transkripsjon | Whisper transkriberer via jobbkø (se docs/features/live_transkripsjon.md) |
| Podcastfabrikken | Lydklipp kan trekkes inn som segment (se docs/concepts/podcastfabrikken.md) |
4. Autentisering: Gjeste-tokens
4.1 Datamodell
guest_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, -- Samlings- eller tema-node
guest_name TEXT NOT NULL, -- Visningsnavn ("Erna Solberg")
questions JSONB NOT NULL, -- [{ "sort": 1, "text": "Hva tenker du om...?" }]
token TEXT UNIQUE NOT NULL, -- Kryptografisk sikker, URL-safe token
expires_at TIMESTAMPTZ NOT NULL,
max_recordings SMALLINT DEFAULT 10, -- Maks antall opptak
recordings_count SMALLINT DEFAULT 0,
created_by TEXT NOT NULL REFERENCES users(authentik_id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
)
4.2 Sikkerhet
- Token er kryptografisk tilfeldig (256-bit, URL-safe base64).
- SvelteKit validerer token ved hvert request: sjekker expiry, recordings_count < max_recordings, og node-tilhørighet.
- Gjestens meldinger merkes med
author_id = NULLogmetadata.guest_name+metadata.guest_token_idfor sporbarhet. - Ingen tilgang til andre noder eller funksjoner.
- Tokenet kan revokeres manuelt av redaksjonen.
4.2b Sikkerhetsdybde (mot token-lekkasje og misbruk)
Et lekket gjeste-token gir direkte filopplasting uten autentisering — dette er høyrisiko. Følgende tiltak begrenser skadepotensialet:
| Tiltak | Implementering | Formål |
|---|---|---|
| Rate limiting per token | SvelteKit middleware: maks 1 opplasting per 30 sek per token | Forhindrer spam/flooding |
| Filtype-validering | SvelteKit: kun audio/* MIME-typer aksepteres, filstørrelse maks 50 MB |
Blokkerer malware-opplasting |
| Malware-scanning | ClamAV sidecar-container scanner opplastede filer før de lagres | Fanger kjent malware |
| Auto-revoke | Token deaktiveres automatisk når recordings_count >= max_recordings |
Begrenser eksponering |
| IP-logging | Logger klient-IP per opplasting i guest_token_usage-tabell |
Sporbarhet ved misbruk |
| Geo-begrensning (valgfritt) | Caddy-nivå: blokker requests fra uventede geolokasjoner | Reduserer angrepsflate |
ClamAV Docker-oppsett:
clamav:
image: clamav/clamav:latest
restart: unless-stopped
volumes:
- /srv/synops/media:/scan:ro
networks:
- sidelinja-net
SvelteKit kaller ClamAV via clamdscan (socket) etter filopplasting, før filen flyttes til endelig plassering. Infiserte filer slettes umiddelbart og tokenet flagges for manuell gjennomgang.
Fremtidig hardening — prosess-isolasjon: Ved økt eksponering (mange aktive guest-tokens, offentlige lenker) bør opplastede filer prosesseres i en isolert kontekst per token. Mulige tilnærminger:
- Firejail/bubblewrap-sandbox for Whisper-prosessering av gjeste-audio
- Dedikert temp-mappe per token som slettes etter prosessering
- Docker sidecar-container for uautentisert filopplasting med egne cgroups
Dette er komplementært til ClamAV (som fanger kjent malware) — sandboxing beskytter mot ukjente angrep. Implementeres når gjeste-tokens eksponeres bredere enn redaksjonell bruk.
4.3 Flyt (teknisk)
Gjest åpner URL med token
→ SvelteKit validerer token
→ Viser spørsmål + opptaksknapp
→ Gjest tar opp svar
→ SvelteKit streamer lydfil til CAS (content-addressable store)
→ Oppretter message (voice_memo) i channelen
→ Oppretter whisper_transcribe-jobb i jobbkøen
→ Inkrementerer recordings_count
→ Redaksjonen ser svaret i Tema-chatten
5. Dataklassifisering
| Data | Kategori | Detaljer |
|---|---|---|
| Gjestens lydopptak | Kritisk (backup) | Unikt innhold |
| Guest tokens | Flyktig (TTL) | Utløper automatisk, slett expired tokens periodisk |
| Spørsmål (JSONB) | Kritisk (PG) | Redaksjonelt innhold |
6. Instruks for Claude Code
guest_tokens-tabellen er ikke en node i grafen — den er ren tilgangsstyring.- Gjeste-UI er en egen SvelteKit-rute (
/guest/[token]) med minimal layout (ingen navbar). - Gjenbruk lydmeldinger-komponenten — ikke bygg en egen opptaksflyt.
- Meldinger fra gjester har
author_id = NULL. Frontend må håndtere dette gracefully (visguest_namei stedet). - Tokenet skal aldri gi tilgang til å lese andre meldinger i channelen — gjesten kan kun skrive.
- Tilgang er styrt via node_access. Token bærer node_id eksplisitt.