Environment variables

An environment variable reaches a sandbox from one of several layers: account-level runtime env resolved by scope, values set on the sandbox itself, secrets bound to an opaque placeholder, or env passed on a single exec. This page covers what each layer is, how conflicts resolve, and how to read the live set.

Account-level runtime env

Platform owners define environment variables once and have them inherited by sandboxes, computers, and agent runs. Runtime env is encrypted at rest and never returned as plaintext by the API, CLI, or SDKs. Responses include metadata and a short preview only.

MIOSA resolves runtime env in this order. More specific values override broader values with the same name:

PrecedenceScopeApplies to
1TenantEvery workspace, project, sandbox, computer, and agent under the tenant
2WorkspaceEvery project, sandbox, computer, and agent in that workspace
3ProjectSandboxes, computers, deployments, and agent runs attributed to that project
4ResourcePer-sandbox or per-computer env set directly on the resource
5RunEnv passed directly to a single exec or agent run

Each runtime env var also has a target. At the same scope, a target-specific value overrides one set to all:

TargetUse it when
allThe value is safe and useful for every runtime surface
sandboxThe value should only appear in sandbox exec/build/agent sessions
computerThe value should only appear in desktop computer commands and agents
agentThe value should be injected into prompt-driven agent runs on sandboxes or computers
deploymentProduction deployment/runtime boot

Variable names must be uppercase shell-safe names matching [A-Z][A-Z0-9_]*.

Sandbox-level env

Env vars passed at create time are injected into the VM at boot and visible to all processes. They are read-only once the sandbox is running; env.list() returns the live set.

Per-sandbox env API

The /api/v1/sandboxes/{id}/env API manages a key-value store attached to a sandbox. Variables are encrypted at rest and injected into the sandbox process environment on start. The API never returns plaintext values, only key names and metadata.

MethodPathDescription
GET/api/v1/sandboxes/{id}/envList all env vars (names only, no values)
PUT/api/v1/sandboxes/{id}/envBulk set or update env vars
DELETE/api/v1/sandboxes/{id}/env/{key}Delete a single env var

PUT is a bulk upsert: existing keys are updated, new keys are created, and keys not present in the request are left unchanged.

curl -X PUT https://api.miosa.ai/api/v1/sandboxes/sbx_abc123/env 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{
    "vars": [
      {"key": "DATABASE_URL", "value": "postgres://user:pass@host/db"},
      {"key": "LOG_LEVEL", "value": "info"}
    ]
  }'

Secrets

For credentials you do not want inside the VM at all, store them as secrets. Every sandbox using a secret sees an opaque placeholder env var; the real key never lives inside the VM, and the swap happens on egress.

sandbox.secrets.set(
    name="openai_key",
    value="sk-proj-...",
    expose_as_env="OPENAI_API_KEY",
)

Inside the sandbox, os.environ["OPENAI_API_KEY"] returns an opaque token such as miosa-tok-7f2a.... When your code makes an outbound call, the MIOSA proxy on the VM’s host sees the placeholder in the Authorization header and swaps it with the real key before forwarding. Rotation is instant: every sandbox picks up the new value on its next outbound request, and the placeholder token does not change.

A secret can be scoped at tenant, workspace, user, external_user, or external_workspace, and the proxy picks the most specific match for the resource making the request.

How a value reaches exec

When a command runs, MIOSA resolves the effective environment in layers and hands it to the process:

  1. Inherited runtime env for the sandbox’s scope and target is materialized at boot.
  2. Sandbox-level env (create-time env or the per-sandbox env API) overrides an inherited value with the same name.
  3. Secrets appear as placeholder env vars exposed via expose_as_env.
  4. env passed to a single exec merges on top for that command only.
result = sbx.exec.run(
    "pytest tests/ -x",
    {"cwd": "/workspace", "env": {"PYTHONPATH": "/workspace"}, "timeout_sec": 120},
)
Was this page helpful?