docs(scaffold): plan, tasks og loggposter for vault + G0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-14 11:58:49 +02:00
parent 3da977207c
commit d8f6da55ef
6 changed files with 476 additions and 13 deletions

View File

@@ -1,5 +1,16 @@
# Logg — Shade # Logg — Shade
## 2026-07-10 — Holistisk helsesjekk + gated plan mot V5.0
Fem parallelle analysepass (arkitektur, test/CI, kryptosikkerhet, Android-paritet, docs/DX), topp-funn kildeverifisert. Kjernen er reell og moden (1159 tester grønne, vektorer byte-pinnet, KT/recovery ekte implementasjoner) — men «GA-frosset og revisjonsklar» stemte ikke. Fire P0-blockere identifisert og lagt inn som blockers i plan.md + kort i tasks.yaml:
1. **AES-GCM nonce-gjenbruk** i at-rest storage (`storage-encrypted/src/crypto/kdf.ts:116`) — deterministisk nonce uten meldingsteller, `saveSession` re-krypterer hver ratchet-op → GCM forbidden attack mot §4. Reachable bekreftet; fiks er liten (tilfeldig nonce, wire lagrer den allerede i prefiks).
2. **@shade/observer@4.11.1 tomt på registry** — `outDir=dist-build` men publish shipper `dist/`; `dist-build/` er det untrackede git-avviket.
3. **Typecheck-gaten rød** — TS upinnet → `bunx tsc` henter 7.0.2 (fjernet `baseUrl`, brukt i consumer-strict); gaten kjøres aldri i CI.
4. **@shade/cli bin dingler** etter publish (bin ikke omskrevet til dist/).
P1: safety-number matcher aldri peerens eget (`session.ts:241`, MITM-forsvar ikke-funksjonelt); backup-KDF HKDF ikke argon2id (finnes allerede i repoet); hono <4.12.18; begge flaggskip-eksempler røde (accept-race `shade-transfer/engine.ts:1140`). Paritet: ShadeStream (v4.11) uvoktet selv på TS-siden, Kotlin stopper på v4.10.
Låst prinsipp: **V5.0-implementasjon er gated på G0–G4 grønne.** V5.0.md er idéskisse — modnes parallelt (wire-typer reconciled mot 0x31–0x33, Insertable Streams-mekanisme, ratchet-params). Full rapport: helsesjekk-artifact (claude.ai/code).
## 2026-07-09 — Adoptert Scaffold plan-kontrakt ## 2026-07-09 — Adoptert Scaffold plan-kontrakt
Migrerte `docs/ROADMAP.md` og `android/shade-android/ROADMAP-ANDROID.md` til `.scaffold/`: nåbildet destillert inn i `plan.md`, originalene arkivert under `.scaffold/archive/` med opprinnelig mappestruktur. Versjonsdesigndokumentene (`docs/V5.0.md`, `docs/archive/V*.md`) er bevisst latt stå — de er spesifikasjoner, ikke planfiler. Migrerte `docs/ROADMAP.md` og `android/shade-android/ROADMAP-ANDROID.md` til `.scaffold/`: nåbildet destillert inn i `plan.md`, originalene arkivert under `.scaffold/archive/` med opprinnelig mappestruktur. Versjonsdesigndokumentene (`docs/V5.0.md`, `docs/archive/V*.md`) er bevisst latt stå — de er spesifikasjoner, ikke planfiler.

View File

