How to Pass Environment Variables to a Sandcastle Sandbox Using the `env` Option

TLDR: Supply an env object when calling a provider factory like podman() or vercel() to inject key-value pairs that override repository and agent environment variables inside the sandbox.

Sandcastle is an open-source sandbox execution framework developed by mattpocock. When running isolated workloads in Docker, Podman, or Vercel Firecracker environments, you often need to pass secrets or configuration via environment variables. The env option lets you inject these directly when configuring your sandbox provider, merging with variables from .sandcastle/.env while maintaining explicit precedence control.

Understanding the env Option Interface

Each sandbox provider accepts an env option through its factory function. According to the source code in src/sandboxes/podman.ts (lines 75-77) and src/sandboxes/vercel.ts (lines 111-112), the option is typed as readonly env?: Record<string, string>.

This interface is consistent across providers including Docker, Podman, Vercel, and the no-sandbox provider. When you invoke a factory, you pass an object containing your environment variables:

import { podman } from "sandcastle/sandboxes/podman";

const sandbox = podman({
  env: {
    API_TOKEN: "abc123",
    NODE_ENV: "production"
  }
});

How Environment Variables Flow Through the System

Sandcastle merges environment variables from three distinct sources before starting the sandbox process.

Provider Configuration and Forwarding

When you call run(), the env object flows through src/startSandbox.ts (lines 34-36), which defines the StartSandboxOptions interface containing the env field. The system then passes this configuration to the provider's create method. For isolated sandbox creation, see lines 91-96 in src/startSandbox.ts where options.provider.create() receives the merged environment.

The Merge Process

Before the sandbox launches, Sandcastle combines variables using the mergeProviderEnv utility found in src/mergeProviderEnv.ts (lines 8-12). The merge order follows this priority:

  1. Repository-resolved environment (from .sandcastle/.env via EnvResolver.ts)
  2. Agent-provided environment variables
  3. Sandbox-provider env option (highest precedence)

Provider-specific environment variables override any duplicate keys from the other sources.

Runtime Injection

The merged map becomes visible inside the container through runtime-specific mechanisms. In src/sandboxes/podman.ts (lines 45-49), the system constructs envArgs that translate to -e KEY=VALUE arguments for the Podman CLI. Similarly, the Vercel provider passes these variables to the Firecracker VM SDK.

Provider-Specific Implementation Examples

Passing Variables to a Podman Sandbox

The Podman provider accepts environment variables through its options interface and converts them to container runtime arguments. As implemented in src/sandboxes/podman.ts (lines 115-116), the create method receives the env object and processes it via the envArgs construction (lines 45-49):

import { podman } from "sandcastle/sandboxes/podman";
import { run } from "sandcastle";

await run({
  sandbox: podman({
    env: {
      DATABASE_URL: "postgres://user:pass@localhost:5432/db",
      LOG_LEVEL: "debug",
    },
  }),
  agent: myAgent,
});

Configuring the Vercel Sandbox (Firecracker VM)

For serverless sandbox execution on Vercel's infrastructure, pass env to the vercel() factory. The implementation in src/sandboxes/vercel.ts (lines 111-112 and 126-128) forwards these variables to the underlying Vercel SDK:

import { vercel } from "sandcastle/sandboxes/vercel";

await run({
  sandbox: vercel({
    env: {
      VERCEL_TOKEN: process.env.VERCEL_TOKEN!,
      CUSTOM_FLAG: "true",
    },
  }),
  agent: myAgent,
});

Using the No-Sandbox Provider

Even when running directly on the host machine without containerization, you can control the environment. The no-sandbox provider accepts the same env interface, allowing you to extend or override process.env:

import { noSandbox } from "sandcastle/sandboxes/no-sandbox";

await run({
  sandbox: noSandbox({
    env: { PATH: "/custom/bin:" + process.env.PATH },
  }),
  agent: myAgent,
});

Environment Variable Precedence and Resolution

Sandcastle resolves environment variables from multiple locations before merging them with your explicit env option. The EnvResolver.ts file (lines 56-72) handles loading from .sandcastle/.env and falling back to process.env.

When mergeProviderEnv.ts (lines 8-12) combines these sources, the provider-specific env always wins conflicts. This design ensures that explicit configuration in code overrides implicit repository settings or agent defaults.

Summary

  • Pass environment variables using the env option when calling provider factories like podman(), vercel(), or noSandbox().
  • The option is typed as Record<string, string> and defined in provider-specific files such as src/sandboxes/podman.ts (lines 75-77).
  • Variables flow through src/startSandbox.ts (lines 34-36) and merge with repository and agent environments via src/mergeProviderEnv.ts (lines 8-12).
  • Provider env values take highest precedence in the merge order.
  • Podman and Docker providers convert the map to -e CLI arguments, while Vercel passes them to the Firecracker SDK.

Frequently Asked Questions

Can I pass sensitive secrets through the env option?

Yes. The env option is the recommended way to inject API tokens, database URLs, and other secrets into your Sandcastle sandbox. Since provider-specific environment variables take precedence over repository .env files, you can safely commit default configuration while injecting production secrets at runtime.

What happens if I define the same variable in .sandcastle/.env and the env option?

The env option value wins. According to src/mergeProviderEnv.ts (lines 8-12), Sandcastle merges sources with the provider env acting as the final override, ensuring explicit code configuration takes precedence over file-based configuration.

Do environment variables work with the no-sandbox provider?

Yes. Even when using noSandbox() to execute directly on the host machine without containerization, the env option functions identically. It merges with the current process environment, allowing you to extend PATH or override specific variables for the agent execution.

Which sandbox providers support the env option?

All Sandcastle providers support this interface: Docker (docker()), Podman (podman()), Vercel (vercel()), and the no-sandbox provider (noSandbox()). Each implements the readonly env?: Record<string, string> option as defined in their respective source files under src/sandboxes/.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →