synops/docs/concepts/den_asynkrone_gjesten.md
vegard 00bf5d27ce Arkitekturbeslutninger: noder er sentrum, edges definerer alt
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>
2026-03-17 10:29:54 +01:00

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

  1. Redaksjonen oppretter en "Gjestesesjon" knyttet til et Tema.
  2. Legger inn spørsmål (tekst) som gjesten skal svare på.
  3. Systemet genererer en unik, tidsbegrenset URL.
  4. URL-en sendes til gjesten via e-post, SMS eller chat.
  5. Gjestens svar (lydmeldinger) dukker opp i Tema-chatten som voice_memo-meldinger, automatisk transkribert.
  6. Redaksjonen triagerer svarene — kan tagge, klippe inn i episode, eller bruke som research.

2.2 Gjestens side

  1. Gjesten åpner lenken i mobilnettleseren. Ingen app, ingen konto, ingen registrering.
  2. Ser en enkel, ren flate: podcast-logo, spørsmålene fra redaksjonen, og en opptaksknapp per spørsmål.
  3. Trykker record, snakker, trykker stopp. Kan lytte tilbake og ta om igjen.
  4. Ved innsending lastes lydfilene opp og gjesten ser en bekreftelse.
  5. 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 = NULL og metadata.guest_name + metadata.guest_token_id for 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 (vis guest_name i 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.