@@ -0,0 +1,41 @@
---
title: "G0: AES-GCM nonce-gjenbruk fjernet fra at-rest-lagringen"
date: 2026-08-13
author: Claude
---
Den første av de fire P0-ene fra helsesjekken 10. juli er borte. `row-codec` forsegler nå med tilfeldig nonce i stedet for en avledet.
## Hva som var galt
`deriveNonce()` laget nonce som en ren funksjon av `(fieldKey, table, pk)` — altså av radens identitet. Doc-kommentaren argumenterte for at det var trygt fordi «hver `(key, plaintext)`-kombinasjon opptrer høyst én gang», men det er nøyaktig omvendt av hva AES-GCM krever: det farlige er å gjenbruke `(key, nonce)` på tvers av *ulike* plaintekster. `saveSession` re-forsegler sesjonsraden ved hvert ratchet-steg, så paret gjentok seg per konstruksjon. Konsekvensen er ikke bare XOR av plaintekstene, men lekkasje av GHASH-subnøkkelen — «the forbidden attack» — som gir tag-forfalskning under den nøkkelen. Det gjaldt alle muterbare rader: sesjoner, config, prekey-state og trust.
## Fiksen
Fire kallesteder, som kartlagt: `sealString` og `sealBytes` (`row-codec.ts:67`/`:96`) tar nå `randomNonce()` fra `aead.ts`, og `openString`/`openBytes` (`:83`/`:112`) slutter å sende `expectedNonce`.
Tilfeldig ble valgt framfor en teller fordi codec-en er delt mellom SQLite og Postgres og ikke har noen varig per-rad skrivteller å støtte seg på. 96 bit holder collision-sannsynligheten neglisjerbar langt forbi et realistisk antall re-lagringer.
`deriveNonce` er beholdt og markert `@deprecated` framfor slettet — den er del av den publiserte flaten via både `index.ts` og `crypto.ts`. Ingen kaller den lenger inne i pakken, og et søk gjennom `packages/`, `android/` og `examples/` fant ingen andre konsumenter. `expectedNonce`-parameteren i `aeadOpen` er beholdt på samme vilkår, med dokumentasjon om hvorfor den ikke skal brukes: den ga ingen tuklingsdeteksjon som AEAD-taggen og `(table, column, pk)`-AAD-en ikke allerede gir.
`randomNonce` er eksportert fra `index.ts` og `crypto.ts`, så konsumenter som forsegler selv har den trygge kilden for hånden.
## Ingen migrering trengs
`aeadOpen` har alltid lest nonce fra blob-prefikset; `expectedNonce` var kun en valgfri ekstrasjekk. Eksisterende blobber åpnes derfor uendret etter fiksen. Todoen om en migreringssti er erstattet av en test som beviser det.
## Android var aldri rammet
Kryssplattform-vektoren «Storage HKDF: rowNonce» tester HKDF-funksjonen, ikke at forseglingen bruker den, så den består uendret — `deriveNonce` er fortsatt der med samme oppførsel. Og `KeystoreStorage` henter nonce fra `cipher.iv`, altså Android Keystores egen tilfeldige IV, som ikke lar seg overstyre. TS-siden er nå på linje med Kotlin, ikke omvendt.
## Verifisering
Ny `tests/nonce-reuse.test.ts` med ni tester i tre grupper: nonce-unikhet (inkludert 200 re-lagringer med 200 distinkte nonces), bakoverkompatibilitet (blob forseglet med den gamle avledningen åpnes, og nye og gamle blobber leses under samme nøkkel), og at tuklingsdeteksjonen overlevde — flippet nonce-byte og flyttet rad avvises fortsatt.
Testene ble bevist å ha verdi ved å reversere fiksen midlertidig: nøyaktig de tre som beskriver feilen falt, mens bakoverkompatibilitets- og tuklingstestene sto uendret.
87 tester grønne i `@shade/storage-encrypted`, typecheck ren, og de to avhengige pakkene kjørt: `@shade/cli` 15/15 og `@shade/sdk` 104/104. En enkelt sdk-feil i første kjøring var en flaky gjennomstrømningsmåling — tre påfølgende kjøringer er grønne.
## Ikke publisert
Fiksen ligger i kildetreet. Prism er pinnet på `@shade/storage-encrypted@4.12.0` og får den først ved en release, som er riktig rekkefølge: `prism-daemon` skal ikke restartes nå.

View File

