An environment file declares the agent, kits, workspace, secrets, ports, MCP servers, and host commands, and
sbx env planprints every change beforecreateorrunapplies it.
A new contributor clones your repository and needs the sandbox you use. That means the shell workload, the hello-mixin, one published port, a greeting variable, and a host command that prepares the workspace. You could send a list of flags. An environment file sends the same thing as one checked-in file, and the contributor sees a plan before anything runs.
When you finish this section, you can write an sbxenv.yaml, read its plan symbol by symbol, and say which edits wait for the next create.
The file
Environment file: a sbxenv.yaml that declares one sandbox and the host resources around it, read by the five sbx env commands {{help-sbx sbx env}}. The capture kit wrote this one:
schemaVersion: "1"
name: m101-env
agent: shell
workspace: .
args:
greeting:
default: hello
description: Greeting word
kits:
- source: ../kits/hello-mixin
env:
GREETING: ${{ env.args.greeting }}
lifecycle:
initialize:
- command: echo init
ports:
- sandbox: 8080
host: 18083Original source
title: the environment file the capture kit wrote
source: capture/fixtures/env/sbxenv.yaml
lang: yaml
---
schemaVersion: "1"
name: m101-env
agent: shell
workspace: .
args:
greeting:
default: hello
description: Greeting word
kits:
- source: ../kits/hello-mixin
env:
GREETING: ${{ env.args.greeting }}
lifecycle:
initialize:
- command: echo init
ports:
- sandbox: 8080
host: 18083| Key | What it declares |
|---|---|
schemaVersion, name, agent |
the file version "1", the sandbox name, the built-in agent or kit |
args |
inputs with default or required: true, read as ${{ env.args.NAME }} |
kits |
mixins, as a reference or as source plus args |
workspace, additionalWorkspaces |
the host directories to mount, with an object form for clone mode |
env |
variables for the sandbox |
secrets, bindings, registries |
credentials with value, ref, or command, and the domains they reach |
mcp.servers |
MCP servers registered with the gateway |
ports |
published ports, sandbox, host, protocol, hostIP |
lifecycle |
host commands under initialize, postCreate, preRemove |
sandboxOptions |
template, memory, cpus, skills, writableEnvFiles, and more |
The keys come from the file reference {{docs-sbx Top-level fields}} and from the command's own help. Three placeholders expand, ${{ env.args.NAME }}, ${{ env.projectDir }}, and ${{ env.fileDir }}, and a $ anywhere else is literal text {{help-sbx sbx env create}}. A relative kit path or workspace resolves against the directory of the file that declares it, so workspace: . mounts the file's own directory {{help-sbx sbx env}}. A file with no workspace mounts nothing {{rel-sbx v0.42.0}}.
The plan
sbx env plan reads the file and prints what applying it would set up. It applies, approves, and records nothing {{help-sbx sbx env plan}}.
── ENVIRONMENT PLAN
m101-env
kits:
+ - source: $CAPTURE/fixtures/kits/hello-mixin
workspace: (present, not recorded as applied, needs your approval)
path: $CAPTURE/fixtures/env
env:
+ GREETING: hello
+ sandbox:
+ name: m101-env
+ agent: shell
ports:
+ - sandbox: 8080
+ protocol: tcp4
+ host: 18083
lifecycle:
initialize:
+ - command: echo init
+ workdir: $CAPTURE/fixtures/env
+ envFiles:
+ - $CAPTURE/fixtures/env/sbxenv.yaml
Plan: + 5 to add, ~ 0 to change, - 0 to destroy.
this plan runs commands on this machine, outside the sandbox, with your own privileges
✓ not approved yet; applying asks onceOriginal source
title: the first plan for m101-env
source: capture/out/14-env-plan.txt
lang: text
note: The LOAD ENVIRONMENT block is cut.
---
── ENVIRONMENT PLAN
m101-env
kits:
+ - source: $CAPTURE/fixtures/kits/hello-mixin
workspace: (present, not recorded as applied, needs your approval)
path: $CAPTURE/fixtures/env
env:
+ GREETING: hello
+ sandbox:
+ name: m101-env
+ agent: shell
ports:
+ - sandbox: 8080
+ protocol: tcp4
+ host: 18083
lifecycle:
initialize:
+ - command: echo init
+ workdir: $CAPTURE/fixtures/env
+ envFiles:
+ - $CAPTURE/fixtures/env/sbxenv.yaml
Plan: + 5 to add, ~ 0 to change, - 0 to destroy.
this plan runs commands on this machine, outside the sandbox, with your own privileges
✓ not approved yet; applying asks onceThe plan has the shape of your file, and the margin carries the verdict. Five symbols exist {{help-sbx sbx env}}. + adds, ~ changes, and - destroys. > marks a command that runs again, and ! marks a resource the environment applied and no longer declares.
A row that is unchanged and already approved is left out. A literal secret value appears as its SHA-256 digest {{docs-sbx Review an environment plan}}. The tcp4 protocol was not in the file: published ports default to it since v0.42.0 {{rel-sbx v0.42.0}}. Figure follows the file through the plan to the state record.
Original source
id: fig-4-4
kind: flow
title: an environment file becomes a plan, an approval, and a state record
claim: sbx env plan turns the file into margin-marked rows, approval on create records them, and a later plan prints only the rows that moved.
caption: Read top down. The plan rows are the env plan capture, and the right branch is the second plan capture, with the greeting argument changed. The left branch after approval follows the help text of sbx env, because the kit never applied the plan.Host commands and approval
The lifecycle block runs on the host, outside the sandbox, with your own privileges. initialize runs on every create and every run, postCreate once the sandbox exists, and preRemove after you confirm sbx env rm {{help-sbx sbx env}}. Commands run through your shell from the project directory, with workdir and timeout per command. An environment with any of them asks on every invocation, even one that does not run them {{help-sbx sbx env}}. The reason is that approving a command also trusts the script it calls. --skip-host-commands runs none of them, --auto-approve answers yes once, and sbx settings set env.rememberHostCommands true asks again only when a command changes.
The state record and the second plan
sbx records what you approved for each environment in its state directory, not next to the file {{help-sbx sbx env}}. A later invocation asks only about what changed {{help-sbx sbx env}}. The exact path was not captured, because the kit ran plan and never create. The second capture changed one input instead:
$ sbx env plan --env-arg greeting=servus ./fixtures/env
…
+ GREETING: servus
…
Plan: + 5 to add, ~ 0 to change, - 0 to destroy.Original source
title: the same file with one argument changed
source: capture/out/14-env-plan-arg.txt
lang: text
note: Only the changed row and the totals are shown.
---
$ sbx env plan --env-arg greeting=servus ./fixtures/env
…
+ GREETING: servus
…
Plan: + 5 to add, ~ 0 to change, - 0 to destroy.Every row is still +, because nothing was applied. After a create, the same edit prints ~ GREETING: hello -> servus and nothing else, in the form the help text shows for a changed kit argument {{help-sbx sbx env}}. Changes to workspaces, kits, ports, secrets, bindings, and sandboxOptions wait for the next create. New env values reach the next session {{docs-sbx Update an environment}}.
Several PATH arguments deep-merge in order, and a later file wins over an earlier one {{help-sbx sbx env create}}, and lists such as ports concatenate rather than override. With no PATH, a .sbxenv.yaml in your home directory merges underneath as a base layer {{help-sbx sbx env create}}. A hidden .sbxenv.yaml in the project is no longer read.
rm, the read-only file, and --cloud
sbx env rm removes the sandbox and the secrets provisioned at its scope. Bindings stay, because they belong to the user. To remove the bindings that this environment declares too, pass --prune-bindings {{help-sbx sbx env rm}}. The file itself is bound read-only inside the sandbox, because an agent that can edit it decides what the next plan asks about. sandboxOptions.writableEnvFiles: true lifts that {{help-sbx sbx env}}.
With --cloud, workspace, additionalWorkspaces, and clone are refused, because they name host directories that a cloud sandbox cannot mount {{help-sbx sbx env}}. Also, host port bindings, MCP definitions, and dynamic secret sources are rejected before any host command runs. The cloud section shows what remains.
Original source
- Expect `sbxenv.yaml` to be read-only inside the sandbox, and set `writableEnvFiles` only when an agent must edit it.
- Run `sbx env plan` before `create` or `run`, and read every `+`, `~`, `-`, `>`, and `!`.
- Expect a prompt on every invocation while the file declares host commands.
- Recreate the environment after a change to kits, ports, secrets, or `sandboxOptions`.Sources: help-sbx sbx env, sbx env create, sbx env plan, sbx env rm (research/sources/help-sbx.md); docs-sbx Sandbox environment files (research/sources/docs-sandboxes.md); rel-sbx v0.42.0 (research/sources/sbx-releases.md); capture/fixtures/env/sbxenv.yaml; capture/out/14-env-plan.txt, 14-env-plan-arg.txt