carlos / platform Public

Box auto-purge can destroy a freshly re-trashed app (tombstone substitution race) Issue open

Found by Astra in the temporary static sites review (design-temporary-sites, CODEX-FINDINGS-TEMPORARY-SITES-R1.md #4, carried as R1.4), and declined there because the race exists today for every app and that spec leaves box code alone.

The race. The domains reconcile auto-purge (internal/domains/reconcile.go §1d, ~226-238) checks the age of the tombstone it read during the 1a listing, then calls release.PurgeApp. PurgeApp → PurgeReady re-reads the tombstone but only checks that a valid one exists, not that it is the same one or still old (internal/release/trash.go ~137-176). If, between the listing and the purge, someone restores the app and trashes it again (or restores, marks it production-off, and re-trashes), the box purges an app that was trashed seconds ago. The 30-day restore window the user was just promised is skipped.

Suggested fix. A guarded purge that takes the DeletedAt the decision was based on and refuses unless the current tombstone matches to the second. The temporary-sites spec introduces exactly this for the console's early purge (release.PurgeAppIfTrashedAt); the box path can call it with the listed marker's clock. It's a small box change, and a test can pin it: restore and re-trash between the listing and the purge, and the purge must refuse.

The window is one reconcile pass wide, so it's unlikely in practice, but the outcome is unrecoverable data loss.