Documentation Index
Fetch the complete documentation index at: https://superradcompanyinc-mintlify-changelog-1777648095.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
See Networking for conceptual overview and TLS Interception for TLS proxy details.
NetworkPolicy
A network access policy consisting of two per-direction defaults and an ordered list of rules evaluated first-match-wins per direction.
| Field | Type | Description |
|---|
| default_egress | Action | Action when no egress-applicable rule matches |
| default_ingress | Action | Action when no ingress-applicable rule matches |
| rules | Vec<Rule> | Ordered list of rules; first match wins |
The default NetworkPolicy::default() is default_egress = Deny + implicit allow@public, default_ingress = Allow + no rules. This preserves today’s public_only egress behavior plus today’s unfiltered published-port behavior. See the defaults rationale for why the defaults are asymmetric.
Presets
Static methods that return pre-configured policies. Most callers should use the builder; presets exist as compat shims for the legacy API.
NetworkPolicy::allow_all() // unrestricted
NetworkPolicy::none() // deny both directions
NetworkPolicy::public_only() // egress=Deny + allow Public; ingress=Allow (today's default)
NetworkPolicy::non_local() // egress=Deny + allow Public+Private; ingress=Allow
NetworkPolicy::builder()
The fluent builder is the primary construction path. String inputs (.ip(&str), .cidr(&str), .domain(&str), .domain_suffix(&str)) are stored raw and parsed at .build() time; the chain stays clean. The first parse / validation failure surfaces as BuildError.
use microsandbox::NetworkPolicy;
let policy = NetworkPolicy::builder()
.default_deny()
.egress(|e| e.tcp().port(443).allow_public().allow_private())
.rule(|r| r.any().deny().ip("198.51.100.5"))
.build()?;
Top-level methods
| Method | Effect |
|---|
.default_allow() | Set both default_egress and default_ingress to Allow |
.default_deny() | Set both to Deny |
.default_egress(Action) | Per-direction override |
.default_ingress(Action) | Per-direction override |
.rule(|r| ...) | Multi-rule batch; direction set inside via .egress() / .ingress() / .any() |
.egress(|e| ...) | Sugar for .rule(|r| { r.egress(); ... }) |
.ingress(|i| ...) | Sugar with direction pre-set to Ingress |
.any(|a| ...) | Sugar with direction pre-set to Any (rule applies in either direction) |
.build() | Result<NetworkPolicy, BuildError> |
The closure signature is FnOnce(&mut RuleBuilder) -> &mut RuleBuilder. A chain ending in any rule-adder (.allow_public(), .deny().ip(...), etc.) returns the builder reference and satisfies the bound. Multi-statement bodies end with an explicit r return.
Inside the closure: RuleBuilder
Inside .rule(|r| ...) (or any of the direction sub-builders), state setters and rule-adders interleave freely. State accumulates eagerly across the closure; each rule-adder commits a rule using the state at that point.
State setters:
| Method | Effect |
|---|
.egress() / .ingress() / .any() | Set direction. Last-write-wins. .any() makes rules apply in both directions |
.tcp() / .udp() / .icmpv4() / .icmpv6() | Add to the protocols set (set semantics; duplicates dedupe). ICMP is egress-only |
.port(u16) | Add a single port to the ports set |
.port_range(u16, u16) | Add an inclusive range. lo > hi records BuildError::InvalidPortRange |
Per-category rule-adders (each commits one rule using the current state):
| Method | Group |
|---|
.allow_public() / .deny_public() | Group::Public (complement of named categories) |
.allow_private() / .deny_private() | Group::Private (RFC1918 + ULA + CGN) |
.allow_loopback() / .deny_loopback() | Group::Loopback (127.0.0.0/8, ::1) — see the loopback-vs-host watch-out |
.allow_link_local() / .deny_link_local() | Group::LinkLocal (169.254.0.0/16, fe80::/10, excluding metadata) |
.allow_meta() / .deny_meta() | Group::Metadata (169.254.169.254). Dangerous on cloud hosts |
.allow_multicast() / .deny_multicast() | Group::Multicast |
.allow_host() / .deny_host() | Group::Host (per-sandbox gateway IPs that back host.microsandbox.internal) |
Bulk-domain shortcuts (one rule per name, inheriting current direction/protocol/port state):
| Method | Effect |
|---|
.allow_domains([..]) / .deny_domains([..]) | One Destination::Domain rule per name. Lazy-parse: invalid names surface as BuildError::InvalidDomain from .build() |
.allow_domain_suffixes([..]) / .deny_domain_suffixes([..]) | One Destination::DomainSuffix rule per suffix. Same lazy-parse contract |
NetworkPolicy::builder()
.default_allow()
.egress(|e| e
.deny_domains(["evil.com", "tracker.example"])
.deny_domain_suffixes([".ads.example", ".doubleclick.net"]))
.build()?
Composite sugar:
| Method | Effect |
|---|
.allow_local() / .deny_local() | Adds three rules atomically: Loopback + LinkLocal + Host. Metadata is explicitly not included (cloud-IMDS opt-in only via .allow_meta()) |
Explicit-rule sub-builder (.allow() / .deny() open it):
.rule(|r| r.egress().tcp().port(443).allow().domain("api.example.com"))
.rule(|r| r.any().deny().cidr("198.51.100.0/24"))
Returns an ExplicitRuleBuilder requiring a destination call (.ip / .cidr / .domain / .domain_suffix / .group / .any) to commit. Dropping without a destination call adds no rule (the type is #[must_use]).
State accumulation
State is not reset between rule-adders within a closure. Callers wanting different state per rule use separate .rule() calls or interleave state setters between adders:
.rule(|r| r.egress()
.tcp().port(443).allow_public() // rule 1: egress, TCP, 443, allow Public
.udp().allow_private()) // rule 2: egress, [TCP, UDP], 443, allow Private
// (UDP added; TCP and 443 still in state)
Shadow detection
At .build() time, the builder walks the rules list and emits a tracing::warn! for any rule whose match set is fully contained in an earlier rule’s match set in a compatible direction. Coverage is Ip / Cidr / Group destinations; domain shadowing is intentionally out of scope (depends on runtime DNS state).
NetworkBuilder
Builder for configuring the sandbox’s network stack. Used in SandboxBuilder::network(|n| n...).
policy()
fn policy(self, policy: NetworkPolicy) -> Self
Set the network access policy. Override the default with a builder-constructed value:
.network(|n| n.policy(NetworkPolicy::builder().default_deny().build()?))
dns()
fn dns(self, f: impl FnOnce(DnsBuilder) -> DnsBuilder) -> Self
Configure DNS interception. Errors accumulated by DnsBuilder cascade up; the outermost SandboxBuilder::build() surfaces them as MicrosandboxError::NetworkBuilder(BuildError).
.network(|n| n
.dns(|d| d
.nameservers(["1.1.1.1".parse::<Nameserver>()?])
.query_timeout_ms(3000)
)
)
max_connections()
fn max_connections(self, max: usize) -> Self
Limit the maximum number of concurrent network connections from the sandbox.
trust_host_cas()
fn trust_host_cas(self, enabled: bool) -> Self
Whether to ship the host’s trusted root CAs into the guest at boot. Default: false. Opt in when egress HTTPS inside the sandbox needs to work behind corporate MITM proxies (Cloudflare Warp Zero Trust, Zscaler, Netskope, etc.) whose gateway CA is installed on the host but unknown to the guest’s stock Mozilla bundle.
tls()
fn tls(self, f: impl FnOnce(TlsBuilder) -> TlsBuilder) -> Self
Configure TLS interception. See TlsBuilder.
on_secret_violation()
fn on_secret_violation(self, action: ViolationAction) -> Self
Set the action taken when a secret placeholder is detected in traffic destined for a host not in the secret’s allow list.
DnsBuilder
Builder for DNS interception settings. Used in NetworkBuilder::dns(|d| d...). Owns rebind protection, nameserver pinning, and the per-query timeout.
rebind_protection()
fn rebind_protection(self, enabled: bool) -> Self
When enabled, DNS responses that resolve to private IP addresses are blocked. This prevents DNS rebinding attacks. Default: true.
nameservers()
fn nameservers<I>(self, nameservers: I) -> Self
where
I: IntoIterator,
I::Item: Into<Nameserver>,
Set the upstream nameservers to forward DNS queries to. Replaces any previously-set nameservers. Each element is convertible into Nameserver (SocketAddr, IpAddr, or a parsed string via "dns.google:53".parse::<Nameserver>()?).
query_timeout_ms()
fn query_timeout_ms(self, ms: u64) -> Self
Set the per-DNS-query timeout in milliseconds. Default: 5000.
TlsBuilder
Builder for TLS interception settings. Used in NetworkBuilder::tls(|t| t...).
| Method | Effect |
|---|
.bypass(pattern) | Skip TLS interception for this domain. Supports *.suffix wildcards |
.intercepted_ports(ports) | TCP ports where interception is active. Default: [443] |
.intercept_ca_cert(path) / .intercept_ca_key(path) | Provide a stable CA across sandbox restarts |
.upstream_ca_cert(path) | Trust an additional CA when verifying upstream servers |
.verify_upstream(bool) | Whether the proxy verifies upstream server certs. Default: true |
.block_quic(bool) | Block QUIC/HTTP3 on intercepted ports. Default: true |
Types
Action
| Value | Description |
|---|
Allow | Permit the traffic |
Deny | Drop the traffic silently |
Wire format: "allow" / "deny".
Direction
| Value | Description |
|---|
Egress | Traffic leaving the sandbox |
Ingress | Traffic entering the sandbox (via published ports) |
Any | Rule applies in either direction |
Wire format: "egress" / "ingress" / "any".
Destination
| Variant | Description |
|---|
Any | Match any address |
Cidr(IpNetwork) | Match a CIDR range (e.g. 10.0.0.0/8); single IPs are stored as /32 or /128 |
Domain(DomainName) | Match an exact domain (e.g. example.com) |
DomainSuffix(DomainName) | Match the apex domain and every subdomain (e.g. example.com and api.example.com) |
Group(DestinationGroup) | Match a predefined address group |
DestinationGroup
| Value | Wire format | Matches |
|---|
Public | "public" | Complement of the other categories — every address not in any other group |
Private | "private" | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, fc00::/7 |
Loopback | "loopback" | 127.0.0.0/8, ::1 (the guest’s own loopback, not the host) |
LinkLocal | "link_local" | 169.254.0.0/16, fe80::/10 (excludes metadata) |
Metadata | "metadata" | Cloud metadata endpoints (169.254.169.254) |
Multicast | "multicast" | 224.0.0.0/4, ff00::/8 |
Host | "host" | Per-sandbox gateway IPs that back host.microsandbox.internal |
DomainName
A validated DNS name. Construction goes through str::parse (or TryFrom<String>), which delegates to hickory_proto::rr::Name and canonicalizes the input (lowercased ASCII, leading and trailing dots stripped) so rule matching is a byte-wise compare against the DNS cache. Invalid inputs return a DomainNameError.
use microsandbox_network::policy::{Destination, DomainName};
let exact: DomainName = "PyPI.Org.".parse()?; // -> "pypi.org"
let suffix: DomainName = ".example.com".parse()?; // -> "example.com"
Labels follow the permissive DNS grammar (RFC 2181 §11), so underscore-prefixed names like _service._tcp.example.com are accepted.
The builder methods (.domain(&str), .domain_suffix(&str)) take strings and parse them lazily at .build() — callers don’t need to construct DomainName directly.
Protocol
| Value | Wire format |
|---|
Tcp | "tcp" |
Udp | "udp" |
Icmpv4 | "icmpv4" |
Icmpv6 | "icmpv6" |
ICMP protocols are egress-only. A rule with direction Ingress or Any carrying an ICMP protocol fails build with BuildError::IngressDoesNotSupportIcmp.
PortRange
| Method | Description |
|---|
PortRange::single(port) | Match a single port |
PortRange::range(start, end) | Match an inclusive range |
Rule
A single network policy rule.
| Field | Type | Description |
|---|
| direction | Direction | Which evaluator considers this rule |
| destination | Destination | Target filter (egress destination / ingress source) |
| protocols | Vec<Protocol> | Set semantics; empty = any protocol |
| ports | Vec<PortRange> | Set semantics; empty = any port. Always guest-side (egress destination port / ingress listening port) |
| action | Action | What to do on match |
Convenience constructors:
| Method | Description |
|---|
Rule::allow_egress(destination) | Allow rule, direction Egress |
Rule::deny_egress(destination) | Deny rule, direction Egress |
Rule::allow_ingress(destination) | Allow rule, direction Ingress |
Rule::deny_ingress(destination) | Deny rule, direction Ingress |
Rule::allow_any(destination) | Allow rule, direction Any |
Rule::deny_any(destination) | Deny rule, direction Any |
ExplicitRuleBuilder
Returned by RuleBuilder::allow() / ::deny(). Requires exactly one destination method call to commit the rule. The type is #[must_use] — dropping without a call adds no rule.
| Method | Effect |
|---|
.ip(impl Into<String>) | Commit with Destination::Cidr of the IP as /32 or /128 |
.cidr(impl Into<String>) | Commit with Destination::Cidr |
.domain(impl Into<String>) | Commit with Destination::Domain |
.domain_suffix(impl Into<String>) | Commit with Destination::DomainSuffix |
.group(DestinationGroup) | Commit with Destination::Group |
.any() | Commit with Destination::Any |
BuildError
Errors surfaced by the builders’ .build() methods. The same enum covers NetworkPolicy::builder(), DnsBuilder, and NetworkBuilder (the network and DNS builders accumulate errors lazily; the first failure surfaces from the outermost .build() in the chain).
| Variant | Cause |
|---|
DirectionNotSet { rule_index } | A rule was committed without .egress() / .ingress() / .any() |
MissingDestination { rule_index } | .allow() or .deny() was called but no destination method followed |
InvalidIp { rule_index, raw } | .ip(&str) got an unparseable value |
InvalidCidr { rule_index, raw } | .cidr(&str) got an unparseable value |
InvalidDomain { rule_index, raw, source } | .domain or .domain_suffix got a value that failed DomainName parse |
InvalidPortRange { rule_index, lo, hi } | .port_range(lo, hi) had lo > hi |
IngressDoesNotSupportIcmp { rule_index } | ICMP protocol on a non-egress rule |
Inside SandboxBuilder::build(), BuildError is wrapped as MicrosandboxError::NetworkBuilder(BuildError).
ViolationAction
Action taken when a secret placeholder is sent to a disallowed host.
| 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. |