docs(future): security backlog items #1-#3 are shipped, deployed and live-verified
Replaces the "deploy-gated / awaiting" wording, which is now stale — the user deployed indexes, rules and functions. Each item carries what was actually proven live rather than what was built: - cleanup: deployed and the sweep runs (the collectionGroup index resolves in prod), but the REAP itself has never been observed — all scheduled functions stalled 2026-07-15 ~20:50. Says so plainly instead of implying it's done. - owner-alert: verified live — requesting device silent, partner alerted, and since self-alerts bypass quiet hours the exclusion is what silenced it. - rotation phase 1: verified across two rotations on a throwaway couple, plus the two bugs the live runs caught (warm-app adoption; C-ROTATE-001). Notes the fixture partner still stranded pending a user-performed Rotate tap.
This commit is contained in:
parent
2f16d1e09c
commit
a98c1e3c8c
41
Future.md
41
Future.md
|
|
@ -23,22 +23,33 @@ Non-blocking ideas: things that work today but could be better, plus feature ide
|
||||||
defeats the on-screen identity check does NOT defeat this). Needs mail infra (SendGrid / a Firebase Auth action)
|
defeats the on-screen identity check does NOT defeat this). Needs mail infra (SendGrid / a Firebase Auth action)
|
||||||
— deferred for that reason. R24-b shipped the on-screen identity + confirm + owner self-alert as the pragmatic
|
— deferred for that reason. R24-b shipped the on-screen identity + confirm + owner self-alert as the pragmatic
|
||||||
interim.
|
interim.
|
||||||
- **Restore-request lifecycle cleanup — BUILT 2026-07-15 (commit `8496bbed`), deploy-gated.** Hourly
|
- **Restore-request lifecycle cleanup — ✅ SHIPPED + DEPLOYED (commit `8496bbed`).** Hourly
|
||||||
`cleanupExpiredRestoreRequests` (collectionGroup on `expiresAt`, catches orphans under deleted couples;
|
`cleanupExpiredRestoreRequests` (collectionGroup on `expiresAt`, catches orphans under deleted couples;
|
||||||
delete-first then a quiet expiry nudge to the requester through the house pipeline). Awaiting:
|
delete-first then a quiet expiry nudge to the requester through the house pipeline). Indexes + function
|
||||||
`firebase deploy --only firestore:indexes` + `--only functions:cleanupExpiredRestoreRequests`, then live verify.
|
deployed; sweep observed running (`scanned 0 … failed 0` = the collectionGroup index resolves in prod).
|
||||||
- **Owner-alert precision — BUILT 2026-07-15 (commit `2dd09d5d`), deploy-gated.** Requesting device embeds its
|
⚠️ **The REAP itself has never been observed live** — every scheduled function stalled 2026-07-15 ~20:50 UTC
|
||||||
FCM token (create-only optional field, best-effort); both self-alerts skip that one device, partner push
|
(see ClaudeReport). Re-verify once scheduling is restored: arm a short-expiry request, watch it reap + nudge.
|
||||||
untouched. Awaiting: `firebase deploy --only firestore:rules` +
|
- **Owner-alert precision — ✅ SHIPPED + DEPLOYED + VERIFIED LIVE (commit `2dd09d5d`).** Requesting device embeds
|
||||||
`--only functions:onRestoreRequested,functions:onRestoreFulfilled`, then live verify.
|
its FCM token (create-only optional field, best-effort); both self-alerts skip that one device, partner push
|
||||||
- **Couple-key rotation / forward secrecy — PHASE 1 BUILT 2026-07-15 (commit `2c44cc6f`), deploy-gated; PHASE 2
|
untouched. Live 2026-07-16 on a throwaway couple: the requesting device got **zero** notifications while the
|
||||||
PLANNED (own checkpoint).** Phase 1 = rotate-forward: new Tink key primary + old retained (history readable,
|
partner's "Help your partner restore 💜" landed — and since self-alerts bypass quiet hours, the exclusion is
|
||||||
zero wire/`isCiphertext` changes), same-phrase re-wrap + strictly-increasing `keyGeneration` on the couple doc,
|
what silenced it.
|
||||||
partner adopts automatically on Home load, 🔑 alert via `onCoupleKeyRotated`, Settings → Security row.
|
- **Couple-key rotation / forward secrecy — ✅ PHASE 1 SHIPPED + DEPLOYED + VERIFIED LIVE (`2c44cc6f`, `cabfca5e`,
|
||||||
Fail-closed path VERIFIED LIVE pre-deploy (PERMISSION_DENIED → "nothing changed", app healthy). Protects
|
`7341f64a`, `8b0bc582`); PHASE 2 PARKED (own checkpoint).** Phase 1 = rotate-forward: new Tink key primary + old
|
||||||
FUTURE content only. Awaiting: rules + `--only functions:onCoupleKeyRotated` deploys, then 2-device happy-path
|
retained (history readable, zero wire/`isCiphertext` changes), same-phrase re-wrap + strictly-increasing
|
||||||
verify (throwaway couple first). Phase 2 (forward secrecy for history) = per-family idempotent re-encrypt
|
`keyGeneration`, partner adopts automatically, 🔑 alert via `onCoupleKeyRotated`, Settings → Security row.
|
||||||
sweeps + backup re-snapshot under the new key + both-devices K1-destruction handshake — plan in
|
Verified live 2-device on a throwaway couple across TWO rotations: partner on the old key genuinely can't read
|
||||||
|
new content (🔒), adopts, then reads BOTH eras. Protects FUTURE content only — a stolen keyset still holds the
|
||||||
|
old key; that's phase 2.
|
||||||
|
**Two bugs the live runs caught, both fixed:** adoption only ran on Home load, so a warm app (or one opened via
|
||||||
|
a chat notification) never adopted — now a couple-doc watcher at app start (`7341f64a`, `8b0bc582`); and
|
||||||
|
**C-ROTATE-001** — a cache-fed `keyGeneration` made the 2nd rotation publish an unchanged generation, so no
|
||||||
|
trigger fired and the partner was stranded on 🔒 permanently while the rotating device saw "success"
|
||||||
|
(`cabfca5e`: server-authoritative read + a rule making the stranding write unrepresentable).
|
||||||
|
**Outstanding:** one fixture partner (Ben/5556) is still stranded from that bug — remedy is a single Rotate tap
|
||||||
|
on Ava (5554) with the current build, which is a fixture credential op the user must perform.
|
||||||
|
Phase 2 (forward secrecy for history) = per-family idempotent re-encrypt sweeps + backup re-snapshot under the
|
||||||
|
new key + both-devices old-key-destruction handshake — plan in
|
||||||
`/home/kaspa/.claude/plans/i-want-you-to-recursive-clover.md`, NOT started (pre-execution review checkpoint).
|
`/home/kaspa/.claude/plans/i-want-you-to-recursive-clover.md`, NOT started (pre-execution review checkpoint).
|
||||||
- **Server-independent anti-rollback freshness.** A malicious server could serve a stale manifest to hide recent
|
- **Server-independent anti-rollback freshness.** A malicious server could serve a stale manifest to hide recent
|
||||||
messages; today mitigated by the `generation` counter + a Phase-1 Firestore cross-check. Add a signed/monotonic
|
messages; today mitigated by the `generation` counter + a Phase-1 Firestore cross-check. Add a signed/monotonic
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue