- Omorganiser docs/: konsepter, features, infra og proposals i egne mapper - Ny docs/erfaringer/ med lærdommer fra chat-implementering (Svelte 5, SpacetimeDB, adapter-mønster) - Oppdater ARCHITECTURE.md: Lag 1 status, ny §10 Erfaringslogg, SpacetimeDB i lokal dev - Oppdater synkronisering.md med implementeringsstatus og designvalg - Oppdater lokal.md med SpacetimeDB og AI Gateway - Utvid PG-skjema med channels, messages, media_files, message_revisions - Legg til seed_dev.sql, migration_safety.md, .env.example - Nye feature-specs: chat, kanban, whiteboard, live_ai, lydmeldinger m.fl. - Nye konsept-specs: studioet, møterommet, redaksjonen, den asynkrone gjesten m.fl. - SpacetimeDB og AI Gateway i docker-compose.dev.yml - collect-docs.sh inkluderer erfaringer/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
4.5 KiB
Markdown
93 lines
4.5 KiB
Markdown
# 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
|
|
|
|
```sql
|
|
guest_tokens (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
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 workspace-tilhørighet.
|
|
- Gjestens meldinger merkes med `author_id = NULL` og `metadata.guest_name` + `metadata.guest_token_id` for sporbarhet.
|
|
- Ingen tilgang til andre channels, workspaces eller funksjoner.
|
|
- Tokenet kan revokeres manuelt av redaksjonen.
|
|
|
|
### 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 media/{workspace_slug}/voice/
|
|
→ 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, ingen workspace-switcher).
|
|
* 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.
|
|
* Alt er workspace-scopet. Token bærer workspace_id eksplisitt.
|