storage_cleanup_job rows in the same database transaction as the row
delete. A row stays until its storage prefix is verified empty. Jobs are internal erasure work: they are not exposed in
the product UI.
What runs
- Node server:
startup/storage-cleanup.tsevery minute. - Vercel: the existing
/api/career/runcron also drains cleanup. - Cloudflare: the scheduled handler runs cleanup alongside the career runner and waits for both before closing the pool.
- Immediate post-delete passes are bounded; they reduce delay but are not the durability guarantee.
Signals
Schedulers log a sanitized aggregate when cleanup remains:Inspect
Bounded aggregate, safe to run in production:id can contain an owner ID, and last_error can contain provider paths or identifiers. Inspect
these locally; do not paste them into an incident channel. Recent failures:
owner_id and storage_key, which contain account identifiers and storage paths.
Recover
After storage is healthy again, requeue abandoned jobs. Reset attempts and the lease token so the scheduler makes a fresh bounded run. Keep the owner and key unchanged:next_attempt_at
to now() instead of resetting its attempts.
Never delete a cleanup row by hand unless its storage prefix has been verified gone. A row is the only remaining
erasure target after a successful database delete.
Account deletion
Deleting a user runs a database trigger that replaces narrower jobs for that owner with oneuploads/<owner-id> job in
the same transaction. The table intentionally has no user foreign key, so account deletion cannot cascade away the
erasure target. Better Auth/admin/guest deletions and the product account-deletion path all use that trigger.
Upload publication holds the same owner lock as deletion. A started upload finishes before deletion can commit; an upload
whose account is already gone is refused. Guest linking excludes both pending and abandoned erasure prefixes from its
copy snapshot, so deleted files cannot escape into the destination account.
Older server instances do not take these locks. During an upgrade, drain their active uploads and account-link requests
before starting the new deletion workers; a mixed-version deployment does not provide this publication guarantee.