feat(server): prune sandbox export artifacts and document the sandbox runtime - #52
Merged
Conversation
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
awtprod
force-pushed
the
impl/sandbox-hardening
branch
from
August 22, 2026 02:24
9458301 to
c27f869
Compare
…pass Nothing ever pruned <stateDir>/sandbox-artifacts: every stop/expire wrote a bundle + manifest + provider store set, and sets for threads deleted out-of-band or settled long ago accumulated forever. Add an age-based sweep (T3_SANDBOX_ARTIFACT_MAX_AGE_SECONDS, default 30 days, explicit 0 disables) that rides the sandbox lifecycle reactor's existing minutely pass. Sets whose sha matches a thread in a non-terminal sandbox lifecycle are kept regardless of age -- their export may seed a re-provision. A set is dated by its newest file, per-run deletions are capped at 1000 sets, and sweep failures are logged with cause without stalling expiry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… file The manifest listed store: <name>.store.tar, but the artifact HTTP route serves only bundle|manifest -- a client following the manifest got a 404 on an advertised file. The store is transcript data used server-side for restore, so rather than serving it, mark it storeServed: false in the manifest. The manifest is written as plain JSON and its only schema-validated reader is the restore path, which ignores unknown fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sandbox env flags existed only as code comments spread across the server. Add the sandbox block to .env.example (all flags with defaults, including the new artifact-retention cap) and an operator-focused docs/operations/sandbox-runtime.md covering the two-flag enable gate, the provision/stop/expire/re-provision lifecycle, export artifacts and where they live, the retention policy, and the store's exclusion from HTTP serving. Linked from sandbox-host.md, which stays host-bootstrap-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
awtprod
force-pushed
the
impl/sandbox-artifact-retention
branch
from
August 22, 2026 02:38
8362077 to
c2f2aed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three gaps from the sandbox audit (items 13, 14, 24):
<stateDir>/sandbox-artifacts/<sha256(threadId)>.{bundle,json,store.tar}and nothing ever pruned the directory — a code comment onT3_SANDBOX_STORE_MAX_BYTESadmitted as much. Per-thread growth is bounded (exports overwrite the same set), but sets for threads deleted out-of-band or settled long ago accumulated forever.store: <name>.store.tar, but the artifact HTTP route serves onlybundle|manifest— a client following the manifest got a guaranteed 404.Fix
sweepExpiredArtifactsonSandboxRuntimeManager), riding the lifecycle reactor's existing minutely pass — no new timer. Deletes artifact sets whose newest file mtime exceedsT3_SANDBOX_ARTIFACT_MAX_AGE_SECONDS(default 30 days, explicit0disables). Sets for threads in a non-terminal sandbox lifecycle are protected regardless of age, since a young export seeds re-provision (deleting one degrades restore to a plain clone and loses the provider's conversation). Per-run deletions capped at 1000 sets (logged if hit); sweep failures are logged with cause and never stall expiry; in-flight.tmpexport temporaries are never eligible.storeServed: false. The only manifest reader (the restore path) reads digests from the thread projection, not the manifest, and the HTTP manifest route serves raw bytes — no schema change needed..env.example(each flag verified against its usage site) and a new operator-focuseddocs/operations/sandbox-runtime.md(enable gate, lifecycle, export artifacts, retention, deletion cleanup), linked from sandbox-host.md without touching the regions PR fix(sandbox): pin podman-static v5.8.4 host bootstrap and harden re-runs #49 rewrites.Tests
7 new tests: sweep old-deleted/young-kept, newest-file dating, active-thread protection,
0disables, temporaries/foreign files ignored, unconfigured/missing-dir no-ops, manifeststoreServedround-trip, and a reactor test proving the periodic pass drives the sweep with the right protected set and survives a sweep failure. Verified the new tests fail with the sweep stashed. Full server suite: 3282 passed (baseline 3275).Built with Claude Fable 5 via Claude Code.
🤖 Generated with Claude Code