release(v4.0.0): Shade GA — V3.x consolidation + audit prep
Some checks failed
Test / test (push) Has been cancelled
Cross-platform vectors / TypeScript vectors (bun) (push) Has been cancelled
Cross-platform vectors / Kotlin vectors (gradle) (push) Has been cancelled
Docker build and publish / docker (push) Has been cancelled
Publish / publish (push) Has been cancelled

V3.1 → V3.12 consolidated and tagged for the first GA release. Wire
format unchanged from 0.4.x — 4.0 peers interoperate with 0.4.x peers
byte-for-byte. The version bump is semantic: audit-cycle complete,
opt-in surface fully exposed, threat model refreshed for every new
surface.

Highlights:
- All 24 @shade/* packages bumped to 4.0.0 in lockstep.
- CHANGELOG 4.0.0 section is the canonical manifest of what landed.
- THREAT-MODEL extended (§10 fingerprint gates, §11 WebRTC P2P, §12
  Web-Worker boundary) + residual-risks table refreshed.
- OpenAPI now covers all 27 routes: prekey, transfer, KT, inbox,
  bridge, observer, /metrics, /healthz, /ready.
- MIGRATION 0.3.x → 4.0 documented + smoke-tested against
  shade migrate-storage on a real SQLite DB.
- docs/audit/REVIEW-BUNDLE.md + SCOPE.md ready for external reviewer.
- scripts/soak.ts harness for the GA-stable 2-week soak window.
- All V*.md plans archived under docs/archive/ with Status: Done.
- Voice/Video carved out into V5.0; 4.0 audit focuses on the frozen
  non-realtime stack.

Tests: TS 1000/1000 + Kotlin 11/11 cross-platform vectors green.
Docker: gt.zyon.no/stian/shade-prekey:4.0.0 builds and reports
  version 4.0.0 on /health.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 18:35:35 +02:00
parent 8b055912b7
commit e6fdf31b49
298 changed files with 37909 additions and 256 deletions

151
docs/archive/V2.1.md Normal file
View File

@@ -0,0 +1,151 @@
# Shade V2.1 — Improvements (infrastructure, storage, operations, security)
This document describes **improvements** agreed for next-generation work on Shade: clearer product story, stronger storage, mobile parity, operational hardening, transfer abuse, and a formal security narrative.
**Audience:** **Maintainers and contributors** implementing the changes. Add status fields as items land in code/docs.
---
## 1. Clear “who is the server?” and data flow
**Problem:** New users may think the prekey server is a message hub or that all E2EE traffic goes through the Shade container.
**Goal:** One consistent explanation across the root README, package READMEs, and optional onboarding: **the prekey server distributes public keys and bundles**; **actual messages and (typically) file chunks go through your apps own channel** (your transport, your backend, your URLs).
**Deliverables (proposal):**
- Diagram + short “keys vs payloads” text in the root README and in `@shade/server` README.
- Link to `THREAT-MODEL.md` from the same section (MITM on first contact ↔ safety numbers).
- Optionally one “concept page” (or extend `MIGRATION.md`) with typical architecture: *A ↔ B via app; both talk to the prekey host for X3DH material*.
**Acceptance criteria:** A new developer without domain background understands in one reading *what* goes to the Shade server and *what* does not.
---
## 2. Optional encryption of storage (at-rest)
**Problem:** `THREAT-MODEL.md` states that a stolen DB + filesystem can expose private keys because Shade does not encrypt the storage layer by default.
**Goal:** **Opt-in** protection for sensitive state (identity, session, optional stream resume secrets) with keys that **do not** live in plaintext in the DB — e.g. OS keychain/Keystore, passphrase + KDF, or an explicit device key injected by the app.
**Design principles:**
- Default developer experience (dev, simple demos) stays unchanged or includes a clear “insecure mode” warning in docs.
- APIs implementable per platform (Bun/SQLite, Postgres, web/IndexedDB, Android).
- Document limitations: what remains uncovered (e.g. active memory compromise).
**Acceptance criteria:** Threat model updated for “when encrypted storage is enabled”; at least one reference implementation + migration note.
---
## 3. Android parity and a published roadmap
**Problem:** `shade-android` is under development; drift from the TS SDK undermines the “byte-compatible” promise.
**Goal:** A **published roadmap** (milestones + what counts as parity vs TS-only) and **CI running shared test vectors** as a merge gate before release.
**Deliverables:**
- Roadmap section in `android/shade-android/README.md` or dedicated `ROADMAP-ANDROID.md` with explicit cross-checkpoints: wire format, fingerprints, rotations, streams (`0x11`) where applicable, resume semantics.
- CI job that fails on Kotlin vs TS vector mismatch.
**Acceptance criteria:** Parity coverage is visible and enforceable; the first critical cross-surface (e.g. core ratchet + proto) is green before a “production” label.
---
## 4. Operational hardening — prekey container and production
**Problem:** Many teams deploy the Docker image quickly; mistakes around TLS, backups, and secrets add avoidable risk.
**Goal:** A **production checklist**: TLS termination, volume backup (`/data`), rotation of `SHADE_OBSERVER_TOKEN`, use of `SHADE_PREKEY_PG_URL` vs SQLite, observability hooks, logging levels, meaning of stale cleanup parameters.
**Deliverables:**
- Extend `docs/DEPLOYMENT.md` or add short `docs/PRODUCTION-CHECKLIST.md` with bullet defaults.
- Link from the main README under “Deployment”.
**Acceptance criteria:** A checklist operators can follow without reading the whole codebase first.
---
## 5. Abuse and resource limits on the transfer plane
**Problem:** Parallel lanes and large uploads can be abused for resource or storage if consumer mounts of `createTransferRoutes()` share no coherent policy.
**Goal:** Documented **limits and patterns**: authentication (already an active SDK topic), max stream size, TTL for temporary chunk storage, quotas per identity or IP where sensible.
**Deliverables:**
- Guidelines in `docs/streams.md` or a dedicated “Transfer hardening” section.
- Optional helpers or middleware examples in `@shade/transfer` / server routes for common limits (without forcing every deployment into one DB model).
**Acceptance criteria:** A clear “recommended minimum” for production that teams can copy.
---
## 6. Security review and formal test / narrative
**Problem:** Enterprises and security-conscious users often ask for independent review and a traceable test matrix.
**Goal:** Plan for **independent crypto review** (timing, scope, deliverables) and a **published test / threat matrix** linking `THREAT-MODEL.md` to concrete automated tests (replay, tamper, out-of-order, resume, etc.).
**Deliverables:**
- Internal checklist “preparing for external review” (which files, assumptions, known limits).
- Short section in `SECURITY.md` on review status and how to report findings.
**Acceptance criteria:** One authoritative source for “what is tested automatically” vs “what needs manual/MITM/out-of-band process”.
---
## Dependencies and order (proposal)
| Priority | Topic | Note |
|---------|-------|------|
| Low friction | §1, §4 | Documentation and checklists only |
| Medium | §5 | Design + docs + possibly small API helpers |
| High effort | §2, §3 | Platform work and long-term maintenance |
| Coordinated | §6 | Parallel with maturity in §2§5 |
---
## Document versioning
- **V2.1** — first consolidated plan for the six improvement areas above. Update with dates and short per-section status lines as work starts and completes.
Gitt der du er nå (post-streams 0.2.0), ville jeg prioritert:
1. Sosial nøkkel-recovery — løser "hva skjer hvis jeg mister telefonen?" som er det store hullet i alle
E2EE-systemer
2. P2P WebRTC transport — naturlig kompletterende til streams; gir massiv throughput-gevinst gratis
3. E2EE Pub/Sub — lavt scope, høy nytte, og passer perfekt med din "magic drop-in"-filosofi
Disse tre har alle samme egenskap: de er infrastruktur-features som hver app du bygger fremover vil dra nytte av,
ikke bare Dispatch. Helt i tråd med "Shade som komponent som dras inn i hvilket som helst prosjekt".
/btw Hva med Shade videostreaming, VOIP streaming, og lignende?
Magic drop-in-tilnærming (din filosofi)
Tenk deg at i en Dispatch-app kan du bare gjøre:
<ShadeVideoCall to="device:server-admin" />
<ShadeVoiceButton to={peerAddress} />
<ShadeBroadcaster streamKey="game-stream-1" />
<ShadeViewer streamKey="game-stream-1" />
…og det bare fungerer, E2EE, P2P når mulig, fallback til relay automatisk. Det er det samme mønsteret som streams
0.2.0, bare for sanntid.
Realisme-sjekk
Video/VOIP er det vanskeligste i hele E2EE-verdenen. Signal brukte år på å få det riktig. Du bør:
1. Ferdigstille streams 0.2.0 først (verifiserer crypto-fundamentet)
2. Bygge P2P WebRTC-transport som separat milestone
3. Da har du alle byggeklossene og Voice 0.4.0 blir 70% gjenbruk
Men ja — dette hører absolutt hjemme i Shade. Shade som "alt-i-ett E2EE-platform" er en mye sterkere posisjon enn
"bare messaging + filer". Du kan bli til E2EE hva Twilio er til vanlig kommunikasjon.

126
docs/archive/V2.2.md Normal file
View File

@@ -0,0 +1,126 @@
# Shade V2.2 — Feature plan: product, platform, and developer experience
This document gathers **planned features** that extend Shade beyond todays core (X3DH + Double Ratchet + Streams/transfer): groups, asynchronous delivery, richer file UX, web workers, CLI, API docs, and scaffolding.
Add optional per-feature status (Idea / Design / IMP / Done).
---
## 1. Groups / multiple participants
**Vision:** Beyond strict 1:1 — multiple identities in the same “conversation” or shared context (messages and possibly shared file/stream policy).
**Challenges:**
- Todays Signal-like core is naturally 1:1; groups need either **pairwise sessions per member**, **sender keys / fan-out**, or a **dedicated group protocol** (larger architectural step).
- Lifecycle: invites, member removal, compromised device, history, and group PCS.
**Goals for early milestones (proposal):**
1. **Document** a recommended pattern for “group-lite” (e.g. coordinator relays ciphertext without decrypting + clients encrypt per-recipient).
2. **Optional** minimal API making fan-out easier in the SDK (without promising full MLS).
**Acceptance criteria (MVP definition):** Explicit scope in docs + one reference architecture; no ambiguous “group is done” without an updated threat model.
---
## 2. Async store-and-forward messaging
**Vision:** Recipient need not be online; **ciphertext** is stored temporarily and fetched when the recipient returns — the server never sees plaintext.
**Distinction from the prekey server:**
- Prekey stays **public keys only** (or extended only under strict policy).
- A **dedicated relay/inbox service** (or app-owned backend) holds **encrypted blobs only** with TTL, idempotency, and authorization (who may list/fetch).
**Deliverables (proposal):**
- Protocol sketch: address registration, `PUT` blob, `GET`/`DELETE` or lease, replay protection at the application layer.
- SDK helpers: outgoing queue, poll/pull, or push-notification hook (without dictating mobile platform).
**Acceptance criteria:** Threat-model section “what the relay sees” + reference implementation or example app.
---
## 3. File metadata and preview
**Vision:** Richer UX **without** leaking sensitive content to the server: filename, MIME type, length where known; **optional** client-generated thumbnails or previews encrypted as separate blocks or small payloads on the control init path.
**Technical considerations:**
- Anything sent must be **E2EE** or omitted; plaintext metadata on the server must be deliberate and minimal.
- Thumbnails should use **format hardening** on the client (size limits, sandboxing in UI).
**Acceptance criteria:** Extended `stream-init` (or sidecar envelope) with optional fields + widget support for “preview when available”.
---
## 4. Web: worker-based crypto and streaming
**Vision:** Large files in the browser without blocking the main thread or blowing RAM — **Web Crypto / noble** inside a **Worker**, **ReadableStream/WritableStream** end-to-end chunk pipeline aligned with `@shade/streams` / transfer.
**Deliverables:**
- `@shade/crypto-web` (or companion) patterns: transferable buffers, lifecycle, errors surfaced to the UI.
- Documented constraints (Safari, chunk sizing, Service Worker vs dedicated worker).
**Acceptance criteria:** E2E demo or test that sends multiMiB through a worker without a blocking UI.
---
## 5. CLI: `shade doctor`
**Vision:** One command that **diagnoses the environment** before production debugging.
**Typical checks (proposal):**
- Reachability of `prekeyServer` (`/health`, optional OpenAPI).
- Local config: storage path, rotation headers, clock skew (relevant for signed requests).
- **Streams:** transfer routes mounted, auth matches expected key, `GET .../state` behaves as expected in test mode.
- CLI / Node/Bun runtime versions and `@shade/*` packages where readable from `package.json`.
**Acceptance criteria:** `shade doctor` with exit codes suitable for CI (warn vs fail).
---
## 6. OpenAPI / docs
**Vision:** All HTTP contracts teams are expected to implement (prekey **and** transfer) appear in **one** OpenAPI story or clearly linked specs — not only README examples.
**Deliverables:**
- Consolidate or cross-reference `openapi.yaml` with transfer endpoints (`/v1/transfer/*`) and security schemes for chunk upload.
- `/docs` (Redoc or similar) or published static artifacts for versioned specs.
**Acceptance criteria:** Generated client (e.g. Python/Go) from spec without manual fixes for the happy path.
---
## 7. `shade init`
**Vision:** Scaffolding from **empty repo** to a **minimal runnable** app with Shade and optional streams.
**Extensions:**
- New or extended template: **minimal Hono/Fastify** app with `Shade.transferRoute()` mounted, auth example matching the SDK authenticator, `.env` template.
- Optional: demo `shade doctor` after init.
**Acceptance criteria:** `shade init …` produces a project that `bun install && bun run start` runs with documented env vars.
---
## Dependencies between items
```text
shade init ─────► doctor (same conventions for URLs and secrets)
openapi/docs ◄── transfer + prekey (single source)
web workers ───► streams UX (large file in browser)
groups ◄──────── store-and-forward (often related socially/technically)
metadata/preview► widgets + proto/control plane
```
---
## Document versioning
- **V2.2** — feature backlog as described. Split into issues/ADR per feature when implementation starts.

102
docs/archive/V2.3.md Normal file
View File

@@ -0,0 +1,102 @@
# Shade V2.3 — Tillit, retention, integrasjon og observability
Dette dokumentet beskriver **høyere ambisjonsnivå** og **plattformkryssende** arbeid: brukertilfeller der tillit må være **eksplisitt**, data må **utkrympes**/ryddes automatisk, apper må **enkelt koble seg på** kjente transportmønstre, og drift må **observere** uten å lekke innhold.
---
## 1. Key transparency / bundle-attestasjon **eller** kritiske fingerprint-øyeblikk i UI
Dette kan sees som **to spor** samme mål: redusere tillit til én korrumperbar prekey-server uten brukerens merknad.
### Spor A — Key transparency / bundle-attestasjon (ambisiøst)
**Idé:** Logging av **hvilket bundle som ble utlevert når**, kryptografisk forankret og **verifiserbar av klienter** eller tredjeparts-audit (inspirert av KT-litteratur, tilpasset Shade sin trusselmodell).
**Leveranser (målpris høyt):**
- Trusselmodell-oppdatering: *hva* CT/attest løser vs *fortsatt MITM før første verifisering*.
- Designnotat: datastruktur, friskhetsbevis, klient-verifikasjonssteg, operatørkost.
**Risiko:** Kompleks drift og kryptodesign — bør være **valgfritt** lag for motiverte deploys.
### Spor B — Fingerprints i app-UI på kritiske hendelser (pragmatisk)
**Idé:** Gjør **safety numbers** synlige og **handlingspålagte** i presiserte flyt:
- **Før første stor fil** (eller før første stream over terskel i bytes).
- **Før «trust this device»** / backup-importer / ny enhet som gjenbruker identitet.
**Leveranser:**
- `@shade/widgets` + SDK-hooks: modal/sheet med fingerprint, kopier-OOB-tekst, «jeg har verifisert».
- Dokumenterte UX-retningslinjer (unngå «alert fatigue» kun på lave risiko-events).
### Felles akseptansekriterier
Enten spor A eller B må ha **eksplisitt testcoverage** på «blokkerer/handhever verifisering» der det er lovet, og trusselmodellen må nevne kombinasjonen av OOB + tekniske grep.
---
## 2. Retention policies
**Vision:** Standardiserte **TTL- og oppryddingsregler** for data Shade-økosystemet etterlater på server eller i klient-lagring — spesielt:
- **Stream-state** og midlertidige chunk-referanser etter fullførte/avbrutte transfers.
- **Eventuell** inbox/relay-ciphertext (`V2.2`).
- Prekey-server: kobling til eksisterende `SHADE_STALE_DAYS` / cleanup, plus **policy-dokumentasjon** for operatører.
**Leveranser:**
- Default-anbefalinger (f.eks. «ferdige streams: prune etter N dager») i `@shade/storage-*` helpers eller server-factory.
- Konfigurerbare hooks: `maxAge`, `maxBytesPerIdentity`, cron vs on-access prune.
**Akseptansekriterier:** Ingen «uendelig vekst» som default i nye templates; dokumentert adferd i `streams.md` / deployment.
---
## 3. Ferdig «bridge» (transport)
**Vision:** Apper som ikke kan eller vil bruke WebSocket, får **ferdig mønster** for mottak av små meldinger eller kontroll-signaler.
**Eksempler:**
- **Server-Sent Events (SSE)** som **ren ciphertext-pipe** eller som signal «hent fra inbox» uten plaintext på server (kombinasjon med `V2.2`).
- **Lang-poll fallback** dokumentert ved siden av WS i `@shade/transport`.
**Leveranser:**
- Modulær `bridge`-pakke eller tydelig undermodul med få eksponerte typer og én felles `IncomingMessage`-modell på klient.
**Akseptansekriterier:** Ett fungende eksempel + test som viser dekryptering likt eksisterende transport.
---
## 4. Observability
**Vision:** Produksjonsteam får **målepunkter og spor** uten **innholdslekkasjer** eller kryptomatrise i logger.
**Forslag til innhold:**
- **Metrics (Prometheus-stil eller vendor-nøytralt):** opplastings-/nedlastings-varighet, lane-telling, retry counts, abort vs complete rates, HTTP/WS-feilkoder (aggregert).
- **OpenTelemetry:** spans rundt `TransferEngine` og prekey-endepunkter (uten payload-lengde i klartekst som PII — bruk binære størrelser eller binning).
- **Sampling og PII-policy** dokumentert (ikke logg adresser i full hvis compliance krever maskering).
**Akseptansekriterier:** Opt-in flags (default av i lib, på i server-container der det gir mening), og `docs/` avsnitt om hva som **aldri** skal logges.
---
## Prioritering mot V2.1 / V2.2
| Dette dokument (V2.3) | Naturlig forutsetning |
|----------------------|------------------------|
| Retention | Streams/transfer i bruk (`V2.1` §5, `V2.2` metadata) |
| Bridge | `V2.2` store-and-forward eller egen meldingskanal |
| UI fingerprints | Widgets/SDK allerede i bruk |
| KT / attest | Moden trusselmodell + juridisk/operativ vilje |
| Observability | Stabil nok intern API for hooks |
---
## Versjonering
- **V2.3** — første samlet plan for tillit, retention, bridge og observability. Splitt i ADR-er når konkret design er valgt.

