---
title: Secrets
description: Rust SDK - Secret injection API reference
---

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](/sandboxes/secrets) for how placeholder substitution works and usage examples.

<div className="msb-glance">

  <p className="msb-gl"><span className="msb-dot static"></span>Static<span className="msb-ct">1</span></p>
  <a className="msb-row" href="#secretbuildernew"><span className="msb-rn">SecretBuilder::new()</span><span className="msb-rg">start a secret builder</span></a>

  <p className="msb-gl"><span className="msb-dot builder"></span>Builder · SecretBuilder<span className="msb-ct">13</span></p>
  <div className="msb-chiprow">
    <a className="msb-chip" href="#env">.env()</a>
    <a className="msb-chip" href="#value">.value()</a>
    <a className="msb-chip" href="#allow_host">.allow_host()</a>
    <a className="msb-chip" href="#allow_host_pattern">.allow_host_pattern()</a>
    <a className="msb-chip" href="#allow_any_host_dangerous">.allow_any_host_dangerous()</a>
    <a className="msb-chip" href="#placeholder">.placeholder()</a>
    <a className="msb-chip" href="#on_violation">.on_violation()</a>
    <a className="msb-chip" href="#require_tls_identity">.require_tls_identity()</a>
    <a className="msb-chip" href="#inject_headers">.inject_headers()</a>
    <a className="msb-chip" href="#inject_basic_auth">.inject_basic_auth()</a>
    <a className="msb-chip" href="#inject_query">.inject_query()</a>
    <a className="msb-chip" href="#inject_body">.inject_body()</a>
    <a className="msb-chip" href="#build">.build()</a>
  </div>

  <p className="msb-gl"><span className="msb-dot builder"></span>Shorthand · SandboxBuilder<span className="msb-ct">2</span></p>
  <a className="msb-row" href="#secret_env"><span className="msb-rn">.secret_env()</span><span className="msb-rg">one-line header-injected secret</span></a>
  <a className="msb-row" href="#secret_entry"><span className="msb-rn">.secret_entry()</span><span className="msb-rg">add a prebuilt SecretEntry</span></a>

  <p className="msb-gl"><span className="msb-dot type"></span>Types</p>
  <div className="msb-chiprow">
    <a className="msb-typepill" href="#hostpattern">HostPattern</a>
    <a className="msb-typepill" href="#secretentry">SecretEntry</a>
    <a className="msb-typepill" href="#secretinjection">SecretInjection</a>
    <a className="msb-typepill" href="#violationaction">ViolationAction</a>
    <a className="msb-typepill" href="#secretconfigerror">SecretConfigError</a>
    <a className="msb-typepill" href="/sdk/rust/networking#violationactionbuilder">ViolationActionBuilder</a>
  </div>

</div>

<p className="msb-label" id="typical-flow">Typical flow</p>

```rust
use microsandbox::Sandbox;

let sb = Sandbox::builder("agent")
    .image("python")
    .secret(|s| s
        .env("OPENAI_API_KEY")             // guest sees $MSB_OPENAI_API_KEY
        .value(std::env::var("OPENAI_API_KEY")?)
        .allow_host("api.openai.com"))     // real value only reaches this host
    .create()
    .await?;

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

## Static methods

---

#### <span className="msb-recv">SecretBuilder::</span><span className="msb-hn">new()</span>
<div className="msb-tags"><span className="msb-tag is-static">static</span></div>

```rust
fn new() -> SecretBuilder
```

Start building a secret with defaults: no env var, no value, no allowed hosts, the default [`SecretInjection`](#secretinjection) (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#secret) hands you a fresh builder and calls [`build()`](#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>

<Accordion title="Example">

```rust
use microsandbox::sandbox::SecretBuilder;

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

</Accordion>

---

## SecretBuilder

Builder for one secret's placeholder, allowed hosts, and injection scopes. Obtained through [`SandboxBuilder::secret(|s| s...)`](/sdk/rust/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()`](#env), [`value()`](#value), and at least one allowed host are required; [`build()`](#build) panics otherwise.

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

---

#### <span className="msb-recv">.</span><span className="msb-hn">env()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
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`](#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&lt;String&gt;</span></div>
    <div className="msb-param-desc">Environment variable name (non-empty, no <code>=</code> or NUL).</div>
  </div>
</div>

---

#### <span className="msb-recv">.</span><span className="msb-hn">value()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
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. **Required.**

<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&lt;String&gt;</span></div>
    <div className="msb-param-desc">The actual credential or token.</div>
  </div>
</div>

---

#### <span className="msb-recv">.</span><span className="msb-hn">placeholder()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
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&lt;String&gt;</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">.</span><span className="msb-hn">allow_host()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn allow_host(self, host: impl Into<String>) -> Self
```

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.

<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 Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Exact hostname, e.g. <code>"api.example.com"</code>. Becomes a <a className="msb-type" href="#hostpattern">HostPattern::Exact</a>.</div>
  </div>
</div>

<Accordion title="Example">

