Skip to main content
Inject credentials into outbound HTTP without ever exposing the real value to the guest. The guest only ever sees a placeholder; microsandbox’s TLS proxy swaps in the real secret on connections to allowed hosts and blocks everything else. See Secrets for how placeholder substitution works and usage examples.

Typical flow

import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("agent")
  .image("python")
  .secret((s) =>
    s.env("OPENAI_API_KEY")              // guest sees $MSB_OPENAI_API_KEY
      .value(process.env.OPENAI_API_KEY!)
      .allowHost("api.openai.com"),      // real value only reaches this host
  )
  .create();

// In the guest, the placeholder is swapped for the real key
// only on TLS connections to api.openai.com.
const out = await sb.exec("python", ["agent.py"]);

SecretBuilder

Fluent builder for one secret’s placeholder, allowed hosts, and injection scopes. Obtained through SandboxBuilder.secret(s => ...) or NetworkBuilder.secret(s => ...); each secret maps an environment variable to a real value that is only revealed when traffic reaches an allowed host through the TLS proxy. Every setter returns the same builder so calls can be chained. env(), value(), and at least one allowed host are required; build() throws otherwise. Adding any secret automatically enables TLS interception.

.env()

env(varName: string): this
Set the environment variable name that holds the placeholder inside the guest. The guest sees $MSB_<varName> (or a custom placeholder), never the real value. Names must be non-empty and cannot contain = or NUL; shell-identifier syntax is not required. Required.

Parameters

varNamestring
Environment variable name (non-empty, no = or NUL).

.value()

value(value: string): this
Set the real secret value. This is the string that replaces the placeholder when a request reaches an allowed host. It never enters the guest VM. Required.

Parameters

valuestring
The actual credential or token.

.placeholder()

placeholder(placeholder: string): this
Override the auto-generated placeholder string. By default microsandbox generates $MSB_<envVar>. Use this when you need a specific format or when the placeholder must match a particular byte length. Placeholders must be non-empty, at most 1024 bytes, and cannot contain NUL, CR, or LF.

Parameters

placeholderstring
Custom placeholder string: non-empty, up to 1024 bytes, no NUL/CR/LF.

.allowHost()

allowHost(host: string): this
.secret((s) =>
  s.env("STRIPE_KEY")
    .value(process.env.STRIPE_KEY!)
    .allowHost("api.stripe.com")
    .allowHost("files.stripe.com"),
)
Add an exact host allowed to receive the real secret value. The proxy checks the host against the connection’s verified TLS identity and observed DNS history. Can be called multiple times to allow several hosts. At least one allowed host (exact, pattern, or any) is required.

Parameters

hoststring
Exact hostname, e.g. “api.example.com” (ASCII case-insensitive).

.allowHostPattern()

allowHostPattern(pattern: string): this
Add a wildcard host pattern. A *.suffix pattern matches the suffix itself and any single-or-multi label subdomain of it.

Parameters

patternstring
Wildcard pattern, e.g. “*.googleapis.com”.

.allowAnyHostDangerous()

allowAnyHostDangerous(iUnderstand: boolean): this
Allow substitution on any host. Every server the guest connects to can then receive the real secret, which effectively disables host-based protection. The call is a no-op unless iUnderstand is true. Only use this when the secret is not sensitive or the sandbox network is fully locked down. This is the one allow-list pattern that skips DNS and TLS-identity pinning.

Parameters

iUnderstandboolean
Must be true to take effect.

.requireTlsIdentity()

requireTlsIdentity(enabled: boolean): this
When true, the secret is only substituted on TLS-intercepted connections where the proxy has verified it is performing MITM and the SNI matches an allowed host. Bypassed TLS is opaque and never receives substitution. Disable only when you know the traffic path is safe and explicitly supports non-TLS substitution. Default: true.

Parameters

enabledboolean
Require verified TLS identity. Default: true.

.injectHeaders()

injectHeaders(enabled: boolean): this
Control whether the placeholder is replaced anywhere in HTTP headers. This is the most common injection scope, covering Authorization: Bearer $MSB_... and similar patterns. Default: true.

Parameters

enabledboolean
Substitute in headers. Default: true.

.injectBasicAuth()

injectBasicAuth(enabled: boolean): this
Control whether Authorization: Basic <base64> credentials are decoded, substituted in the decoded user:password, then re-encoded. Orthogonal to injectHeaders: this flag handles the encoded-credentials case for the Basic scheme; injectHeaders handles literal substitution in any header line, including non-Basic Authorization schemes (Bearer, Digest). Default: true.

Parameters

enabledboolean
Substitute inside Basic Auth credentials. Default: true.

.injectQuery()

