SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet). Kode fjernet: - spacetimedb/ Rust-modul og spacetime.json - maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers) - frontend module_bindings/ (23 auto-genererte filer) - spacetimedb npm-avhengighet fra package.json - scripts/test-sanntid.sh (testet STDB-flyt) Infrastruktur: - Docker-container stoppet og fjernet fra docker-compose.yml - Caddy: fjernet /spacetime/* reverse proxy - maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler - .env.example: fjernet SpacetimeDB-seksjoner Dokumentasjon oppdatert: - CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell - docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg - docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet" - 37 andre docs oppdatert (features, concepts, infra, ops, retninger) - Alle kode-kommentarer med STDB-referanser oppdatert Verifisert: maskinrommet bygger og starter OK, frontend bygger OK, helsesjekk returnerer 200. Caddy reloadet.
9.2 KiB
Universell input og mottak
Status: Besluttet.
Én multimodal input-primitiv. Én personlig mottaksflate. Alt som fanges er en node. Hva det "er" bestemmes av edges. Hvordan det presenteres bestemmes av mottakeren.
Input-primitiven
Én overflate som fanger alt:
- Tekst — skriving, Markdown, kodeblokker
- Lyd — voice memo, diktering → automatisk transkribert
- Bilde — foto, skjermbilde, tegning
- AI-støtte — spør AI, få forslag, la den transformere input
- URL — lim inn lenke, den berikes automatisk
Brukeren gjør det samme uansett kontekst — skriver, snakker eller tegner i input-feltet. Forskjellen mellom "dagbok", "chatmelding" og "kanban-kort" er ikke hva brukeren gjør — det er hvilke edges som knyttes til resultatet.
Én pipeline
All input går gjennom samme tekniske pipeline: maskinrommet → PG → NOTIFY → WebSocket (sanntid).
Konteksten bestemmer routing, ikke en teknisk modus:
- Du er i et møte → inputen streames live til andre deltakere
- Du er alene på bussen → inputen lander som privat node
- Du er i en podcast-kanal → inputen går inn i produksjonspipeline
Samme pipeline. Ulike edges.
Input-metode og innholdstype er ortogonale
Du kan snakke inn et kanban-kort. Du kan tegne en kalenderoppføring. Input-primitiven bryr seg ikke om hva det blir — den fanger det som kommer inn. Alt etterpå er edges.
Én overflate å perfeksjonere
All UX-investering konsentreres ett sted. Én perfekt input-opplevelse — responsiv, multimodal, med god AI-støtte — i stedet for ti middelmådige spesialgrensesnitt.
Editoren
Input-komponenten er en TipTap-editor (ProseMirror-basert) som konfigureres med ulike extensions basert på kontekst.
Kontekst setter default, brukeren bestemmer
Konteksten (kommunikasjonsnoden du er i, visningen du bruker) setter en default editor-konfigurasjon. Men brukeren kan alltid overstyre — utvide til full editor eller forenkle. Ingen kunstig grense mellom "chatmelding" og "artikkel."
Du kan skrive en gjennomformatert tekst i chatten. Men hvis en
chatmelding inspirerer en artikkel, drar du den til artikkelverktøyet
på arbeidsflaten. En ny innholdsnode opprettes med source_material-edge
til meldingen. Artikkelen skrives i et verktøy designet for langform —
chatinput er designet for samtale. Se arbeidsflaten.
Editoren husker brukerens valg per kontekst via preferanser på brukernoden.
Presets
| Kontekst | Default extensions | Eksempel |
|---|---|---|
| Chat | Tekst, markdown, kodeblokker, lenker | Enkel melding |
| Artikkel/blogg | + overskrifter, bilder, embeds, blockquotes, tabeller | Publisert tekst |
| Show notes | + lister, tidskoder, lenker | Episodenotater |
| Kanban-kort | Tekst, sjekklister | Oppgavebeskrivelse |
Presets er bare default — brukeren kan utvide eller forenkle med en knapp eller tastatursnarvei. Ikke en modebytte, bare at flere verktøy blir tilgjengelig.
Tekstlagring
Noden lagrer to representasjoner:
content TEXT— ren tekst uten formatering, for fulltekstsøk og enkel visningmetadata.document JSONB— strukturert TipTap/ProseMirror- dokument for rendering
{
"type": "doc",
"content": [
{ "type": "paragraph", "content": [
{ "type": "text", "text": "Her er en intro." }
]},
{ "type": "image", "attrs": {
"node_id": "uuid-av-cas-node", "alt": "Diagram"
}},
{ "type": "paragraph", "content": [
{ "type": "text", "text": "Teksten fortsetter." }
]}
]
}
content genereres automatisk fra dokumentet ved lagring — bare
teksten, uten markup. Editoren produserer begge.
For enkle meldinger (ren tekst uten formatering) er
metadata.document null — content er alt som trengs.
Bilder og media i tekst
Bilder i dokumentet refererer til CAS-noder via node_id.
CAS-noden er en egen node med has_media-edge til innholdsnoden.
Dokumentstrukturen bestemmer hvor bildet plasseres i teksten.
Tekst er på noden. Binærfiler er andre noder koblet med edges. Ren separasjon: tekst er innhold, binærfiler er vedlegg som kan plasseres inline.
Edges definerer alt
Hva en node "er" bestemmes utelukkende av edges:
- Node +
belongs_to→ kanal = chatmelding - Node +
belongs_to→ board +status= kanban-kort - Node +
scheduled→ tidspunkt = kalenderoppføring - Node uten edges til andre = privat notat
- Node +
mentions→ topic = faktoid i kunnskapsgrafen - Node uten edges = løs tanke, ennå uorganisert
Nye noder fra eksisterende. Chatmelding → oppgave = ny
oppgavenode med source_material-edge til meldingen. Meldingen
forblir uendret. For lettvekts-triage (noden vises i kanban OG
chat): legg til status-edge direkte — ingen ny node nødvendig.
Multitype er naturlig. En node kan ha flere roller samtidig
— både kanban-kort og kalenderoppføring og faktoid. Det er for
noder som genuint tjener flere formål, ikke for å "gro" en
chatboble til en artikkel. Artikkelen er en ny node med
source_material-edge. Se arbeidsflaten.
Edge-tildeling
Når du "bare sier noe" — hvem bestemmer edges?
- Kontekst gir det meste. Du er i en samtale →
belongs_to- edge til samtalen. Du er alene → privat. Dekker 80%. - Eksplisitt handling. Du drar en node til kanban-brettet
(legg til board-edge) eller til artikkelverktøyet (ny node med
source_material-edge). Du tagger noe. Du setter en dato. Se arbeidsflaten for kompatibilitetsmatrise. - AI-foreslått. Systemet foreslår
mentions-edge når du nevner en person. Foreslår kanban når noe ligner en oppgave.
Detaljer for AI-foreslåtte edges avklares ved implementering.
Mottak-primitiven
Der input er "én overflate som fanger alt", er mottak "én overflate som presenterer alt tilpasset deg."
Mottaker bestemmer format
All lyd transkriberes. All tekst kan leses opp (TTS). Noden har alltid begge representasjoner. Mottaker setter sin preferanse:
- Trond snakker inn en tanke → node med lyd + transkripsjon
- Peter har tekst-preferanse → ser transkripsjonen
- Vegard har lyd-preferanse → hører originallyd
Modalitet er ikke en egenskap ved meldingen, men ved lesningen.
Dimensjoner ved mottak
Format. Lyd, tekst, visuelt — mottaker bestemmer.
Filtrering. Mottaksflaten filtrerer basert på dine edges: kanaler du følger, personer du samarbeider med, topics du er interessert i.
Prioritering. AI-assistert vekting: ubesvarte meldinger, oppgaver med frist, noder endret siden sist. Ikke en notifikasjonsliste — en vektet visning av det som er relevant.
Tempo. Sanntid (ting streamer inn) eller asynkront (digest, oppsummering).
Mottaksflaten er en visning av grafen
"Noder med edge til meg, vektet på relevans og tid." Ikke en egen mekanisme — en spørring mot grafen med deg som sentrum.
Kommunikasjonsnoden — den tredje primitiven
Input fanger. Mottak presenterer. Kommunikasjonsnoden er stedet der folk møtes — en node som samler deltakere, definerer tilgangsregler, og fungerer som kontekst.
Én node, mange former
| Variant | Deltakere | Input | Mottak |
|---|---|---|---|
| Én-til-én | 2 | Begge | Begge |
| Gruppechat | N | Alle | Alle |
| Møte | N | Alle | Alle |
| Allmøte | 1 + N | Leder | Alle lytter |
| Podcastinnspilling | 2-4 + N | Verter | Alle lytter |
| Livesending | 1-4 + ∞ | Verter | Streamet |
| Asynkron gjest | 1 + 1 | Gjest | Redaksjonen |
Forskjellen er edge-konfigurasjoner, ikke ulike systemer:
owner-edge — kontrollerer nodenmember-edge — kan gi input og mottareader-edge — kan kun motta
Kontekst arves automatisk
Input i en kommunikasjonsnode arver kontekst-edges. Sier du noe
i et møte → noden får belongs_to-edge til møtet automatisk.
Livssyklus
- Live — deltakere til stede, input streames
- Asynkron — deltakere gir input i eget tempo
- Avsluttet — arkivert, alt som ble sagt er noder med edges
- Gjenåpnet — reaktivert ("vi tar opp tråden fra forrige møte")
Skalering er edge-endring
Samtale → møte = flere deltaker-edges. Møte → livesending = offentlige mottak-edges. Livesending → podcast = publiserings-edges på arkivert innhold.
Tekniske forutsetninger
STT (tale → tekst): løst
Faster-whisper kjører lokalt, god norsk kvalitet.
TTS (tekst → tale): løsbart
Start med ElevenLabs bak AI Gateway, bytt til lokal modell når kvaliteten holder. Backend-swap bak gatewayen — brukeren merker ingenting.
Visninger er spørringer
Chat = noder med kanal-edge, sortert på tid. Kanban = noder med board-edge, gruppert på status. Kalender = noder med dato-edge, på tidslinje. Dagbok = private noder, sortert på tid. Mottaksflate = noder med edge til deg, vektet.
Alle leser fra samme graf. Ingen har "sin egen" data.
Forhold til andre retninger
- Noder er sentrum — visibility, tilgangsmatrise, aliaser
- Datalaget — PG er eneste datakilde, sanntid via LISTEN/NOTIFY + WebSocket
- Maskinrommet — validering, routing, CAS, tunge jobber (Whisper, TTS, AI)
- Rom, ikke forum — kommunikasjonsnoden er den konkrete realiseringen av "rommet"