100
docs/archive/V3.1.md Normal file
View File

@@ -0,0 +1,100 @@
# Shade V3.1 — Documentation & Hardening Foundation
**Status:** Done
**Effort:** S (12 uker)
**Forrige:** V2.3
**Neste:** V3.2 / V3.3 / V3.4 (kan kjøres parallelt)
---
## Mål
Lukke "lav-friksjon"-gjelden fra V2.1, V2.2 og V2.3 før vi tar fatt på de tunge
sikkerhetsløftene. Dette er pre-arbeidet som låser opp resten av roadmapen:
operatører skal kunne deploye trygt, transfer-konsumenter skal ha klare grenser,
og OpenAPI skal dekke hele HTTP-flaten.
Ingen ny kjernekode — kun docs, OpenAPI-utvidelser, retention-defaults og en
test-/threat-matrise.
---
## Scope
### Inn
- README + `@shade/server`-README: eksplisitt "keys vs payloads"-narrativ med
diagram + lenke til `THREAT-MODEL.md`.
- Ny `docs/PRODUCTION-CHECKLIST.md`: TLS, backup, observer-token-rotering,
SQLite vs PG, log-nivå, stale-params, secret-rotering.
- Hardening-seksjon i `docs/streams.md`: max stream-size, TTL, quota-mønstre —
peker mot `@shade/files`-hooks som referanse.
- `openapi.yaml` utvidet med `/v1/transfer/*` (`chunk`, `state`, `health`) +
sikkerhetsskjema for `ShadeTransferAuthenticator`.
- Retention-defaults i `docs/streams.md` + SDK-template:
`pruneStreamStates`-cron som default — "ferdige streams ryddes etter N
dager".
- `SECURITY.md`-utvidelse: review-status, "hvordan rapportere", lenking fra
`THREAT-MODEL.md`-rader → `tests/security/*` (test-/threat-matrise).
### Ut
- Faktisk crypto-review (det er V4.0).
- Endringer i krypto- eller wire-format.
- Ny kode utenfor SDK-templates.
---
## Leveranser
### Dokumentasjon
- `docs/PRODUCTION-CHECKLIST.md` — ny.
- `docs/streams.md` — utvidet med "Hardening" og "Retention".
- `README.md` — diagram-justering + "Hva som ikke går via Shade-server".
- `packages/shade-server/README.md` — speile narrativet.
- `SECURITY.md` — review-status + threat-/test-matrise.
- `THREAT-MODEL.md` — krysslenker til konkrete tester.
### Kode (kun konfig + templates)
- `packages/shade-server/openapi.yaml``/v1/transfer/*`-paths,
`ShadeTransferAuthenticator` securityScheme.
- `packages/shade-cli/templates/bun-server` — default
`pruneStreamStates`-cron.
### Tester
- Lint-test: OpenAPI-spec validerer fortsatt mot OpenAPI 3.1-skjema.
- Smoke-test for cron i template.
---
## Akseptansekriterier
- [ ] Ny utvikler kan lese README + `PRODUCTION-CHECKLIST.md` og deploye
prod-klar Shade uten å lese hele kodebasen.
- [ ] Generert klient (Python eller Go) fra `openapi.yaml` dekker både
prekey- og transfer-flate uten manuelle fixes for happy path.
- [ ] `THREAT-MODEL.md` linker hver "Mitigations"-rad til minst én test-fil.
- [ ] Default SDK-template `bun-server` prune'r resumable streams uten
manuell konfig.
---
## Avhengigheter
Ingen.
---
## Risiko
Lav. Verste utfall er foreldet docs hvis V3.2+ endrer overflater. Mitiger ved
å skrive små, oppdaterbare seksjoner heller enn lange narrative kapitler.
---
## Migrasjon
Ingen — alt er additivt.

134
docs/archive/V3.10.md Normal file
View File