injectQuery(enabled: boolean): this
Control whether the placeholder is replaced in the URL query string (the ?key=value portion of the request line). Default: false.

Parameters

enabledboolean
Substitute in query parameters. Default: false.

.injectBody()

injectBody(enabled: boolean): this
Control whether the placeholder is replaced in request bodies when microsandbox can inspect and rewrite them safely. Encoded bodies are forwarded unchanged, and body placeholders in unsupported body formats are blocked rather than leaked. Default: false.

Parameters

enabledboolean
Substitute in request bodies. Default: false.

.onViolation()

onViolation(configure: (b: ViolationActionBuilder) => ViolationActionBuilder): this
.secret((s) =>
  s.env("API_KEY")
    .value(process.env.API_KEY!)
    .allowHost("api.github.com")
    .onViolation((v) =>
      v.blockAndLog().passthroughHost("api.anthropic.com"),
    ),
)
Configure violation behavior for this secret. Overrides the sandbox-wide secret violation policy and can let selected hosts receive the placeholder unchanged. See ViolationActionBuilder for the full set of block* and passthrough* methods. Passthrough hosts do not receive the real secret value; substitution still only happens for hosts configured with allowHost() or allowHostPattern(). When a per-secret passthrough policy does not match the request host, microsandbox falls back to the sandbox-wide secret violation action.

Parameters

Configure the per-secret violation action.

.build()

build(): SecretEntry
Materialize the SecretEntry. Called for you by SandboxBuilder.secret, so you rarely call it directly. If placeholder was not set, it defaults to $MSB_<envVar>. Throws a MicrosandboxError if env or value was not set, or if the allow-list is empty.

Returns

The materialized secret entry.

ViolationActionBuilder

Fluent builder for the action taken when a secret placeholder is sent to a disallowed host. Obtained through the closure passed to SecretBuilder.onViolation(v => ...) (per-secret) or NetworkBuilder.onSecretViolation(v => ...) (sandbox-wide). The terminal block* methods set the base action; the passthrough* methods carve out hosts that get the placeholder forwarded unchanged.

.block()

block(): this
Silently drop a violating request. The guest sees a connection reset. This is the default.

.blockAndLog()

blockAndLog(): this
Drop the request and emit a warning log on the host side.

.blockAndTerminate()

blockAndTerminate(): this
Drop the request, log an error, and shut down the entire sandbox.

.passthroughHost()

passthroughHost(host: string): this
Forward requests to an exact host with the placeholder unchanged, instead of blocking. The host does not receive the real value. Non-matching hosts fall back to the base block* action.

Parameters

hoststring
Exact hostname to forward unchanged.

.passthroughHostPattern()

passthroughHostPattern(pattern: string): this
Forward requests to any host matching a wildcard pattern with the placeholder unchanged.

Parameters

patternstring
Wildcard pattern, e.g. “*.internal.example.com”.

.passthroughAllHosts()

passthroughAllHosts(iUnderstand: boolean): this
Forward the placeholder unchanged to every host instead of blocking. The call is a no-op unless iUnderstand is true. The real value is still never substituted on these hosts; this only stops the request from being dropped.

Parameters

iUnderstandboolean
Must be true to take effect.

SandboxBuilder shorthand

Two methods on SandboxBuilder for adding secrets without reaching into a NetworkBuilder. Both automatically enable TLS interception.

.secret()

secret(configure: (s: SecretBuilder) => SecretBuilder): this
await using sb = await Sandbox.builder("payments-worker")
  .image("python")
  .secret((s) =>
    s.env("STRIPE_KEY")
      .value(process.env.STRIPE_KEY!)
      .allowHost("api.stripe.com")
      .allowHostPattern("*.stripe.com")
      .injectHeaders(true)
      .injectQuery(false),
  )
  .create();
Add a secret with full configuration via a SecretBuilder closure. The builder’s build() is called for you.

Parameters

Configure the secret.

.secretEnv()

secretEnv(envVar: string, value: string, allowedHost: string): this
await using sb = await Sandbox.builder("agent")
  .image("python")
  .secretEnv("OPENAI_API_KEY", process.env.OPENAI_API_KEY!, "api.openai.com")
  .create();
Three-argument shorthand. Auto-generates the placeholder as $MSB_<envVar> and allows substitution only on allowedHost. The default injection scopes apply (headers and Basic Auth enabled, query and body disabled).
Plaintext at rest. The value is persisted verbatim in the durable sandbox config until a later modify rotate migrates the entry to a source reference. Use this path when you hold only a value; a future host-side secret store will switch it to import-then-reference with no signature change.

Parameters

envVarstring
Environment variable name (non-empty, no = or NUL).
valuestring
Secret value.
allowedHoststring
Allowed destination host (exact match).

NetworkBuilder shorthand

