Oppdaterer tasks.md (19.6 → ferdig), dokumenterer workspace node_kind i nodes.md og personlig arbeidsflate i arbeidsflaten.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
160 lines
5.6 KiB
Markdown
160 lines
5.6 KiB
Markdown
# Nodes — spesifikasjon
|
|
|
|
**Status: Besluttet.**
|
|
|
|
> Alt er noder. Brukere, team, prosjekter, innhold, møter, mediefiler,
|
|
> kunnskapsgraf-entiteter — alt er rader i `nodes`-tabellen. Hva en
|
|
> node "er" bestemmes av dens edges, ikke av noden selv.
|
|
|
|
## Skjema
|
|
|
|
```sql
|
|
CREATE TYPE visibility AS ENUM ('hidden', 'discoverable', 'readable', 'open');
|
|
|
|
CREATE TABLE nodes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
node_kind TEXT NOT NULL DEFAULT 'content',
|
|
title TEXT,
|
|
content TEXT,
|
|
visibility visibility NOT NULL DEFAULT 'hidden',
|
|
metadata JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
created_by UUID REFERENCES nodes(id)
|
|
);
|
|
|
|
CREATE INDEX idx_nodes_kind ON nodes (node_kind);
|
|
CREATE INDEX idx_nodes_created_by ON nodes (created_by);
|
|
CREATE INDEX idx_nodes_visibility ON nodes (visibility);
|
|
```
|
|
|
|
## Kolonner
|
|
|
|
### `id`
|
|
UUID, generert. Primærnøkkel for alt i systemet.
|
|
|
|
### `node_kind`
|
|
Hint om hva noden primært er. Freeform streng — ikke en enum,
|
|
samme filosofi som edge-typer. Edges kan gi noden flere roller
|
|
utover dette.
|
|
|
|
Kjente node_kinds:
|
|
|
|
| Kind | Eksempel |
|
|
|------|----------|
|
|
| `person` | Vegard, Trond, Bjørn (alias) |
|
|
| `team` | Podcastteamet |
|
|
| `collection` | Sidelinja Podcast, Research-gruppa |
|
|
| `content` | Chatmelding, bloggpost, notat, dagboknotis |
|
|
| `communication` | Møte, samtale, livesending |
|
|
| `topic` | Kunnskapsgraf-entitet (Jonas Gahr Støre, Skolepolitikk) |
|
|
| `media` | CAS-node (lydfil, bilde, video) |
|
|
| `agent` | AI-agent (Claude, system) |
|
|
| `system_announcement` | Systemvarsler |
|
|
| `ai_preset` | AI-verktøy-preset (prompt, modellprofil, kategori) |
|
|
| `workspace` | Personlig arbeidsflate (én per bruker, auto-provisjonert) |
|
|
|
|
Listen vokser organisk etter behov.
|
|
|
|
### `title`
|
|
Intern visningsnavn brukt i lister, søkeresultater, mottaksflaten.
|
|
Universelt — navnet på en person, arbeidstittelen på en bloggpost,
|
|
navnet på en podcast.
|
|
|
|
- Vegard: `'Vegard'`
|
|
- Sidelinja: `'Sidelinja Podcast'`
|
|
- Bloggpost: `'Hvorfor noder er sentrum'`
|
|
- Chatmelding: `NULL` (har sjelden tittel)
|
|
|
|
**Viktig distinksjon:** `title` er det interne displaynavnet. For
|
|
publisert innhold er den offentlige overskriften en *egen node* med
|
|
`title`-edge til artikkelen — fordi den kan ha varianter, A/B-testes,
|
|
og har en egen forfatter/tidspunkt. Se
|
|
[publisering](../concepts/publisering.md) § "Presentasjonselementer".
|
|
|
|
### `content`
|
|
Ren tekst uten formatering. Brukes til fulltekstsøk og enkel
|
|
visning. For rike dokumenter (formatert tekst med bilder) genereres
|
|
`content` automatisk fra `metadata.document` ved lagring.
|
|
|
|
- Bloggpost: teksten uten markup (generert fra `metadata.document`)
|
|
- Chatmelding: `'Hei, er du klar?'`
|
|
- Voice memo: `NULL` ved opprettelse, fylles etter transkribering
|
|
- Brukernode: `NULL`
|
|
- CAS-node: `NULL` (binærdata lever på disk)
|
|
|
|
For formatert innhold: se `metadata.document` under metadata.
|
|
|
|
### `visibility`
|
|
Default synlighet for alle uten eksplisitt edge.
|
|
Se [noder er sentrum](../retninger/bruker_ikke_workspace.md)
|
|
for full spesifikasjon av visibility-nivåer og traverseringsregelen.
|
|
|
|
### `metadata`
|
|
JSONB for alt typespesifikt som ikke er tittel eller ren tekst:
|
|
|
|
- Person: `{ "display_name": "Vegard", "preferences": { ... } }`
|
|
- CAS-node: `{ "cas_hash": "abc123", "mime": "audio/mp3", "size_bytes": 84000000 }`
|
|
- Kommunikasjonsnode: `{ "started_at": "...", "ended_at": "..." }`
|
|
- Samlings-node: `{ "pruning_profile": "conservative", "theme": "dark" }`
|
|
- Rikt dokument: `{ "document": { "type": "doc", "content": [...] } }`
|
|
|
|
`metadata.document` inneholder TipTap/ProseMirror JSON for formatert
|
|
innhold (overskrifter, bilder, blockquotes, etc.). Bilder refererer
|
|
til CAS-noder via `node_id`. For enkle meldinger (ren tekst) er
|
|
`document` null — `content` er alt som trengs. Se
|
|
[universell input](../retninger/universell_input.md) for detaljer.
|
|
|
|
### `created_at`
|
|
Tidsstempel, automatisk.
|
|
|
|
### `created_by`
|
|
Referanse til noden som opprettet denne noden. For alias-bruk:
|
|
`created_by` settes til aliasnoden, ikke brukernoden bak.
|
|
|
|
## Brukernoder
|
|
|
|
En brukernode er en node med `node_kind = 'person'` og en rad i
|
|
`auth_identities`:
|
|
|
|
```sql
|
|
CREATE TABLE auth_identities (
|
|
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
|
|
authentik_sub TEXT UNIQUE NOT NULL,
|
|
email TEXT UNIQUE NOT NULL
|
|
);
|
|
```
|
|
|
|
Alt annet — profil, preferanser, relasjoner — er noden og dens
|
|
edges. Autentiseringstabellen er en tynn bro mellom HTTP-sesjonen
|
|
og grafen.
|
|
|
|
## CAS-noder (mediefiler)
|
|
|
|
Binærfiler er noder med `node_kind = 'media'`. Selve biten lever
|
|
på disk i CAS-lageret. Noden bærer bare metadata.
|
|
|
|
```
|
|
Episode #42 (content)
|
|
──has_media──→ CAS-node (media, metadata: { cas_hash: "abc123", mime: "audio/mp3" })
|
|
──has_media──→ CAS-node (media, metadata: { cas_hash: "def456", mime: "text/srt" })
|
|
──belongs_to──→ Sidelinja Podcast (collection)
|
|
```
|
|
|
|
En innholdsnode kan ha mange mediefiler via edges. Pruning-logikken
|
|
i maskinrommet opererer på CAS-noder — sletter binærfilen fra disk,
|
|
men noden kan leve videre som tombstone.
|
|
|
|
## Eierskap og tilgang
|
|
|
|
`created_by` gir redigeringsrett til egne noder. Men sletting og
|
|
strukturelle endringer scopes av rollen i konteksten:
|
|
|
|
- Du kan alltid redigere noder du opprettet.
|
|
- Sletting krever `owner` eller `admin` i samlings-noden
|
|
innholdet tilhører — selv om du opprettet det.
|
|
- En privat node (ingen samlings-edge) kan slettes fritt av creator.
|
|
|
|
Flere noder kan ha `owner`-tilgang til samme node via
|
|
tilgangsmatrisen (direkte og transitiv). Forretningslogikk for
|
|
hva ulike tilgangsnivåer tillater lever i maskinrommet, ikke i
|
|
databasen.
|