148 lines
4.2 KiB
Markdown
148 lines
4.2 KiB
Markdown
|
|
# 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
|
|||
|
|
|
|||
|
|
- `<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.
|