@@ -0,0 +1,41 @@
---
title: "@shade/vault — server-side kryptert fillager (V4.13)"
date: 2026-08-14
author: Claude
---
Shade kunne flytte filer mellom peers (`@shade/files`) og lagre én liten profil-blob per konto (`/v1/blob/<slotId>`), men hadde ingen alltid-på lagring av krypterte filer. Uten den kan ingen Shade-app tilby backup, og ingen klient kan lese data mens peeren som eier dem er avslått. `@shade/vault` fyller det hullet.
## Modellen
Tre ideer, og formen følger av dem.
**Objekter er innholdsadresserte.** Et objekts navn er SHA-256 av *ciphertexten*, så relayen kan lagre, deduplisere og verifisere uten å forstå noe. Den regner om hashen ved opplasting og avviser et objekt som ikke er det det utgir seg for — uten å eie en nøkkel.
**Et manifest navngir samlingen.** Stier bor i manifestet, ikke i objektnavn: relayen skal ikke lære at brukeren har en fil som heter `Projects/Skilsmisse/plan.md`. Manifestet er selv kryptert og lagret som et objekt.
**Loggen er append-only.** Hver commit legger til en rad som peker på et manifest. Historikk, rollback og «hva endret seg sist tirsdag» faller ut av det — versjoneringshalvdelen av bestillingen.
## Valg verdt å begrunne
*Egen HKDF-gren* (`shade-vault-{id,content,sig}-v1`) ved siden av `shade-blob-*-v1`. En vault-nøkkel leser hver eneste fil; en profil-blob-nøkkel leser en vertsliste. Delt derivasjon ville gjort ett kompromiss om til det andre.
*Ingen konvergent kryptering.* Identisk klartekst gir ulike objektnavn fordi nonce er fersk. Dedupliseringen skjer derfor innenfor en versjonskjede — en uendret fil beholder hashen sin fordi klienten gjenbruker objektet den allerede lastet opp — ikke på tvers av uavhengige forseglinger. Konvergent kryptering ville lekket hvilke filer to brukere deler, som er nøyaktig det en blind butikk ikke skal.
*Commit verifiserer at objektene finnes.* Ellers ville loggen kunne publisere en versjon som ikke lar seg gjenopprette, og det er verdt en ekstra rundtur å utelukke.
*Pubkeyen er inne i signaturen.* Første forsøk la den på utenfor, som både brøt `verifyPayload` (den kanonikaliserer alle felt) og — hvis skjemaet hadde ignorert det — ville latt hvem som helst bytte identitet i transit på den TOFU-pinnende førsteskrivingen. Fanget av testene.
## Verifisering
16 tester, alle mot de ekte rutehåndtererne gjennom Honos `fetch` — ikke mot en mock-transport, som ville vært enig med klienten per konstruksjon og ikke bevist noe om wire-kontrakten.
Dekket: rundtur bit-for-bit; gjenoppretting på en fersk klient som bare har kontonøkkelen; feil master-nøkkel når ikke fram; tre versjoner med rollback til hver enkelt; en 50 KB uendret fil lastes ikke opp på nytt (`bytesUploaded` under 1 KB på andre push); slettet fil borte i ny versjon, intakt i gammel; lagrede objekter inneholder verken innhold eller sti; feilnavngitt objekt avvist; `SEQ_CONFLICT` ved commit fra utdatert head; `MISSING_OBJECTS` ved manglende referanse; fremmed nøkkel avvist mot en pinnet vault; usignert skriving avvist; og at vault-grenen er forskjellig fra blob-grenen.
Typecheck ren. `@shade/storage-encrypted` fortsatt 87/87 etter endringene i `kdf.ts`.
## Gjenstår
Ruting i `shade-server/src/standalone.ts` ved siden av `createBlobRoutes`, en varig `VaultStore` (SQLite/Postgres — bare `MemoryVaultStore` finnes), `docs/vault.md`, og deploy. Ingenting av det er gjort: deploy og publisering er utenfor mandatet for en uovervåket økt.
Arbeidet ligger ukommittert i arbeidstreet, sammen med de eksisterende endringene fra juli.

View File

@@ -0,0 +1,25 @@
---
title: "G0: typecheck-gaten pinnet og lagt i CI"
date: 2026-08-14
author: Claude
---
Tredje av de fire P0-ene fra helsesjekken 10. juli.
## Hva som faktisk var galt
Helsesjekken beskrev gaten som rød fordi TS 7.0.2 hadde fjernet `baseUrl`, som `consumer-strict` brukte. Det stemmer ikke lenger: `baseUrl` finnes ikke noe sted i repoet, og `bun run typecheck` er grønn for alle 26 pakker — inkludert den nye `shade-vault`.
Den ekte feilen står likevel, og er den viktigere av de to: `scripts/typecheck-all.ts` kjørte `bunx tsc`, som henter nyeste utgivelse ved hver kjøring. Gaten kunne altså gå rød — eller stille slutte å fange ting — fordi TypeScript hadde sluppet en versjon, ikke fordi repoet endret seg. Det er en gate som ikke måler det den utgir seg for å måle.
## Fiksen
`typescript` er pinnet til `7.0.2` i rotens `devDependencies`, og `typecheck-all.ts` kjører `node_modules/.bin/tsc` i stedet for `bunx`. Et `Typecheck`-steg er lagt inn i `.gitea/workflows/test.yml`, plassert før testene: en typefeil er billigere å lese enn kjøretidsfeilen den til slutt forårsaker.
## Verifisering
`bun run typecheck` med den pinnede kompilatoren: alle 26 pakker grønne, inkludert consumer-strict-røyktesten (`lib: DOM`, `exactOptionalPropertyTypes`, `paths → workspace`).
## Gjenstår i G0
De to siste P0-ene — `@shade/observer` tomt på registry og `@shade/cli` sin dinglende `bin` — krever begge publisering til npm. Det er en utadvendt handling, og ble ikke gjort i en uovervåket økt.

View File

