--- name: bohrium-sandbox compatibility: "Requires bohr CLI (npm @dptech-corp/bohr-cli); verified with 2.9.19. Account operations require Bohrium authentication and network access." description: >- Operate Bohrium cloud sandboxes with the `bohr sandbox` CLI: sandbox lifecycle, quota self-query, command execution, interactive PTY sessions, file transfer, per-sandbox cost inspection, templates, container image commit/build, Wenyon dataset mounts, and installing software inside sandboxes. Use when the user mentions bohr sandbox, sdbx, a cloud sandbox, sandbox cost or spend, quota, or any Bohrium sandbox operation; also when sandbox access times out or fails, or create reports "running sandbox limit reached" / "template limit reached". --- # Bohrium Sandbox (`bohr sandbox`) ## Choose the task path | Goal | Required reference/route | |---|---| | Run one calculation and collect files | Doctor/quota → create → upload → foreground exec → download → authorized cleanup | | Run long-lived/background work | [Execution modes](references/sandbox/execution-modes.md) and [exec recovery](references/sandbox/exec.md) | | Read/write a Wenyon dataset | [Instance lifecycle and settlement](references/sandbox/lifecycle.md#mount-a-wenyon-dataset-in-an-instance) | | Install software, expand storage, or save an image | Relevant software/storage/image reference before that operation | ## Workflow: run a script and deliver its output 1. Check `doctor`'s `data.ready`, quota, selected template/resources and authorized cost. Confirm the script, remote paths and local output destination. For this ordinary, non-Wenyon example, generate one create request ID and retain it across tool calls: ```bash REQUEST_ID="$(uuidgen)" bohr sandbox create sdbxagent --timeout 3600 --request-id "$REQUEST_ID" --yes -o json ``` 2. Set `` to the successful response's canonical `data.sandbox_id` (`sandboxID` is the compatibility spelling). Receipt/error wrappers need the reconciliation flow below; an operation ID is not a sandbox ID. ```bash bohr sandbox files write --source ./run.py /tmp/run.py -o json bohr sandbox exec -o json -- python /tmp/run.py bohr sandbox files read /tmp/result.json --destination ./result.json -o json ``` 3. The script must actually produce `/tmp/result.json`. For foreground execution require `data.result_known=true` and `data.exit_code=0`, then inspect the requested output. A background receipt with `start_confirmed=true` and `result_known=false` confirms launch only. In argv mode put global CLI flags before `--`; anything after it is a remote argument. 4. Verify download's reported `output`, transfer summary and manifest against the requested local artifact before cleanup. Delete only after outputs are safe and disposal is authorized. Wenyon write mounts additionally require the explicit commit/discard settlement below. 5. Unknown create outcomes use the original request ID and scope to reconcile; unknown exec outcomes use the returned process/request evidence in the exec reference. Re-running create or a side-effecting script can duplicate work. A local deadline does not prove the remote task ended. CLI for managing **on-demand cloud sandboxes** on the Bohrium platform via the `bohr sandbox` command group (open-platform v4). Covers sandbox lifecycle (`create`, `exec`, `delete`, `list`, `describe`), quota self-query (`quota`), interactive PTY sessions, rootfs inspection and resize, file transfer, template management, image commit/build, and software installation inside sandboxes. This is **not** the E2B SDK: it does not need `E2B_API_KEY` and does not call `api.e2b.dev`. Everything runs on the Bohrium platform, authenticated with a Bohrium AccessKey. Creating a Sandbox with a Wenyon instance mount additionally needs a Wenyon/Vouch user JWT from a named environment variable; the AccessKey cannot substitute for that JWT. > **Proxy toggle.** Sandboxes default to no outbound HTTP proxy. For overseas > access (PyPI `pypi.org`, GitHub, HuggingFace, etc.), toggle the > `proxy.example.com` proxy on with the snippet in > [`references/sandbox/network.md`](references/sandbox/network.md), then toggle > it off when done. Domestic access via the image-level Aliyun mirror is > unaffected. > **Default-behavior pitfalls (read first for ML / long-running jobs).** > Several defaults are fine for quick interactive use but bite training > workloads: the overlay disk has a **30Gi base** and any extra must be > requested when creating the template (SKU has no disk field), > per-user **running-sandbox quota** (Running+Hibernating — check > `bohr sandbox quota -o json` before create; on `limit reached` free slots > then retry), `exec` has no execution deadline (network failures and sandbox lifetime still apply), binary downloads use > `files read --destination`; stdout byte mode emits base64, `delete` is **irreversible** and a > stopped sandbox's files can't be read, the outbound proxy is **off**, and a > template image with a mutable `:latest` tag is **rejected** (use an > immutable tag). See the consolidated list in > [`references/sandbox/pitfalls.md`](references/sandbox/pitfalls.md) before > driving a sandbox through an agent. ## Agent rules - On an uncertain create or a lost response, do not rerun it blindly: query the original request ID with `bohr sandbox describe --create-request-id`, preserving the profile and identity. A single lookup 404 does not establish that no sandbox was created. For template preparation failures, inspect template status; only inspect a failed-instance record when an instance is actually identified. - Pass `--wenyon-dataset-id`, `--wenyon-mount-path`, `--wenyon-mode` and `--wenyon-version` to **sandbox create**, using an ordinary compute template. Default read/current pins existing data; write/current preloads it; only write/none starts empty. `--wenyon-user-token-env WENYON_USER_JWT` names an environment variable, not a Wenyon/Vouch user JWT literal; `BOHR_ACCESS_KEY` cannot substitute. Do not combine with `--inherit-auth`. End writable mounts with `delete --commit` or `delete --discard`; use `--wait` and verify `produced_version` for a saved result. On uncertain create, use `describe --create-request-id` before any retry. See lifecycle.md for the recovery deadline and no-secret rules. - Pass `sandbox create --inherit-auth` only when the user explicitly authorizes propagating the caller's login into that sandbox. It is default off. Root and any process granted read access to the private tmpfs bundle can use the inherited credential; both AccessKey and Vouch require a statically verified npm bohr-cli 2.6.45+ layout. Vouch never includes a refresh token and expires with its access token. If injection returns `PARTIAL_FAILURE`, delete the returned sandbox without inspecting/reusing it and do not rerun create; subtype `sandbox_auth_rollback_uncertain` makes deletion immediate. For `sandbox_create_outcome_uncertain`, list and reconcile by request ID before another create. - If `template create --shm-size` reports that the server did not echo the requested `shm_size`, the template may already exist. Do not retry blindly; inspect the template first, then delete or recreate it only after the backend is upgraded or corrected. - Treat `/share` and `/personal` as potentially network-backed. Do not repeatedly run recursive `find`, `du`, `grep -R`, or content searches over them. For repeated filename lookup, build one private, path-only local index for the narrowest useful subtree, refresh it on writes/mount changes or after 10 minutes, and verify live paths before mutations. See [`references/sandbox/remote-storage-index.md`](references/sandbox/remote-storage-index.md). - To inspect per-sandbox spend, `bohr sandbox list --query all` shows `COST` with a unit per row. Read `paymentType`: `1` is photons, `0` or missing is CNY. Photon rows prefer `photonCost`, falling back to `cost`; cash rows use `cost`. Missing `cost` means unavailable unless a photon row has `photonCost`; never invent zero. An explicit zero is known at query time. JSON preserves both fields and their absence. These are job costs, not `notice.billing.balance`; settlement can lag lifecycle changes. - Commands and new PTYs default to `root`. Use `--user` only when explicitly requested; an existing PTY keeps its original user. - Positional `exec` arguments preserve argv boundaries. Pass shell pipelines, expansion and redirection through `-c/--command`; use `--` before remote argv to stop local flag parsing. See exec.md for outcome-unknown recovery and retained scripts. - Before every non-Dockerfile snapshot through canonical `bohr image commit`, run `bohr sandbox storage inspect -o json` immediately before submit. If the command group is disabled, use sandbox-local `df -Pk` on the actual write paths as described in the storage guide; this measures capacity but does not enable resize. Commit apply can consume rootfs in a short burst, so never start commit and wait for an 80% threshold to trigger expansion. Estimate total capacity from live `used_bytes` plus a defensible uncompressed commit delta; unknown delta is not zero. If growth is needed, require `capabilities.dynamic_adjustment.available=true` before resize; false or unknown needs another storage plan, not automatic retries. Proceed only after resize `"status": "ready"`; inspect `complete` only describes evidence, and a PATCH alone is not Ready. Resize is grow-only and cannot shrink a rootfs. This is an Agent workflow, not automatic CLI preflight, and does not apply to `bohr image build --dockerfile`. Read the formula and stop conditions in [`references/images.md`](references/images.md) and Ready evidence in [`references/sandbox/storage.md`](references/sandbox/storage.md). ## Prerequisites ```bash export BOHR_ACCESS_KEY= bohr sandbox doctor -o json # verify local auth and control-plane connectivity ``` For an existing sandbox and an authorized HTTP(S) target, add `--sandbox --target ` to test that data-plane path. This connects the instance and may extend its lifetime. See lifecycle.md for layer and submission-stage semantics. The verdict is `data.ready`, not the exit code: `sandbox doctor` exits 0 whenever the diagnosis itself ran, ready or not. Check `data.ready` (and `data.checks[]` for which item failed) before creating anything. ## Billable commands need `--yes` when you are not a human at a terminal `sandbox create`, `image build` and `image commit` create billable resources. Since 2.5.18 the CLI refuses them outside an interactive terminal unless you confirm — and an agent's piped stdin is never a terminal, so this is your default situation: ```json {"ok": false, "error": {"code": "CONFIRMATION_REQUIRED", "subtype": "billing_resource", "hint": {"action": "confirm", "command": "bohr sandbox create --yes"}}} ``` Exit code is **10** and nothing ran — no sandbox, no charge. This is not a transient failure; re-running the identical command gets the identical refusal. - Authorized to spend? Re-run the same command with `--yes`. - Not authorized? Report the refusal to the user and stop. Do not add `--yes` on your own initiative. - Keep confirmation enabled. Change persistent billing confirmation only when the user explicitly requests that configuration change; it is not a workaround for a refused operation. Destructive operations keep their own confirmation. - `--dry-run` is never gated. Check prices with `bohr billing pricing`. - Before creating, preview the cost with `bohr sandbox create