Skip to content

Forget a deleted host in the exchange, and prove the janitor's teardown - #40

Merged
czpython merged 1 commit into
mainfrom
dru-401-per-box-lifecycle
Sep 7, 2026
Merged

Forget a deleted host in the exchange, and prove the janitor's teardown#40
czpython merged 1 commit into
mainfrom
dru-401-per-box-lifecycle

Conversation

@czpython

@czpython czpython commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What changes

Teardown was built in the two PRs before this one. This PR closes the ticket with what was left.

  • The exchange forgets a deleted host on its next pass. Secrets.forget_deleted_hosts drops the secrets of every host that has no row, so no fetch runs for a dead box.
  • A test proves the janitor's path: an expired host's secrets go before its VM, through delete_host, the same as an API delete. The pool shed takes the same path.
  • The docs say that the janitor deletes an expired host the same way, and that a sandbox must be removed through drukbox, never with sbx rm, since its secrets would stay in sbx's store until its row expires.
  • On docker-sbx a value directory that cannot be removed at teardown is a provider error, so the row stays for a retry.

Where this differs from the ticket

  • The ticket has host deletion call delete_secret for each entry in host.secrets, by placeholder. The seam PR before this one removes the sandbox's whole scope in one call, listed from sbx, and never reads the row. The row keeps only a fingerprint of the placeholder, and a row whose secrets no longer decrypt must still go.
  • The exchange has one timer for all hosts, not one per entry, so there is no timer to stop. It forgets the entries instead.
  • The janitor reaps expired hosts and abandoned provisions. There is no orphan reaper, and this PR adds none. A sandbox removed by hand keeps its scoped secrets until its row expires or is deleted. The deploy doc says so.

Names

New names, open to change: Secrets.forget_deleted_hosts.

Gates

uv run ruff check, uv run ruff format --check, uv run pyright, and uv run pytest are green.

Acceptance

docker-sbx on the KVM bed, with a host on a 60 second lease, an anthropic secret from an issuer stub, and a static github secret:

  • claude -p answered ok while the lease lasted, and the exchange pushed seven times.
  • The janitor, run once from the branch image, reaped the expired host.
  • The host row, the sandbox, the secrets in the sandbox's scope, the value files, and the workspace are all gone.
  • Twenty seconds later the exchange had pushed nothing more for the dead box and logged no failed push for it. The token is nowhere in the exchange log.

Review

The adversarial review reported one finding, applied: on docker-sbx the value directory was removed with errors suppressed, so a filesystem error would have let the VM and the row go while plaintext files stayed behind with nothing left to retry. The removal now raises a provider error on any failure but a missing directory, which keeps the row for a retry. The provider's own best-effort removal on a failed create stays, since nothing else removes the files on that path.

@czpython
czpython force-pushed the dru-401-per-box-lifecycle branch from 8f8c085 to c5b7e95 Compare September 6, 2026 16:30
@czpython
czpython force-pushed the dru-461-refresh-on-expiry branch 4 times, most recently from 0cd8887 to 10404df Compare September 7, 2026 05:52
Base automatically changed from dru-461-refresh-on-expiry to main September 7, 2026 05:57
@czpython
czpython force-pushed the dru-401-per-box-lifecycle branch from c5b7e95 to 2d75103 Compare September 7, 2026 05:59
The exchange drops the secrets of every host that has no row on each
pass of its timer, so no fetch runs for a dead box. A test proves that
the janitor removes an expired host's secrets before its VM, through
the same path as an API delete.

On docker-sbx a value directory that cannot be removed at teardown is
a provider error now, so the row stays for a retry and no value file
outlives its row. A missing directory is a removed one.

The docs say that the janitor deletes an expired host the same way, and
that a sandbox must be removed through drukbox, never with sbx rm, since
its secrets would stay in sbx's store until its row expires.
@czpython
czpython force-pushed the dru-401-per-box-lifecycle branch from 2d75103 to f3373ed Compare September 7, 2026 06:01
@czpython
czpython merged commit ab1e3e1 into main Sep 7, 2026
6 checks passed
@czpython
czpython deleted the dru-401-per-box-lifecycle branch September 7, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant