Repository navigation
fix(env): env snapshots and GitHub image builds work with an object store that can't presign - #90
Merged
Conversation
…tore that can't presign A remote VM sandbox delivered its file by uploading it to a presigned URL. The local object store signs nothing, so env snapshots and GitHub image builds both refused it. - read_vm_file copies a file off a VM host range by range: tail | head | base64 over exec, four ranges in flight, each length-checked. - upload_vm_file puts a VM file into a store. When the store signs a PUT URL, the VM uploads straight to it, as before. Otherwise agent-env copies the file off and put_file_at's it. - Env snapshots and GitHub builds deliver through upload_vm_file. - Each snapshot's tarball gets a key of its own. A write-once store then takes a second snapshot of the same env and universe, and an artifact version's tarball never changes under it. On S3, a re-snapshot now adds an object instead of overwriting one. - A GitHub build is refused before a remote build VM is created when its image store is a registry on this machine, which that VM can't push to. The local provider builds as before. - is_loopback_host moves next to registry_host_from_ref, so the bundle preflight and GitHub builds share it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Measured on 200 MiB files: each exec gets a small share of a provider's bandwidth, so throughput scales with the number of ranges in flight. | ranges in flight (4 MiB each) | beta_scale | modal_vm | | ----------------------------- | ---------- | --------- | | 4 | 10.3 MiB/s | 2.0 MiB/s | | 8 | 16.1 MiB/s | 3.7 MiB/s | | 16 | 22.4 MiB/s | 8.0 MiB/s | That holds 64 MiB of ranges in memory at once. The path only runs when the store can't presign. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Eight hex characters left a small chance that two snapshots of one env and universe draw the same key. In the write-once local store that fails the second upload after its image is built. On S3 it overwrites a tarball an earlier artifact version still names. Sixteen keep the longest filename at 224 bytes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lengthen GitHub build suffixes - read_vm_file fails with the VM's answer when `wc -c` doesn't return a number. A terminated VM can answer an exec with success and no output, which surfaced as a bare `invalid literal for int()`. - GitHub build names take a 16-hex suffix, as snapshot tarballs now do. A repeat would fail the upload into a write-once store and overwrite an earlier version's tarball on S3. - _write_bytes_to_vm_path drops its local base64 import; the module imports it now. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| dockerfile_repo_path = df_parts.path | ||
| config = get_config() | ||
| suffix = uuid.uuid4().hex[:8] | ||
| suffix = uuid.uuid4().hex[:16] |
There was a problem hiding this comment.
A bare image ID with 224 characters could build with the old suffix. The new 16-character suffix makes its build-context filename 256 bytes long. The local object store cannot create that filename, so the GitHub build fails after the VM has done its work. Keep the generated filename within the store’s limit.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/artifact/artifacts/docker_image.py
Line: 234
Comment:
**Long image IDs break builds**
A bare image ID with 224 characters could build with the old suffix. The new 16-character suffix makes its build-context filename 256 bytes long. The local object store cannot create that filename, so the GitHub build fails after the VM has done its work. Keep the generated filename within the store’s limit.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Collaborator
Author
There was a problem hiding this comment.
Fixed in 9eba072: the tarballs are named {suffix}.tar.gz and {suffix}-context.tar.gz under github-builds/{id}/, so their names are at most 31 bytes whatever the id. The local-store test now also builds a 238-character id, the longest Docker accepts for the build name {id}-{suffix}.
The build's directory already names the image id, so the tarball names don't repeat it. With the longer suffix, an id of 224 characters or more made the build context's filename longer than the 255 bytes the local store's filesystem allows. Now the names are 31 bytes at most, whatever the id, and an id is limited only by Docker's name length. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Summary
Two flows on a remote VM sandbox refused to run when the object store can't presign, the local store for example:
DockerImageArtifact.put_from_githubcouldn't deliver its tarballs.Both had the VM upload its file to a presigned URL. Now they deliver through one helper:
upload_vm_file(sandbox, vm_path, store, object_url). When the store signs a PUT URL, the VM uploads straight to it, as before; S3 is unchanged. Otherwise agent-env copies the file off the VM andput_file_ats it. A sandbox never connects to this machine.read_vm_file(sandbox, vm_path, local_path). Copies a file off the VM range by range:tail -c +N | head -c M | base64over exec, with 16 ranges of 4 MiB in flight.Behavior changes worth a look
<image tag>-<16 hex>.tar.gz). The local store is write-once, so a second snapshot of the same env and universe would otherwise fail. It also means an artifact version's tarball never changes under it. On S3, a re-snapshot now adds an object where it used to overwrite one.localhost:5000. The local sandbox provider builds as before.is_loopback_hostmoves next toregistry_host_from_ref, so the bundle preflight and GitHub builds share it.Testing
Unit:
make unit-testpasses. New and updated tests cover:curlupload;curl;@localenv snapshot landing in the local store.Against real VMs, on
modal_vmand on another remote VM provider, with the local document and object stores and a hosted registry. Nothing shared was written.read_vm_fileat 0 B, 1 B, 4 MiB ±1, 12 MiB and 200 MiB: sha256 matchesupload_vm_fileinto the local store: sha256 matchesSandbox is shutting down)_snapshot_servicedbon a seeded servicedb (300k rows), twice: two 161 MB tarballs in the write-once storeput_from_githubon a remote build VMThe length check is load-bearing. After the VM was terminated, the other provider answered an exec with success and empty output. The copy failed with
read 0 of 4194304 bytes … refusing a truncated copyrather than storing a truncated tarball.Ranges in flight (200 MiB, 4 MiB ranges):
2 MiB ranges did worse at 16 in flight (2.4 and 14.3 MiB/s).
The restore direction is unchanged and slow. It pushes an object the store can't sign onto a VM through the existing push path: one base64 chunk per exec, in sequence, from an object read whole into memory. The 161 MB snapshot took 1148 s to load on the other provider, and the 70 MB image took 502 s. Modal's chunks are a third of the size, which is why the Modal restores weren't run. Speeding that up is separate work.
Not in this PR
🤖 Generated with Claude Code
The PR appears safe to merge; no new issue or outstanding previous finding remains.
Fix with agent prompt
Summary
The PR lets environment snapshots and GitHub image builds upload VM files to stores that cannot sign PUT URLs. It also gives snapshot tarballs unique keys and checks that remote build VMs do not need to reach a registry on this machine.
Reviews (4) · Last reviewed commit: "fix(artifact): name GitHub build tarball..." · Reviewed by Greptile