> ## Documentation Index
> Fetch the complete documentation index at: https://docs.microsandbox.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets

> Configure destination-bound sandbox credentials with the Rust SDK.

See [Secrets](/sandboxes/secrets) for usage examples and security boundaries.

## SecretBuilder

#### <span className="msb-recv">SecretBuilder::</span><span className="msb-hn">new()</span>

```rust theme={null}
fn new() -> SecretBuilder
```

<Accordion title="Example">
  ```rust theme={null}
  use microsandbox::sandbox::SecretBuilder;

  let entry = SecretBuilder::new()
      .env("STRIPE_KEY")
      .value(stripe_key)
      .allow("api.stripe.com")
      .build();
  ```
</Accordion>

Start building a secret with defaults: no env var, no value, no allowed hosts, the default [`SecretSubstitution`](#secretsubstitution) (headers and Basic Auth on, query and body off), no per-secret violation override, and `require_tls_identity = true`. You rarely call this yourself: [`SandboxBuilder::secret(|s| s...)`](/sdk/rust/sandbox#sandbox-secret) hands you a fresh builder and calls [`build()`](#secret-build) for you. `SecretBuilder::default()` is equivalent.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#secretbuilder">SecretBuilder</a></div>
    <div className="msb-param-desc">A builder with default injection scopes.</div>
  </div>
</div>

<p className="msb-member-group">Builder methods</p>

Builder for one secret's placeholder, allowed hosts, and injection scopes. Obtained through [`SandboxBuilder::secret(|s| s...)`](/sdk/rust/sandbox#sandbox-secret) or [`SecretBuilder::new()`](#secretbuildernew); 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 `Self`, so calls chain. [`env()`](#secret-env), exactly one of [`value()`](#secret-value) or [`source()`](#secret-source), and at least one allowed host are required; [`build()`](#secret-build) panics otherwise.

Import path: `microsandbox::sandbox::SecretBuilder`. Adding any secret automatically enables TLS interception.

#### <span className="msb-recv">secret.</span><span className="msb-hn">env()</span>

```rust theme={null}
fn env(self, var: impl Into<String>) -> Self
```

Set the environment variable name that holds the placeholder inside the guest. The guest sees `$MSB_<var>` (or a custom [`placeholder`](#secret-placeholder)), never the real value. Names must be non-empty and cannot contain `=` or NUL; shell-identifier syntax is not required since Linux only requires a `NAME=value` shape. **Required.**

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>var</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">Environment variable name (non-empty, no <code>=</code> or NUL).</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">value()</span>

```rust theme={null}
fn value(self, value: impl Into<String>) -> Self
```

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, but inline values are persisted verbatim in the durable sandbox configuration. Mutually exclusive with [`source()`](#secret-source); prefer a source reference when available.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>value</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">The actual credential or token.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">source()</span>

```rust theme={null}
fn source(self, source: SecretSource) -> Self
```

Resolve secret material from a host-side reference at spawn time. Exactly one of `source()` and `value()` must be set. The durable configuration stores only the reference, not the resolved plaintext.

```rust theme={null}
use microsandbox::sandbox::{SecretBuilder, SecretSource};

let entry = SecretBuilder::new()
    .env("API_KEY")
    .source(SecretSource::Env { var: "API_KEY".into() })
    .allow("api.example.com")
    .build();
```

#### <span className="msb-recv">secret.</span><span className="msb-hn">placeholder()</span>

```rust theme={null}
fn placeholder(self, placeholder: impl Into<String>) -> Self
```

Override the auto-generated placeholder string. By default microsandbox generates `$MSB_<env_var>`. 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 ([`MAX_SECRET_PLACEHOLDER_BYTES`](#secretconfigerror)), and cannot contain NUL, CR, or LF.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>placeholder</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">Custom placeholder string: non-empty, up to 1024 bytes, no NUL/CR/LF.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">allow()</span>

```rust theme={null}
fn allow(self, host: impl AsRef<str>) -> Self
```

<Accordion title="Example">
  ```rust theme={null}
  .secret(|s| s
      .env("STRIPE_KEY")
      .value(stripe_key)
      .allow("api.stripe.com")
      .allow("files.stripe.com"))
  ```
</Accordion>

Add an exact host or wildcard pattern allowed to receive the real secret value. A `*.suffix` pattern matches the suffix itself and its subdomains. 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.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>host</code><span className="msb-type">impl AsRef\<str></span></div>
    <div className="msb-param-desc">Exact hostname or wildcard pattern, e.g. <code>"api.example.com"</code> or <code>"\*.example.com"</code>. Parsed as a <a className="msb-type" href="#hostpattern">HostPattern</a>.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">allow\_any\_host\_dangerous()</span>

```rust theme={null}
fn allow_any_host_dangerous(self, i_understand_the_risk: bool) -> Self
```

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 `i_understand_the_risk` is `true`. Only use this when the secret is not sensitive or the sandbox network is fully locked down. Adds a [`HostPattern::Any`](#hostpattern) to the allow list, the one pattern that skips DNS and TLS-identity pinning.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>i\_understand\_the\_risk</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Must be <code>true</code> to take effect.</div>
  </div>
</div>

<span id="secret-allow_passthrough_for" />

#### <span className="msb-recv">secret.</span><span className="msb-hn">allow\_placeholder\_for()</span>

```rust theme={null}
fn allow_placeholder_for(self, host: impl AsRef<str>) -> Self
```

Allow an exact host, wildcard pattern, or `*` to receive the placeholder unchanged when it cannot be substituted. Repeated calls are additive. The old `allow_passthrough_for()` name remains available as a deprecated alias. This does not grant access to the real value: substitution still requires an allowed host and an enabled request location.

#### <span className="msb-recv">secret.</span><span className="msb-hn">violation\_action()</span>

```rust theme={null}
fn violation_action(self, action: SecretViolationAction) -> Self
```

Override the sandbox-wide blocking action for this secret. With no override, the network's [`secret_violation_action()`](/sdk/rust/networking#network-secret_violation_action) applies; its default is `SecretViolationAction::BlockAndLog`. Passthrough is a separate per-secret host policy.

<Accordion title="Example">
  ```rust theme={null}
  use microsandbox::{Sandbox, SecretViolationAction};

  let sb = Sandbox::builder("worker")
      .image("python")
      .secret(|s| s
          .env("API_KEY")
          .value(api_key)
          .allow("api.github.com")
          .allow_placeholder_for("api.anthropic.com")
          .substitute_in_body(true)
          .violation_action(SecretViolationAction::BlockAndTerminate))
      .secret_violation_action(SecretViolationAction::BlockAndLog)
      .create()
      .await?;
  ```
</Accordion>

#### <span className="msb-recv">secret.</span><span className="msb-hn">require\_tls\_identity()</span>

```rust theme={null}
fn require_tls_identity(self, enabled: bool) -> Self
```

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`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>enabled</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Require verified TLS identity. Default: <code>true</code>.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">substitute\_in\_headers()</span>

```rust theme={null}
fn substitute_in_headers(self, enabled: bool) -> Self
```

Control whether the placeholder is replaced anywhere in HTTP headers, including decoded and re-encoded Basic authentication credentials. This is the most common injection scope, covering `Authorization: Bearer $MSB_...` and similar patterns. Default: `true`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>enabled</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Substitute in headers. Default: <code>true</code>.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">substitute\_in\_query()</span>

```rust theme={null}
fn substitute_in_query(self, enabled: bool) -> Self
```

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

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>enabled</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Substitute in query parameters. Default: <code>false</code>.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">substitute\_in\_body()</span>

```rust theme={null}
fn substitute_in_body(self, enabled: bool) -> Self
```

Control whether the placeholder is replaced in HTTP/1 request bodies. When enabled, fixed-length bodies up to 16 MiB are substituted and their `Content-Length` updated; larger fixed-length bodies are blocked. Chunked bodies are decoded and re-encoded with fresh chunk sizes. Encoded bodies pass through unchanged. HTTP/2 DATA-frame substitution is not supported, so matching body placeholders are blocked rather than leaked. Default: `false`.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>enabled</code><span className="msb-type">bool</span></div>
    <div className="msb-param-desc">Substitute in request bodies. Default: <code>false</code>.</div>
  </div>
</div>

#### <span className="msb-recv">secret.</span><span className="msb-hn">build()</span>

```rust theme={null}
fn build(self) -> SecretEntry
```

Materialize the [`SecretEntry`](#secretentry). Called for you by [`SandboxBuilder::secret`](/sdk/rust/sandbox#sandbox-secret), so you rarely call it directly. If [`placeholder`](#secret-placeholder) was not set, it defaults to `$MSB_<env_var>`.

<p className="msb-label">Returns</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><a className="msb-type" href="#secretentry">SecretEntry</a></div>
    <div className="msb-param-desc">The materialized secret entry.</div>
  </div>
</div>

<p className="msb-label">Panics</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-desc">Panics if <code>env</code> was not set, if neither or both of <code>value</code> and <code>source</code> were set, or if the allow list is empty. Use <a className="msb-type" href="#secret-allow_any_host_dangerous">allow\_any\_host\_dangerous(true)</a> for an explicit any-host secret.</div>
  </div>
</div>

## SandboxBuilder

Two convenience methods on [`SandboxBuilder`](/sdk/rust/sandbox#sandboxbuilder) for the common cases, so you don't have to spell out a [`SecretBuilder`](#secretbuilder) closure. Both automatically enable TLS interception.

#### <span className="msb-recv">sandbox.</span><span className="msb-hn">secret\_env()</span>

```rust theme={null}
fn secret_env(
    self,
    env_var: impl Into<String>,
    value: impl Into<String>,
    allowed_host: impl Into<String>,
) -> Self
```

<Accordion title="Example">
  ```rust theme={null}
  let sb = Sandbox::builder("agent")
      .image("python")
      .secret_env("OPENAI_API_KEY", api_key, "api.openai.com")
      .create()
      .await?;
  ```
</Accordion>

Equivalent to `.secret(|s| s.env(env_var).value(value).allow(allowed_host))`. The placeholder is auto-generated as `$MSB_<env_var>` and the default injection scopes apply (headers and Basic Auth enabled, query and body disabled).

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>env\_var</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">Environment variable name (non-empty, no <code>=</code> or NUL).</div>
  </div>

  <div className="msb-param">
    <div className="msb-param-key"><code>value</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">Secret value.</div>
  </div>

  <div className="msb-param">
    <div className="msb-param-key"><code>allowed\_host</code><span className="msb-type">impl Into\<String></span></div>
    <div className="msb-param-desc">Allowed destination host (exact match).</div>
  </div>
</div>

#### <span className="msb-recv">sandbox.</span><span className="msb-hn">secret\_entry()</span>

```rust theme={null}
fn secret_entry(self, entry: SecretEntry) -> Self
```

<Accordion title="Example">
  ```rust theme={null}
  use microsandbox::sandbox::SecretBuilder;

  let entry = SecretBuilder::new()
      .env("STRIPE_KEY")
      .value(stripe_key)
      .allow("api.stripe.com")
      .build();

  let sb = Sandbox::builder("billing")
      .image("python")
      .secret_entry(entry)
      .create()
      .await?;
  ```
</Accordion>

Add a pre-built [`SecretEntry`](#secretentry) directly. Use this escape hatch when you already have a materialized entry. For example, the entry can be produced by [`SecretBuilder::build()`](#secret-build), loaded from configuration, or constructed by hand for full control over the serialized shape. Both [`secret()`](/sdk/rust/sandbox#sandbox-secret) and [`secret_env()`](#sandbox-secret_env) funnel through this method. The entry is validated when the sandbox is built; an invalid one surfaces as a config error rather than a panic.

<p className="msb-label">Parameters</p>

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>entry</code><a className="msb-type" href="#secretentry">SecretEntry</a></div>
    <div className="msb-param-desc">A materialized secret entry.</div>
  </div>
</div>

## HostPattern

<p className="msb-backref">Used by <a href="#secretentry">SecretEntry</a> · <a href="#secret-allow">allow()</a> · <a href="#secret-allow_placeholder_for">allow\_placeholder\_for()</a></p>

Destination host pattern used by secret policies.

| Variant | Builder methods | Matches |
| - | - | - |
| `Exact(String)` | `allow("api.example.com")`, `allow_placeholder_for("api.example.com")` | Exact hostname, ASCII case-insensitive |
| `Wildcard(String)` | `allow("*.example.com")`, `allow_placeholder_for("*.example.com")` | The suffix and its subdomains |
| `Any` | `allow_any_host_dangerous(true)`, `allow_placeholder_for("*")` | Every host |

Use the explicit dangerous opt-in for any-host substitution; `allow("*")` panics.

#### <span className="msb-recv">pattern.</span><span className="msb-hn">matches()</span>

```rust theme={null}
fn matches(&self, hostname: &str) -> bool
```

Whether `hostname` matches this pattern (ASCII case-insensitive).

## SecretEntry

<p className="msb-backref">returned by <a href="#secret-build">SecretBuilder::build()</a> · used by <a href="#sandbox-secret_entry">secret\_entry()</a></p>

Materialized secret configuration produced by [`SecretBuilder`](#secretbuilder).

| Field | Type | Description |
| - | - | - |
| <span id="entry-env_var" />`env_var` | `String` | Environment variable holding the placeholder. Non-empty, no `=` or NUL. |
| <span id="entry-value" />`value` | `Zeroizing<String>` | The real secret value; never enters the guest. Inline values are persisted; source-backed durable entries carry an empty value until host-side resolution. Debug output redacts the value. |
| <span id="entry-source" />`source` | `Option<SecretSource>` | Host-side reference resolved at spawn time. Prefer the builder's [`source()`](#secret-source) method rather than populating a resolved entry manually. |
| <span id="entry-placeholder" />`placeholder` | `String` | What the guest sees. Non-empty, at most 1024 bytes, no NUL/CR/LF. |
| <span id="entry-allowed_hosts" />`allowed_hosts` | `Vec<HostPattern>` | Hosts allowed to receive the real value. |
| <span id="entry-substitution" />`substitution` | [`SecretSubstitution`](#secretsubstitution) | Where in the request the value may be substituted. |
| <span id="entry-violation_action" />`violation_action` | `Option<SecretViolationAction>` | Per-secret violation override. `None` falls back to the sandbox-wide action. |
| <span id="entry-passthrough_hosts" />`passthrough_hosts` | `Vec<HostPattern>` | Destinations allowed to receive the unchanged placeholder. Empty by default. |
| <span id="entry-require_tls_identity" />`require_tls_identity` | `bool` | Require verified TLS identity before substituting. Default: `true`. |

#### <span className="msb-recv">entry.</span><span className="msb-hn">validate()</span>

```rust theme={null}
fn validate(&self, secret_index: usize) -> Result<(), SecretConfigError>
```

Validate this entry's env var, allowed hosts, enabled substitution locations, and placeholder.

## Types

### SecretSubstitution

<p className="msb-backref">Used by <a href="#secretentry">SecretEntry.substitution</a></p>

Request locations enabled through the `substitute_in_*` methods. At least one must remain enabled. On credential-allowed hosts that pass the secret's identity checks, disabled locations forward the placeholder unchanged. Other destinations must match the secret's passthrough list or follow the violation action.

| Field | Type | Default | Description |
| - | - | - | - |
| `headers` | `bool` | `true` | Substitute in headers, including Basic authentication. |
| `query` | `bool` | `false` | Substitute in the URL query string. |
| `body` | `bool` | `false` | Substitute in supported HTTP/1 bodies. |

When body substitution is enabled, fixed-length HTTP/1 bodies up to 16 MiB update `Content-Length`; larger fixed-length bodies are blocked. Chunked HTTP/1 bodies are decoded and re-encoded with fresh chunk sizes. Encoded bodies pass through unchanged. HTTP/2 DATA-frame body substitution is unsupported, and matching body placeholders are blocked.

### SecretViolationAction

<p className="msb-backref">Used by <a href="#secretentry">SecretEntry</a> · <a href="#secret-violation_action">violation\_action()</a> · <a href="/sdk/rust/networking#network-secret_violation_action">NetworkBuilder::secret\_violation\_action()</a></p>

Blocking action when a placeholder cannot be substituted or passed through. A per-secret action overrides the network default. Passthrough is a host policy, not an enum variant.

| Variant | Description |
| - | - |
| `Block` | Block the request without logging. |
| `BlockAndLog` | Block and emit a warning on the host. This is the default. |
| `BlockAndTerminate` | Block, log an error, and shut down the sandbox. |

### SecretConfigError

<p className="msb-backref">returned by <a href="#secretentry">SecretEntry::validate()</a></p>

Why a secret entry failed validation. Each variant carries the offending `secret_index`. The placeholder ceiling is the public constant `MAX_SECRET_PLACEHOLDER_BYTES = 1024`.

| Variant | Description |
| - | - |
| `EmptyEnvVar` | The environment variable name is empty. |
| `EnvVarContainsEquals` | The environment variable name contains `=`. |
| `EnvVarContainsNul` | The environment variable name contains NUL. |
| `MissingAllowedHosts` | No allowed hosts were configured. |
| `MissingSubstitutionLocation` | Headers, query, and body substitution were all disabled. |
| `EmptyPlaceholder` | The placeholder is empty. |
| `PlaceholderTooLong` | The placeholder exceeds `MAX_SECRET_PLACEHOLDER_BYTES` (carries `actual_bytes`, `max_bytes`). |
| `PlaceholderContainsNul` | The placeholder contains NUL. |
| `PlaceholderContainsLineBreak` | The placeholder contains CR or LF. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.