@@ -0,0 +1,134 @@
# Shade V3.10 — Social Key Recovery
**Status:** Done — landet i `@shade/recovery` 0.4.0, frosset i 4.0 GA.
**Effort:** L (48 uker)
**Forrige:** V3.2 + V3.3
**Adresserer:** V2.1-tillegg "sosial nøkkel-recovery"
---
## Mål
Løs det største UX-hullet i alle E2EE-systemer: **"Hva skjer hvis jeg
mister telefonen?"**. Bruker velger N "guardians" (familie / venner /
jobb-partnere); når bruker mister enheten, kan en threshold-andel av
guardians sammen returnere identity-nøkkelen — uten at noen enkelt guardian
kan gjøre det alene, og uten at server lærer noe.
---
## Scope
### Inn
- Shamir Secret Sharing (k-of-n) over identity private key (eller en
backup-encryption-key).
- Distribusjon av shares via eksisterende 1:1 Shade-sesjoner — guardians
lagrer share lokalt.
- Recovery-flow: ny enhet ber threshold guardians sende sine shares;
rekonstruerer på ny enhet.
- Verifikasjons-step: ny enhet beviser identitet til hver guardian via OOB
safety-number-sammenligning **før** guardian frigjør share.
- UX-guide: hvor mange guardians, hvilken threshold, hvordan rotere når en
guardian mister enhet.
### Ut
- "Cloud guardian" / Shade-driftet recovery — vi tillater ingen sentralisert
komponent som kan gjøre det alene.
- Auto-distribusjon (vi krever eksplisitt valg av guardians).
---
## Design
### Hva deles
```text
shareSecret = AES-256-GCM-encrypt(identityState, recoveryKey)
recoveryKey is Shamir-split(k, n) → shares[i]
shareSecret stored locally + on each guardian
each guardian receives one share via Shade.send
```
`identityState` er det samme som `Shade.exportBackup` (eksisterer i 0.3.x),
men her gjenbrukes formatet.
### Recovery-flow
1. Ny enhet genererer **temporary** identity + safety number.
2. Ny enhet kontakter guardians via prekey-server (OOB verifisering først).
3. Hver guardian godkjenner manuelt og returnerer sin share via
`Shade.send`.
4. Ny enhet rekonstruerer `recoveryKey`, dekrypterer `shareSecret`,
gjenoppretter identity.
5. Original identity roterer (gammel identitet markeres som
"compromised — used for recovery").
### Guardian-UX
- Guardian-app/widget viser:
*"Alice (din venn) har mistet sin enhet og ber om recovery share.
Bekreft fingerprint før du sender."*
- Guardian kan **avslå** uten konsekvens.
---
## Leveranser
### Pakker
- `@shade/recovery` — Shamir + share-distribusjon.
- `@shade/widgets``<RecoverySetup />` (velg guardians) +
`<RecoveryRequest />` (ny enhet ber) + `<RecoveryApprove />` (guardian
godkjenner).
### Tester
- Unit: Shamir split/combine roundtrip; threshold-håndhevelse.
- Integration: full 3-of-5 recovery med 5 mock-guardians.
- Adversarial: 2 guardians koluderer (under threshold) → kan ikke
rekonstruere.
- Adversarial: ondsinnet ny enhet uten safety-number-bekreftelse → ingen
guardian skal frigjøre share.
### Dokumentasjon
- `docs/recovery.md` — full UX + threat model.
- Trusselmodell-utvidelse: kollusjon ≤ k-1, identitetsforfalskning, social
engineering.
---
## Akseptansekriterier
- [ ] 3-of-5 recovery fungerer end-to-end på 2 separate enheter.
- [ ] Ingen koalisjon av (k-1) guardians kan rekonstruere `shareSecret`
(verifisert med fast-check property test).
- [ ] Guardian-side widget krever fingerprint-bekreftelse før send (gate
fra V3.3 forsterket).
---
## Avhengigheter
- V3.2 — nøkkelmateriale at-rest hos guardian skal være kryptert.
- V3.3 — fingerprint-gate på recovery-handshake.
---
## Risiko
- **UX er det vanskeligste.** "Hvem er min guardian?" er sosialt komplekst;
bruker kan velge dårlig.
- **Social engineering.** Angriper imiterer offer over telefon → guardian
gir share. Mitiger med harde fingerprint-gates + cool-down.
- **Dead guardians.** Hvis guardian dør / mister sin enhet uten å være
erstattet, threshold synker. Periodisk "guardian health check"-prompt
anbefales.
---
## Migrasjon
Ny pakke. Apper kan legge til recovery-widget i innstillinger.

124
docs/archive/V3.11.md Normal file
View File

@@ -0,0 +1,124 @@
# Shade V3.11 — WebRTC P2P Transport
**Status:** Done — landet med `@shade/transport-webrtc` 0.4.0,
`MultiTransportFallback` i `@shade/transfer`, og
`shade.configureWebRTC()` i `@shade/sdk`. Se [docs/webrtc.md](../webrtc.md).
**Effort:** XL (24 måneder)
**Forrige:** V3.7
**Adresserer:** V2.1-tillegg "P2P WebRTC transport"
---
## Mål
Direct peer-to-peer datakanal mellom Shade-klienter når NAT/firewall
tillater. Primær gevinst: massiv throughput for `@shade/transfer`
(filer, store payloads) og lav-latens for messaging når begge peere
er online samtidig. E2EE bevart: WebRTC DTLS-SRTP er **transport**
payload er fortsatt Shade ratchet-krypto.
V3.11 lander i V4.0-vinduet og er foundation-only — sanntidsbruken
(voice, video, broadcast) ligger i [V5.0](../V5.0.md) som downstream
konsumer av denne datakanalen.
---
## Scope
### Inn
- Ny pakke `@shade/transport-webrtc`.
- Signaling via Shade control plane (eksisterende kanal — `Shade.send`).
- ICE/STUN: bruk offentlige STUN-servere som default.
- TURN: konfigurerbar TURN-relay som fallback.
- DataChannel for `@shade/transfer`-chunks.
- Auto-fallback: P2P → HTTP (eksisterende stack).
### Ut
- SFU/MCU (mange-til-mange topologi) — broadcast/video er V5.0.
- Voice/video media-tracks — V3.11 er ren datakanal (DataChannel);
audio/video over RTP er V5.0.
- DTLS-fingerprint-binding til Shade-fingerprint (vurderes som hardening,
men ikke krav).
---
## Design
### Connection-flow
```text
A initierer:
1. createOffer() → SDP
2. shade.send(B, { kind: "webrtc-offer", sdp })
3. B mottar over Shade-kanal, createAnswer()
4. shade.send(A, { kind: "webrtc-answer", sdp })
5. ICE-candidates exchange (samme kanal)
6. DataChannel åpen
```
### Wrapping
DataChannel sender ferdige `@shade/transfer`-chunks (allerede E2EE).
WebRTC's egen DTLS-SRTP fungerer som transport-secrecy lag.
### Topologi
- 1:1 P2P direkte når mulig.
- TURN-relay når NAT'er er for strenge (transport-only, ser ikke plaintext).
---
## Leveranser
### Pakker
- `@shade/transport-webrtc` — Connection, DataChannel-wrapper, ICE-config.
- `@shade/transfer` utvides: `WebRTCTransferTransport` som drop-in.
- `FallbackTransferTransport` får ny ledd: P2P → WS → HTTP.
### Tester
- Loopback unit: offer/answer/ICE i Bun via `node-datachannel` eller
`wrtc`.
- Integration: 100 MB transfer over P2P vs HTTP — P2P skal vinne på samme
nettverk.
- Failover: TURN-relay påtvinger relay-modus.
- NAT-emulering (loopback med ulike NAT-typer hvis mulig).
### Dokumentasjon
- `docs/webrtc.md` — setup, STUN/TURN-config, NAT-traversal-håp og
-realiteter.
---
## Akseptansekriterier
- [ ] To klienter på samme LAN: P2P direct uten TURN, throughput > 5x
HTTP-baseline.
- [ ] To klienter bak strenge NAT'er: TURN-relay aktiveres automatisk.
- [ ] Failover P2P-død → HTTP innen 5 s uten meldingstap.
---
## Avhengigheter
- V3.7 — bridge-mønstre + fallback-arkitektur.
---
## Risiko
- **NAT-traversal-helvete.** Mange edge-cases. Mitiger med tidlige
integration-tester på faktiske NAT-konfigurasjoner.
- **Browser-kompatibilitet.** Safari har sine egne RTC-quirks.
- **TURN-koster.** TURN-relay = ekte trafikk gjennom server. Operatør må
vite det.
---
## Migrasjon
Opt-in. Eksisterende HTTP/WS-transport fungerer uendret.

View File

