carlos / platform Public
| Earlier finding | Disposition at 830ad828 | |---|---| | R5.1 — Route-only permanent service can become temporary | Not fixed completely: namespace enforcement has the gaps below. | | R5.2 — Expiry markers defeat the empty-bucket safeguard | Fixed: tripped passes write nothing; markers do not count as evidence. | | R6.1 — Local inventory misses remote routes | Not fixed: the rollout scan does not prevent subsequent writes from older binaries. | | R6.2 — Reconciliation can reassign permanent routes | Not fixed: the namespace invariant required to exclude those routes is not established. |
- P1 — §3: the rollout scan establishes no lasting namespace protection.
The rollout requires checking existing routes, but does not require upgrading or disabling every older route writer before enabling temporary creation. After a clean scan, an older binary can create a permanent, route-only tmp-report service on another box: carlos add writes the supplied app directly into the registry (cli.go:198, cli.go:255).
Temporary creation on the console can then pass its local check. Reconciliation can assign that permanent route to the temporary app’s sole bucket owner and delete it upon expiry (reconcile.go:735, reconcile.go:206).
Fix: make rollout require namespace enforcement on every route writer before the inventory check and feature enablement. Specify how older writers remain excluded afterward. Test an attempted conflicting route creation after the scan.
- P1 — §3: legacy grant backfill bypasses the namespace reservation.
console grant <address> tmp-report calls Store.Grant (main.go:124), which inserts the name into owners without namespace validation (store.go:522). On console startup, BackfillOpsMemberships inserts an unclaimed grant name directly into apps, bypassing both claimApp and CreateAppIn (server.go:1285, store.go:661).
With the proposed column default, this creates permanent tmp-report with expires_at = 0. That directly violates ruling 7 and invalidates the table’s assumption that an existing tmp- row must originate from temporary creation.
Fix: cover grant creation and backfill explicitly. Refuse new reserved-name grants, prevent backfill from creating permanent reserved-name rows, and include existing grants and console rows in rollout checks.
- P2 — §§2, 5: an interrupted purge can disable its own retry.
Consider a deployment whose only remaining app is being early-purged. PurgeApp deletes every other object before deleting the marker (trash.go:257). If the final marker deletion fails, only deleted.json remains.
Every subsequent expiry pass now trips §2’s marker-excluding guard and stops before retrying the purge. Contrary to §5, retaining purge_after does not make the next pass resume. The marker remains until another writer adds objects or the box’s 30-day purge runs (reconcile.go:228).
Fix: provide independently verified bucket identity or a durable, bucket-bound purge-resumption mechanism. Preserve the wrong-bucket safeguard. Test failure on the final marker deletion with no other app objects.
- P2 — §§5, 7: direct-mode delete still promises recovery for 30 days.
The direct-mode branch obtains its deadline from headlessTrash and prints it (apps.go:250). That helper returns the marker timestamp plus TrashCooloff (apps.go:307).
For a temporary app, console materialization can subsequently establish the three-day purge deadline. The operator has already been told the app remains restorable for 30 days. effectivePurgeAt(row) cannot fix this branch because direct mode has no console row.
Fix: specify direct-mode deadline reporting explicitly. Obtain the authoritative deadline through the console, or report it as unknown when operating without one. Do not infer temporary status from tmp-, because kept apps retain that name. Add direct-mode deletion to the deadline-display tests.
Verdict: revise.