synops/docs/setup/migration_safety.md
vegard 0a467066ba Synops v2: arkitektur, retninger og dokumentasjon
Nystart basert på arkitektonisk innsikt fra Sidelinja v1.
Koden er ny, visjon og primitiver er validert gjennom tidligere arbeid.

Inneholder:
- Komplett arkitekturdokumentasjon (docs/arkitektur.md)
- 6 vedtatte retninger (docs/retninger/)
- Alle concepts, features, proposals og erfaringer fra v1
- Server-oppsett og drift (docs/setup/)
- LiteLLM-konfigurasjon (API-nøkler via env)
- Editor.svelte referanse fra v1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 06:43:08 +01:00

148 lines
5.4 KiB
Markdown

# Migration Safety Checklist
Sjekkliste for alle som kjører PostgreSQL-migrasjoner — lokalt eller i prod.
## Før migrering
- [ ] Les migrasjonfilen og forstå hva den gjør
- [ ] Har migrasjonen en tilhørende **down-migrering**? (påkrevd for skjema-endringer)
- [ ] Tar migrasjonen backup-hensyn? (dropper den kolonner/tabeller med data?)
## Etter migrering
### RLS-verifisering (KRITISK)
Etter *enhver* migrering som oppretter eller endrer tabeller med `workspace_id`:
```sql
-- 1. Verifiser at RLS er aktivert på alle workspace-tabeller
SELECT tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND tablename IN ('nodes', 'graph_edges', 'messages', 'channels',
'media_files', 'job_queue', 'message_attachments')
ORDER BY tablename;
-- Forventet: rowsecurity = true for alle
-- 2. Verifiser at policies eksisterer
SELECT tablename, policyname, cmd, qual
FROM pg_policies
WHERE schemaname = 'public'
ORDER BY tablename;
-- Forventet: workspace_isolation_* policy for hver tabell
-- 3. Test isolasjon: sett workspace A, forsøk å lese workspace B
SET app.current_workspace_id = '<workspace_a_uuid>';
SELECT count(*) FROM nodes WHERE workspace_id = '<workspace_b_uuid>';
-- Forventet: 0 rader (RLS blokkerer)
-- 4. Verifiser at superuser IKKE blokkeres (Rust workers trenger dette)
RESET app.current_workspace_id;
SET ROLE postgres;
SELECT count(*) FROM nodes;
-- Forventet: alle rader synlige
```
### Indeksverifisering
```sql
-- Sjekk at viktige indekser finnes
SELECT indexname, tablename
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename IN ('nodes', 'graph_edges', 'messages')
ORDER BY tablename;
```
### Constraint-verifisering
```sql
-- Sjekk at foreign keys er intakte
SELECT tc.table_name, tc.constraint_name, tc.constraint_type
FROM information_schema.table_constraints tc
WHERE tc.table_schema = 'public'
AND tc.constraint_type IN ('FOREIGN KEY', 'CHECK', 'UNIQUE')
ORDER BY tc.table_name;
```
## RLS Leak Hunter (CI-test)
`SET app.current_workspace_id` er en skjult single point of failure — en glemt SET i en ny feature, en feil i connection-pool, eller en ny tjeneste som kobler til PG uten middleware kan føre til cross-workspace datalekkasje. Denne testen fanger det opp.
### Automatisk CI-test (to-workspace leak detection)
Kjøres i migrasjonstester og som egen CI-steg:
```sql
-- Opprett to test-workspaces
INSERT INTO workspaces (id, name, slug) VALUES
('aaaaaaaa-0000-0000-0000-000000000001', 'Workspace A', 'ws-a'),
('aaaaaaaa-0000-0000-0000-000000000002', 'Workspace B', 'ws-b');
-- Seed testdata i begge
INSERT INTO nodes (id, node_type, workspace_id) VALUES
('bbbbbbbb-0000-0000-0000-000000000001', 'tema', 'aaaaaaaa-0000-0000-0000-000000000001'),
('bbbbbbbb-0000-0000-0000-000000000002', 'tema', 'aaaaaaaa-0000-0000-0000-000000000002');
-- TEST 1: Sett workspace A, forsøk å lese workspace B
SET app.current_workspace_id = 'aaaaaaaa-0000-0000-0000-000000000001';
DO $$
BEGIN
IF (SELECT count(*) FROM nodes WHERE workspace_id = 'aaaaaaaa-0000-0000-0000-000000000002') > 0 THEN
RAISE EXCEPTION 'RLS LEAK: Workspace A kan lese Workspace B sine noder!';
END IF;
END $$;
-- TEST 2: Uten SET (tom current_setting) skal returnere 0 rader
RESET app.current_workspace_id;
DO $$
BEGIN
-- For vanlig bruker (ikke superuser) bør dette returnere 0
IF (SELECT count(*) FROM nodes) > 0 AND current_setting('is_superuser') = 'off' THEN
RAISE EXCEPTION 'RLS LEAK: Uautentisert tilkobling kan lese data!';
END IF;
END $$;
```
### Audit-trigger (produksjon)
Valgfri trigger som logger mistenkelige queries i prod:
```sql
-- Tabell for RLS-audit
CREATE TABLE IF NOT EXISTS rls_audit_log (
id BIGSERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL,
current_workspace TEXT,
session_user TEXT NOT NULL,
query_timestamp TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Funksjon som logger når current_workspace_id ikke er satt
CREATE OR REPLACE FUNCTION audit_rls_context() RETURNS TRIGGER AS $$
BEGIN
IF current_setting('app.current_workspace_id', true) IS NULL
OR current_setting('app.current_workspace_id', true) = '' THEN
IF current_setting('is_superuser') = 'off' THEN
INSERT INTO rls_audit_log (table_name, operation, current_workspace, session_user)
VALUES (TG_TABLE_NAME, TG_OP, current_setting('app.current_workspace_id', true), session_user);
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
```
**Kjør leak hunter mot ALLE tabeller med workspace_id — ikke bare de som er listet over.** Nye tabeller legges til i listen automatisk via introspeksjon:
```sql
-- Finn alle tabeller med workspace_id-kolonne (bør alle ha RLS)
SELECT t.tablename
FROM pg_tables t
JOIN information_schema.columns c ON c.table_name = t.tablename
WHERE c.column_name = 'workspace_id'
AND t.schemaname = 'public'
AND NOT EXISTS (
SELECT 1 FROM pg_policies p WHERE p.tablename = t.tablename
);
-- Forventet: 0 rader. Enhver rad her = tabell med workspace_id UTEN RLS-policy.
```
## Automatisering
Disse sjekkene kjøres automatisk i migrasjonstestene (se `docs/arkitektur.md` §10.2). Manuell kjøring er kun nødvendig ved prod-migrasjoner til automatiserte tester er på plass. **RLS Leak Hunter bør prioriteres som første CI-steg — den beskytter mot den mest alvorlige feilkategorien (cross-workspace datalekkasje).**