default, which cannot be deleted.
- A variable is a plain value. Commands in the sandbox see it, and members see it in the console and API.
- A secret is write-only. No API route returns its value, and the sandbox never sees it. Commands see a placeholder, and the host swaps in the real value on HTTPS requests to the hosts you allow.
zode env command in the CLI.
Set up an environment
env_ id. The API’s environment routes take the env_ id; ?environment= on create takes either.
Owners and admins can create and delete environments and set variables and secrets. Members can read variables and secret names.
Which environment a sandbox reads
You choose it when you create the sandbox:?environment=<name or id> on POST /v1/sandboxes, or the environment option of Sandbox.create. Leave it out and the sandbox reads default. An unknown environment returns 400 unknown_environment and nothing is created.
zode newhas no environment flag, so sandboxes made with the CLI readdefault.- A fork reads its parent’s environment.
- You cannot move a sandbox to another environment after it is made.
- Deleting an environment unlinks its sandboxes; their next commands get nothing from it.
When values are read
A sandbox reads its environment at the start of every exec or attach session: eachzode exec, zode ssh, SDK exec() and API exec call. A change applies to the next command. Processes that are already running keep what they started with.
Each secret’s placeholder stays the same when you change its value, and the host gets the new value within about 30 seconds. Rotating a secret does not need a restart.
Per-command variables
You can also pass variables to one command. They win over the environment’s variables and secrets when names clash.--env-file reads KEY=VALUE lines, skips blank lines and # comments, drops a leading export and strips quotes around values. Flags go before the sandbox name. Over attach, pass variables in the X-Zode-Env header; see Attach.
How secrets work
- You store a secret with a value and
allowed_hosts, for exampleapi.stripe.comor*.example.com(any subdomain, not the apex). - Commands in the sandbox see
STRIPE_KEY=zode_vault_..., a placeholder that is the same in every session. - Your code uses it as usual, for example
Authorization: Bearer $STRIPE_KEY. - TCP traffic from the sandbox to ports 443 and 80 leaves through an egress proxy on the host. For hosts on a secret’s allowlist, the proxy decrypts the request, finds the placeholder in the URL, headers or body, and swaps in the real value.
NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, REQUESTS_CA_BUNDLE and CURL_CA_BUNDLE so Node, Python and curl trust it too.
What happens to a request carrying the placeholder:
What this protects against
The real value is never in the VM: not in the environment, files, memory, shell history, logs, snapshots or forks. An agent that prints its environment, a dependency that uploadsenv to its own server, or a prompt injection that asks for your keys can only leak the placeholder.
What it does not protect against
- Use of the secret. Code in the sandbox can still call the allowed hosts with your key, and do anything the key permits. Give sandboxes keys with the narrowest scope your provider offers.
- Hosts that echo requests. If an allowed host returns request headers or bodies in its responses, the real value comes back into the sandbox. Allow only the API hosts that need the key.
- Variables. Variables are plain values inside the sandbox. Put anything sensitive in a secret.
Limits
- Only bearer tokens and header keys for HTTPS APIs. Request-signed credentials and connection strings are refused with 400
secret_unsupported: names starting withAWS_SECRET_ACCESS_KEYorAWS_SESSION_TOKEN, and values starting withpostgres://,postgresql://,mysql://,mongodb://,mongodb+srv://,redis://or-----BEGIN. Use short-lived credentials for those. - Swapping works on port 443 only. Other ports are not proxied, so a placeholder sent to them goes out unchanged.
- Clients that pin certificates fail against allowed hosts, because the proxy presents its own certificate.
- Requests to allowed hosts use HTTP/1.1, and request bodies over 32 MiB are refused with 413.
- No secrets reach custom image builds.
Limits
A variable and a secret in the same environment cannot share a name (409
name_taken).