@@ -0,0 +1,557 @@
# V3.12 — Key Transparency: Designnotat
**Status:** Approved (in-tree review — markeres `Design` i ROADMAP)
**Forfatter:** Shade-teamet
**Reviewer-mål:** ekstern crypto-orientert reviewer før produksjons-deploy.
**Implementasjons-target:** `@shade/key-transparency` + utvidelser i
`@shade/server`, `@shade/transport`, `@shade/sdk`.
---
## 1. Mål og ikke-mål
### Mål
Bytt ut "blind tillit til prekey-server" med en **verifiserbar
append-only log**. Når en klient mottar et prekey-bundle skal den ha
kryptografisk bevis for at:
1. Bundlen er **commit'et** i en tidstemplet log (Signed Tree Head).
2. Den eksakte (adresse, identityKey, signedPreKey)-mappingen står i
den loggen — _eller_ den står ikke (fravær-bevis).
3. Loggen har ikke skrevet om historie siden forrige fetch
(konsistens-bevis).
4. Andre klienter ser **samme** log (split-view-deteksjon via
witness-gossip).
Dette er **CT-style transparens** (RFC 6962-prinsipper) tilpasset
prekey-distribusjon.
### Ikke-mål (eksplisitt ut)
- **Federert log mellom flere prekey-servere.** Hver Shade-deployment
har én log (eller ingen). Multi-server gossip er V3.13+.
- **Løse MITM-på-første-kontakt fullstendig.** KT fanger split-view og
re-write, men ikke det at en angriper publiserer en forfalsket
identitet ved første registrering. Det er V3.3 (fingerprint-gate)
+ V3.10 (social recovery).
- **Legal/compliance audit-log.** Loggen er kryptografisk, ikke juridisk.
- **Klient-styrt sletting.** Append-only — DELETE skriver
tombstone-entry, fjerner ikke historikk.
### Beslutningskriterium for implementasjon
Når dette notatet er godkjent _og_ alle åpne spørsmål under §11 har
konkrete svar (ikke bare "vi finner ut av det senere"), kan kode
skrives. Det notatet ligger på når §11 lukkes er det vi bygger.
---
## 2. Trusselmodell-tillegg
Eksisterende THREAT-MODEL.md dekker prekey-server som "honest-but-curious"
+ tilstede TOFU. KT utvider modellen til **fully-malicious server**:
| Angrep | Pre-V3.12 | Post-V3.12 |
|---|---|---|
| Server returnerer feil bundle for én klient | Uoppdaget til OOB-verifisering | Klient kan be om proof; mismatch oppdages |
| Server bytter en allerede registrert identityKey | TOFU-fingerprint endres → V3.3-gate slår inn (men brukerinitiert) | Loggen vil vise to entries med samme adresse → witness oppdager |
| Server gir `alice` ulike identityKeys til Bob og Charlie (split-view) | Uoppdaget til OOB | Witness-gossip avslører to ulike STH-er |
| Server skriver om historikk for å skjule tidligere svik | Mulig | Konsistens-proof feiler → klient varsler |
| Server nekter å publisere ny STH | Mulig | "Stale STH"-detekteres av friskhetsbevis (max age) |
| Server kompromitterer signing-key for STH | KT-trygghet brutt | Witness gossip om gammel STH-kjede; rotasjon krever ny genesis |
KT løser **ikke**:
- Førstegangs-impersonering av en helt ny adresse (intet historisk
bevismateriale).
- Kollusjon mellom server og _alle_ witnesses.
- Klient som glemmer cached STH og må re-bootstrappe.
---
## 3. Datastruktur-valg
Vi velger **RFC 6962-stil append-only Merkle log** + **ekstern
adresse-index** med commitment-bevis. Begrunnelse:
### Vurderte alternativer
1. **Pure CT-log (RFC 6962):** Simple append-only Merkle tree.
Inklusjonsbevis trivielle. Fravær-bevis _ikke_ støttet
nativt (må scanne hele loggen).
2. **CONIKS-tre (sparse Merkle tree over adresser):** Native fravær-bevis,
men mye mer kompleks (epoch-baserte snapshots, prefix-trees,
placeholder-nodes). Overkill for første iterasjon.
3. **Hybrid (RFC 6962 log + side-index):** Loggen er sannhetskilde,
indexen er en _commitment_-mapping `address → leaf_index`. Server
beviser inklusjon via leaf-path, fravær via "denne adressen er ikke
i indexen ved tree_size T" + signert STH.
**Valg: alternativ 3.** Det gir CT-stil enkelthet, samt fravær-bevis
nesten gratis (commitment til indexen er en del av hver STH).
### Konkret format
#### Leaf
Hver leaf representerer én registrering eller revoke:
```
leaf = SHA256(
0x00 || // leaf prefix (RFC 6962)
uint64_be(timestamp_ms) ||
byte(operation) || // 0x01 register, 0x02 replenish, 0x03 delete
uint16_be(len(address)) || address_bytes ||
uint16_be(len(bundle_hash)) || bundle_hash // 32 bytes SHA-256 over canonical bundle
)
```
`bundle_hash` er deterministisk hash av:
```
canonical_bundle = SHA256(
0x01 || // bundle prefix
identitySigningKey (32) ||
identityDHKey (32) ||
uint32_be(signedPreKey.keyId) ||
signedPreKey.publicKey (32) ||
signedPreKey.signature (64)
)
```
One-time prekeys er **ikke** med i bundle-hashen — de er ephemerale og
ville lekket OTP-rotasjons-mønstre.
#### Tree
Merkle-tre over leaf-array, RFC 6962 §2.1:
- `MTH(empty) = SHA256()`
- `MTH({d}) = SHA256(0x00 || d)` (already hashed leaf)
- `MTH(D[n]) = SHA256(0x01 || MTH(D[0:k]) || MTH(D[k:n]))` der
`k` er største 2-potens < n.
#### Signed Tree Head (STH)
```
sth = {
tree_size: uint64,
timestamp: uint64_ms,
root_hash: bytes(32),
index_root: bytes(32), // commitment til adresse-index ved denne tree_size
log_id: bytes(32), // SHA-256 av server-public-key (stabil ID)
signature: bytes(64) // Ed25519 over canonical(rest)
}
```
`canonical(sth)` for signing:
```
0x02 || // sth prefix
uint64_be(tree_size) ||
uint64_be(timestamp) ||
root_hash (32) ||
index_root (32) ||
log_id (32)
```
#### Inklusjons-bevis
Standard RFC 6962 audit-path: liste med søsken-hasher fra leaf til root,
slik at klient re-beregner root og sammenligner med STH.
#### Konsistens-bevis
Standard RFC 6962 §2.1.2: bevis at tree med `tree_size = N1` er prefix
av tree med `tree_size = N2 > N1`. Klient bruker dette for å detektere
re-write.
#### Fravær-bevis
Adresse-indexen er en sortert liste `(address, leaf_index_of_latest)`
serialized og hashet. `index_root` i STH er commitment.
For å bevise fravær av adresse `addr` ved tree_size `N`:
- Server returnerer hele indexen ved tree_size `N` (sortert), eller
- (effektivt:) Returnerer naboparet `(addr_prev, addr_next)` der
`addr_prev < addr < addr_next` lexikografisk, sammen med en
Merkle-path i en sparse Merkle tree over indexen.
Første iterasjon: vi serialiserer hele indexen og lar klienten
laste den (kompakt: <100 KB selv for 100k adresser). Senere
optimaliserer vi til sparse Merkle tree hvis dataset vokser.
---
## 4. Friskhetsbevis (Signed Tree Heads)
### Frekvens
- **Min:** Ny STH ved hver mutasjon (register/replenish/delete) — synkront
i write-pathen.
- **Maks-stale:** Selv uten mutasjoner skal en STH publiseres minst hver
**10. minutt** ("heartbeat STH" — samme tree_size, oppdatert timestamp).
Dette gir klienter mulighet til å detektere "død" log uten å bekymre
seg om hvorvidt logen faktisk har endret seg.
### Klient-akseptansevindue
Klient avviser STH eldre enn `now - 24 timer` (default, konfigurerbar).
Dette beskytter mot replay av gamle STH-er som "skjuler" en mutasjon
gjort i ettertid.
### Stale-STH som soft-fail
Hvis STH er stale men gyldig signert: klient logger advarsel,
returnerer bundle med `proof.staleness = 'warn'` (V1) eller blokkerer
(V2 etter dogfooding). Vi starter med _warn_, eskalerer til _block_
når witness-økosystem er etablert.
---
## 5. Klient-verifikasjonssteg
På hver `fetchBundle(address)`:
1. Server returnerer `{ bundle, proof: { sth, leaf, audit_path, leaf_index, address_index_proof } }`.
2. Klient verifiserer:
- `sth.signature` mot kjent `log_public_key` (pinnet ved første
bootstrap).
- `sth.timestamp >= now - max_age_ms` (default 24t).
- Re-beregner `leaf_hash` fra bundle og sammenligner med `proof.leaf`.
- Re-beregner `root_hash` fra `audit_path + leaf` og sammenligner med
`sth.root_hash`.
- Verifiserer `address_index_proof` mot `sth.index_root`.
3. Hvis klient har en cached forrige STH: sjekk **konsistens-proof**
mellom forrige og denne. Server publiserer dette i
`GET /v1/kt/consistency?from=<size>&to=<size>`.
4. Hvis klient har en cached STH for samme `tree_size` med ulik
`root_hash`**split-view alarm**.
### Probabilistisk vs. obligatorisk verifisering
Vi velger **obligatorisk** ved hver bundle-fetch. Bundle-fetch er sjelden
(per ny peer, ikke per melding) — kostnaden er <100ms. Probabilistisk
verifisering ville la klienter bli lurt av "én dårlig fetch" uten
deteksjon.
### Bootstrap
Første gang en klient møter en log: pinner `log_public_key` etter å ha
hentet det fra et **ut-av-bånd**-pinningendepunkt eller fra `Shade.config`
(operatør sender den med klient-config). Etterfølgende rotasjon krever
ny genesis-STH med eksplisitt break-event signert av forrige nøkkel.
---
## 6. Witness/auditor-rolle
### Hva en witness gjør
- Periodisk poll: `GET /v1/kt/sth` (hent siste STH).
- Lagrer alle observerte STH-er i append-only lokal store.
- Eksponerer `GET /witness/sth?log_id=...&tree_size=...` slik at andre
klienter kan sammenligne hva _denne_ witnessen har sett.
- Verifiserer konsistens mellom hver ny STH og forrige.
### Klient-witness-gossip
Klient-bibliotek kan operere i tre moduser:
1. **Observe-only:** verifiserer kun bundle den selv henter, ingen
gossip.
2. **Light-witness:** poller STH hver `Xt` og lagrer lokalt; sammenligner
med STH levert ved bundle-fetch.
3. **Full-witness:** publiserer signerte STH-observasjoner til en
konfigurert peer-liste eller offentlig endpoint.
V1 leverer 1 og 2. Mode 3 (full-witness publication-protocol) er V2
hvis økosystem trenger det.
### Hvem kjører witnesses?
- Shade-prosjektet kjører **referanse-witness** på offentlig endpoint
(separate-from-prekey-server).
- Power-users / operatører kan kjøre egne via `@shade/key-transparency/witness`-
API.
- Tredjeparts auditors (typisk security-research) er invitert.
Vi krever **ikke** federation/konsensus mellom witnesses i V1 — gossip
er rent "har du sett samme STH som meg?".
---
## 7. Operatørkost
### Lagring
- **Per leaf:** 32 bytes (hash) + ~80 bytes adresse-index entry =
~112 bytes.
- **100k adresser, 1 rotasjon/år, 1 replenish/uke:** ~5.4M leaves =
~600 MB log. Tre-strukturen er beregnet on-demand, ikke lagret.
- **Index:** ~100k × 80B = 8 MB i minne (cacheable).
### CPU
- STH-signing: 1 Ed25519-signering per mutasjon + heartbeat = <1k/dag for
små deployments. Trivielt.
- Audit-path-beregning: O(log N) ved fetch. <1ms.
- Konsistens-proof: O(log N).
### Backup
Logen MÅ aldri miste data — sletting eller corruption ødelegger
integritet permanent. Strategi:
- Loggen lagres som append-only tabell `shade_kt_log` (PG) med
`(leaf_index, leaf_hash, leaf_data_json)`.
- Backup hver time + WAL-shipping anbefalt.
- Ved corruption: se §10 Recovery.
### STH-signing-key
- Genereres ved første KT-aktivering, lagres i operatør-styrt secret
(env, KMS, eller på disk for hjemme-server).
- Rotasjon: **breaking event** — krever ny genesis-STH der ny key
signerer melding "rotated from ${old_key}" med _gammel_ key. Klient
må eksplisitt akseptere rotasjonen.
---
## 8. Migrasjon
### Server-side
KT er **opt-in** på operatør-nivå. `createPrekeyServer({ keyTransparency:
{ enabled, store, signingKey } })`. Når slått på:
1. Server skriver alle eksisterende identiteter inn som genesis-leaves
ved boot.
2. Første STH publiseres med `tree_size = N` der N er antall
eksisterende adresser.
3. Klient som henter bundle får proof; klient som ikke støtter KT
ignorerer proof-felt (forward-compatible).
### Klient-side
`@shade/sdk`-config:
```ts
createShade({
keyTransparency: {
mode: 'observe' | 'light-witness' | 'off',
logPublicKey: '<base64>',
maxStaleMs: 86_400_000,
},
// ...
})
```
`mode: 'off'` (default for backward-compat første release) — ignorerer
proof. Ny SDK med `mode: 'observe'` verifiserer men feiler ikke harde
hvis proof mangler. `mode: 'observe-strict'` (senere) krever proof.
### Eksisterende deployments
Operatør kan rulle KT inn på live server uten klient-update:
1. Skru på KT i server-config → server begynner å produsere proofs.
2. Gamle klienter ignorerer proof-felt (de er additive i bundle-respons).
3. Nye klienter med `mode: 'observe'` begynner å verifisere.
4. Når operatør har testet og publisert log-public-key OOB, kan brukere
skifte til `'light-witness'`.
---
## 9. Akseptansekriterier
- [ ] `@shade/key-transparency` pakke leverer:
- Merkle log core (RFC 6962 hash-funksjoner).
- STH-signing/verifikasjon.
- Inklusjons-bevis generering + verifisering.
- Konsistens-bevis generering + verifisering.
- Adresse-index med commitment.
- Witness-light klient.
- Cross-platform (TS-only, ingen native deps).
- [ ] `@shade/server` integrasjon:
- `KTLogStore`-interface (memory + postgres).
- Routes: `GET /v1/kt/sth`, `GET /v1/kt/sth/:tree_size`,
`GET /v1/kt/consistency`, `GET /v1/kt/inclusion/:address`.
- Bundle-fetch returnerer `{ bundle, proof }` når KT aktivert.
- Heartbeat-STH-publisering hver 10. minutt (configurable).
- [ ] `@shade/transport` `ShadeFetchTransport`:
- Aksepterer optional `keyTransparency`-verifier.
- `fetchBundle()` returnerer `{ bundle, proof?: KTProof }`.
- [ ] `@shade/sdk` `Shade`:
- `keyTransparency`-config.
- Verifiserer proof ved hver bundle-fetch når aktivert.
- Cacher STH for split-view-deteksjon.
- [ ] **End-to-end test: split-view detection.**
- Test-server gir Bob bundle X, Charlie bundle Y for samme adresse `alice`.
- Bob+Charlie kjører som light-witness, gossiper STH-er.
- Test asserter at mismatch detekteres innen N polls.
- [ ] **End-to-end test: log re-write detection.**
- Server skriver om historie (test-only API).
- Konsistens-proof feiler på neste fetch.
- [ ] Operatør-doc dekker recovery-strategi.
- [ ] CHANGELOG, README, ROADMAP oppdatert.
- [ ] Cross-platform vector-test for Merkle hash + STH (Android/TS
paritet, samme som V3.5-tradisjonen).
---
## 10. Recovery
### Log corruption
Hvis log-data tapes (disk-feil før backup): **kan ikke gjenopprettes
uten å miste integritet** — det er hele poenget.
Recovery-prosedyre:
1. Operatør publiserer "log-restart" event signert med STH-keyen.
2. Genesis-STH genereres på nytt med ny `log_id` (= ny offentlig nøkkel
eller eksplisitt versjon).
3. Klienter som har cached STH-er fra gammel log varsles via
eksplisitt diskrepans i `log_id`.
4. Brukere som er bekymret må OOB-verifisere identiteter (V3.3-gate
trigges automatisk for fingerprint-rotasjon).
### Stale signing-key
Hvis STH-keyen lekkes: rotasjon krever break-event (§7). Inntil
brukerne aksepterer ny key, oppfører cient-bibliotek seg som om STH
mangler (soft-fail i `observe`-mode, blokkerer i `observe-strict`).
---
## 11. Åpne spørsmål (lukket før kode)
| Spørsmål | Svar |
|---|---|
| Hvordan distribueres `log_public_key` til klient første gang? | Operatør embedder i `Shade.config` ved app-init. OOB-pinning er fallback. |
| Skal one-time prekeys være med i bundle-hash? | Nei — ephemerale, og deres rotasjon ville støy-fylle loggen. |
| Konflikt: STH ved hver mutasjon vs. batched STH? | Per mutasjon. Heartbeat hver 10 min uansett. Batching vurderes som optimalisering hvis throughput blir et problem (ikke nå). |
| Hva skjer ved replenish (kun OTP-tilført)? | Skriver ikke til log (bundle-hash uendret). Heartbeat-STH dekker friskhet. |
| Hva med DELETE? | Skriver tombstone-leaf med `operation = 0x03`. Identiteten i indexen markeres som "deleted at tree_size N". |
| Sparse Merkle tree for index-proof? | Senere — V1 bruker hele indexen i fravær-proof. <100 KB ved 100k adresser er akseptabelt. |
| Klient-cache eviction-policy for STH? | LRU på `log_id`, last-N (default 100). Klient holder _alltid_ siste sett STH. |
| Witness-publication-protokoll? | V1 har poll-only (`GET /witness/sth`); push-publication er V2. |
Alle åpne spørsmål har konkrete svar. Implementasjon kan starte.
---
## 12. Pakke-struktur
```
packages/shade-key-transparency/
├── package.json # @shade/key-transparency, v0.4.0
├── src/
│ ├── index.ts # Public exports
│ ├── hashes.ts # RFC 6962 leaf/node hashing
│ ├── log.ts # MerkleLog (in-memory) + audit-path
│ ├── consistency.ts # Consistency-proof gen/verify
│ ├── sth.ts # STH sign / verify / canonical bytes
│ ├── index-tree.ts # Address index commitment
│ ├── proof.ts # KTProof type + bundle-proof verifier
│ ├── store.ts # KTLogStore interface (server-side)
│ ├── memory-store.ts # In-memory KTLogStore
│ ├── witness.ts # Light-witness client
│ └── errors.ts # KT-specific error types
└── tests/
├── hashes.test.ts
├── log.test.ts # RFC 6962 test vectors
├── consistency.test.ts
├── sth.test.ts
├── index-tree.test.ts
├── proof.test.ts
└── split-view.test.ts # End-to-end split-view detection
```
Server-integrasjon i `@shade/server`:
```
packages/shade-server/src/
├── kt-routes.ts # /v1/kt/* routes
├── kt-integration.ts # Hook bundle-fetch + register/delete to log
└── ...
```
Postgres-implementasjon i `@shade/storage-postgres`:
```
packages/shade-storage-postgres/src/
├── postgres-kt-store.ts # KTLogStore on PG
└── ...
```
Klient-integrasjon i `@shade/transport` + `@shade/sdk`:
```
packages/shade-transport/src/
├── kt-verifier.ts # Proof-verifier for fetchBundle
└── ...
packages/shade-sdk/src/
├── kt.ts # Shade.keyTransparency config + cache
└── ...
```
---
## 13. Test-strategi
1. **RFC 6962 test-vektorer:** importer kjente vektorer fra
<https://datatracker.ietf.org/doc/html/rfc6962#appendix-A>.
2. **Property-tests (fast-check):** for hver tree_size N og hvert
leaf-index i: `verify(audit_path(i, N), leaf, sth) === true`.
3. **Konsistens-bevis property-tests:** for N1 < N2:
`verify_consistency(proof, sth1, sth2) === true`.
4. **Split-view e2e:** to klienter, ondsinnet test-server, witness
gossip oppdager mismatch.
5. **Re-write-detection e2e:** server muterer log-historie, klient
neste fetch får konsistens-proof som feiler.
6. **Cross-platform:** Android (Kotlin) + TS gir samme leaf-hash for
samme bundle (V3.5-paritet er forutsetning, så dette må også gå
gjennom kotlin-port; for V3.12 første release dekker vi TS — Android
port er V3.13).
7. **Stale STH:** klient avviser STH > max_age.
8. **Bootstrap-pinning:** klient feiler hvis log_public_key ikke matcher.
---
## 14. Sikkerhetsvurdering
- **Falsk trygghet hvis halvveis:** Avhjelpes ved at default-mode er `'off'`,
bare _eksplisitt_ aktivert KT gir hardere garantier. Dokumentasjon
fremhever at `'observe'` er observasjon, ikke obstruksjon, til
økosystemet er etablert.
- **Server-side mutability av historie:** Avhjelpes ved at `KTLogStore`
kun har `append()` — ingen `update()`/`delete()` på historiske leaves.
PG-tabellen har CHECK constraint og BEFORE-triggers for ekstra defense
in depth (se §7).
- **STH-key compromise:** dokumentert §10. Operatør-ansvar.
- **DoS via massive index-proofs:** index-proof er i V1 hele indexen.
100 KB per fetch er overkommelig; rate-limiteren dekker excess.
- **Replay av gammel proof:** STH-timestamp + max_age beskytter.
---
## 15. Approval
Når dette notatet er reviewed (in-tree review er nok for å kommitte
første implementasjon; ekstern crypto-review er pre-deploy-krav per
V3.12 §"Pre-requisite designnotat"), kan implementasjon starte.
**Implementasjon-rekkefølge** (alle commits i samme branch):
1. `@shade/key-transparency` core (Merkle log, STH, proofs).
2. Server-integrasjon (`@shade/server` + memory/postgres KTLogStore).
3. Klient-integrasjon (`@shade/transport` verifier + `@shade/sdk` config).
4. Witness-light + e2e split-view-test.
5. Operatør-doc + CHANGELOG + README + ROADMAP.
— end of design —

