# Scaffold kanban-kort. Skjema per kort: # - id: kort-slug # unik i fila # title: "" # description: "" # status: todo # todo | doing | done | blocked # column: todo # speiler status (kanban-kolonne) # priority: medium # low | medium | high # assignee: human # cursor | claude | human | any # context: "" # valgfri: hvorfor/hvor i koden # acceptance: "" # valgfri: hva «ferdig» betyr # todos: # - text: "Underoppgave" # done: false # created: 2026-07-09 # updated: 2026-07-09 # completed_at: null tasks: # ─────────────── G0 — Stopp blødningen (P0-blockere) ─────────────── - id: g0-gcm-nonce-reuse title: "Fjern AES-GCM nonce-gjenbruk i at-rest storage" description: > deriveNonce() lager deterministisk nonce fra (fieldKey, tabell, pk) uten meldingsteller. saveSession re-krypterer sesjonsraden ved hvert ratchet-steg, så to DB-snapshots deler (key, nonce) over ulik plaintext = GCM forbidden attack. Systemisk: gjelder alle muterbare rader (config, prekey-state, trust). status: done column: done priority: high assignee: claude context: > Kallesteder kartlagt 2026-08-13: kun row-codec.ts:67 og :96 lager nonce, og :83/:112 tvinger den ved lesing. deriveNonce() er eksportert fra både index.ts og crypto.ts, så den beholdes (deprecated) framfor å brekke konsumenter. VERIFISERT: aeadOpen leser alltid nonce fra blob-prefiks (aead.ts:72) og expectedNonce er kun en valgfri ekstrasjekk — eksisterende blobber åpnes derfor uendret, og ingen migreringssti trengs. acceptance: "Tilfeldig nonce per skriving; aeadOpen leser nonce fra prefiks (dropp expectedNonce-tvang). Negativ test: to session-snapshots ved ulik message-count deler ikke (key, nonce). Test som åpner en blob forseglet med gammel deterministisk nonce. Alle eksisterende tester grønne." todos: - text: "row-codec.ts:67/96 → tilfeldig 12-byte nonce, lagret i blob-prefiks" done: true - text: "Dropp expectedNonce-argumentet i row-codec.ts:83/112" done: true - text: "Negativ regresjonstest for nonce-unikhet på tvers av re-saves" done: true - text: "Bakoverkompatibilitetstest: blob med gammel deterministisk nonce åpnes" done: true - text: "Marker deriveNonce deprecated (fortsatt eksportert fra index/crypto)" done: true - text: "IKKE PUBLISERT — Prism er pinnet på 4.12.0 og får fiksen først ved release" done: false created: 2026-07-10 updated: 2026-08-13 completed_at: 2026-08-13 - id: g0-observer-publish title: "Fiks @shade/observer publiserings-artefakt (tomt på registry)" description: > outDir=dist-build avviker fra alle andre pakker (dist). Publish kjører rm -rf dist og shipper files:[dist] → tarballen mangler JS, main peker på manglende fil. dist-build/ er det untrackede avviket i git status. status: todo column: todo priority: high assignee: claude context: "packages/shade-observer/tsconfig.json (outDir), scripts/publish-all.ts:109/186-227, dashboard copy-to-observer.ts-konflikten" acceptance: "outDir=dist; dashboard-SPA-konflikt løst eksplisitt; 4.11.2 republisert med verifisert tarball; dist-build/ i .gitignore." todos: - text: "tsconfig outDir → dist" done: false - text: "Løs dashboard-SPA vs publish-eierskap til dist/" done: false - text: "gitignore dist-build/ + fjern restene" done: false - text: "Republiser observer 4.11.2" done: false created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g0-typecheck-gate title: "Pin TypeScript, fiks baseUrl, kjør typecheck i CI" description: > TS ikke pinnet noe sted → bunx tsc henter 7.0.2 som fjernet baseUrl (brukt i consumer-strict). typecheck exit 1 nå → prepublish:check rød. Gaten kjøres heller aldri i CI. status: done column: done priority: high assignee: claude context: "tests/consumer-strict/tsconfig.json:17 (baseUrl), scripts/typecheck-all.ts (bruker upinnet bunx tsc), .gitea/workflows/test.yml" acceptance: "typescript pinnet som devDependency og brukt av typecheck-all.ts; baseUrl fjernet; bun run typecheck grønn og kjøres i .gitea/workflows/test.yml." todos: - text: "Pin typescript i root devDependencies" done: true - text: "typecheck-all.ts bruker lokal tsc, ikke bunx latest" done: true - text: "Fjern baseUrl fra consumer-strict tsconfig (var allerede borte)" done: true - text: "Legg typecheck-steg i test.yml" done: true created: 2026-07-10 updated: 2026-08-14 completed_at: 2026-08-14 - id: g0-cli-bin-tarball-verify title: "Fiks cli bin-omskriving + tarball-verifisering i publish" description: > bin.shade=src/cli.ts omskrives ikke til dist/ (rewriteEntryPointsForDist dekker kun main/types/exports). npm i -g @shade/cli gir brutt binær. Samme rot som observer: publish mangler integritetssjekk. status: todo column: todo priority: high assignee: claude context: "scripts/publish-all.ts:186-196 (rewriteEntryPointsForDist), packages/shade-cli/package.json (bin)" acceptance: "bin omskrives til dist/; publish asserterer at bun pm pack inneholder main-filen for hver pakke; npm i -g @shade/cli kjørbar." todos: - text: "Utvid rewriteEntryPointsForDist til pkgJson.bin" done: false - text: "Legg tarball-innhold-assert (bun pm pack) i publish-flyten" done: false created: 2026-07-10 updated: 2026-07-10 completed_at: null # ─────────────── G1 — Revisjonsklar kjerne (P1-sikkerhet) ─────────────── - id: g1-symmetric-safety-number title: "Symmetrisk safety-number (lagre peer-signeringsnøkkel per sesjon)" description: > getRemoteFingerprint sender remoteIdentityKey (DH) som begge argumenter → H(dh‖dh) ≠ peerens egen H(sig‖dh). OOB-verifisering kan aldri matche → primær MITM-forsvar ikke-funksjonelt. status: todo column: todo priority: high assignee: claude context: "packages/shade-core/src/session.ts:238-245, SDK verify()/markPeerVerified()/isPeerVerified() shade.ts:590/649/659" acceptance: "Peer-signeringsnøkkel lagres per sesjon; kanonisk symmetrisk safety-number; kryssparts-test alice.verify(bob, bob.getIdentityFingerprint())===true uten MITM." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g1-argon2id-backup title: "Koble argon2id inn i backup-KDF (TS + Kotlin)" description: > Backup-passord-KDF er HKDF → offline brute-force i HKDF-hastighet. argon2idAsync fra @noble/hashes er allerede dependency og brukes i storage-encrypted. Kotlin har deriveMasterKeyArgon2id via Bouncy Castle. status: todo column: todo priority: high assignee: claude context: "packages/shade-sdk/src/backup.ts:112-119, android/.../backup/BackupKdf.kt:27-36, referanse: storage-encrypted/src/crypto/kdf.ts:73-91" acceptance: "Backup-KDF = argon2id på begge plattformer; backup-vektor re-pinnet; cross-platform-vektortest grønn." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g1-hono-upgrade-audit title: "Oppgrader hono ≥4.12.18 + bun audit-gate" description: > hono <4.12.18 har bodyLimit-bypass (svekker §7 64 KiB body-cap), path traversal i serve-static, IP-restriction-bypass. Dev/test: happy-dom (critical VM-escape), vite, postcss. status: todo column: todo priority: high assignee: claude context: "hono i shade-server/inbox-server/transfer/observer/transport-bridge; bun.lock pinner nåværende" acceptance: "hono ≥4.12.18 overalt; bun audit uten runtime-high/critical; §7 DoS-cap re-evaluert." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g1-at-rest-tamper-test title: "Håndhevende §4 at-rest-tuklingstest + bestill ekstern review" description: > SECURITY.md-cellen «§4 at-rest session DB» er «none yet» → ikke håndhevet. Ekstern krypto-review + pentest står «Pending V4.0» og må ikke skli forbi V5.0. status: todo column: todo priority: high assignee: any context: "SECURITY.md:18 (celler uten testlenke ikke håndhevet), THREAT-MODEL §4" acceptance: "Negativ test som beviser at at-rest-tukling fanges (AAD/tag); SECURITY.md-celle lenket. Ekstern review + pentest bestilt." created: 2026-07-10 updated: 2026-07-10 completed_at: null # ─────────────── G2/G3-hoder (nedbrytes når G0/G1 nærmer seg) ─────────────── - id: g2-ci-wall title: "G2: CI som mur — lint/format/coverage/soak + rydd .github + arkitektur" description: > biome (lint+format), coverage-terskel (gulv=dagens), soak:smoke i CI; fjern/synk stale .github/workflows (7 pakker→npmjs); publish kjører prepublish:check; bryt server↔inbox-server-syklus; rydd sdk/cli/files ubrukte deps; dashboard private; pin bun i workflows; kjør alle 8 examples (fiks accept-race i shade-transfer/engine.ts:1140). status: todo column: todo priority: medium assignee: claude context: "Se helsesjekk-artifact 2026-07-10 (P2-arkitektur + CI-hull)" acceptance: "PR blokkeres ved brudd på typecheck·lint·format·test·coverage; alle 8 examples grønne i CI; ingen udeklarert/ubrukt hard dep." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g3-parity-lock title: "G3: Paritetslås — ShadeStream-vektorer + Kotlin v4.11 + interop" description: > ShadeStream (v4.11) har null vektorer selv på TS-siden. Kotlin stopper på v4.10. Lag vektorer (3-DH deriveStreamRootKey, wire 0x31-0x33, frame seal/open) konsumert av begge; port ShadeStream til Kotlin; utvid cross-vectors path-filter; PARITY-VERSION-sjekk; symmetriser alle 14 vektorfiler; Robolectric-tester for KeystoreStorage; E2E socket-interop. status: todo column: todo priority: medium assignee: claude context: "packages/shade-core/src/stream.ts, scripts/generate-vectors.ts, .gitea/workflows/cross-vectors.yml, android/shade-android-keystore (0 tester)" acceptance: "Begge sider verifiserer alle 14 vektorer + ShadeStream; minor-bump uten Android-ack ⇒ rød CI; E2E TS-server↔Kotlin-klient over socket grønn." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: g4-docs-truth title: "G4: Docs = sannhet — oppdater til 4.11.x, fiks lenker/remote/eksempler" description: > README/MIGRATION/CHANGELOG/CONTRIBUTING frosset på 4.0. Riktig remote er Gitea (ikke GitHub). Legg indexeddb/ShadeStream/broadcast i README+ SHADE-BY-SCENARIO; Postgres at-rest quick-start + salt-livssyklus; per-pakke README for sdk/core/files/streams/storage-encrypted/transport. status: todo column: todo priority: medium assignee: claude context: "README.md, MIGRATION.md, CHANGELOG.md, CONTRIBUTING.md, docs/SHADE-BY-SCENARIO.md" acceptance: "Én sannhetskilde for versjon; ingen brutte doc-lenker (CI link-check); hvert ECOSYSTEM-scenario har kjørende CI-testet eksempel-sti." created: 2026-07-10 updated: 2026-07-10 completed_at: null # ─────────────── Åpne beslutninger (Stian avgjør i plandiskusjon) ─────────────── - id: decision-g3-vs-g4-order title: "BESLUTNING: rekkefølge G3 (paritetslås) vs G4 (docs)" description: > Foreslått G3 før G4 fordi «production»-label på Android henger på paritet. Men hvis Prism/Vyvern trenger docs-sannhet raskere, kan de bytte plass. status: blocked column: blocked priority: medium assignee: human acceptance: "Rekkefølge bestemt; plan.md Neste-seksjon speiler valget." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: decision-external-review-timing title: "BESLUTNING: bestille ekstern krypto-review/pentest nå eller etter G0+G1" description: > Parallelt nå (raskere kalender) vs etter G0+G1-fiksene (revisor ser korrekt kjerne — nonce-fiks + symmetrisk safety-number inne). Fable heller mot fikse først. status: blocked column: blocked priority: medium assignee: human context: "Kobles til g1-at-rest-tamper-test; SECURITY.md-celler «Pending V4.0»" acceptance: "Tidspunkt bestemt; g1-at-rest-tamper-test oppdatert deretter." created: 2026-07-10 updated: 2026-07-10 completed_at: null - id: decision-argon2id-migration title: "BESLUTNING: argon2id backup-migreringsstrategi" description: > Re-pinning av backup-vektor betyr at eksisterende 4.x-backups må migreres. Big-bang i 4.12 vs dobbelt-lesende overgangsvindu (les HKDF-legacy, skriv argon2id). status: blocked column: blocked priority: medium assignee: human context: "Kobles til g1-argon2id-backup" acceptance: "Migreringsstrategi valgt; g1-argon2id-backup-kortet får todos deretter." created: 2026-07-10 updated: 2026-07-10 completed_at: null # ─────────────── V4.13 — @shade/vault (server-side kryptert fillager) ─────────────── - id: vault-package title: "@shade/vault — innholdsadressert kryptert fillager på relayen" description: > Shade kan flytte filer mellom peers (@shade/files) og lagre én liten profil-blob per konto (/v1/blob/), men har ingen alltid-på lagring av krypterte filer. Uten den kan ingen Shade-app tilby backup, og ingen klient kan lese data når peeren er avslått. Klienten krypterer og navngir etter hash av ciphertext; serveren ser aldri klartekst. status: doing column: doing priority: high assignee: claude context: > Bestilt av Scaffold-backupen 2026-08-13, men generell: docs/files.md peker selv på Dispatch/Mail/Drive-style apps som konsumenter. Egen HKDF-gren ved siden av shade-blob-*-v1 i storage-encrypted/src/crypto/kdf.ts, så et vault-kompromiss ikke rører profil-slotten. Rutes i shade-server/src/ standalone.ts ved siden av createBlobRoutes. acceptance: > Klient kan pushe/hente en samling krypterte objekter mot relayen uten at serveren kan lese noe; oplog gir versjonsliste og henting av tidligere versjon; ny enhet med kontocredentials rekonstruerer hele samlingen. SHADE_VAULT_DB_PATH dokumentert med samme advarsel som SHADE_BLOB_DB_PATH. todos: - text: "HKDF-gren for vault-nøkler (slot, innhold, signatur)" done: true - text: "Innholdsadressert put/get med CAS-semantikk" done: true - text: "Append-only oplog per samling — versjonsliste og rollback" done: true - text: "Serverruter + persistenskonfigurasjon i standalone.ts" done: true - text: "docs/vault.md + README-rad i statustabellen" done: false - text: "IKKE DEPLOYET — krever SHADE_VAULT_DB_PATH i compose + Dokploy-runde" done: false created: 2026-08-13 updated: 2026-08-14 completed_at: null