# Deletion Ledger > Status: **contract definition**. The hardening workstream (plan ยง9) implements > this; nothing here is verified in code yet. ## Purpose Space deletion is final and must survive disaster restores. A backup restored after a purge would otherwise resurrect rows, objects and metadata of deleted spaces. The deletion ledger is an append-only record of completed deletions that any restore **must** replay before serving traffic. ## Lifecycle 1. **Request** โ€” user requests space deletion. The space is locked immediately (no logins, writes or imports); a 30-day cancellation window starts. 2. **Cancel** โ€” within the window the owner may cancel; the lock is lifted, nothing is purged. 3. **Purge** (after the window, via Celery worker): - hard-delete all space rows (financial tables, categories, payees, imports, memberships, users where applicable); - delete all S3 objects of the space and destroy the per-file DEK rows (crypto-shred, ADR 0004); - append one ledger entry **only after** all phases succeed. 4. **Ledger entry** โ€” append-only, minimal fields: `space_id`, `requested_at`, `purged_at`, `executed_by (job id)`, `phases_completed`, `integrity_hash` (chain hash over prior entry). No financial payloads. ## Replay after restore On any disaster restore (see `backup-and-restore.md` step 6): 1. Load ledger entries with `purged_at` later than the restored backup timestamp. 2. For each: re-delete the space's rows (restored backups may contain them again), re-delete its objects, re-destroy DEK rows. 3. Verify: no row with `space_id` in any financial table; bucket prefix absent; decryption of the space's files impossible. 4. Record the replay in the drill log. ## Implementation notes - The ledger table itself is never purged by space deletion and is included in backups. - A purge job must be idempotent and resumable: each phase is committed separately and the ledger entry is written last, in the same transaction as the final phase marker. - Ledger integrity is checked on startup after restore (chain hash mismatch = manual escalation).