Aggregert ressursforbruk-dashboard som spør mot resource_usage_log
(oppgave 15.7). Tre visninger: totaler per ressurstype, per samling,
og daglig tidsserie. AI drill-down viser forbruk per jobbtype og
modellnivå (fast/smart/deep).
Backend: GET /admin/usage med days- og collection_id-filtre.
Frontend: /admin/usage med filterbare tabeller og fargekodede kort.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sentralisert logging av alle ressurskrevende operasjoner til
resource_usage_log-tabellen (opprettet i migrasjon 009).
Ny kode:
- resource_usage.rs: hjelpemodul med log() og find_collection_for_node()
- bandwidth.rs: Caddy JSON-logg-parser med nattlig batch-jobb (kl 03:00)
Logging lagt til i handlere:
- AI: summarize, ai_edges (token-telling via LiteLLM usage-felt),
agent (placeholder — claude CLI gir ikke token-info)
- Whisper: duration_seconds, model, language, mode
- TTS: refaktorert til sentralisert modul, lagt til collection_id
- CAS: logger nye filer ved upload (ikke dedup)
- LiveKit: logger join-hendelser (faktisk deltaker-minutter
krever webhook-integrasjon i fremtiden)
Caddy-config: JSON access logging aktivert for sidelinja.org og
synops.no i /srv/synops/config/caddy/Caddyfile (utenfor repo).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nytt admin-dashboard for sanntids serverhelse med fire hoveddeler:
1. Tjeneste-status: Parallelle helsesjekker for alle 7 tjenester
(PG, STDB, Caddy, Authentik, LiteLLM, Whisper, LiveKit) med
latens-måling og statusrapportering (up/down/degraded).
2. System-metrikker: CPU-load via /proc/loadavg, minne via
/proc/meminfo, disk via statvfs, oppetid via /proc/uptime.
Vises med progress-bars og fargekodede terskler.
3. PG-statistikk: Aktive tilkoblinger, maks-tilkoblinger,
databasestørrelse og aktive spørringer.
4. Logg-tilgang: Filtrerbar visning av logger fra alle tjenester.
Bruker journalctl for systemd-tjenester og docker logs for
containere. Konfigurerbart antall linjer per tjeneste.
Backend: health.rs med tokio::join! for parallelle sjekker.
Frontend: /admin/health med auto-polling hvert 10. sekund.
Backup-sjekk rapporterer ok/stale/missing (ingen backup satt opp ennå).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer prioritetsregler, ressursgrenser og LiveKit-bevisst
resource governor for jobbkø-workeren, pluss disk-overvåking med varsling.
Hovedkomponenter:
1. Prioritetsregler (job_priority_rules-tabell):
- Konfigurerbar base_priority, cpu_weight, max_concurrent per jobbtype
- LiveKit-justering: livekit_priority_adj og block_during_livekit
- Timeout per jobbtype
- Admin-API for å endre regler uten restart
2. Ressurs-governor i worker-loopen:
- Semaphore: maks 3 samtidige jobber
- CPU-vektgrense: total vekt maks 8 (Whisper=5, render=1, etc.)
- Per-type concurrency-grense
- LiveKit-status sjekkes med 10s cache-TTL
- Jobber utsettes/nedprioriteres ved aktive LiveKit-rom
- Individuell timeout per jobb (default 600s)
- Jobber kjøres i egne tokio-tasks (parallell dispatch)
3. Disk-overvåking:
- Sjekker diskbruk hvert 60. sekund via statvfs
- Terskler: 85% warning, 90% critical, 95% emergency
- Logger til disk_status_log (siste 1000 målinger beholdes)
- Admin-API: GET /admin/resources/disk med historikk
4. Admin-API:
- GET /admin/resources — samlet ressursstatus
- GET /admin/resources/disk — diskstatus med historikk
- POST /admin/resources/update_rule — oppdater prioritetsregel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin kan nå se, filtrere, retrye og avbryte jobber via /admin/jobs.
Backend:
- jobs.rs: list_jobs(), count_by_status(), distinct_job_types(),
retry_job(), cancel_job() for admin-spørringer
- intentions.rs: GET /admin/jobs, POST retry_job/cancel_job handlers
- main.rs: tre nye ruter
Frontend:
- /admin/jobs: statusoppsummering med antall per status, filter på
type/status, paginert tabell med retry/avbryt-knapper, 5s polling
- /admin: navigasjonslenke til jobbkø
Migrasjon:
- 013_job_queue.sql: formaliserer job_queue-tabellen med admin-indekser
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer koordinert nedstenging der admin setter et
vedlikeholdstidspunkt, brukere ser nedtelling, og systemet
stenger ned trygt etter at aktive jobber er ferdige.
Nye filer:
- maskinrommet/src/maintenance.rs — MaintenanceState med atomiske
flagg, shutdown-koordinator (vent på scheduled_at → blokker
nye jobber/LiveKit → vent på kjørende jobber → exit)
- frontend/src/routes/admin/+page.svelte — admin-panel for
vedlikehold med statusvisning og aktive sesjoner
Endringer:
- jobs.rs: sjekker maintenance.is_active() før dequeue
- intentions.rs: nye endepunkter (initiate/cancel/status), blokkerer
join_communication under vedlikehold
- main.rs: MaintenanceState i AppState, nye ruter
- api.ts: klientfunksjoner for maintenance-API
- adminpanelet.md: dokumenterer implementerte endepunkter
Flyt: admin → GET /admin/maintenance_status (se aktive sesjoner)
→ POST /intentions/initiate_maintenance → varsel broadcast via STDB
→ frontend nedtelling → scheduled_at nådd → active=true → jobbkø
pauset + LiveKit blokkert → vent maks 5 min → process::exit(0)
→ systemd restarter maskinrommet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer automatisk A/B-testing for forside-varianter:
- PG-migrasjon 012: ab_events-tabell for impression/klikk-logging
med hour_of_week (0-167) for tidspunkt-normalisering
- Variant-rotasjon: ab_select() velger tilfeldig blant testing-varianter
ved forside-rendering, winner prioriteres, retired filtreres bort
- Impression-logging: asynkron fire-and-forget ved forside-serve
(både cache-hit og -miss), lagres i ab_events
- Klikk-attribusjon: artikkelbesøk sjekker forside-cache for aktive
AB-varianter og logger klikk. Eksplisitt tracking via
GET /pub/{slug}/t/{article_id}?v={edge_id}
- Periodisk evaluator (300s intervall): z-test for proporsjoner
(p < 0.05), minimum 100 impressions per variant, oppdaterer
edge-metadata (ab_status, impressions, clicks, ctr)
- Redaktør-overstyring: POST /intentions/ab_override markerer
valgt variant som winner, andre som retired (krever owner/admin)
- Auto-initialisering: maybe_start_ab_test() setter ab_status=testing
automatisk når >1 variant av samme type opprettes
Alle 42 tester passerer inkludert 3 nye z-test-tester.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Publisert tittel, ingress, OG-bilde og undertittel er nå egne noder
koblet til artikler via title/subtitle/summary/og_image-edges.
Rendering bruker presentasjonselementer med fallback til artikkelfelt.
Backend:
- Ny query: GET /query/presentation_elements?article_id=...
- render_article_to_cas henter presentasjonselementer via edges
- fetch_article + fetch_index_articles bruker pres.elementer
- Batch-henting for forsideartikler (én SQL-spørring)
- ArticleData utvides med subtitle + og_image
- Alle fire temaer viser subtitle og OG-bilde
- SEO og_image-tag fylles fra presentasjonselement
Frontend:
- PresentationEditor.svelte: opprett/rediger tittel, undertittel,
ingress, OG-bilde med variantvelger (editorial/ai/social/rss)
- Integrert i PublishDialog via <details>-seksjon
- API-klient: fetchPresentationElements(), deleteNode()
Grunnlag for A/B-testing (oppgave 14.17): edge-metadata støtter
ab_status/impressions/clicks/ctr, best_of() prioriterer winner > testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Navigasjonslenker (Arkiv, Søk) i base.html header
- Dokumentasjon av implementerte sidetyper i publisering.md
- Markerer oppgave 14.15 som ferdig i tasks.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redaktør kan nå opprette en diskusjonstråd direkte fra den redaksjonelle
arbeidsflaten, knyttet til en innsendt artikkel og dens forfatter.
Backend:
- create_communication utvides med context_id som oppretter belongs_to-edge
fra kommunikasjonsnoden til kontekstnoden (artikkelen)
- editorial_board-spørringen returnerer discussion_ids per kort (kommunikasjonsnoder
med belongs_to til artikkelen)
Frontend:
- "Start samtale"-knapp på hvert redaksjonelt kort oppretter kommunikasjonsnode
med redaktør som owner og forfatter som member, og navigerer til chatten
- Eksisterende samtaler vises som lenker på kortet
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redaktøren ser alle artikler med submitted_to-edge til samlingen,
gruppert i fire kolonner etter status. Drag-and-drop mellom kolonner
endrer status via update_edge. Siste kolonne ("Planlagt") åpner en
dialog for å sette publish_at i edge-metadata — klar for oppgave 14.12
(planlagt publisering).
Backend: GET /query/editorial_board med forfatterinfo og edge-metadata.
Frontend: /editorial/[id] med sanntidsoppdateringer via SpacetimeDB.
Lenke fra PublishingTrait når require_approval er aktivt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Maskinrommet håndhever nå publiseringsregler for samlinger med
require_approval: true i publishing-traiten:
- submitted_to-edge: kun roller i submission_roles (+ owner/admin) kan
opprette. Metadata settes automatisk: status=pending, submitted_at=now.
- belongs_to-edge til require_approval-samling: kun owner/admin.
- Status-endring på submitted_to: kun owner/admin av samlingen.
PublishingConfig utvidet med require_approval (default false) og
submission_roles (default ["member"]).
Nye hjelpefunksjoner: get_publishing_config, get_user_role_for_node,
user_is_owner_or_admin. EdgeRow utvidet med source_id/target_id.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Maskinrommet orkestrerer, CLI-verktøy gjør jobben
- Spatial canvas med drag-and-drop som primær interaksjon
- To retninger: dra ut = ny node, dra inn = transformer
- Arbeidsflaten er en node (rekursivt)
- Caddy flyttet til native i teknologitabellen
- CLI-verktøy lagt til i lagmodell og teknologivalg
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Maskinrommet og Claude deler samme CLI-verktøykasse. Maskinrommet
kaller dem fra jobbkøen, Claude fra terminalen. Alt maskinrommet
gjør kan Claude simulere — nyttig for debugging, testing og utvikling.
Nye features bygges som CLI-verktøy fra start. Eksisterende kode
brytes ut gradvis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Personlig publiseringsflyt for samlinger med publishing-trait der
require_approval: false. Bruker kan publisere artikler fra mottak
til samlingen, og avpublisere ved å fjerne belongs_to-edge.
Backend:
- Nytt delete_edge-endepunkt i maskinrommet med tilgangskontroll
og automatisk forside-cache-invalidering ved avpublisering
Frontend:
- PublishDialog: forhåndsvisning, slug-editor, tema-info, bekreftelse
- EditorTrait: publiser/avpubliser-knapper på innholdsnoder i
publiseringssamlinger, velger for upubliserte artikler
- deleteEdge i API-klienten
Docs:
- Oppdatert api_grensesnitt.md med delete_edge, update_edge, set_slot
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny retning: arbeidsflaten.md — spatial canvas med verktøy-paneler.
Drag-and-drop mellom verktøy oppretter nye noder med source_material-edges.
Noder muterer ikke — de føder nye noder.
Oppdatert docs:
- universell_input.md: "retyping" → "nye noder fra eksisterende"
- rom_ikke_forum.md: "bli" → "føde", siloer → verktøy-paneler
- universell_overfoering.md: blokker → verktøy-paneler, dual-modell
- meldingsboks.md: multi-rolle → visning i flere kontekster
Nye docs:
- arbeidsflaten.md: retning med kompatibilitetsmatrise og inkompatibilitet
- artikkelverktoy.md: langform TipTap-editor med drag-and-drop mottak
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny intention `POST /intentions/set_slot` for redaksjonell
kontroll over forside-slots i publiseringssamlinger.
Håndhever:
- Maks 1 hero: gammel ikke-pinned hero flyttes til strøm
- featured_max: eldste ikke-pinned featured FIFO til strøm
- pinned-flagg beskytter mot automatisk fjerning
- Krever owner/admin-tilgang til samlingen
- Trigger forside-rerendering etter slot-endring
Returnerer liste over displaced edges slik at frontend
kan vise hva som ble flyttet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
serve_index støtter nå index_mode fra publishing-trait:
- "static": render_index-jobb rendrer forsiden til CAS ved publisering,
samlingens metadata.rendered_index.index_hash peker til CAS-fil,
serveres med Cache-Control: immutable
- "dynamic" (default): in-memory cache med konfigurerbar TTL
(index_cache_ttl, default 300s), invalidert ved belongs_to-endringer
Tre separate indekserte PG-spørringer erstatter den gamle
alt-i-ett-spørringen — filtrerer på slot i edge-metadata
(hero/featured/strøm) med LIMIT, bruker GIN-indeks.
Trigger-logikk utvidet: belongs_to-edge-opprettelse legger
render_index-jobb i kø (statisk) eller invaliderer cache (dynamisk).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer rendering-pipeline: metadata.document (TipTap JSON) → HTML
via Tera-templates → CAS-lagring → metadata.rendered oppdateres.
Nye moduler:
- tiptap.rs: Konverterer TipTap/ProseMirror JSON til HTML. Støtter
paragraph, heading, blockquote, lister, code_block, image, hr,
og marks (bold, italic, strike, code, link, underline).
XSS-sikker med HTML-escaping.
- render_article jobb i jobbkøen: Henter node + samling, konverterer
document → HTML, rendrer med Tera + tema, lagrer i CAS, oppdaterer
nodens metadata.rendered med html_hash og renderer_version.
Endringer:
- publishing.rs: SeoData-struct med OG-tags, canonical URL, JSON-LD.
render_article_to_cas() for full pipeline. serve_article() serverer
fra CAS (immutable cache) hvis pre-rendret, fallback til on-the-fly.
RENDERER_VERSION=1 for fremtidig bulk re-rendering.
- intentions.rs: Trigger render_article-jobb automatisk når belongs_to
edge opprettes til samling med publishing-trait.
- Alle 4 artikkel-templates: SEO-block med meta description, OG-tags
(type, title, description, url, site_name, image, published_time),
canonical URL, RSS-link, og JSON-LD structured data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kobler kommunikasjonsnoder til LiveKit for sanntidslyd.
Bruker sender join_communication-intensjon, maskinrommet validerer
tilgang og returnerer signert LiveKit JWT-token + rom-URL.
Nye komponenter:
- maskinrommet/src/livekit.rs: JWT token-generering (HS256-signert
med LIVEKIT_API_SECRET, 1-times TTL, publisher/subscriber-roller)
- POST /intentions/join_communication: validerer deltaker-edge,
genererer token, oppretter rom i STDB, oppdaterer node-metadata
- POST /intentions/leave_communication: fjerner deltaker fra STDB
- POST /intentions/close_communication: stenger rom (krever owner)
- SpacetimeDB: live_room + room_participant tabeller for sanntids
deltakerliste (frontend abonnerer via WebSocket)
SpacetimeDB-modul publisert som synops-v2 (ny identitet etter
at den opprinnelige ikke lenger var tilgjengelig). .env oppdatert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny jobbtype `summarize_communication` som henter alle meldinger fra
en kommunikasjonsnode, sender dem til LiteLLM for oppsummering, og
oppretter en content-node med sammendraget. Sammendraget knyttes til
kommunikasjonsnoden med `belongs_to`-edge (del av samtalen) og
`summary`-edge (lett å finne sammendrag for en gitt samtale).
API-endepunkt: POST /intentions/summarize { communication_id }
Verifiserer at brukeren er deltaker i samtalen. Jobbprioritiet 3
(bakgrunn). Modell konfigurerbar via AI_SUMMARY_MODEL env-variabel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny jobbtype `suggest_edges` som automatisk trigges ved opprettelse av
content-noder med tilstrekkelig tekst (≥20 tegn). Sender innholdet til
LiteLLM (sidelinja/rutine) via AI Gateway, parser JSON-respons med
topics og mentions, og oppretter topic-noder + mentions-edges i grafen.
Flyten:
1. create_node oppdager content-node med nok tekst → enqueue suggest_edges
2. Worker henter node-innhold og eksisterende topics fra PG
3. LLM analyserer tekst og returnerer foreslåtte topics/mentions
4. Nye topic-noder opprettes (med ai_generated-flagg i metadata)
5. mentions-edges opprettes fra innholdsnode til topic/entitet-noder
6. Deduplisering: gjenbruker eksisterende topics ved case-insensitivt match
Filer:
- maskinrommet/src/ai_edges.rs: Ny modul med LLM-kall og edge-opprettelse
- maskinrommet/src/jobs.rs: suggest_edges registrert i dispatcher
- maskinrommet/src/intentions.rs: Trigger i create_node
- docs/: Oppdatert jobbkø og AI gateway-docs med ny jobbtype
NB: Krever gyldig API-nøkkel i LiteLLM (OpenRouter/Gemini/Anthropic).
Jobben feiler gracefully med retry+backoff ved manglende nøkkel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Setter opp AI Gateway med LiteLLM som sentralisert proxy for alle
AI-kall. PG eier all modellkonfigurasjon — LiteLLM er stateløs.
- Migrasjon 008: ai_model_aliases, ai_model_providers, ai_job_routing
med seed-data for sidelinja/rutine og sidelinja/resonering
- Config-generering fra PG: scripts/generate-litellm-config.sh
filtrerer bort providers med tomme API-nøkler
- Docker-container kjører på sidelinja-net (intern, ingen eksponert port)
- Maskinrommet har AI_GATEWAY_URL via maskinrommet-env.sh
- API-nøkkel-placeholders i .env (GEMINI, ANTHROPIC, XAI)
- Oppdatert docs/infra/ai_gateway.md med faktisk config
Verifisert: container healthy, modellaliaser eksponert, maskinrommet
har korrekt gateway-URL. Reelle API-kall krever at Vegard fyller
inn leverandør-nøkler i /srv/synops/.env.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Legger til LiveKit som Docker-tjeneste for WebRTC-støtte.
Konfigurasjonen bruker livekit/livekit-server med signaling
proxyet gjennom Caddy på /livekit/*, og UDP 50000-50100 eksponert
direkte for WebRTC media-strømmer.
Endringer:
- docker-compose.yml: livekit-service (på /srv/synops/)
- livekit.yaml: server-konfig (på /srv/synops/config/livekit/)
- Caddy: /livekit/* route aktivert
- UFW: åpnet UDP 50000-50100 + TCP 7881
- maskinrommet-env.sh: LIVEKIT_URL/KEY/SECRET for Rust-API
- produksjon.md: oppdatert med LiveKit-detaljer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny /diary-route som viser brukerens private noder — de som kun har
owner-edge og ingen delte edges til andre. Gruppert etter dato,
nyeste først, med inline oppretting av nye innlegg.
Dagbok-knapp med tellebadge lagt til i mottak-siden.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ny rute /calendar som viser alle noder med scheduled-edge i et
månedsbasert kalenderrutenett. Bruker edge-metadata { at: ISO8601 }
for tidspunkt, med T12:00:00-konvensjon for heldagshendelser.
Funksjoner:
- Månedsnavigering med «I dag»-snarvei
- Drag-and-drop for å flytte hendelser mellom datoer (updateEdge)
- Inline-oppretting med tittel og valgfritt klokkeslett
- Fargekoding etter node_kind
- Hendelsesliste under rutenett for gjeldende måned
- Kalender-lenke med hendelsesteller på mottak-siden
- Sanntid via SpacetimeDB (edgeStore.byType('scheduled'))
Arkitekturvalg: Bruker scheduled-edges direkte fra SpacetimeDB
i stedet for legacy calendar_events-tabellen. En node blir en
kalenderoppføring ved å ha en scheduled-edge — konsistent med
«hva edges gjør med noder»-prinsippet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer kanban som noder+edges uten separate tabeller:
- Board = collection-node med metadata.board og metadata.columns
- Kort = content-noder med belongs_to-edge til board
- Status via status-edge (kort→board) med metadata.value
- Posisjon via belongs_to-edge metadata.position
Backend:
- POST /intentions/update_edge — oppdater edge-type/metadata
- GET /query/board?board_id= — hent kort med status og posisjon
Frontend:
- /board/[id] route med kolonner, drag-and-drop, kortoppretting
- Sanntid via SpacetimeDB edge-subscriptions
- Board-oppretting og navigasjon fra mottak-siden
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dokumenterer gjeldende modell der egenutviklet kode (maskinrommet,
SvelteKit) kjører native via systemd, mens tredjepartstjenester
(PG, STDB, Authentik, Caddy, Whisper, LiteLLM) kjører i Docker.
- CLAUDE.md: ny driftsmodell-tabell, Claude-agent-seksjon
- docs/arkitektur.md: teknologivalg med kjøremodus-kolonne
- docs/setup/produksjon.md: maskinrommet native instruksjoner
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fjerner alle referanser til lokal WSL2-instans og to-instans-modellen.
All utvikling skjer nå direkte på produksjonsserveren via Claude Code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Node.js, Rust og Claude Code installert på produksjonsserveren.
Repo klonet til /home/vegard/synops/, tmux-alias konfigurert.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Oppretter transcription_segments-tabellen i PostgreSQL som master-kopi
for alle transkripsjoner. transcribe.rs er oppdatert fra verbose_json
til SRT-format med full parse → segment-innsetting pipeline.
Endringer:
- Migration 005: transcription_segments med GIN fulltekstsøk (norsk)
- transcribe.rs: SRT-parser, segment-innsetting, node-oppdatering
- Miljøvariabler: WHISPER_MODEL (default "medium"), WHISPER_INITIAL_PROMPT
- Docker-compose: nye env vars for maskinrommet-containeren
- Docs: oppdatert podcastfabrikken, arkitektur, primitiver, CLAUDE.md
Tabellen kjørt på server, maskinrommet restartet med nye env vars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer komplett pipeline for automatisk transkripsjon av lydfiler:
- PostgreSQL jobbkø (job_queue-tabell med status, retry, backoff)
- Worker-loop i maskinrommet som poller hvert 2. sekund
- Whisper-integrasjon: leser CAS-fil, sender multipart til faster-whisper API
- Postprosessering: filtrerer hallusinerte segmenter (no_speech_prob > 0.6)
- Oppdaterer media-nodens content-felt med transkripsjon og metadata
- Automatisk trigger: upload_media enqueuer jobb for audio/*-filer
Testet ende-til-ende på server: jobb plukkes opp, Whisper prosesserer,
node oppdateres. Retry med eksponentiell backoff ved feil.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Satt opp faster-whisper-server (fedirz/faster-whisper-server:latest-cpu)
som Docker-tjeneste på produksjonsserveren. Ingen GPU tilgjengelig —
bruker CPU med int8-kvantisering og large-v3 modell for best norsk kvalitet.
Verifisert:
- Transkripsjon fungerer via OpenAI-kompatibelt API
- verbose_json med segmenter og tidskoder OK
- Docker DNS-oppslag fra sidelinja-net fungerer
- Maskinrommet har WHISPER_URL=http://faster-whisper:8000
- RAM-bruk ~2.5 GB med modell lastet
Konfigurasjon:
- Image: fedirz/faster-whisper-server:latest-cpu
- Modell: large-v3 (norsk), int8, CPU
- CAS montert read-only for direkte filtilgang
- Healthcheck via python3 (curl ikke tilgjengelig i image)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer Row Level Security for tunge PostgreSQL-spørringer.
Maskinrommet skriver som superuser (sidelinja), men leser med
SET LOCAL ROLE synops_reader som er underlagt RLS-policies.
Endringer:
- Migration 004: synops_reader rolle, current_node_id() funksjon,
RLS-policies på nodes (created_by/node_access/visibility),
edges (endepunkt-tilgang + system-edge-skjuling),
og node_access (kun egne rader)
- queries.rs: RLS-kontekst-helper (set_rls_context) og
GET /query/nodes endepunkt med søk, filtrering og paginering
- migration_safety.md: omskrevet fra v1 workspace-RLS til
node_access-basert RLS med oppdaterte leak hunter-tester
Verifisert på server: hidden noder filtrert for ukjente brukere,
synlige for eiere. Edges filtrert tilsvarende.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Legger til steg 4 i recompute_access: når en bruker melder seg inn i
et team (member_of-edge), arver brukeren all tilgang teamet allerede
har. Tidligere håndterte funksjonen kun retningen "team får ny tilgang
→ propager til eksisterende medlemmer" (steg 3). Nå håndteres begge
retninger:
- Steg 3: Team får tilgang → alle eksisterende medlemmer arver
- Steg 4: Ny bruker melder seg inn → arver teamets eksisterende tilgang
Testet med scenario: Trond → Podcastteamet → Sidelinja → Episode 42.
Trond arver member-tilgang til alle tre noder via team-transitivitet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer autentisering med Authentik via @auth/sveltekit:
- OIDC authorization code flow med PKCE og state-verifisering
- JWT-callback lagrer authentik_sub (SHA256-hash, ikke UUID) for
konsistens med maskinrommets auth_identities-tabell
- Server hooks: alle ruter unntatt /signin og /auth/* krever sesjon
- Uautentiserte brukere redirectes til /signin (303)
- Innloggingsside med client-side signIn('authentik')
- Hovedside viser innlogget bruker med logg ut-knapp
- TypeScript-typer utvidet med JWT.authentik_sub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Oppretter frontend/ med SvelteKit, TypeScript, TailwindCSS v4 og
adapter-node. PWA-manifest med SVG-ikon. Vite proxy til
api.sidelinja.org for lokal utvikling med HMR.
Oppdaterer docs/setup/lokal.md med riktige stier (frontend/ og
maskinrommet/ i stedet for web/ og rust/).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maskinrommet kjører nå som Docker-service i server-stacken:
- Lagt til maskinrommet-service i docker-compose.yml på server
- Intern nettverkstilgang til PG (postgres:5432) og STDB (spacetimedb:3000)
- Caddy proxyer api.sidelinja.org → maskinrommet:3100
- Verifisert: health-endpoint, PG-tilkobling, STDB-tilkobling, warmup,
auth-middleware (401 uten token)
Oppdatert docs/setup/produksjon.md med:
- Maskinrommet i service-oversikt og Caddyfile
- SpacetimeDB-variabler i .env-template
- Deploy-instruksjoner for bygging av Docker-image
- Verifiseringssjekkliste
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer tre nye skrivestier i maskinrommet med tilgangskontroll:
- POST /intentions/create_edge — opprett retningsbestemt edge mellom
to noder. Validerer at begge noder eksisterer og edge_type er satt.
- POST /intentions/update_node — partial update av eksisterende node.
Kun oppgitte felter endres, resten beholdes fra PG.
- POST /intentions/delete_node — slett node med cascade av edges.
Tilgangskontroll for update/delete: brukeren må enten være created_by
på noden, eller ha en owner/admin-edge til den. Sjekkes mot PG som
autoritativ kilde.
Alle endepunkter følger samme mønster som create_node:
STDB-skriving (instant) → async PG-persistering → umiddelbar respons.
Verifisert på server med 10 testcaser:
1. /me med gyldig token → 200
2. create_node → 200 med node_id
3. create_edge (gyldig) → 200 med edge_id
4. create_edge (ugyldig source) → 400
5. create_edge (tom edge_type) → 400
6. update_node (partial, eier) → 200
7. update_node (ingen tilgang) → 403
8. delete_node (eier) → 200
9. delete_node (ingen tilgang) → 403
10. update via owner-edge (Sidelinja) → 200
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer den første intensjonen i maskinrommet — skrivestien
som gjør at frontend kan opprette noder via maskinrommet.
Flyten:
1. Valider input (node_kind, visibility, metadata)
2. Generer UUIDv7 (tidssortert)
3. Skriv til SpacetimeDB (instant — frontend ser noden umiddelbart)
4. Spawn async tokio-task for PG-persistering
5. Returner node_id uten å vente på PG
Verifisert på server med fire testcaser:
1. Uten auth → 401
2. Ugyldig visibility → 400 med feilmelding
3. Minimal request (tomt body) → 200, node opprettet med defaults
4. Full request → 200, node verifisert i både STDB og PG
Også: Dockerfile oppdatert til Rust 1.88 (avhengigheter krevde >1.86),
og api_grensesnitt.md oppdatert med endepunktdokumentasjon.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fiks NULL-håndtering i warmup (COALESCE for title/content/created_by)
- Renere health check (delete nonexistent node i stedet for create+delete)
- Dokumenter HTTP API-format og warmup-flyt i erfaringer
- Lagre STDB-token i server .env
- Republiser STDB-modul etter containerrestart
Verifisert: warmup laster 2 noder + 1 edge, /health viser stdb=connected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fikser audience-validering (AUTHENTIK_CLIENT_ID som forventet aud)
- Oppdaterer seed-data med reell Authentik sub for Vegard
- Fikser DATABASE_URL i .env: peker nå til synops-database (ikke sidelinja)
- Dokumenterer maskinrommet-miljøvariabler i produksjon.md
- Markerer oppgave 2.2 som ferdig i tasks.md
Verifisert på server med fem testcaser:
1. /health (public) → 200
2. /me uten token → 401 "Mangler Authorization-header"
3. /me med ugyldig token → 401 "Ugyldig token"
4. /me med gyldig JWT, ukjent sub → 401 "Ukjent brukeridentitet"
5. /me med gyldig JWT, kjent sub → 200 med node_id
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Det gamle dokumentet beskrev en v1-arkitektur der SvelteKit var web-API
og Rust kun var workers. Med maskinrommet-retningen (besluttet) er Rust
en axum HTTP API-server som mottar intensjoner fra frontend og eier
alle skrivinger. Dokumentet er omskrevet til å reflektere dette.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OIDC-provider "sidelinja-web" og applikasjon "Sidelinja" var allerede
konfigurert i Authentik fra v1-oppsettet. Verifisert at alt fungerer:
- Discovery-endepunkt svarer korrekt
- JWKS tilgjengelig for JWT-validering (RS256)
- Redirect URIs konfigurert for prod + lokal utvikling (regex)
- Implicit consent flow (ingen godkjenningsskjerm)
- Client credentials lagret i server .env
Opprettet docs/erfaringer/authentik_oppsett.md som kanonisk referanse
for OIDC-konfigurasjonen — endepunkter, redirect URIs, scopes,
JWT-validering for maskinrommet, og API-administrasjon.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>