Environment Files & Secrets

ជំពូកនេះមិនទាន់ត្រូវបានបកប្រែជាភាសាខ្មែរនៅឡើយទេ — សូមអានជាភាសាអង់គ្លេសខាងក្រោម។

Table of Contents

  1. Why declare instead of type
  2. The sbxenv.yaml schema
  3. Secrets: three ways to supply a value
  4. Plan before you apply
  5. The full lifecycle: create, exec, rm
  6. A worked example
  7. Beginner Pitfalls

1. Why declare instead of type

Every sandbox in this course so far has been created by typing a command: sbx run --name sbx-crash-course shell, later sbx ports ... --publish 3000:3000, later still a kit flag from Part 10. That's fine solo, but it doesn't survive being handed to a teammate, or to CI, or to your own future self in six months. An environment file — sbxenv.yaml — captures the whole setup as one reviewable, shareable file: the agent, the workspace, any kits, environment variables, secrets, ports, and MCP servers a project's sandbox needs, in one place.

Note: like kits, environment files are an experimental, evolving part of sbx. The schema below matches the documented behavior at the time of writing.

2. The sbxenv.yaml schema

A minimal file needs only two fields:

schemaVersion: "1"
agent: claude

The fuller set of fields you'll actually use on a real project:

FieldPurpose
nameSandbox name (defaults to <agent>-<workspace folder name>)
workspacePath to mount, or { path, clone: true } for a private clone
additionalWorkspacesExtra mounted directories, each with an optional readOnly flag
envPlain environment variables
argsParameters the file itself accepts (default, required, enum, pattern)
sandboxOptionsResource sizing: cpus, memory, gpu, template, profile
kitsKits to apply — OCI references, Git URLs, local paths, or ZIP archives
portsPort mappings (sandbox, host, protocol)
secretsCredentials to provision — see the next section
bindingsWhich domains a provisioned credential is allowed to be injected into
mcp.serversMCP servers to register (see Part 12)
lifecycleHost commands to run at initialize, postCreate, preRemove

Relative paths in the file resolve from the file's own directory, and two built-in variables help with that: ${{ env.fileDir }} (the directory containing sbxenv.yaml) and ${{ env.projectDir }} (the workspace you pointed the command at).

You can also keep a ~/.sbxenv.yaml with personal defaults (agent, preferred workspace root) that every project file layers on top of — lists concatenate, scalars get overridden, and the user file can't set name.

3. Secrets: three ways to supply a value

secrets declares credentials the sandbox needs, without putting the raw value in a file you might commit:

secrets:
  anthropic:
    ref: op://Private/Anthropic/api-key   # from a secrets vault
    refresh: 55m
  github:
    command: gh auth token                # run a host command, use its output
  demo:
    value: "not-a-real-secret"             # literal — fine for local demos only

Each secret needs exactly one of value (a literal — visible in the file, so don't use it for anything real), ref (a vault URI your secrets backend resolves), or command (a command run on the host, whose output becomes the secret value). bindings then says which domains that credential is allowed to reach:

bindings:
  github:
    apiKey:
      domains:
        - api.github.com

The proxy injects the real value into outbound requests to those domains only — the agent inside the sandbox never sees the raw secret, the same credential-injection model you already saw with the MCP gateway.

4. Plan before you apply

Because an environment file can run host-side commands (lifecycle, secrets.<name>.command), sbx shows you a plan before doing anything:

sbx env plan

This prints every host command it would run and why, every credential it would provision (literal values shown only as a SHA-256 digest, references shown as-is), and every environment variable, port, path, and MCP registration it would set up. Nothing runs until you either approve interactively or pass --auto-approve. A plan with no host commands at all applies silently on later runs, once you've approved it the first time.

5. The full lifecycle: create, exec, rm

sbx env create              # provision secrets, create the sandbox, don't attach
sbx env run                 # same, then attach to the agent
sbx env exec -- npm test    # run a command in the existing sandbox, no lifecycle hooks
sbx env rm                  # remove the sandbox and the secrets scoped to it

All four read sbxenv.yaml from the current directory by default, or a path you pass explicitly. sbx env rm --prune-bindings also removes the host-global credential bindings and MCP registrations the file created — by default those two things persist after the sandbox itself is gone, since other sandboxes might still be using them.

Changing env values or MCP servers takes effect on the next sbx env run. Changing anything structural — the workspace, kits, ports, secrets, or sandboxOptions — needs the sandbox recreated: sbx env rm, edit the file, sbx env create again.

6. A worked example

A version of the backend stack from Part 6, declared instead of typed:

schemaVersion: "1"
name: sbx-crash-course-backend
agent: shell
workspace: ./backend

env:
  NODE_ENV: development

ports:
  - sandbox: 3000
    host: 3000

secrets:
  db_password:
    value: postgres

lifecycle:
  postCreate:
    - command: npm install
      workdir: ${{ env.fileDir }}/backend
sbx env run

One command reproduces the exact sandbox — workspace, port, and installed dependencies — that took several manual steps back in Part 6.

7. Beginner Pitfalls

  • secrets.<name>.value is a literal string stored in the file. It's fine for the demo case above, but never use it for a real API key — use ref (a vault) or command (something like gh auth token or op read) instead.
  • A plan with host commands (a lifecycle step, or a secrets.*.command) asks for approval every single invocation unless you either pass --auto-approve or turn on the persistent env.rememberHostCommands setting — don't reach for --skip-host-commands just to make the prompt go away, since that also skips whatever setup those commands were doing.
  • Editing sbxenv.yaml doesn't automatically apply to an already-running sandbox for structural fields (workspace, kits, ports, secrets). You need the explicit sbx env rm → edit → sbx env create cycle, the same as you'd expect from any declarative config.

ទិញកាហ្វេជូនខ្ញុំ

ចូលចិត្តមេរៀននេះមែនទេ? សូមពិចារណាទិញកាហ្វេជូនខ្ញុំមួយកែវ ដើម្បីគាំទ្រការបង្កើតមាតិកាឥតគិតថ្លៃបន្ថែមទៀត!