docs/sdk/rust/secrets.mdx
See Secrets for how placeholder substitution works and usage examples.
HostPattern is used anywhere a secret policy needs to match the destination host. Allow-list patterns decide where the real secret value may be substituted; passthrough patterns decide where the placeholder may be forwarded unchanged.
| Variant | Builder methods | Matches |
|---|---|---|
HostPattern::Exact("api.example.com") | allow_host("api.example.com"), passthrough_host("api.example.com") | That exact SNI host |
HostPattern::Wildcard("*.example.com") | allow_host_pattern("*.example.com"), passthrough_host_pattern("*.example.com") | Hosts matching the wildcard pattern |
HostPattern::Any | allow_any_host_dangerous(true), passthrough_all_hosts(true) | Every host |
Passthrough host patterns do not make a secret eligible for substitution. They only prevent blocking when the guest sends the placeholder to a matching host, so the request is forwarded with the placeholder unchanged.
Exact and wildcard allow-list entries are pinned to observed DNS answers. For TLS-intercepted traffic, substitution requires the TLS SNI to match the pattern and the original guest destination IP to have been returned by the sandbox DNS interceptor for a matching hostname. HostPattern::Any is the only allow-list pattern that skips this DNS-to-IP pin. Empty allow-lists are rejected by the builder and treated as deny-all by the runtime.
Builder for configuring a secret's placeholder, allowed hosts, and injection scopes. Obtained through SandboxBuilder::secret(|s| s...). Each secret maps an environment variable to a real value that is only revealed when traffic goes to an allowed host via the TLS proxy.
fn allow_any_host_dangerous(self, i_understand_the_risk: bool) -> Self
Allow substitution on any host. This means any server the guest connects to will receive the real secret - effectively disabling host-based protection. Only use this when the secret is not sensitive or the sandbox network is fully locked down.
Parameters
| Name | Type | Description |
|---|---|---|
| i_understand_the_risk | bool | Must be true to take effect |
fn allow_host(self, host: impl Into<String>) -> Self
Add a host that is allowed to receive the real secret value. For TLS-intercepted traffic, the proxy matches this against the SNI (Server Name Indication) in the TLS ClientHello and verifies that the original destination IP was resolved for that host by the sandbox DNS interceptor. Can be called multiple times to allow multiple hosts.
Parameters
| Name | Type | Description |
|---|---|---|
| host | impl Into<String> | Exact hostname (e.g. "api.openai.com") |
fn allow_host_pattern(self, pattern: impl Into<String>) -> Self
Add a wildcard host pattern. The * matches any subdomain prefix.
Parameters
| Name | Type | Description |
|---|---|---|
| pattern | impl Into<String> | Wildcard pattern (e.g. "*.googleapis.com") |
fn env(self, var: impl Into<String>) -> Self
Set the environment variable name that will hold the placeholder inside the guest. The guest sees $MSB_<var> (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
| Name | Type | Description |
|---|---|---|
| var | impl Into<String> | Environment variable name (non-empty, no = or NUL) |
fn inject_basic_auth(self, enabled: bool) -> Self
Control whether Authorization: Basic <base64> credentials are decoded, substituted in the decoded user:password, and re-encoded. Orthogonal to inject_headers: this flag handles the encoded-credentials case for Basic scheme; inject_headers handles literal substitution in any header line, including non-Basic Authorization schemes (Bearer, Digest).
Parameters
| Name | Type | Description |
|---|---|---|
| enabled | bool | Default: true |
fn inject_body(self, enabled: bool) -> Self
Control whether the placeholder is replaced in the HTTP request body. When enabled on HTTP/1 requests with no body content encoding, the TLS proxy rewrites framed bodies safely: Content-Length bodies up to 16 MiB are buffered until complete and the Content-Length header is updated when the body size changes; larger fixed-length bodies are blocked rather than buffered unboundedly. Transfer-Encoding: chunked bodies are decoded and re-encoded with fresh chunk sizes. Chunk trailers are preserved; original chunk boundaries and chunk extensions are not preserved. Bodies with a non-identity Content-Encoding are forwarded unchanged. HTTP/2 DATA-frame body substitution is not supported yet; matching body placeholders are blocked instead of forwarded unchanged.
Parameters
| Name | Type | Description |
|---|---|---|
| enabled | bool | Default: false |
fn inject_headers(self, enabled: bool) -> Self
Control whether the placeholder is replaced anywhere in HTTP headers. This is the most common injection scope - covers Authorization: Bearer $MSB_... and similar patterns.
Parameters
| Name | Type | Description |
|---|---|---|
| enabled | bool | Default: true |
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).
Parameters
| Name | Type | Description |
|---|---|---|
| enabled | bool | Default: false |
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, may be up to 1024 bytes, and cannot contain NUL, CR, or LF.
Parameters
| Name | Type | Description |
|---|---|---|
| placeholder | impl Into<String> | Custom placeholder string: non-empty, up to 1024 bytes, no NUL/CR/LF |
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. Bypassed TLS is opaque and does not receive request-body or header substitution. Disable only if you know the traffic is safe and the connection path explicitly supports non-TLS substitution.
Parameters
| Name | Type | Description |
|---|---|---|
| enabled | bool | Default: true |
fn on_violation(
self,
f: impl FnOnce(ViolationActionBuilder) -> ViolationActionBuilder,
) -> Self
Configure violation behavior for this secret. This can override the sandbox-wide secret violation policy and can allow selected hosts to receive the placeholder unchanged:
.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")
})
)
Passthrough hosts do not receive the real secret value; substitution still only happens for hosts configured with allow_host() or allow_host_pattern().
If a per-secret passthrough policy does not match the request host, microsandbox falls back to the sandbox-wide secret violation action. A per-secret passthrough rule cannot weaken a stricter global default such as BlockAndTerminate.
For intercepted HTTP traffic, each request's authority must match the TLS SNI, ignoring case and an optional port. HTTP/1 uses the Host header; observed HTTP/2 uses the :authority pseudo-header. A request that uses SNI=front.example but Host: target.example or :authority: target.example is blocked before it reaches upstream. This check is applied per request on keep-alive connections and per stream for HTTP/2 HEADERS frames.
Secret substitution is not performed inside TLS bypass streams, HTTP CONNECT tunnels, SOCKS tunnels, or plain HTTP connections. Those paths can still be constrained with network policy, and placeholder violations are enforced only where microsandbox can inspect the request bytes.
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.
Parameters
| Name | Type | Description |
|---|---|---|
| value | impl Into<String> | The actual credential or token |
fn secret_env(self, env_var: impl Into<String>, value: impl Into<String>, allowed_host: impl Into<String>) -> Self
Convenience method on SandboxBuilder. Equivalent to .secret(|s| s.env(env_var).value(value).allow_host(allowed_host)). Uses default injection scopes (headers enabled, body disabled).
Parameters
| Name | Type | Description |
|---|---|---|
| env_var | impl Into<String> | Environment variable name (non-empty, no = or NUL) |
| value | impl Into<String> | Secret value |
| allowed_host | impl Into<String> | Allowed destination host |
Configured globally via NetworkBuilder::on_secret_violation() or per secret via SecretBuilder::on_violation(). Determines 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.
| Value | Description |
|---|---|
Block | Silently drop the request. The guest sees a connection reset. This is the default. |
BlockAndLog | Drop the request and emit a warning log on the host side. |
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 use the default secret violation action. |