Repository navigation
feat(snapshot_env): hand a service that takes parts grants an object of its own to upload to - #151
Open
earakely-scale wants to merge 22 commits into
Open
earakely-scale wants to merge 22 commits into
earakely-scale wants to merge 22 commits into
Conversation
…of its own to upload to When a service's card lists http-put-parts (client.write_kinds), snapshot_env begins a write of agentenv-snapshots/<uuid>/<env>.zip (application/zip, up to 16 GiB) and sends it as data/get's write_object. On an uploaded_object reply it completes the write and registers the bundle in place, with the same key shape and file name as a self-upload; any other reply, an error or a timeout aborts it. A completion failure is an UploadFailedError, a ValueError, so it fails only that service. Services that do not declare parts are offered what they are today. A service that also advertises the credentials extension still gets the pushed credentials, so a parts upload that fails can fall back to them. Each export now logs one line naming how the bundle came (parts, namespace, service upload, inline, stream, data or export-state) and how long it took, with the bytes and parts of a parts upload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' into edgararakelyan/snapshot-parts-grant
… abort racing its completion A write begun or completing in a thread when the export is cancelled is now seen through: the export waits for the thread, aborts a write that began after the cancellation, and lets a completion finish before its cleanup runs. The creds push moved inside the try, so a cancellation there aborts the write. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' into edgararakelyan/snapshot-parts-grant
…' into edgararakelyan/snapshot-parts-grant
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r CA; snapshot e2e through a parts grant A MultiEnv on the local backend runs its MCP servers from a compose file that never gave them the local grant server's CA, so every grant handed to one failed TLS verification. Each MCP server now mounts the CA's trust files where LOCAL_TRUST_ENV points, as a single local container already gets them. The new slow-tier test snapshots a MultiEnv whose items server takes write_object, checks the bundle it uploaded is registered and holds its state, and loads the snapshot back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' into edgararakelyan/snapshot-parts-grant
…n/local-compose-trust-e2e
…' into edgararakelyan/snapshot-parts-grant
…n/local-compose-trust-e2e
… start Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' into edgararakelyan/snapshot-parts-grant
…n/local-compose-trust-e2e
…ppings On Linux, host.docker.internal resolves in a container only through extra_hosts, which the compose gave the gateway alone. A service handed the local CA's trust files now gets the mapping as well, so it reaches the grant server it trusts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… work dir The local CA's folder lives under the state dir, which can sit outside what Docker shares (an XDG_STATE_HOME elsewhere), so mounting it from there could fail. LocalSandbox.stage_local_trust copies the trust files into the sandbox's work dir, which is kept on a shared path, and the compose mounts that copy by a path relative to itself. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
@greptileai review |
…napshot-parts-grant # Conflicts: # src/agent_env/providers/env_providers/env_gateway_provider.py # src/agent_env/providers/sandbox_providers/local_docker/provider.py # src/agent_env/task_step/task_steps/snapshot_env.py # tst/unit/providers/env_providers/env_gateway_provider_test.py # tst/unit/providers/sandbox_providers/local_sandbox_test.py
…chine A local VM's compose services are handed local grants as local Docker's are, so the trust files are written through the sandbox's own host beside the compose file, for every sandbox that runs on this machine, instead of copied into local Docker's work dir alone. A server's own variables win over the trust ones, as they do for a container, and a snapshot reload's compose keeps the files mounted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ase64 also reads A host write past one chunk decoded `base64 -d FILE`, which macOS's base64 refuses, so a local sandbox on a Mac failed any large write_host_file, as staging a compose's trust bundle does on every local MultiEnv deploy. Reading the file from stdin decodes the same on Linux and macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…napshot-parts-grant
… staged trust files readable A store may answer a service that takes parts with one PUT; the export's log line no longer reads a part size that grant lacks, which failed the whole snapshot after the object was made. The trust files staged for a compose are made 755/644 whatever the deploy's umask, so a service running as any user reads them, and the deploy's mount of them is pinned in a unit test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Merged the updated #150 in.
Locally, on top of |
This branch has not been deployed
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.
Stacked on #150 → #149 → #147: merge those in order, then this retargets to
main.Summary
snapshot_envbecomes the first issuer ofhttp-put-partsgrants.When a service's card lists the kind (
client.write_kinds, from #149):agentenv-snapshots/<uuid>/<env>.zip, asapplication/zip, up toENV_SNAPSHOT_MAX_BYTES(16 GiB, about 256 part URLs), and sends it asdata/get(write_object=...).uploaded_objectreply: it completes the write and registers the bundle in place, with the same key shape, file name and content type a service self-upload gets. A store may answer with one PUT instead of parts; that write is completed and registered the same way.try, so a cancellation there aborts the write too.UploadFailedError, aValueError, soexport_onefails only that service, as with any other bad export.Services that don't declare parts are offered exactly what they get today.
Rollout choice to review: a parts-declaring service that also advertises the credentials extension still gets the pushed credentials. A parts upload that fails therefore falls back to them, which keeps the change regression-free while services move over. Dropping the push is the later removal.
Per-service timing log. Each export now logs one line naming how its bundle came and how long it took, with bytes and parts for a parts upload. The ways are:
partsobject(one PUT)namespaceservice uploadinlinestreamdataexport-stateNothing recorded per-service export time before, and it's the comparison a rollout needs. The part count comes from the protocol's
part_ranges.Fix: services in a local compose stack can reach and trust the local grant server. A MultiEnv on a sandbox on this machine renders its MCP servers into a compose file, which never gave them the local transfer CA or the host mapping. So every local grant handed to one failed with
CERTIFICATE_VERIFY_FAILED,write_namespacegrants included. The e2e below found it. When the sandbox runs on this machine (ON_THIS_MACHINE: local Docker, a local VM) and the store issues local grants, each MCP server now gets three things, as a single local container already does:/app/trust, which local Docker keeps in the sandbox's Docker-shared work dir and a local VM in the VM), readable by any user, and mounted read-only whereLOCAL_TRUST_ENVpoints;LOCAL_TRUST_ENV, under the server's own variables, which win, as they do for a single container;extra_hosts(howhost.docker.internalresolves on Linux and in a local VM).A MultiEnv's snapshot reload, which renders the compose again, keeps them. A sandbox elsewhere, or a store without local grants, renders exactly the compose it did.
Fix: a large host write on a Mac. Staging the trust bundle (a few hundred KB) is the first host write on a local sandbox past one chunk, and the chunked write decoded with
base64 -d FILE, which macOS'sbase64refuses. It now decodes from stdin, the same on Linux and macOS.Labelled
bump-version: this is the release issuers pin to start offering parts grants.Verification
An end-to-end prototype of this stack ran in a real sandbox: a 2 vCPU / 8 GB VM, with an env service holding an 8.6 GB dataset.
End-to-end A/B on the same VM size, same service, data and bucket, 4 runs each:
That higher CPU came from per-chunk hashing in the uploader, since fixed in #147; upload-only CPU is now about 0.58× boto3's.
Tests
snapshot_env_object_write_test.pycovers:The existing
snapshot_envtests pass unchanged.End to end (slow tier).
multi_env_snapshot_local_test.pydoes the following:write_object.snapshot_envstep, and check the bundle came through the parts tier.agentenv-snapshots/<uuid>/items.zip, holds the state, and isapplication/zip.env_gateway_provider_test.pychecks that MCP servers get the trust files, env and host mappings only when there is a local CA, with a server's own variables winning; that any sandbox on this machine, and no other, has the files written beside its compose and the deploy mounts them; and, on a real local sandbox under a 077 umask, that a bundle past one chunk lands whole and readable. The MultiEnv reload keeps them, and the compose fixture now closes the grant server its tests start. Locally, main's MultiEnv, env-trigger and server-env integration tests pass with the change.Mutants, all killed:
UploadFailedErrornot aValueError;to_threadfor beginning and for completing;try;The unit tier passes.
🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issues remain.Summary
Adds object-write grants to service snapshots while keeping existing export paths available.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Read service card] --> B{Takes parts grants?} B -->|Yes, store can issue one| C[Begin object write] B -->|No| D[Use existing export offer] C --> E[Request service data] D --> E E --> F{Reports object upload?} F -->|Yes| G[Complete write] G --> H[Register uploaded bundle] F -->|No| I[Abort unused write] I --> J[Read existing reply format]Reviews (11) · Last reviewed commit: "fix(snapshot_env): log a write a store m..." · Reviewed by Greptile