# Shade V3.6 — Async Store-and-Forward (Inbox) **Status:** Done **Effort:** L (4–8 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.