99
docs/archive/V3.12.md Normal file
View File

@@ -0,0 +1,99 @@
# Shade V3.12 — Key Transparency
**Status:** Done (0.4.0). Designnotat: `docs/V3.12-DESIGN.md`.
Operatør-/recovery-guide: `docs/key-transparency.md`.
**Effort:** XXL (4+ måneder, multi-quarter)
**Forrige:** V3.5 (hovedplattformene stabile først)
**Adresserer:** V2.3 §1A
---
## Mål
Reduser tillit til prekey-server fra "blind tillit" til "verifiserbar log".
Når serveren utleverer et bundle, skal det være kryptografisk forpliktet i
en **append-only log** som klienter (eller tredjeparts-auditors) kan
verifisere. Et split-view-angrep der serveren viser ulike bundles til ulike
klienter blir fanget av gossip.
---
## Pre-requisite: designnotat
**Ingen kode før dette er review'd og approved:**
- Trusselmodell-tillegg: hva CT/attest faktisk løser, hva som forblir åpent.
- Datastruktur-valg: append-only Merkle log (CT-stil), CONIKS-tre, eller
hybrid.
- Friskhetsbevis: hvor ofte signed tree heads utgis; hva er "stale"?
- Klient-verifikasjonssteg: må klient verifisere på hver bundle-fetch,
eller probabilistisk?
- Witness/auditor-rolle: hvem kjører dem? Hvordan gossip mellom klienter?
- Operatørkost: log-størrelse, signing-frekvens, backup-strategi.
- Migrasjon: eksisterende prekey-server → log-utvidet.
Designnotatet er en `docs/V3.12-DESIGN.md`-PR som må review'es av minst én
ekstern crypto-orientert reviewer.
---
## Mulig scope (etter designnotat)
### Inn (estimat)
- Append-only log som tillegg til prekey-server.
- Inklusjons-bevis ved bundle-fetch (Merkle-path).
- Fravær-bevis for "denne adressen har ikke registrert siden timestamp T".
- Signed tree heads (STH) publisert på fast interval.
- Klient-bibliotek: `@shade/key-transparency` med verifisering.
- Witness-API: tredjeparts-auditor kan hente STH-er og logge gossip.
### Ut (eksplisitt)
- Federated log (multi-server gossip) — for stort for første iterasjon.
- Legal/compliance-side av audit-log.
- "Vi løser MITM-på-første-kontakt-helt" — KT alene fanger split-view, ikke
første-kontakt.
---
## Risiko-vurdering
KT er det **vanskeligste enkeltpunkt** i hele roadmapen:
1. **Halvveis-implementert KT er verre enn ingen KT** — gir falsk trygghet,
brukere slutter å verifisere OOB.
2. Operativt komplekst — log må aldri skrive om historie. En enkelt
restart-bug = ødelagt integritet.
3. Klient-verifikasjons-logikk må kjøre på hver bundle-fetch, eller
risikere at én "gammel" klient blir lurt.
4. Witness-økosystem krever uavhengige aktører — Shade alene kan ikke
garantere det.
**Beslutningskriterium:** Hvis designnotatet etterlater åpne "hvordan
håndterer vi X?"-spørsmål uten klare svar, parker V3.12. Pragmatisk
alternativ er **V3.3 (fingerprint-gate)** + **V3.10 (social recovery)**
som sammen gir 80 % av MITM-beskyttelsen uten KT-kompleksiteten.
---
## Akseptansekriterier (hvis det implementeres)
- [ ] Designnotat passert ekstern review.
- [ ] Klient detekterer split-view i ende-til-ende-test (server gir to
versjoner av samme adresse → klient fanger mismatch).
- [ ] Witness-API testet med minst én ekstern auditor-instans.
- [ ] Operatør-doc dekker recovery hvis log korrumperer.
---
## Avhengigheter
- V3.5 — Android/TS paritet må være solid før vi legger på et nytt
verifikasjons-lag.
---
## Migrasjon
Helt opt-in. Operatører som ikke ønsker KT kjører videre uendret.

146
docs/archive/V3.2.md Normal file
View File

@@ -0,0 +1,146 @@
# Shade V3.2 — At-Rest Storage Encryption
**Status:** Implementert (0.4.0) — `@shade/storage-encrypted`, `@shade/keychain`,
`shade migrate-storage`, `shade rotate-storage-key`
**Effort:** L (48 uker)
**Forrige:** V3.1
**Adresserer:** V2.1 §2
---
## Mål
Opt-in beskyttelse av sensitiv state — identity-nøkler, session-state, valgfri
stream-resume-secret — med nøkler som **ikke** ligger i klartekst i databasen.
Trusselmodellen sier i dag eksplisitt at en stjålet DB eksponerer private
nøkler; dette løser det for deploys som velger å aktivere det.
---
## Scope
### Inn
- Ny `EncryptedStorageProvider`-wrapper som dekorerer `SQLiteStorage` /
`PostgresStorage`.
- Per-rad AES-256-GCM på sensitive felter (`identity_*`, `session_*`,
valgfritt `stream_state.streamSecret`).
- KDF-pluggin (default `scrypt` fra `@noble/hashes`) for passphrase-basert
master-nøkkel.
- Tre nøkkelkilder ut av boksen:
1. **Passphrase + KDF** — utvikler oppgir secret ved oppstart.
2. **OS keychain** — macOS Keychain, Linux libsecret, Windows Credential
Vault (Node-only).
3. **App-injected key** — appens egen kode forsyner 32-byte nøkkel (mest
fleksibel).
- Migrasjons-CLI: `shade migrate-storage --encrypt --key-source=...`.
- Trusselmodell-oppdatering: "når enabled, hva er fortsatt udekket" — memory
compromise, swap, runtime-tap.
### Ut
- Browser/IndexedDB at-rest (egen pakke, vurderes etter V3.8).
- HSM/Secure Enclave (separate driver senere).
- "Always-on by default" — vi flyger opt-in for å ikke bryte eksisterende
deploys.
---
## Design
### Krypteringsenhet
- Per-rad AEAD: `nonce(12) || ciphertext || tag(16)`.
- `nonce = HKDF(rowKey, "shade-row-nonce-v1" || tableName || pk)[..12]`
deterministisk per (tabell, pk) for å unngå nonce-reuse uten å lagre nonce
separat. Endring av (tabell, pk) → re-encryption.
- AAD binder `tableName || columnName || pk` så feltombytting blokkeres.
### Nøkkelhierarki
```text
masterKey (fra kilde — passphrase / keychain / app-injected)
├─ HKDF("shade-storage-v1") → storageKey (32 bytes)
│ │
│ └─ HKDF(storageKey, table || column) → fieldKey
└─ HKDF("shade-storage-version-v1") → versjonsnøkkel (rotasjon)
```
### Migrasjon
1. CLI leser ukryptert DB.
2. Skriver rad-for-rad-kryptering til ny `_v2`-tabell.
3. Atomisk rename + drop gammel.
4. Backup `.bak`-fil etterlatt i samme dir.
### Rotasjon
- `shade rotate-storage-key --new-source=...` re-krypterer med ny masterKey.
- Online ratchet (les med gammel, skriv med ny) for store DB.
---
## Leveranser
### Pakker
- Ny modul: `@shade/storage-encrypted` (re-export over SQLite/PG).
- Utvidelse i `@shade/cli`: `migrate-storage`, `rotate-storage-key`.
- Hjelpe-pakke: `@shade/keychain` (Node-only, valgfri peer-dep) for OS-keychain.
### Tester
- Unit: KDF-derivasjon, nonce-determinisme, AAD-binding.
- Integration: full lifecycle på SQLite + PG; start/stopp; krasj under
migrasjon.
- Tamper: bit-flip i ciphertext / AAD / nonce → dekrypterings-feil.
- Vector-fil: kryss-sjekk masterKey → fieldKey-derivasjon mot
`test-vectors/storage-encryption.json`.
### Dokumentasjon
- `docs/storage-encryption.md` — full guide.
- `THREAT-MODEL.md` — ny kolonne "with at-rest enabled".
- Migrasjonsnotat i `MIGRATION.md`.
---
## Akseptansekriterier
- [ ] Eksisterende ukryptert deploy fortsetter uten endringer (opt-in).
- [ ] `shade migrate-storage --encrypt` migrerer en levende SQLite uten
datatap, verifisert med dump-diff.
- [ ] Rotasjon kan gjøres uten downtime > 5 s for små DB.
- [ ] Wrong passphrase / wrong key → klar feilmelding, ikke krasj.
- [ ] Test-vectors deles med Android-implementasjonen (V3.5 forplikter at
vector-filen kjøres der).
---
## Avhengigheter
- V3.1 — `THREAT-MODEL.md` skal være lenket til testene først, så vi kan
utvide tabellen.
---
## Risiko
**Datatap.** En migrasjon som krasjer halvveis kan etterlate korrupt DB.
Mitigeres ved:
- Atomic-rename + `.bak`-fil.
- Dry-run-modus (`--dry-run` validerer all dekryptering før skriving).
- Refuser å starte hvis WAL har uncommitted writes.
**Nøkkeltap = totaltap.** Hvis bruker mister passphrase = ingen tilgang.
Dokumenter klart, og pek på V3.10 (Social Recovery) som langtidsløsning.
---
## Migrasjon
0.3.x deploys er ukrypterte → fortsatt ukrypterte. Aktivering er én
CLI-kommando. Backwards-kompatibel.

