# Shade V3.3 — Fingerprint Gates & Trust UX **Status:** Done **Effort:** M (2–4 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 - `` — render-prop wrapper som blokkerer barn til verifisert. - `` 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` — ``, utvidet ``. ### 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.