Secrets

Secrets are environment variables whose real values stay on the host and are delivered through microsandbox’s secret mechanism. The VM receives a placeholder, not the real value. The network proxy replaces that placeholder only when a request is sent to an allowed host over a connection whose destination can be verified.

This is different from an ordinary entry in env: values from env are copied into the VM as normal environment variables. Put credentials in env.secret or env.secret.yaml, never in env.

Note: If you use OpenCode’s /connect command, see OpenCode authentication. That is the recommended workflow for credentials, which opencode stores in auth.json; it uses a literal microsandbox placeholder and an explicit home: mapping. The provision-host-config: false setting shown there belongs in the top-level launcher configuration in ~/.config/agents-sandbox/config.yaml.

Format

Two file formats are supported: legacy text and structured YAML. YAML files take precedence over legacy files for the same key.

Legacy format — env.secret

One KEY=value@host per line. The part after the last @ is a policy tag restricting which microsandbox runtime hosts can access the secret. Values may contain @ — everything before the last @ is the value. Each entry must define a host explicitly; omitting the host part drops the secret with a warning.

# .agents-sandbox/env.secret
GITHUB_TOKEN=ghp_xxxxxxxxxxxx@github.com
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx@anthropic.com

YAML format — env.secret.yaml

A YAML object map from env-var name to { value, host?, hosts?, allow_any_host_dangerous? }. Values may contain any characters including @. host and hosts are optional when allow_any_host_dangerous is set, but otherwise required — entries with neither hosts nor a dangerous flag are dropped with a warning. JSON is also accepted (YAML 1.2 is a JSON superset).

# .agents-sandbox/env.secret.yaml
GITHUB_TOKEN:
  value: "ghp_xxx@corp"
  host: microsandbox
ANTHROPIC_API_KEY:
  value: sk-ant-xxxxxxxx
  hosts: [gw-a.internal, gw-b.internal]
# No hosts defined — this entry is dropped with a warning
DROPPED_KEY:
  value: secret-value
TRUSTED_KEY:
  value: secret-value
  allow_any_host_dangerous: true

An empty value is valid and will be passed through unchanged.

Precedence

Files are merged from lowest to highest precedence per key, in this order:

  1. user-level env.secret (legacy KEY=value@host)
  2. project-level env.secret (legacy)
  3. user-level env.secret.yaml
  4. project-level env.secret.yaml

A YAML entry always wins over a legacy entry for the same key, even across levels — a user-level env.secret.yaml overrides a project-level env.secret. The YAML entry fully replaces the legacy entry’s hosts — if a YAML entry omits host, hosts, and allow_any_host_dangerous, the resulting entry has no hosts and is dropped with a warning.

Supported files

  • ~/.config/agents-sandbox/env.secret — user-level, legacy text format
  • ~/.config/agents-sandbox/env.secret.yaml — user-level, structured YAML (or JSON)
  • .agents-sandbox/env.secret — project-level, legacy text format
  • .agents-sandbox/env.secret.yaml — project-level, structured YAML (or JSON)

Placeholder values

When no custom placeholder is configured, microsandbox uses the following guest-visible value:

$MSB_<SECRET_NAME>

For example, the guest value of GITHUB_TOKEN is $MSB_GITHUB_TOKEN.

Trusting the CA in applications

Replacing a placeholder requires the proxy to terminate TLS towards the allowed host. The VM trusts a microsandbox CA at /.msb/tls/ca.pem; microsandbox’s guest init installs that CA into the system store and points the common runtimes at it through SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, and CURL_CA_BUNDLE. curl, git, Python, Go, and Node need no extra setup.

Applications that keep their own certificate store do not read any of that. For those the runner image installs microsandbox’s trust helper as msb-trust:

msb-trust all      # every supported application whose tools are installed
msb-trust java     # the JDK's default trust store
msb-trust chrome   # the current user's Chrome/Chromium NSS store

The JDK’s trust store is root-owned, so run msb-trust java from agents-sandbox shell --root; chrome writes the current user’s store and needs no elevation.

The helper imports the CA, preserves the certificates that are already there, and never installs packages. java needs keytool on PATH (or JAVA_HOME pointing at a JDK) and chrome needs certutil (libnss3-tools/nss-tools); a missing tool is reported instead of installed. msb-trust <application> --help lists what that application needs. The helper’s own messages say sh msb-trust.sh …, the layout upstream expects when you save the script next to your code, so translate the name to msb-trust when you follow them. The Java recipe has the full setup and the import every VM boot needs.

Two things the helper does not cover. Containers built inside the VM have their own root store, so a docker build that downloads over HTTPS fails certificate verification: copy the CA out of the VM (/usr/local/share/ca-certificates/microsandbox-ca.crt) into the build context and install it in that Dockerfile, then run update-ca-certificates. Applications with a bundled trust store also have to be configured individually.

OpenCode authentication

OpenCode stores credentials created by /connect in:

~/.local/share/opencode/auth.json

That is different from the agents-sandbox input directory:

~/.config/agents-sandbox/opencode/

(opencode2 uses ~/.config/agents-sandbox/opencode2/ instead; see the fallback note in Agent configuration.) An auth.json placed in the latter directory is mirrored to /home/dev/.config/opencode/auth.json; it is not automatically OpenCode’s credential store. To use secret placeholders in OpenCode’s credential store, provision the placeholder file explicitly with the launcher’s home: key.

For example, for GitHub Copilot:

# ~/.config/agents-sandbox/config.yaml
provision-host-config: false
home:
  # The source is relative to ~/.config/agents-sandbox/config.yaml.
  .local/share/opencode/auth.json: opencode/auth.json
// ~/.config/agents-sandbox/opencode/auth.json
{
  "github-copilot": {
    "type": "oauth",
    "access": "$MSB_GH_COPILOT_ACCESS_TOKEN",
    "refresh": "$MSB_GH_COPILOT_REFRESH_TOKEN",
    "expires": 0
  }
}
# ~/.config/agents-sandbox/env.secret.yaml
GH_COPILOT_ACCESS_TOKEN:
  value: "<access-token>"
  hosts:
    - api.githubcopilot.com
GH_COPILOT_REFRESH_TOKEN:
  value: "<refresh-token>"
  hosts:
    - api.githubcopilot.com

The network policy is separate from the secret allowlist. The VM must be allowed to reach the same endpoint, for example:

# ~/.config/agents-sandbox/config.yaml
network:
  egress-allow:
    - api.githubcopilot.com

Use agents-sandbox config home to verify the home: mapping. agents-sandbox config agent shows the agent mirror and drop-in candidates, but does not show home: mappings or files already present in the persistent home volume.

Note: After running /connect with opencode inside the agents-sandbox, you can use ! to switch to shell mode and then run

cat ~/.local/share/opencode/auth.json

to see the credentials stored in the sandbox (to store them outside in env.secret.yaml).


Docs built from release v0.6.0