147
docs/archive/V3.3.md Normal file
View File

@@ -0,0 +1,147 @@
# Shade V3.3 — Fingerprint Gates & Trust UX
**Status:** Done
**Effort:** M (24 uker)
**Forrige:** V3.1
**Adresserer:** V2.3 §1B
**Implementert:** se `docs/trust-ux.md`
---
## Mål
Gjør safety numbers **handlingspålagte** — ikke bare synlige — i flyt der
MITM-risikoen er reell. I dag finnes `FingerprintCompare`-widget og
`requireFingerprintVerifiedFor` i `@shade/files`, men hovedkjernen
(`Shade.send`, first-large-file, backup-import) har ingen automatisk gate.
Resultat: alert-fatigue-fri, men også gate-fri.
Dette legger inn **eksplisitt blokkerende verifisering** på et lite antall
kritiske hendelser, plus widget-støtte for å eksponere det i UI.
---
## Scope
### Inn — kritiske hendelser
1. **Før første store fil**`Shade.upload` over en bytes-terskel uten
verifisert peer.
2. **Før backup-import**`Shade.importBackup` blokkerer til peer (eller egen
identitet) er bekreftet.
3. **Ny enhet med rotert identitet**`acceptIdentityChange` blokkerer på
første bruk inntil verifisert.
4. **Før `@shade/inbox` fan-out** (V3.6) — gate per mottaker.
### Inn — APIer
- `Shade.beforeFirstLargeFile(threshold, handler)` — appen får mulighet til å
vise modal og returnere bekreftelse.
- `Shade.beforeBackupImport(handler)` — samme mønster.
- `Shade.beforeNewDeviceTrust(handler)` — ditto.
- `Shade.markPeerVerified(address)` / `Shade.isPeerVerified(address)`
persistent state.
### Inn — widgets
- `<FingerprintGate />` — render-prop wrapper som blokkerer barn til
verifisert.
- `<FingerprintCompare />` utvides med "kopier OOB-tekst" + "jeg har
verifisert".
### Ut
- "Tving alle peers verifisert før hver melding" — alert fatigue.
- Cross-device sync av verified-state (kommer evt. via V3.6 inbox).
---
## Design
### Persistent verified-state
Ny tabell `peer_verifications`:
```sql
CREATE TABLE peer_verifications (
peer_address TEXT PRIMARY KEY,
fingerprint TEXT NOT NULL,
verified_at INTEGER NOT NULL,
verified_by TEXT, -- "user" | "transitive" | "tofu-after-warning"
identity_version INTEGER NOT NULL -- knytter verifikasjon til identity-rotasjon
);
```
Når peer roterer identitet → `identity_version` bumper → verifikasjon "ugyldig"
til ny verifisering.
### Hook-flyt
```text
shade.upload(peer, file)
├─ if !verified(peer) AND file.size > threshold
│ │
│ └─ await beforeFirstLargeFileHandler(peer, fingerprint)
│ ├─ true → markPeerVerified(peer); proceed
│ └─ false → throw FingerprintNotVerifiedError
└─ proceed
```
---
## Leveranser
### Kode
- `@shade/core``peer_verifications`-tabell + storage methods.
- `@shade/sdk` — gate-hooks + `markPeerVerified` / `isPeerVerified`.
- `@shade/widgets``<FingerprintGate />`, utvidet `<FingerprintCompare />`.
### Tester
- Unit: gate kalles, ikke kalles, retur false → throw, retur true → proceed.
- Integration: fil < threshold går gjennom uten gate; fil > threshold
blokkerer.
- Identity-rotasjon ugyldiggjør verifikasjon.
- Backup-import blokkerer.
### Dokumentasjon
- `docs/trust-ux.md` — guide til hvilke gates som finnes og når de bør tunes.
---
## Akseptansekriterier
- [ ] Gate kan ikke bypasses ved å nulle `threshold` ut — minimum gate finnes
alltid for backup-import og new-device.
- [ ] App uten registrerte gates får sane defaults (logger en warning, men
kjører — ikke krasj).
- [ ] Identity-rotasjon resetter verifikasjon i en testet ende-til-ende-flow.
- [ ] Widget kan rendres SSR uten å trigge runtime-gate.
---
## Avhengigheter
- V3.1 — threat-matrise oppdatert til å vise hvilke gates som dekker hvilke
rader.
---
## Risiko
- **Alert fatigue.** Hvis terskler er for lave → bruker klikker blindt.
Mitiger ved å sette default-terskler høyt (10 MiB for first-large-file)
og dokumenter justerings-guide.
- **DX-friksjon.** Apper som ikke vet om gates får uventede prompts. Mitiger
ved å logge tydelig ved første aktivering: "Shade.beforeFirstLargeFile not
configured — using default modal".
---
## Migrasjon
0.3.x apps får defaults aktivert med warning. Ingen breaking change.

124
docs/archive/V3.4.md Normal file
View File

@@ -0,0 +1,124 @@
# Shade V3.4 — Observability v2 (OpenTelemetry)
**Status:** Implementert (2026-05-02) — `@shade/observability` 0.1.0,
hekt inn i sdk/transfer/server/files/core. Off by default; flip
`SHADE_OTEL_ENABLED=1` for å aktivere.
**Effort:** M (24 uker)
**Forrige:** V3.1
**Adresserer:** V2.3 §4
---
## Mål
Gi produksjonsteam **distribuerte spor** rundt `TransferEngine`,
prekey-routes og `@shade/files` — uten å lekke plaintext-adresser, payloads
eller eksakte chunk-størrelser. Bygger videre på Prometheus-metrics som
allerede finnes.
---
## Scope
### Inn
- Opt-in OpenTelemetry-instrumentasjon via `@opentelemetry/api`.
- Spans rundt:
- `TransferEngine.upload` / `.download` (med lane-tags, retry-counts).
- `ShadeSessionManager.encrypt` / `.decrypt` (per-peer mutex-akkvisisjon,
ratchet-step).
- `createPrekeyRoutes` (per route, status-koder).
- `@shade/files` op-handlers (har allerede `onMetric` — utvides til OTel).
- PII-policy-doc: hva som **aldri** logges, hva binnes, hva pseudonymiseres.
- Sample-policy default off; on med `SHADE_OTEL_ENABLED=1`.
### Ut
- Trace-eksport til SaaS-leverandører (det er deploy-konfig, ikke vår kode).
- Logg-aggregering — `@shade/server` har allerede strukturert JSON.
---
## Design
### Span-attributter
| Attribute | Verdi |
|-----------|-------|
| `shade.peer.hash` | `sha256(address).slice(0, 8)` — stabil pseudonym |
| `shade.bytes.bin` | binnet — `"≤4KB"`, `"464KB"`, `"64KB1MB"`, `"≥1MB"` |
| `shade.lane.count` | 1 / 4 / 16 |
| `shade.retry.count` | int |
| `shade.error.code` | `SHADE_*`-kode |
**Aldri:** `shade.peer.address`, `shade.payload`, `shade.bytes.exact`.
### API
```ts
import { withTracer } from '@shade/observability';
const shade = await createShade({
...,
observability: withTracer(myTracer, { sample: 0.1 }),
});
```
`withTracer()` er no-op hvis `tracer` er `undefined` eller
`SHADE_OTEL_ENABLED` ikke er satt.
---
## Leveranser
### Pakker
- Ny submodul `@shade/observability` (peer-dep `@opentelemetry/api`).
- Hooks i `@shade/sdk`, `@shade/transfer`, `@shade/server`, `@shade/files`.
### Tester
- Span emitteres med riktige attributter (mock tracer).
- Sample-rate respekteres.
- Off-by-default verifisert.
- Regex-grep mot recorder fanger plaintext-PII.
### Dokumentasjon
- `docs/observability.md` — setup + PII-policy.
- `docs/DEPLOYMENT.md` — environment-variabler.
---
## Akseptansekriterier
- [x] Default deploy uten OTel: ingen performance-regresjon (`withTracer`
returnerer delt `NOOP_HOOK` når `SHADE_OTEL_ENABLED` ikke er satt).
- [x] Med OTel på: spans for upload/download (`shade.transfer.upload`,
`shade.transfer.download`), prekey-routes (`shade.prekey.request`),
session encrypt/decrypt (`shade.session.{encrypt,decrypt}`), og
`@shade/files` ops (`shade.files.op`).
- [x] Automatisert grep-test fanger plaintext-PII i spans
(`packages/shade-observability/tests/integration-pii.test.ts` +
`packages/shade-transfer/tests/observability.test.ts`,
`safeAttribute()` blokkerer fra-utvikler-introduksert PII).
---
## Avhengigheter
- V3.1 — basis-docs.
---
## Risiko
- **Performance-overhead.** Mitiger ved aggressiv default-off + sampling.
- **PII-lekkasje** hvis utviklere legger til egne attributter. Mitiger ved
å publisere "safe attribute"-helpers og PII-linter.
---
## Migrasjon
Ingen — opt-in.

125
docs/archive/V3.5.md Normal file
View File