The same secret configuration is available inside SandboxBuilder.network(n => ...) for callers who are already configuring networking. These methods set the secrets and the sandbox-wide violation policy directly on the network.

.secret()

secret(configure: (s: SecretBuilder) => SecretBuilder): this
await using sb = await Sandbox.builder("agent")
  .image("python")
  .network((n) =>
    n.secret((s) =>
      s.env("OPENAI_API_KEY")
        .value(process.env.OPENAI_API_KEY!)
        .allowHost("api.openai.com"),
    ),
  )
  .create();
Add a secret with full configuration via a SecretBuilder closure. Identical in behavior to SandboxBuilder.secret.

Parameters

Configure the secret.

.secretEnv()

secretEnv(envVar: string, value: string, placeholder: string, allowedHost: string): this
Four-argument shorthand. Same as the SandboxBuilder form but lets you provide the placeholder explicitly instead of auto-generating $MSB_<envVar>.

Parameters

envVarstring
Environment variable name (non-empty, no = or NUL).
valuestring
Secret value.
placeholderstring
Explicit placeholder: non-empty, up to 1024 bytes, no NUL/CR/LF.
allowedHoststring
Allowed destination host (exact match).

.secretEnvSimple()

secretEnvSimple(envVar: string, value: string, allowedHost: string): this
Three-argument shorthand on NetworkBuilder. Auto-generates the placeholder as $MSB_<envVar>, matching SandboxBuilder.secretEnv.

Parameters

envVarstring
Environment variable name (non-empty, no = or NUL).
valuestring
Secret value.
allowedHoststring
Allowed destination host (exact match).

.onSecretViolation()

onSecretViolation(configure: (b: ViolationActionBuilder) => ViolationActionBuilder): this
await using sb = await Sandbox.builder("agent")
  .image("python")
  .network((n) =>
    n.secretEnvSimple("OPENAI_API_KEY", process.env.OPENAI_API_KEY!, "api.openai.com")
      .onSecretViolation((v) => v.blockAndLog()),
  )
  .create();
Set the sandbox-wide secret violation policy: what happens when any secret’s placeholder is sent to a host outside that secret’s allow-list. Per-secret SecretBuilder.onViolation overrides this for the secret it is set on. See ViolationActionBuilder for the available actions.

Parameters

Configure the sandbox-wide violation action.

Types

SecretEntry

Returned by SecretBuilder.build()

The object produced by SecretBuilder.build(). You normally construct one with the builder, but the shape is available for callers that prefer plain objects.
FieldTypeDescription
envVarstringEnvironment variable name (non-empty, no = or NUL)
valuestringReal secret value (stays on the host)
placeholderstring | nullCustom placeholder: non-empty, up to 1024 bytes, no NUL/CR/LF; defaults to $MSB_<envVar> when null
allowedHostsreadonly string[]Allowed hosts (exact match)
allowedHostPatternsreadonly string[]Wildcard host patterns
allowAnyHostbooleanPermit substitution to any host
requireTlsIdentitybooleanRequire verified TLS identity before substituting
injectionSecretInjectionWhere in the request the value may be substituted

SecretInjection

Used by SecretEntry.injection

Controls where the TLS proxy substitutes the placeholder with the real value. Each field is optional; omitted fields use the default. Set through the inject* methods on SecretBuilder.
FieldTypeDefaultDescription
headers?booleantrueReplace the placeholder anywhere in HTTP headers.
basicAuth?booleantrueDecode Authorization: Basic <base64> credentials, substitute in the decoded user:password, and re-encode. Other schemes (Bearer, Digest) are handled by headers.
queryParams?booleanfalseReplace the placeholder in URL query parameters.
body?booleanfalseReplace the placeholder in request bodies when microsandbox can inspect and rewrite them safely. Encoded bodies are forwarded unchanged, and body placeholders in unsupported body formats are blocked instead of being leaked.

ViolationAction

Built by ViolationActionBuilder

The string identifier for the base action taken when a secret placeholder is sent to a disallowed host. Configured through ViolationActionBuilder (via SecretBuilder.onViolation or NetworkBuilder.onSecretViolation). The ViolationActions array enumerates all values.
type ViolationAction = "block" | "block-and-log" | "block-and-terminate" | "passthrough";
ValueBuilder methodDescription
'block'block()Silently drop the request. The guest sees a connection reset. This is the default.
'block-and-log'blockAndLog()Drop the request and emit a warning log on the host side.
'block-and-terminate'blockAndTerminate()Drop the request, log an error, and shut down the entire sandbox.
'passthrough'passthroughHost() / passthroughHostPattern() / passthroughAllHosts()Forward matching hosts with the placeholder unchanged. Non-matching hosts use the base block action.