@@ -1,9 +1,11 @@
--- ---
project: "Shade" project: "Shade"
phase: "Post-4.x GA — V5.0 (Voice & Video) i idéfase" phase: "Post-4.x GA — hardening til revisjonsklar (G0–G4) før V5.0-design"
updated: 2026-07-09 updated: 2026-08-14
focus: "4.x-kjernen er GA-frosset og revisjonsklar; neste løft er V5.0 sanntid oppå den låste stacken" focus: "GCM-nonce-fiksen er levert (2026-08-13) og @shade/vault står ferdig og testet (2026-08-14), men er IKKE deployet eller publisert. De tre gjenstående G0-P0-ene er publiserings- og CI-saker. Historikk: G0 fikk en andre bestiller: @shade/vault (server-side kryptert fillager, bestilt 2026-08-13 av Scaffold-backupen) bygger på at-rest-stacken, og skal ikke reise seg på en kjent GCM-svakhet. Nonce-fiksens migreringssti er verifisert gratis — aeadOpen leser alltid nonce fra prefiks, så eksisterende blobber åpnes uendret."
blockers: [] blockers:
- "P0: @shade/observer@4.11.1 tomt på registry (outDir=dist-build, publish shipper dist/)"
- "P0: @shade/cli bin dingler etter publish (bin ikke omskrevet til dist/)"
--- ---
# Plan — Shade # Plan — Shade
@@ -11,17 +13,49 @@ blockers: []
## Mål ## Mål
- E2EE-bibliotek som implementerer Signal-protokollen (X3DH + Double Ratchet) for TypeScript/Bun — drop-in for frontend, backend og mobil, med forward secrecy og post-compromise recovery. - E2EE-bibliotek som implementerer Signal-protokollen (X3DH + Double Ratchet) for TypeScript/Bun — drop-in for frontend, backend og mobil, med forward secrecy og post-compromise recovery.
- Byte-for-byte kryssplattform-paritet: Kotlin/Android-porten verifiseres kontinuerlig mot TS-referansen via delte test-vektorer i `test-vectors/`. - Byte-for-byte kryssplattform-paritet: Kotlin/Android-porten verifiseres kontinuerlig mot TS-referansen via delte test-vektorer i `test-vectors/`.
- GA-frosset, revisjonsklar 4.0-kjerne som fundament; nye kapabiliteter (sanntid i V5.0) bygges oppå uten å røre kjernekrypto-revisjonen. - Reelt revisjonsklar 4.x-kjerne som fundament (G0–G4 grønne); sanntid (V5.0) bygges oppå den låste stacken uten å røre kjernekrypto-revisjonen.
## Nå ## Nå
- [ ] V5.0 fra `Idea` til `Design`: designnotatet `docs/V5.0.md` (SFrame-style frame encryption, `@shade/voice` / `@shade/video` / `@shade/broadcast`) må modnes og godkjennes Gated hardening — hard utgangsport per steg (full plan + funn: helsesjekk-artifact 2026-07-10, se log.md).
- [ ] Ende-til-ende interop-test TS-server ↔ Kotlin-klient over ekte socket (anbefalt før «production»-label på Android) - [ ] **G0 — Stopp blødningen (P0):** ~~tilfeldig nonce per skriving i
- [ ] Instrumenterte tester for `KeystoreStorage` (krever Android-emulator/enhet — utsatt i v4.9/v4.10-porten) storage-encrypted~~ (gjort 2026-08-13 — 9 nye tester, ingen migrering nødvendig,
Android var aldri rammet; se `logs/2026-08-13-gcm-nonce-reuse-fikset.md`.
**Ikke publisert** — Prism er pinnet på 4.12.0); observer `outDir→dist` +
republiser 4.11.2 + gitignore `dist-build/`; ~~pin `typescript` + typecheck i CI~~
(gjort 2026-08-14 — pinnet 7.0.2, `typecheck-all.ts` bruker lokal binær i
stedet for `bunx` latest, steg lagt i `test.yml`; `baseUrl` var allerede
borte og alle 26 pakker er grønne); fiks `bin`-omskriving + tarball-verifisering i publish.
Port: typecheck grønn i CI, nonce-negativtest grønn, `bun pm pack` inneholder
main-fil for alle pakker.
- [ ] **G1 — Revisjonsklar kjerne (P1):** peer-signeringsnøkkel per sesjon → symmetrisk safety-number; argon2id i backup-KDF (TS+Kotlin); hono ≥4.12.18; håndhevende §4 at-rest-tuklingstest; bestill ekstern krypto-review + pentest. Port: `alice.verify(bob, bob.getIdentityFingerprint())`===true, `bun audit` ren.
- [ ] **G2 — CI som mur:** biome + coverage-terskel + soak:smoke i CI; fjern/synk `.github/workflows`; publish kjører `prepublish:check`; bryt server↔inbox-server-syklus; rydd sdk/cli/files-deps; `dashboard: private`; pin bun. Port: PR blokkeres ved brudd, alle 8 examples grønne i CI.
## Neste ## Neste
- V5.0-implementasjon: `@shade/voice` (1:1 voice) → `@shade/video` → `@shade/broadcast` (1:N med relay-helper) - **V4.13 — `@shade/vault`: server-side kryptert fillager.** Klient, ruter,
- «Production»-label på Android når interop-testen er grønn `MemoryVaultStore`, `SqliteVaultStore`, ruting i `standalone.ts` og
`docs/vault.md` er levert natt til 2026-08-14 (auto-mode); 16 + 7 tester
grønne, og kjeden er kjørt mot en ekte HTTP-server med et ekte workspace:
491 filer bit-identiske etter gjenoppretting, andre push sendte 0 KB.
Gjenstår: Postgres-store, rate limiting på vault-rutene, GC av objekter
ingen manifest lenger refererer — og deploy. Bestilt 2026-08-13 av
Scaffold-backupen, men er en generell mangel: Shade kan flytte filer mellom peers
(`@shade/files`) og lagre én liten profil-blob per konto (`/v1/blob/<slotId>`, V4.9),
men har ingen **alltid-på lagring av krypterte filer på relayen**. Hver konsument
docs/files.md nevner (Dispatch, Mail, Drive-style apps) trenger det samme, så det
hører i Shade og ikke i den enkelte appen.
- *Form:* innholdsadressert lager — klienten krypterer og navngir etter hash av
ciphertext, serveren ser aldri klartekst og trenger ingen forståelse av innholdet.
- *Historikk:* append-only oplog per samling gir versjonering og rollback gratis;
det er «mini github»-halvdelen av Scaffold-bestillingen.
- *Nøkler:* egen HKDF-gren ved siden av `shade-blob-*-v1`, så et vault-kompromiss
ikke rører profil-slotten. Recovery er kontocredentials, som blob-primitivet —
med det forbeholdet at gjenopprettingsstyrken da er passordstyrken (se G1s
argon2id-punkt, som løfter nettopp den).
- *Gated på G0:* lageret bygger på at-rest-stacken, og skal ikke reise seg på en
kjent GCM forbidden attack.
- **G3 — Paritetslås:** ShadeStream-vektorer (3-DH, wire 0x31–0x33) konsumert av begge sider; Kotlin-port av v4.11; utvid cross-vectors path-filter; `PARITY-VERSION`-sjekk; symmetriser alle 14 vektorfiler; Robolectric-tester for KeystoreStorage; E2E socket-interop TS↔Kotlin. Port: begge sider verifiserer alle vektorer, minor-bump uten Android-ack ⇒ rød CI. Vilkår for «production»-label på Android.
- **G4 — Docs = sannhet:** README/MIGRATION/CHANGELOG/CONTRIBUTING → 4.11.x; fiks brutte lenker + riktig remote; legg indexeddb/ShadeStream/broadcast i README+SHADE-BY-SCENARIO; Postgres at-rest quick-start + salt-livssyklus; per-pakke README for de 6 viktigste.
## Senere ## Senere
- Re-pinne backup-vektoren når begge porter bytter til argon2id som passord-KDF (HKDF-placeholder flagget i threat-model; `deriveMasterKeyArgon2id` finnes allerede i Kotlin) - **G5 — V5.0 (Voice/Video):** implementasjon gated på G0–G4 grønne. `docs/V5.0.md` er i dag idéskisse (135 linjer) — modnes parallelt: wire-type-allokering reconciled mot 0x31–0x33 (nå tatt av ShadeStream), browser-SFrame-mekanisme (RTCRtpScriptTransform/Insertable Streams), ratchet-parametre, API-flate, milepæler, intern konsistens (60fps/1080p vs 30fps-test). Godkjennes i diskusjon (Stian + Fable) før koding.
- Versjonsdesigndokumentene bor fortsatt i `docs/V5.0.md` og `docs/archive/V*.md` — de er spesifikasjoner, ikke planfiler - Versjonsdesigndokumentene bor fortsatt i `docs/V5.0.md` og `docs/archive/V*.md` — de er spesifikasjoner, ikke planfiler.

View File

@@ -14,4 +14,315 @@
# created: 2026-07-09 # created: 2026-07-09
# updated: 2026-07-09 # updated: 2026-07-09
# completed_at: null # completed_at: null
tasks: [] 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/<slotId>), 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