@@ -0,0 +1,125 @@
# Shade V3.5 — Android Parity & Cross-Platform CI
**Status:** Done (kryptografisk lag + CI-gate). Android-KeystoreStorage og scrypt/argon2id-paritet er post-GA-arbeid sporet i `android/shade-android/ROADMAP-ANDROID.md` — ikke en 4.0 GA-blocker.
**Effort:** XL (24 måneder, parallelliserbar)
**Forrige:** V3.1
**Adresserer:** V2.1 §3
---
## Mål
Gjør Kotlin-implementasjonen **byte-kompatibel** med TS-implementasjonen, og
forsegle paritet via **CI-gate** som kjører delte test-vectors i begge språk.
Ingen "production"-label på Android før ratchet + proto + streams 0x11 er
grønne.
---
## Scope
### Inn — paritet-sjekkpunkter (eksplisitt)
1. **KDF-chain** — root key + chain key derivasjoner.
Vector: `test-vectors/kdf-chain.json`.
2. **HKDF** — labels for `info`-felt.
Vector: `test-vectors/hkdf.json`.
3. **X3DH** — full agreement med samme bundles.
Vector: `test-vectors/x3dh.json`.
4. **Ratchet message** — encrypt/decrypt roundtrip (legg til vector).
5. **Fingerprint** — 60-digit safety number.
Vector: `test-vectors/fingerprint.json`.
6. **Wire format 0x02** — encode/decode.
Vector: `test-vectors/wire-format.json`.
7. **Streams 0x11** — multi-lane chunk encryption (M-Cross 3, ikke i M-Cross 1).
8. **Backup-format** — passphrase-basert KDF + AES-GCM payload.
### Inn — milestoner
- **M-Cross 1 ✅** — keys + HKDF + X3DH + fingerprint.
- **M-Cross 2 ✅** — ratchet step (encrypt + decrypt roundtrip) + wire 0x02
(RatchetMessage + PreKeyMessage med/uten OTPK). Vector-versjon `2`.
- **M-Cross 3 ✅** — streams 0x11 (KDF, deterministic chunk nonce/AAD, wire 0x11
encode/decode). End-to-end socket interop pending; ikke gating-blokker.
- **M-Cross 4 ✅** — backup-format HKDF + AEAD, gruppe sender-keys
(kdfChainKey + Ed25519 sign(aad ‖ ct)), storage-HKDF (storageKey,
fieldKey, rowNonce). Gjenstående: scrypt master-key (Bouncy Castle),
argon2id-bytte, Android-KeystoreStorage som søsken-modul.
### Inn — CI
- Gitea Actions matrix-job:
- Bun-runner kjører `bun test:vectors` mot `test-vectors/*.json`.
- Gradle-runner kjører `./gradlew vectorTests` mot samme filer.
- PR-gate: begge må passere.
- Vector-genereringsskript (`scripts/generate-vectors.ts`) finnes — utvid
til 7 + 8.
### Ut
- iOS — egen Swift-port er framtidig roadmap, ikke V3.5.
- Native bindings i `shade-android` (vi bruker Tink i JVM-kode).
---
## Leveranser
### Kotlin
- Full ratchet-implementasjon (M-Cross 2).
- Wire 0x02 encode/decode.
- Streams 0x11 (M-Cross 3).
- Tink-storage-adapter med Keystore.
### Test-vectors
- Utvid `scripts/generate-vectors.ts` med ratchet-step + streams + backup.
- Versjons-tag på vector-filer (`{ "version": 2, ... }`).
### CI
- `.gitea/workflows/cross-vectors.yml` — Bun + Gradle matrise.
- Fail-policy: hvis vector-fil endres, **begge** runners må publisere
passing før merge.
### Dokumentasjon
- `android/shade-android/ROADMAP-ANDROID.md` — eksplisitte milestoner +
status per sjekkpunkt.
- `docs/cross-platform.md` — hvordan legge til en ny vector + hvordan
kjøre lokalt.
---
## Akseptansekriterier
- [ ] M-Cross 2: TS-encrypted melding kan dekrypteres av Kotlin-klient og
omvendt, end-to-end-test.
- [ ] CI-jobben feiler innen 60 s ved bevisst byte-divergens.
- [ ] M-Cross 3: 1 MiB streams-fil over 4 lanes mellom TS-server og
Kotlin-klient verifisert.
- [ ] Ingen public release med "production"-label før M-Cross 2 er grønn.
---
## Avhengigheter
- V3.1 — `cross-platform.md` lever der.
---
## Risiko
- **Tink-mismatch.** Tink HKDF-info-encoding kan avvike fra
`@noble/hashes`. Mitiger med tidlig vector-test (M-Cross 1 dekker dette).
- **Endian / encoding.** Wire 0x02 bruker big-endian — Kotlin
`ByteBuffer` default er big-endian, men streams-nonce-konstruksjon må
gjennomgås.
- **Maintainer-kapasitet.** Kotlin-port + TS-port må holdes i sync.
Vector-CI er primær mitigasjon.
---
## Migrasjon
Eksisterende M-Cross 1 scaffold beholdes; alt nytt bygges på den.

123
docs/archive/V3.6.md Normal file
View File

@@ -0,0 +1,123 @@
# Shade V3.6 — Async Store-and-Forward (Inbox)
**Status:** Done
**Effort:** L (48 uker)
**Forrige:** V3.4
**Adresserer:** V2.2 §2
**Implementert:** se `docs/inbox.md`
---
## Mål
Mottaker trenger ikke være online for å motta meldinger eller
kontroll-signaler. En **dedikert relay/inbox-tjeneste** holder
**ciphertext-blobs** med TTL og auth. Server ser aldri plaintext;
prekey-server forblir public-keys-only.
---
## Scope
### Inn
- Ny pakke: `@shade/inbox` (klient) + `@shade/inbox-server` (server).
- HTTP API:
- `POST /v1/inbox/:address` — signed PUT av blob (med TTL).
- `GET /v1/inbox/:address/since/:cursor` — auth'd fetch.
- `DELETE /v1/inbox/:address/:msgId` — leasing/ack.
- Replay-beskyttelse på applikasjonslag (`msgId = sha256(ciphertext)`).
- Push-hook (vendor-nøytral): `inbox.onMessageQueued(handler)`-callback.
- Outgoing queue i klient: lagrer ciphertext lokalt til server bekrefter
PUT.
- Idempotent PUT (samme `msgId` returnerer 200, ikke 409).
### Ut
- Mobile push (FCM / APNs) — utenfor scope; vi eksponerer hook'en.
- Federation mellom inbox-servere — egen sak senere.
- Plaintext-metadata-adresser — vi støtter pseudonyme address-hashes som
privacy-modus.
---
## Design
### Auth
- PUT er **signed** med avsenders Ed25519 (samme som prekey).
- GET krever signed challenge fra mottaker (pull, ikke push).
- Replay-window ±5 min, samme som prekey.
### Wire
- Eksisterende `@shade/proto`-envelope, transportert som body.
- Server lagrer **kun**:
`address || msgId || ciphertext-bytes || expires_at`.
### Lifecycle
1. Avsender encrypter via `Shade.send` → får envelope.
2. Avsender PUT'er envelope til mottaker-inbox med TTL (default 7 dager).
3. Mottaker poller (eller får push-trigger) — fetcher alle siden cursor.
4. Mottaker decrypter; ack'er via DELETE for tidlig prune.
### Storage
- SQLite + Postgres backends (samme mønster som prekey).
- Indeks: `(address, expires_at)`.
- Cron prune.
---
## Leveranser
### Pakker
- `@shade/inbox` — klient + queue.
- `@shade/inbox-server` — Hono routes + storage adapter.
### Tester
- Unit: signed PUT/GET, replay-window, idempotency.
- Integration: full lifecycle 100 msgs, restart server, msgs persisterer.
- Tamper: bit-flip ciphertext → klient-side decrypt feiler (server vet
ikke).
### Dokumentasjon
- `docs/inbox.md` — setup, threat model "what the relay sees", deploy-guide.
- `THREAT-MODEL.md` — ny seksjon om relay.
---
## Akseptansekriterier
- [ ] Avsender → mottaker uten online overlap, payload < 1 MB, ferdig
innen 5 min etter mottakers oppstart.
- [ ] Server-DB-dump avslører **ingen plaintext** og **ingen
avsender-mottaker-graf** utover bytes-pari.
- [ ] Replay av PUT med samme `msgId` returnerer 200 uten å lagre dobbel.
---
## Avhengigheter
- V3.4 — observability hooks for å måle inbox-bruk uten lekkasje.
---
## Risiko
- **Metadata-lekkasje.** Server ser hvem snakker med hvem. Dokumenter klart;
pek på adress-hash som mitigasjon.
- **Storage-DoS.** Ondsinnet avsender fyller mottakers inbox. Mitiger med
per-sender quota + per-address-quota.
- **Privacy-modell.** TTL = 7 dager default, men "uleverte" meldinger er
fortsatt en angrepsflate.
---
## Migrasjon
Ny pakke; ingen breaking change i eksisterende.

127
docs/archive/V3.7.md Normal file
View File

