Pass secrets into a sandbox
Read secrets from the host and inject them into a sandbox at runtime.
Pass task-specific credentials from the host into a sandbox at runtime. Keep secrets out of image specifications, build commands, and copied application files.
The runtime examples apply to both local and hosted Fleet guests. They use a connected Linux sandbox named sb with a POSIX shell and Python 3. Put your application at /app/main.py before running them. Use credentials with only the permissions and lifetime the task needs.
Inject secrets at runtime#
Set DATABASE_URL and GITHUB_TOKEN in the host environment through your secret manager. Then call await run_with_secrets(sb) from your async code:
import os
import shlex
from cua_sandbox import Sandbox
async def run_with_secrets(sb: Sandbox):
db_url = os.environ['DATABASE_URL']
gh_token = os.environ['GITHUB_TOKEN']
command = (
f'DATABASE_URL={shlex.quote(db_url)} '
f'GITHUB_TOKEN={shlex.quote(gh_token)} '
'python3 /app/main.py'
)
result = await sb.shell.run(command)
if not result.success:
raise RuntimeError('Application failed; inspect sanitized diagnostics')Keep the assignments and application in the same shell.run() call. An export in one call does not persist into another call. shlex.quote() preserves spaces, quotes, and newlines as literal values in a POSIX shell; environment values cannot contain a NUL byte.
The assignments apply to the application and its child processes. They do not configure later shell calls. Wait for the application to finish, and avoid leaving background children running with inherited credentials.
These commands contain the secret values. Shell quoting prevents shell interpretation; it does not redact command logs, traces, process inspection, or application output. Use a trusted sandbox, restrict access to its diagnostics, and do not print the command or enable shell tracing (set -x).
Keep image configuration non-sensitive#
For local image builds, use Image.env() for non-sensitive config, not for secrets. The following builder example is local-only. For hosted Fleet, prepare configuration in the prebuilt guest artifact before publishing it.
from cua_sandbox import Image
img = (
Image.linux()
.apt_install('python3')
.env(
LOG_LEVEL='info',
APP_ENV='production',
PORT='8080',
)
).env() values are stored in the Image spec and visible to anyone who can inspect it. Do not use .env() for API keys, passwords, or tokens.
Provide a temporary credential file#
For an application that reads an SSH key file, create a credential for the task and set TASK_SSH_KEY_FILE to its host file path. Restrict that host file to its owner (chmod 600). Configure /app/main.py to read the sandbox file path from TASK_SSH_KEY_FILE, then call await run_with_key_file(sb):
import os
from pathlib import Path
import shlex
from cua_sandbox import Sandbox
async def run_with_key_file(sb: Sandbox):
key_content = Path(os.environ['TASK_SSH_KEY_FILE']).read_bytes().decode('utf-8')
script = f"""
set -eu
umask 077
secret_dir=$(mktemp -d /tmp/cua-task-secret.XXXXXXXXXX)
trap 'rm -f "$secret_dir/task-key"; rmdir "$secret_dir"' EXIT
trap 'exit 1' HUP INT TERM
printf '%s' {shlex.quote(key_content)} > "$secret_dir/task-key"
chmod 600 "$secret_dir/task-key"
TASK_SSH_KEY_FILE="$secret_dir/task-key" python3 /app/main.py
"""
result = await sb.shell.run(script)
if not result.success:
raise RuntimeError('Credential task failed; inspect sanitized diagnostics')This example accepts a UTF-8 text key without NUL bytes. printf '%s' writes its content without adding or removing a trailing newline. mktemp -d creates a private directory, and umask 077 restricts the file to its owner from creation. Mode 600 permits only the owner to read and write the key; it does not protect against root or other processes running as that owner.
The exit trap removes the temporary file and directory after the application exits, including an ordinary nonzero exit. Forced termination, a shell timeout, or loss of the sandbox connection can prevent cleanup. The host key remains in place. Do not snapshot the sandbox while it holds credentials, and revoke the task credential when finished. Removing the file does not securely erase copies, logs, or snapshots.
Finish the task#
Check the application's exit status without printing credentials or unreviewed output. Confirm that credential-consuming processes have stopped and that temporary files were removed. If cleanup was interrupted, remove the remaining task files or destroy the disposable sandbox before reusing it. Revoke or expire credentials through the issuing service, and keep host secret files out of version control.