Done in https://github.com/badbundle/vault-app/pull/647.
The erase: VaultEraser.erase() is a journaled erase that returns the device to a fresh plain store. It:
- locks the session, releases the plain store, and journals
erasing; - removes
vault-slots.v1first, so every vault becomes unreadable at once; - removes the temp files, the lock file, the plain store's files, archives, rehash files and leftover backup PDFs;
- removes every keychain item: the HMAC keys, the backup password and its record, the attempt count and the wrap stamp;
- clears the backup event, the auto-backup config and the PDF hint;
- clears QuickType and reloads widgets;
- creates a fresh, empty plain store, then removes the journal.
Real and duress vaults erase identically.
Keychain registry: SecureStorageKey is now a CaseIterable registry, and the eraser switches over every case. A new keychain item (VAULT-48's device key) won't build until the erase handles it.
Wiring: unlock returns .wrongPassword(reachesEraseThreshold:), so the lock screen knows when to call it. That's VAULT-34's wiring.
Recovery:
- An interrupted erase is finished at launch, before anything reads the vault.
- If that fails, an "Erase Not Finished" screen with Try Again keeps the vault hidden.
- Unlock refuses while an erase is pending.
- If the journal can't be written, a fallback still destroys the vault.
- Recovery also finishes an erase when neither file is left.
Tests:
- A failure and a crash at every one of the 28 steps, plus two-fault combinations, each followed by relaunch.
- A registry-based check that every keychain item is gone.
- Two-vault equality, concurrency, and the threshold.
Review: a security review found four should-fixes: a double fault leaving a dead device, unlock ignoring an erasing journal, a failed launch erase being swallowed, and retained settings. A re-review then found the wrap stamp wasn't erased. All were fixed before merging.