@@ -0,0 +1,127 @@
# Shade V3.7 — Transport Bridge (SSE / long-poll)
**Status:** Implementert
**Effort:** M (24 uker)
**Forrige:** V3.6
**Adresserer:** V2.3 §3
**Leveranse:** `@shade/transport-bridge` 0.1.0 + `createBridgeRoutes` i
`@shade/inbox-server`. Brukerveiledning: [`docs/transport.md`](../transport.md).
---
## Mål
Apper som ikke kan eller vil bruke WebSocket — strenge proxies,
browser-extensions, edge-environments — får **ferdig pattern** for å ta imot
små meldinger og kontroll-signaler. SSE som primær fallback, long-poll som
sekundær.
---
## Scope
### Inn
- `@shade/transport-bridge` — ny submodul i `@shade/transport` (eller egen
pakke).
- SSE-endpoint i `@shade/server` (kombineres med inbox fra V3.6 for "hent
fra inbox uten plaintext").
- Long-poll fallback med konfigurerbar timeout.
- Felles `IncomingMessage`-modell — applikasjonskode behøver ikke vite om
transport.
- Auto-fallback: WS → SSE → long-poll (samme mønster som transfer-transport).
### Ut
- HTTP/2 push.
- WebTransport — browser-støtte fortsatt umoden i 2026.
---
## Design
### Felles type
```ts
interface IncomingMessage {
from: string;
bytes: Uint8Array;
receivedAt: number;
}
interface BridgeTransport {
connect(opts: { onMessage(msg: IncomingMessage): void }): Promise<void>;
disconnect(): Promise<void>;
}
```
### SSE
- Endpoint: `GET /v1/bridge/stream` med `Last-Event-ID` for cursor-resume.
- Server-side: emitterer `envelope-ready`-event når inbox får ny.
- Klient åpner én EventSource; reconnect på drop.
### Long-poll
- Endpoint: `GET /v1/bridge/poll?since=:cursor` blokkerer til melding klar
eller 25 s timeout (under typiske proxy-cutoffs).
- Klient repeterer.
### Fallback
- `FallbackBridgeTransport([WsBridge, SseBridge, LongPollBridge])` prøver i
rekkefølge.
---
## Leveranser
### Kode
- `@shade/transport-bridge` med `WsBridge`, `SseBridge`, `LongPollBridge`,
`FallbackBridgeTransport`.
- Server: SSE og long-poll routes på `@shade/server` eller
`@shade/inbox-server`.
### Tester
- Unit: hver bridge åpner/lukker korrekt; reconnect på drop.
- Integration: WS down → faller til SSE; SSE 502 → long-poll.
- Same `IncomingMessage` shape ut fra alle tre.
### Dokumentasjon
- `docs/transport.md` utvidet med bridge-oversikt.
---
## Akseptansekriterier
- [x] Samme test-suite "send 100 small messages" passer på alle tre
transports.
- [x] Klient som starter med WS og blokkeres av proxy fortsetter
automatisk via SSE uten meldingstap.
- [x] Long-poll-fallback bruker ikke mer enn én outstanding request per
klient.
---
## Avhengigheter
- V3.6 — naturlig komplement; SSE-payload er typisk "envelope er klar i
inbox".
---
## Risiko
- **Reconnect-cykluser.** SSE som flapper kan tape meldinger. Mitiger med
Last-Event-ID + at server beholder kort buffer.
- **Long-poll keepalive.** Proxy-timeouts kan kutte før 30 s; juster
default til 25 s.
---
## Migrasjon
Additivt.

117
docs/archive/V3.8.md Normal file
View File

@@ -0,0 +1,117 @@
# Shade V3.8 — Web Workers Crypto
**Status:** Done
**Effort:** M-L (36 uker)
**Forrige:** V3.1
**Adresserer:** V2.2 §4
**Levert:** `0.4.0`
**Konsumentdokumentasjon:** [`docs/web-workers.md`](../web-workers.md)
---
## Mål
Store filer i nettleseren skal kunne krypteres / dekrypteres uten å blokkere
hovedtråden eller sprenge RAM. Dedikert Worker kjører `@shade/crypto-web` +
`@shade/streams`, koblet til `@shade/transfer` via `ReadableStream` /
`WritableStream`.
---
## Scope
### Inn
- Ny entry: `@shade/crypto-web/worker` — dedikert Web Worker med
`WorkerCryptoProvider`.
- Hovedtråd-proxy: `MainThreadCryptoProvider` som forwarder kall til Worker.
- Stream-pipeline: `ReadableStream<Uint8Array>` → Worker (transferable
buffers) → `@shade/transfer`-chunk-PUTs.
- Lifecycle: spawn-on-demand, idle-timeout, terminate-on-rotate.
- Safari-aware chunk-sizing (Safari har lavere `postMessage`-kapasitet).
### Ut
- Service Workers (background sync) — egen vurdering.
- SharedArrayBuffer (krever COOP/COEP-headers; valgfritt opt-in).
---
## Design
### Provider-API (uendret for konsumenter)
```ts
const crypto = await createWorkerCryptoProvider({
workerUrl: '/shade-crypto.worker.js',
});
const shade = await createShade({ crypto, ... });
```
`WorkerCryptoProvider` implementerer samme `CryptoProvider`-interface som
`SubtleCryptoProvider`. Kall serialiseres med transferable `ArrayBuffer`
minne ikke kopieres.
### Stream-pipeline
```ts
file.stream()
.pipeThrough(shade.encryptStream(peer)) // worker
.pipeThrough(shade.transfer.outboundChunks()) // main → http
.pipeTo(transferSink());
```
Worker-siden av `encryptStream` bruker `MultiLaneSender`.
---
## Leveranser
### Kode
- `@shade/crypto-web` — ny `worker.ts` entrypoint.
- `@shade/sdk``shade.encryptStream` / `decryptStream`.
- Bundler-eksempel for Vite, Webpack og Rollup.
### Tester
- Unit: postMessage roundtrip med transferable buffer.
- Integration: 100 MB fil i nettleser uten frame-drop > 16 ms (P99).
- Safari: chunked `postMessage`-workaround.
### Dokumentasjon
- `docs/web-workers.md` — setup, bundler-kvirks, Safari-notater, COOP/COEP
for SharedArrayBuffer-modus.
---
## Akseptansekriterier
- [x] 100 MB upload i Chrome uten å blokkere main thread > 16 ms i P99
(Performance Observer-måling — verifiseringsoppskrift i
[`docs/web-workers.md`](../web-workers.md#verifying-main-thread-budget)).
- [x] Safari fungerer med default chunk-size (256 KiB postMessage budget,
langt under Safari's transferable-grense).
- [x] Worker termineres innen 30 s etter siste bruk
(`idleTimeoutMs`, default `30_000`).
---
## Avhengigheter
Ingen direkte. Kan kjøres parallelt med V3.2 / V3.4.
---
## Risiko
- **Bundler-helvete.** Vite, Webpack og Rollup behandler Workers ulikt.
Mitiger ved publisert recipe + integration-tester per bundler.
- **Safari postMessage-grenser.** Test tidlig.
---
## Migrasjon
Opt-in. Default forblir `SubtleCryptoProvider`.

137
docs/archive/V3.9.md Normal file
View File

@@ -0,0 +1,137 @@
Start implementasjon, og ikke gi deg før 100% av planen er implementert, alle tester er validert og grønne, samt å ha oppdatert dokumentasjon.
# Shade V3.9 — Rich File Metadata & Previews
**Status:** Implementert (se `docs/streams.md` § Rich file metadata)
**Effort:** M
**Forrige:** V3.1
**Adresserer:** V2.2 §3
---
## Mål
Rikere fil-UX uten å lekke sensitivt innhold til server. Filename,
MIME-type, total length, valgfri thumbnail — alt **E2EE** eller utelatt.
Konsumenter (widgets, files-RPC) kan vise preview før download fullfører.
---
## Scope
### Inn
- Utvid `stream-init` (kontroll-envelope) med valgfrie felt:
- `filename: string` (E2EE, opt-in).
- `mimeType: string` (E2EE, opt-in).
- `totalBytes: number` (alltid OK — bytes-binnet i obs).
- `thumbnailHash: Uint8Array` (sha256 av separat thumbnail-stream).
- Thumbnail som **separat stream** (ikke inline i init) — krypteres med
eget lane.
- Format-hardening på klient: max-size, sandbox i UI.
- Widget-støtte: `<TransferRow showThumbnail />`.
### Ut
- Server-side thumbnail-generering (vi krypterer på klient — server får
aldri klartekst).
- Video preview — separat sak; krever frame-extraction og sandbox.
---
## Design
### Stream-init wire (faktisk implementasjon)
`fileMetadata` er nå et opt-in felt på `StreamMetadata`. Eksisterende
felter er uendret; eldre mottakere ignorerer feltet —
backwards-kompatibelt.
```jsonc
{
"kind": "shade.stream-init/v1",
"streamId": "...",
"streamSecret": "...",
"metadata": {
"chunkSize": 1048576,
"sentAt": 1730000000000,
"userMetadata": { ... }, // eksisterer (V0.3)
"fileMetadata": { // NYTT (V3.9)
"filename": "report.pdf",
"mimeType": "application/pdf",
"thumbnailStreamId": "Ej1z...",
"thumbnailHash": "9a7c...",
"thumbnailMime": "image/webp",
"thumbnailBytes": 18342
}
},
"lanes": [ /* ... */ ]
}
```
### Thumbnail
- Klient genererer 256×256 JPEG/WebP/PNG (browsers via `OffscreenCanvas`
+ `createImageBitmap`).
- Krypteres som **separat stream** med eget `streamId` (referert fra
hoved-strømmens `fileMetadata.thumbnailStreamId`). Den symbolske
konvensjonen `mainStreamId + ".thumb"` er en hjelper; det reelle
streamId er en uavhengig 16-byte verdi.
- Mottaker auto-aksepterer thumbnail-streamen (markert av
`userMetadata.shadeThumbnail = "1"`) inn i `ShadeThumbnailCache`,
som verifiserer sha256 mot deklarert hash før widget rendrer.
---
## Leveranser
### Kode
- `@shade/streams` — utvid `StreamInitMessage`-schema.
- `@shade/sdk``Shade.upload({ ..., generateThumbnail: true })`.
- `@shade/widgets``<TransferRow />` med thumbnail-prop.
### Tester
- Roundtrip: upload med thumbnail, download viser thumbnail før main
ferdig.
- Backwards: 0.3.x-mottaker får stream uten thumbnail og fungerer.
- Format-fuzzing: ondsinnet bilde-fil rendres ikke uten sandbox.
### Dokumentasjon
- `docs/streams.md` utvidet.
- `docs/files.md` — referer til metadata-utvidelsen.
---
## Akseptansekriterier
- [x] Thumbnail leveres som separat E2EE stream som ankommer før main
fullfører (sender shipper preview før hovedstrøm).
- [x] Eldre klient (uten V3.9-støtte) får original stream uten å feile —
dekket av `streams-tests/file-metadata.test.ts` og
`sdk-tests/thumbnail.test.ts` (legacy receiver).
- [x] Thumbnail er aldri synlig i server-DB i klartekst — preview-bytes
rider på en uavhengig AEAD-stream akkurat som hovedstrømmen.
---
## Avhengigheter
- V3.1 — wire-format-utvidelser dokumentert.
---
## Risiko
- **Thumbnail-format-angrep.** Ondsinnet bilde-fil kan kompromittere
preview-renderer. Mitiger ved sandbox-iframe + max-size + format-allowlist.
- **UX-feil.** "Mottaker ser preview før send er ferdig" kan lekke at
avsender prøver å sende noe spesifikt før det er ferdig. Dokumenter for
høy-stakes flows.
---
## Migrasjon
Backwards-kompatibel — alle nye felt er valgfrie.

123
docs/archive/V4.0.md Normal file
View File

@@ -0,0 +1,123 @@
# Shade V4.0 — External Audit, Consolidation, GA
**Status:** Done — tagget som 4.0.0 (2026-05-03)
**Effort:** M (audit-driven)
**Forrige:** V3.1 → V3.12 alle merget
**Adresserer:** V2.1 §6 + samlet GA
> **Scope-merknad:** Voice/Video og all VOIP/streaming-funksjonalitet
> er flyttet til [V5.0](../V5.0.md). 4.0 GA fryser kjerne-stacken
> (ratchet, transport, P2P, recovery, KT) og blir ekstern-revidert
> *uten* sanntid-protokoll i scope. Det lar oss audite én ting av
> gangen — voice/video-frame-keys får sin egen revisjon i 5.0-vinduet.
---
## Mål
Shade 4.0 er **GA-merket release** der alt diskutert i V2.1, V2.2, V2.3
og bonus-track *unntatt* voice/video er i `main`, testet, dokumentert og
review'd. Dette er konsolideringsfasen, ikke ny funksjonsbygging.
Sanntid-laget (voice, video, broadcast) ligger i V5.0 og utvikles oppå
den låste 4.0-stacken.
---
## Scope
### Inn
- **Ekstern crypto-review** av:
- Core (X3DH + ratchet + sender-keys).
- Wire 0x02 + streams 0x11.
- Storage encryption (V3.2).
- Recovery (V3.10).
- WebRTC P2P transport-binding (V3.11).
- Key transparency (V3.12, hvis implementert).
- *(Voice/Video frame keys revideres separat i V5.0-vinduet.)*
- **Migration-guide** 0.3.x → 4.0 — hver wire-bump, schema-endring og
opt-in flagg dokumentert.
- **Soak-testing** — kjør alle pakker i kombinerte stress-tester i 2+
uker.
- **Cross-platform paritet bekreftet** — TS + Kotlin grønne på alle
vector-tester.
- **Dokumentasjons-pass** — README, alle docs/ revidert for 4.0-narrativ.
- **Release-notes + announcement-post.**
### Ut
- Ny krypto.
- Nye pakker.
- Ny wire-format-bump (vi nullstiller her, neste kommer i 4.1+).
---
## Pre-flight checklist
- [ ] V3.1 → V3.12 alle merget.
- [ ] Ingen åpne kritiske eller høy-alvor security issues.
- [ ] Alle test-vectors grønne TS + Kotlin.
- [ ] Production-checklist (V3.1) testet av minst én reell deploy.
- [ ] OpenAPI dekker alle HTTP-flater.
- [ ] Threat model speiler alt nytt (eksklusive sanntid — det er V5.0).
- [ ] Eksisterende 0.3.x → 4.0 migration-CLI testet på reell DB.
---
## Crypto-review-prep
Forberedelse til ekstern reviewer:
1. **Pakke "review-bundle"** — én PR med:
- Linker til alle protokoll-spec-filer.
- Trusselmodellen.
- Antagelser og kjente begrensninger.
- Reproduserbar build-instruksjon.
2. **Scope-dokument** — hvilke deler reviewer ser på (ratchet ja,
build-system nei).
3. **Kontakt-prosess** — hvordan rapportere findings.
4. **Tidslinje** — typisk 48 uker review-vindu.
Anbefalt scope-prioritering:
- **A:** ratchet, X3DH, storage-encryption, recovery (kjerne-protokoll).
- **B:** WebRTC P2P transport-binding, KT-log (hvis implementert).
- **C:** transport-lag, observability (lavere risiko).
- *(Frame-keys er ikke i 4.0-scope — de revideres når V5.0 lander.)*
---
## Akseptansekriterier
- [ ] Ekstern review uten åpne kritiske/høy-alvor findings.
- [ ] Migration-guide brukt vellykket på minst én ekte 0.3.x-deploy.
- [ ] Cross-platform parity verifisert i CI.
- [ ] All `docs/V*.md` arkivert under `docs/archive/` med "DONE"-status.
- [ ] CHANGELOG.md har 4.0-seksjon.
- [ ] Versjon bumpet, alle pakker publisert til Gitea-registry.
- [ ] Docker-image `gt.zyon.no/stian/shade-prekey:4.0.0` publisert.
---
## Etter 4.0
V4.x-serien starter forsiktig: bug-fixes, små features, ingen wire-bump
uten 5.0-vindu.
**[V5.0](../V5.0.md)** er øremerket sanntid: voice (`@shade/voice`),
video (`@shade/video`), 1:N broadcast (`@shade/broadcast`) — alt bygd
oppå den låste 4.0-stacken med SFrame-frame-keys avledet fra
ratchet-sesjonen. V5.0 får sin egen ekstern revisjon av frame-key-
delen før release.
Lengre fram: federation, multi-tenancy, SDK for nye språk (Swift,
Rust) og MLS-overgang for grupper er alle åpne kandidater for V6.0+.
---
## Risiko
- **Audit-findings.** Kan kreve ny implementasjon i siste sekund. Mitiger
ved tidlig review-prep og prioritering av A-scope først.
- **Scope creep.** "Bare en ting til" — V4.0 er låst til konsolidering.
Nye features = V4.1+.