Skip to content

docs(sandbox): publish the installed sandbox as a containerDisk - #3155

Merged
r33drichards merged 1 commit into
mainfrom
mc-containerdisk
Aug 13, 2026
Merged

docs(sandbox): publish the installed sandbox as a containerDisk#3155
r33drichards merged 1 commit into
mainfrom
mc-containerdisk

Conversation

@r33drichards

Copy link
Copy Markdown
Collaborator

Follow-up to #3131. That PR merged before this section was finished, so these commits were left on the branch after it closed.

Adds Publish the installed sandbox as a containerDisk: turn the sandbox you just built by hand into an OCI image, push it to GHCR, and boot it locally and on Fleet — so nobody has to repeat the GUI install, least of all on Fleet.

Covered

  • what a containerDisk is (an OCI image whose entire content is /disk/disk.img, which despite the name is qcow2)
  • never sign in before exporting, with the three concrete places a credential survives
  • creating the instance and fetching Java 17 without a Microsoft account
  • the sb.stop() trap that deletes the session disk
  • qemu-img convert -O qcow2 -c, the two-line Dockerfile, and --provenance=false --sbom=false
  • GHCR being private on first push, and write:packages
  • the os_type override, and why a Windows containerDisk otherwise boots SeaBIOS against a GPT disk
  • five troubleshooting rows

Verified end to end while writing it

Published as ghcr.io/trycua/minecraft-agent:1.20.1 (repo created for this) and booted from it:

step result
export 8 min 44 s, 7,697,072,128 bytes from a 3.40 GB overlay
build + push 6 min 28 s, one 7,631,366,006-byte layer, plain manifest — no unknown/unknown children
anonymous pull manifest 200 with no credentials at all
local boot pulled and booted; game files, Mesa beside javaw.exe, no accounts.json
Fleet boot READY in 157 s

That Fleet boot is the first end-to-end confirmation that a custom Windows containerDisk on a public non-ECR registry runs there. It needs cua-sandbox 0.3.2 (#3148); before that the gateway refused any non-allowlisted image.

Two corrections in the last commit, both from running it

The retry advice was wrong. The section said to retry a BindDeadlineExceeded boot. Doing that returns 403 k8s request is not allowed on update template — both branches of pool_admission.rego are guarded by input.method != "PATCH", so a template can be created but never updated, and reusing the name makes the SDK patch it. The retry needs a new name. Sequence measured: cold attempt hit the 300 s deadline → same name returned 403 → fresh name reached READY in 157 s.

tunnel.forward(3000) does not work on every Fleet path. A sandbox from a pool claim carries the base FleetTransport, which does not implement port forwarding (pool.py:161); only a directly-created sandbox carries FleetCloudTransport. The error names the transport, not the cause.

Not verified

  • The game was never launched from the published image. Prism will not add an offline account without a Microsoft account first, so there is no credential-free smoke test. Files verified present, not running. This image's Java came from the Installations tab rather than from a Launch, so Prism auto-detecting java-runtime-gamma is untested.
  • The Fleet sandbox was torn down when the script exited, so the service URL was not curled afterwards. READY does imply the readiness wait passed.

Credential hygiene

The first export was discarded. It passed every filesystem check — accounts.json gone, in-guest findstr clean, Prism showing an empty account wizard — and the raw image still had 47 hits for the refresh-token prefix and profile name, in pagefile.sys, in file slack past a log's valid-data length, and in unallocated clusters the zero-fill missed. The published image was rebuilt without ever signing in. Scanned before publishing: refresh-token prefix 0, profile name 0. The 86 JWT headers present decode to RFC 7515 example data (http://example.com/is_root":true}), and the pristine base image already carries 58 of them.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

🤖 Generated with Claude Code

Turn the hand-built sandbox into an OCI containerDisk, push it to GHCR, and boot
it locally and on Fleet, so the GUI install never has to be repeated — least of
all on Fleet.

Covers what a containerDisk is, never signing in before exporting and the three
places a credential survives if you do, creating the instance and fetching Java
without a Microsoft account, the sb.stop() trap, qemu-img convert, the two-line
Dockerfile, --provenance=false --sbom=false, GHCR being private on first push,
and the os_type override that keeps a Windows disk off SeaBIOS.

Verified end to end as ghcr.io/trycua/minecraft-agent:1.20.1: export 8m44s for
7,697,072,128 bytes, build and push 6m28s as a plain manifest, anonymous pull
200 with no credentials, local boot with the game files intact, and a Fleet boot
READY in 157 s — the first confirmation that a custom Windows containerDisk on a
public non-ECR registry runs there.

Retrying a BindDeadlineExceeded boot under the same name returns 403 on update
template, because both branches of the gateway image policy are guarded by
input.method != "PATCH": a template can be created but never updated. The retry
needs a new name. tunnel.forward() is also not available on a pool-claim
sandbox, which carries the base FleetTransport.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@r33drichards
r33drichards merged commit 2eb5e82 into main Aug 13, 2026
10 checks passed
@r33drichards
r33drichards deleted the mc-containerdisk branch August 13, 2026 21:16
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