```rust
.secret(|s| s
    .env("STRIPE_KEY")
    .value(stripe_key)
    .allow_host("api.stripe.com")
    .allow_host("files.stripe.com"))
```

</Accordion>

---

#### <span className="msb-recv">.</span><span className="msb-hn">allow_host_pattern()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn allow_host_pattern(self, pattern: impl Into<String>) -> Self
```

Add a wildcard host pattern. A `*.suffix` pattern matches the suffix itself and any single-or-multi label subdomain of it.

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

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>pattern</code><span className="msb-type">impl Into&lt;String&gt;</span></div>
    <div className="msb-param-desc">Wildcard pattern, e.g. <code>"*.googleapis.com"</code>. Becomes a <a className="msb-type" href="#hostpattern">HostPattern::Wildcard</a>.</div>
  </div>
</div>

---

#### <span className="msb-recv">.</span><span className="msb-hn">allow_any_host_dangerous()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
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 className="msb-recv">.</span><span className="msb-hn">on_violation()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn on_violation(
    self,
    f: impl FnOnce(ViolationActionBuilder) -> ViolationActionBuilder,
) -> Self
```

Configure violation behavior for this secret. Overrides the sandbox-wide secret violation policy and can let selected hosts receive the placeholder unchanged. See [`ViolationActionBuilder`](/sdk/rust/networking#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 [`allow_host()`](#allow_host) or [`allow_host_pattern()`](#allow_host_pattern). When a per-secret passthrough policy does not match the request host, microsandbox falls back to the sandbox-wide secret violation action.

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

<div className="msb-params">
  <div className="msb-param">
    <div className="msb-param-key"><code>f</code><a className="msb-type" href="/sdk/rust/networking#violationactionbuilder">FnOnce(ViolationActionBuilder)</a></div>
    <div className="msb-param-desc">Configure the per-secret violation action.</div>
  </div>
</div>

<Accordion title="Example">

```rust
.secret(|s| s
    .env("API_KEY")
    .value(api_key)
    .allow_host("api.github.com")
    .on_violation(|v| v
        .block_and_log()
        .passthrough_host("api.anthropic.com")))
```

</Accordion>

---

#### <span className="msb-recv">.</span><span className="msb-hn">require_tls_identity()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
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">.</span><span className="msb-hn">inject_headers()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn inject_headers(self, enabled: bool) -> Self
```

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

<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">.</span><span className="msb-hn">inject_basic_auth()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn inject_basic_auth(self, enabled: bool) -> Self
```

Control whether `Authorization: Basic <base64>` credentials are decoded, substituted in the decoded `user:password`, then re-encoded. Orthogonal to [`inject_headers`](#inject_headers): this flag handles the encoded-credentials case for the Basic scheme; `inject_headers` handles literal substitution in any header line, including non-Basic Authorization schemes (`Bearer`, `Digest`). 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 inside Basic Auth credentials. Default: <code>true</code>.</div>
  </div>
</div>

---

#### <span className="msb-recv">.</span><span className="msb-hn">inject_query()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn inject_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">.</span><span className="msb-hn">inject_body()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn inject_body(self, enabled: bool) -> Self
```

Control whether the placeholder is replaced in HTTP/1 request bodies. 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">.</span><span className="msb-hn">build()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn build(self) -> SecretEntry
```

Materialize the [`SecretEntry`](#secretentry). Called for you by [`SandboxBuilder::secret`](/sdk/rust/sandbox#secret), so you rarely call it directly. If [`placeholder`](#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> or <code>value</code> was not set, or if the allow list is empty. Use <a className="msb-type" href="#allow_any_host_dangerous">allow_any_host_dangerous(true)</a> for an explicit any-host secret.</div>
  </div>
</div>

---

## Shorthand

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">.</span><span className="msb-hn">secret_env()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn secret_env(
    self,
    env_var: impl Into<String>,
    value: impl Into<String>,
    allowed_host: impl Into<String>,
) -> Self
```

Equivalent to `.secret(|s| s.env(env_var).value(value).allow_host(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&lt;String&gt;</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&lt;String&gt;</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&lt;String&gt;</span></div>
    <div className="msb-param-desc">Allowed destination host (exact match).</div>
  </div>
</div>

<Accordion title="Example">

```rust
let sb = Sandbox::builder("agent")
    .image("python")
    .secret_env("OPENAI_API_KEY", api_key, "api.openai.com")
    .create()
    .await?;
```

</Accordion>

---

#### <span className="msb-recv">.</span><span className="msb-hn">secret_entry()</span>
<div className="msb-tags"><span className="msb-tag is-builder">builder</span></div>

```rust
fn secret_entry(self, entry: SecretEntry) -> Self
```

Add a pre-built [`SecretEntry`](#secretentry) directly. This is the escape hatch when you already have a materialized entry, for example one produced by [`SecretBuilder::build()`](#build), loaded from configuration, or constructed by hand for full control over the serialized shape. Both [`secret()`](/sdk/rust/sandbox#secret) and [`secret_env()`](#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>

<Accordion title="Example">

```rust
use microsandbox::sandbox::SecretBuilder;

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

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

</Accordion>

---

## Types

### HostPattern
<div className="msb-tags"><span className="msb-tag is-type">enum</span></div>

<p className="msb-backref">used by <a href="#secretentry">SecretEntry</a> · <a href="#allow_host">allow_host()</a> · <a href="#allow_host_pattern">allow_host_pattern()</a> · <a href="/sdk/rust/networking#violationaction">ViolationAction</a></p>

Matches a destination host anywhere a secret policy needs one. Allow-list patterns decide where the real value may be substituted; passthrough patterns (on [`ViolationAction`](#violationaction)) decide where the placeholder may be forwarded unchanged. Passthrough patterns never make a secret eligible for substitution. Exact and wildcard allow-list entries are pinned to observed DNS answers and TLS identity; `Any` is the only allow-list pattern that skips this pinning.

| Variant | Builder methods | Matches |
|---------|-----------------|---------|
| `Exact(String)` | [`allow_host()`](#allow_host), `passthrough_host()` | That exact host, ASCII case-insensitive |
| `Wildcard(String)` | [`allow_host_pattern()`](#allow_host_pattern), `passthrough_host_pattern()` | The suffix and its subdomains, e.g. `*.example.com` |
| `Any` | [`allow_any_host_dangerous(true)`](#allow_any_host_dangerous), `passthrough_all_hosts(true)` | Every host |

| Method | Signature | Description |
|--------|-----------|-------------|
| `matches` | `fn matches(&self, hostname: &str) -> bool` | Whether `hostname` matches this pattern (ASCII case-insensitive). |

### SecretEntry
<div className="msb-tags"><span className="msb-tag is-type">struct</span></div>

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

The materialized, serializable form of a secret that is handed to the network engine. The real `value` is redacted from the `Debug` output. Built for you by [`SecretBuilder`](#secretbuilder); construct it directly only when you need full control over the serialized shape, then pass it to [`secret_entry()`](#secret_entry).

| Field | Type | Description |
|-------|------|-------------|
| `env_var` | `String` | Environment variable holding the placeholder. Non-empty, no `=` or NUL. |
| `value` | `String` | The real secret value (never enters the guest). |
| `placeholder` | `String` | What the guest sees. Non-empty, at most 1024 bytes, no NUL/CR/LF. |
| `allowed_hosts` | `Vec<HostPattern>` | Hosts allowed to receive the real value. |
| `injection` | [`SecretInjection`](#secretinjection) | Where in the request the value may be substituted. |
| `on_violation` | `Option<ViolationAction>` | Per-secret violation override. `None` falls back to the sandbox-wide action. |
| `require_tls_identity` | `bool` | Require verified TLS identity before substituting. Default: `true`. |

| Method | Signature | Description |
|--------|-----------|-------------|
| `validate` | `fn validate(&self, secret_index: usize) -> Result<(), SecretConfigError>` | Validate this entry's env var, allowed hosts, and placeholder. |

### SecretInjection
<div className="msb-tags"><span className="msb-tag is-type">struct</span></div>

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

Which parts of an outbound HTTP request the placeholder may be substituted in. Set through the [`inject_*`](#inject_headers) methods on [`SecretBuilder`](#secretbuilder).

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `headers` | `bool` | `true` | Substitute anywhere in HTTP headers. |
| `basic_auth` | `bool` | `true` | Substitute inside decoded `Authorization: Basic` credentials. |
| `query_params` | `bool` | `false` | Substitute in the URL query string. |
| `body` | `bool` | `false` | Substitute in HTTP/1 request bodies (see [`inject_body()`](#inject_body) for limits). |

### ViolationAction
<div className="msb-tags"><span className="msb-tag is-type">enum</span></div>

<p className="msb-backref">used by <a href="#secretentry">SecretEntry</a> · <a href="#on_violation">on_violation()</a> · <a href="/sdk/rust/networking#violationactionbuilder">ViolationActionBuilder</a></p>

What happens when the guest sends a request containing a secret placeholder to a host that is **not** in the secret's substitution allow list. Built through [`ViolationActionBuilder`](/sdk/rust/networking#violationactionbuilder), set globally via [`NetworkBuilder::on_secret_violation()`](/sdk/rust/networking#on_secret_violation) or per secret via [`SecretBuilder::on_violation()`](#on_violation). Also documented on the [Networking](/sdk/rust/networking#violationaction) page.

| Variant | Description |
|---------|-------------|
| `Block` | Silently drop the request. The guest sees a connection reset. |
| `BlockAndLog` | Drop the request and emit a warning log on the host side. This is the default. |
| `BlockAndTerminate` | Drop the request, log an error, and shut down the entire sandbox. |
| `Passthrough(Vec<HostPattern>)` | Forward matching hosts with the placeholder unchanged. Non-matching hosts fall back to the default secret violation action. |

### SecretConfigError
<div className="msb-tags"><span className="msb-tag is-type">enum</span></div>

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