Skip to main content

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.

See Networking for conceptual overview and TLS Interception for TLS proxy details.

NetworkPolicy

A list of rules plus two per-direction defaults, evaluated first-match-wins. Use a static factory for a preset, build a literal, or chain through NetworkPolicy.builder() for the fluent builder:
import { NetworkPolicy, Rule, Destination } from "microsandbox";

// Custom policy literal
const custom = {
  defaultEgress: "deny",
  defaultIngress: "allow",
  rules: [
    Rule.allowEgress(Destination.domain("api.openai.com")),
    Rule.denyEgress(Destination.group("metadata")),
  ],
};

// Or via the builder
const built = NetworkPolicy.builder()
  .defaultDeny()
  .egress((e) => e.tcp().port(443).allowPublic())
  .build();
Pass any of these to NetworkBuilder.policy(...).

Rule order matters

The first matching rule wins, so a broad rule placed before a narrow one swallows it:
const policy = {
  defaultEgress: "deny",
  defaultIngress: "allow",
  rules: [
    Rule.allowEgress(Destination.cidr("10.0.0.0/8")),  // matches everything in 10.x
    Rule.denyEgress(Destination.cidr("10.0.0.5/32")),  // never reached
  ],
};
Put specific rules before general ones.

Shadow detection

NetworkPolicyBuilder.build() walks the rules and warns when a rule is fully covered by an earlier one in the same direction. Only ip, cidr, and group destinations are checked; domain coverage depends on runtime DNS and is skipped. Builds still succeed; the warning surfaces as a host-side tracing::warn! from the rust core:
WARN rule #1 (Egress Cidr(10.0.0.5/32) Deny) is shadowed by rule #0 (Egress Cidr(10.0.0.0/8) Allow); to narrow, place the more specific rule first
Policy literals constructed via Rule.allowEgress(...) etc. do not run through the builder and skip this check.

State accumulation

State setters (.tcp(), .port(), etc.) inside a RuleBuilder callback carry into every rule-adder that follows. State is not reset between adders:
NetworkPolicy.builder()
  .egress((r) => r
    .tcp().port(443).allowPublic()    // rule 1: egress, TCP, 443, allow Public
    .udp().allowPrivate()             // rule 2: egress, [TCP, UDP], 443, allow Private
  )
  .build();
Use separate .rule() / .egress() callbacks for rules that need different state.

NetworkPolicy.allowAll()

static allowAll(): NetworkPolicy
Unrestricted network access, including to private addresses and the host machine.

NetworkPolicy.builder()

static builder(): NetworkPolicyBuilder
Start a fluent NetworkPolicyBuilder. Equivalent to new NetworkPolicyBuilder().

NetworkPolicy.none()

static none(): NetworkPolicy
Deny all traffic. The guest is fully offline. exec and fs still work since they use the host-guest channel, not the network.

NetworkPolicy.nonLocal()

static nonLocal(): NetworkPolicy
Allow egress to public + private (LAN) destinations; ingress allowed by default.

NetworkPolicy.publicOnly()

static publicOnly(): NetworkPolicy
Block private address ranges and cloud metadata endpoints. Allow everything else. This is the default policy.

NetworkPolicyBuilder

Fluent builder for NetworkPolicy. The closure passed to .rule() / .egress() / .ingress() / .any() receives a RuleBuilder; state setters and rule-adders chain freely. The first parse / validation failure surfaces from .build().
import { NetworkPolicy } from "microsandbox";

const policy = NetworkPolicy.builder()
  .defaultDeny()
  .egress((e) => e.tcp().port(443).allowPublic().allowPrivate())
  .rule((r) => r.any().deny((d) => d.ip("198.51.100.5")))
  .build();

any()

any(configure: (r: RuleBuilder) => RuleBuilder): this
Sugar for rule() with direction pre-set to Any. Rules committed inside apply in both directions.

build()

build(): NetworkPolicy
Materialize the accumulated state into a NetworkPolicy. Lazily parses every recorded .ip() / .cidr() / .domain() / .domainSuffix() input, validates direction-set and ICMP-egress-only invariants, and emits a host-side warning for each shadowed rule pair.

defaultAllow()

defaultAllow(): this
Set both defaultEgress and defaultIngress to "allow".

defaultDeny()

defaultDeny(): this
Set both defaultEgress and defaultIngress to "deny".

defaultEgress()

defaultEgress(action: "allow" | "deny"): this
Per-direction override for the egress default action. Parameters
NameTypeDescription
action"allow" | "deny"Default action for egress

defaultIngress()

defaultIngress(action: "allow" | "deny"): this
Per-direction override for the ingress default action. Parameters
NameTypeDescription
action"allow" | "deny"Default action for ingress

egress()

egress(configure: (r: RuleBuilder) => RuleBuilder): this
Sugar for rule() with direction pre-set to Egress.

ingress()

ingress(configure: (r: RuleBuilder) => RuleBuilder): this
Sugar for rule() with direction pre-set to Ingress.

rule()

rule(configure: (r: RuleBuilder) => RuleBuilder): this
Open a multi-rule batch closure. Direction must be set inside via .egress(), .ingress(), or .any() before any rule-adder.

RuleBuilder

Per-rule-batch builder. Lives only inside the callback passed to .rule() / .egress() / .ingress() / .any() on a NetworkPolicyBuilder. State setters and rule-adders interleave freely; state accumulates eagerly across the callback (see State accumulation).

Direction setters

Last-write-wins. ICMP rule-adders are egress-only at build time.

any()

any(): this
Set direction to Any for subsequent rule-adders. Rules committed after this apply in both directions.

egress()

egress(): this
Set direction to Egress for subsequent rule-adders.

ingress()

ingress(): this
Set direction to Ingress for subsequent rule-adders.

Protocol setters

Protocols accumulate as a set; duplicates dedupe.

icmpv4()

icmpv4(): this
Add Icmpv4 to the protocols set. Egress-only; an ICMP rule on an Ingress or Any direction fails build.

icmpv6()

icmpv6(): this
Add Icmpv6 to the protocols set. Egress-only; same rules as icmpv4().

tcp()

tcp(): this
Add Tcp to the protocols set.

udp()

udp(): this
Add Udp to the protocols set.

Port setters

Ports accumulate as a set; duplicates dedupe. Always guest-side (egress destination port / ingress listening port).

port()

port(port: number): this
Add a single port to the ports set. Parameters
NameTypeDescription
portnumberPort number 0..=65535

portRange()

portRange(lo: number, hi: number): this
Add an inclusive port range. lo > hi records an error surfaced at .build() time. Parameters
NameTypeDescription
lonumberLower bound (inclusive)
hinumberUpper bound (inclusive)

ports()

ports(ports: number[]): this
Add multiple single ports. Equivalent to calling port() once per element.

Group rule-adders

Each adder commits one rule using the current state and the named destination group.

allowHost()

allowHost(): this
Allow the Host group: per-sandbox gateway IPs that back host.microsandbox.internal. This is the right shortcut for “let the sandbox reach my host’s localhost”, not allowLoopback().

allowLinkLocal()

allowLinkLocal(): this
Allow the LinkLocal group (169.254.0.0/16, fe80::/10). Excludes the metadata IP 169.254.169.254.

allowLoopback()

allowLoopback(): this
Allow the Loopback group (127.0.0.0/8, ::1). The guest’s own loopback, not the host. To reach a service on the host’s localhost, use allowHost() instead. See the loopback-vs-host watch-out.

allowMeta()

allowMeta(): this
Allow the Metadata group (169.254.169.254). Dangerous on cloud hosts (exposes IAM credentials).

allowMulticast()

allowMulticast(): this
Allow the Multicast group (224.0.0.0/4, ff00::/8).

allowPrivate()

allowPrivate(): this
Allow the Private group (RFC1918 + ULA + CGN).

allowPublic()

allowPublic(): this
Allow the Public group (complement of named categories: every IP not in any other group).

denyHost()

denyHost(): this
Deny the Host group.

denyLinkLocal()

denyLinkLocal(): this
Deny the LinkLocal group.

denyLoopback()

denyLoopback(): this
Deny the Loopback group.

denyMeta()

denyMeta(): this
Deny the Metadata group.

denyMulticast()

denyMulticast(): this
Deny the Multicast group.

denyPrivate()

denyPrivate(): this
Deny the Private group.

denyPublic()

denyPublic(): this
Deny the Public group.

Domain rule-adders

Singular forms add one rule; plural forms add one rule per element.

allowDomain()

allowDomain(name: string): this
Add one Destination::Domain allow rule. Parameters
NameTypeDescription
namestringFully qualified domain name

allowDomainSuffix()

allowDomainSuffix(suffix: string): this
Add one Destination::DomainSuffix allow rule. Matches the apex and any subdomain. Parameters
NameTypeDescription
suffixstringDomain suffix

allowDomainSuffixes()

allowDomainSuffixes(suffixes: string[]): this
Add one Destination::DomainSuffix allow rule per suffix.

allowDomains()

allowDomains(names: string[]): this
Add one Destination::Domain allow rule per name.

denyDomain()

denyDomain(name: string): this
Add one Destination::Domain deny rule. Parameters
NameTypeDescription
namestringFully qualified domain name

denyDomainSuffix()

denyDomainSuffix(suffix: string): this
Add one Destination::DomainSuffix deny rule. Matches the apex and any subdomain. Parameters
NameTypeDescription
suffixstringDomain suffix

denyDomainSuffixes()

denyDomainSuffixes(suffixes: string[]): this
Add one Destination::DomainSuffix deny rule per suffix.

denyDomains()

denyDomains(names: string[]): this
Add one Destination::Domain deny rule per name.

Composite rule-adders


allowLocal()

allowLocal(): this
Add three allow rules atomically: Loopback + LinkLocal + Host. Each uses the callback’s current state. Metadata is intentionally not included; opt in via allowMeta() separately.

denyLocal()

denyLocal(): this
Add three deny rules atomically: Loopback + LinkLocal + Host. Metadata is intentionally not included.

Explicit-destination rule-adders

.allow() / .deny() open a RuleDestinationBuilder callback. Exactly one destination call commits the rule.
NetworkPolicy.builder()
  .egress((r) => r
    .tcp().port(443).allow((d) => d.domain("api.example.com"))
    .deny((d) => d.cidr("198.51.100.0/24"))
  )
  .build();

allow()

allow(configure: (d: RuleDestinationBuilder) => RuleDestinationBuilder): this
Begin an explicit-destination rule with action Allow.

deny()

deny(configure: (d: RuleDestinationBuilder) => RuleDestinationBuilder): this
Begin an explicit-destination rule with action Deny.

Rule

Factory for individual policy rules. Each method returns a single Rule value.

Rule.allowAny()

static allowAny(destination: Destination): Rule
Allow rule with direction any (matches in either direction). Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.allowEgress()

static allowEgress(destination: Destination): Rule
Allow rule with direction egress. Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.allowIngress()

static allowIngress(destination: Destination): Rule
Allow rule with direction ingress. Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.denyAny()

static denyAny(destination: Destination): Rule
Deny rule with direction any (matches in either direction). Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.denyEgress()

static denyEgress(destination: Destination): Rule
Deny rule with direction egress. Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.denyIngress()

static denyIngress(destination: Destination): Rule
Deny rule with direction ingress. Parameters
NameTypeDescription
destinationDestinationTarget filter

Rule.allowDns()

static allowDns(): Rule
Allow plain DNS (UDP/53 and TCP/53) to the sandbox gateway, i.e. the in-process DNS forwarder. The standard one-liner for opening DNS under a deny-by-default policy. See DNS as egress for the underlying semantics. DoT (TCP/853) is intentionally not included; add an explicit Group::Host tcp/853 allow rule if needed (and pair with TLS interception).

Destination

Factory for rule destinations.

Destination.any()

static any(): Destination
Match any destination.

Destination.cidr()

static cidr(cidr: string): Destination
Match an IP range. Parameters
NameTypeDescription
cidrstringCIDR notation (e.g. "10.0.0.0/8")

Destination.domain()

static domain(domain: string): Destination
Match an exact domain. Parameters
NameTypeDescription
domainstringFully qualified domain name

Destination.domainSuffix()

static domainSuffix(suffix: string): Destination
Match the apex domain and every subdomain. Parameters
NameTypeDescription
suffixstringDomain suffix

Destination.group()

static group(group: DestinationGroup): Destination
Match a predefined address group. Parameters
NameTypeDescription
groupDestinationGroupGroup keyword

PortRange

Factory for port ranges.

PortRange.range()

static range(start: number, end: number): PortRange
Match an inclusive port range. Parameters
NameTypeDescription
startnumberLower bound (inclusive)
endnumberUpper bound (inclusive)

PortRange.single()

static single(port: number): PortRange
Match a single port. start and end are set to the same value. Parameters
NameTypeDescription
portnumberPort number

NetworkBuilder

Returned to the callback you pass to SandboxBuilder.network(...). Every setter returns the same builder.

denyDomainSuffixes()

denyDomainSuffixes(...suffixes: string[]): this
Deny egress to all subdomains of these suffixes. Same enforcement layers as denyDomains(). Parameters
NameTypeDescription
suffixesstring[]Domain suffixes

denyDomains()

denyDomains(...names: string[]): this
Deny egress to these exact domains. Each entry adds a deny Domain("...") policy rule that fires at DNS resolution (REFUSED), TLS first-flight (SNI), and TCP egress (cache fallback). Prepended onto the policy. Parameters
NameTypeDescription
namesstring[]Fully qualified domain names

dns()

dns(f: (d: DnsBuilder) => DnsBuilder): this
Configure DNS interception. See DnsBuilder.

enabled()

enabled(b: boolean): this
Enable or disable networking entirely. Parameters
NameTypeDescription
bbooleanWhen false, no network interface is created

maxConnections()

maxConnections(n: number): this
Limit the maximum number of concurrent network connections from the sandbox.

onSecretViolation()

onSecretViolation(action: ViolationAction): this
Set the action taken when a secret reaches a disallowed host. See ViolationAction.

policy()

policy(policy: NetworkPolicy): this
Set the policy. Use a NetworkPolicy factory or a literal.

port()

port(host: number, guest: number): this
Publish a TCP port from the guest to the host. Parameters
NameTypeDescription
hostnumberPort on the host
guestnumberPort inside the sandbox

portUdp()

portUdp(host: number, guest: number): this
Publish a UDP port from the guest to the host. Parameters
NameTypeDescription
hostnumberPort on the host
guestnumberPort inside the sandbox

secret()

secret(f: (s: SecretBuilder) => SecretBuilder): this
Add a secret. See SecretBuilder.

secretEnv()

secretEnv(envVar: string, value: string, placeholder: string, allowedHost: string): this
Four-arg explicit-placeholder shorthand for adding a secret without opening a builder callback. Parameters
NameTypeDescription
envVarstringEnvironment variable name
valuestringReal secret value
placeholderstringPlaceholder string surfaced to the guest
allowedHoststringSingle hostname allowed to receive the real value

tls()

tls(f: (t: TlsBuilder) => TlsBuilder): this
Configure TLS interception. See TlsBuilder.

trustHostCAs()

trustHostCAs(enabled: boolean): this
Whether to ship the host’s trusted root CAs into the guest at boot. Default: false. Opt in for 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.

DnsBuilder

Builder for DNS interception settings. Used in NetworkBuilder.dns(d => ...). Owns rebind protection, nameserver pinning, and the per-query timeout.

nameservers()

nameservers(servers: string[]): this
Override upstream nameservers. Replaces any previously-set nameservers. Parameters
NameTypeDescription
serversstring[]Each entry is IP, IP:PORT, HOST, or HOST:PORT

queryTimeoutMs()

queryTimeoutMs(ms: number): this
Per-DNS-query timeout in milliseconds.

rebindProtection()

rebindProtection(enabled: boolean): this
Toggle DNS rebinding protection. When enabled, DNS responses resolving to private IPs are blocked.

TlsBuilder

Builder for TLS interception settings. Used in NetworkBuilder.tls(t => ...).

blockQuic()

blockQuic(block: boolean): this
Block QUIC on intercepted ports, forcing TCP/TLS fallback.

bypass()

bypass(pattern: string): this
Skip TLS interception for hosts matching this glob (e.g. "*.internal.corp"). Use for domains with certificate pinning. Parameters
NameTypeDescription
patternstringGlob pattern

interceptCaCert()

interceptCaCert(path: string): this
Path to a PEM file used as the intercepting CA’s certificate.

interceptCaKey()

interceptCaKey(path: string): this
Path to a PEM file used as the intercepting CA’s private key.

interceptedPorts()

interceptedPorts(ports: number[]): this
TCP ports where interception is active. Default: [443].

upstreamCaCert()

upstreamCaCert(path: string): this
Path to a PEM file with extra root CAs the proxy should trust.

verifyUpstream()

verifyUpstream(verify: boolean): this
Verify upstream server certificates. Default true. Set to false only for self-signed servers.

Types

NetworkConfig

Built network configuration produced by NetworkBuilder.build().
FieldTypeDescription
enabledbooleanMaster enable flag
portsreadonly PublishedPort[]Port publishings
policyNetworkPolicy | nullActive policy
dnsDnsConfig | nullDNS interception
tlsTlsConfig | nullTLS interception
secretsreadonly SecretEntry[]Secret entries
secretViolationViolationAction | nullAction on disallowed secret use
maxConnectionsnumber | nullMaximum concurrent connections
trustHostCAsbooleanShip host CAs into the guest

NetworkPolicy

interface NetworkPolicy {
  readonly defaultEgress: Action;
  readonly defaultIngress: Action;
  readonly rules: readonly Rule[];
}

Rule

interface Rule {
  readonly direction: Direction;
  readonly destination: Destination;
  readonly protocols: readonly Protocol[]; // empty = any
  readonly ports: readonly PortRange[];    // empty = any
  readonly action: Action;
}

Destination

type Destination =
  | { kind: "any" }
  | { kind: "cidr"; cidr: string }
  | { kind: "domain"; domain: string }
  | { kind: "domainSuffix"; suffix: string }
  | { kind: "group"; group: DestinationGroup };

Action

type Action = "allow" | "deny";

Direction

type Direction = "egress" | "ingress" | "any";

Protocol

type Protocol = "tcp" | "udp" | "icmpv4" | "icmpv6";

DestinationGroup

type DestinationGroup =
  | "public"
  | "loopback"
  | "private"
  | "link-local"
  | "metadata"
  | "multicast"
  | "host";
ValueDescription
'public'Public internet (everything not in another group)
'loopback'Guest’s own 127.0.0.0/8 / ::1
'private'RFC1918 LAN ranges
'link-local'169.254.0.0/16 / fe80::/10
'metadata'Cloud metadata endpoints (169.254.169.254, fd00:ec2::254)
'multicast'Multicast addresses
'host'The host machine, reached via host.microsandbox.internal

PortRange

interface PortRange {
  readonly start: number;
  readonly end: number;
}

RuleDestinationBuilder

Returned by RuleBuilder.allow(d => ...) / .deny(d => ...). Exactly one destination call commits the rule; dropping without a destination call silently does nothing.
MethodDescription
.ip(ip)Commit with Destination::Cidr of the IP as /32 or /128
.cidr(cidr)Commit with Destination::Cidr
.domain(domain)Commit with Destination::Domain
.domainSuffix(suffix)Commit with Destination::DomainSuffix
.group(group)Commit with Destination::Group. group is a DestinationGroup string
.any()Commit with Destination::Any

DnsConfig

FieldTypeDescription
nameserversreadonly string[]Upstream nameservers
rebindProtectionboolean | nullDNS rebinding protection toggle
queryTimeoutMsnumber | nullPer-query timeout

TlsConfig

FieldTypeDescription
bypassreadonly string[]Bypass globs (e.g. "*.googleapis.com")
verifyUpstreamboolean | nullVerify upstream certs
interceptedPortsreadonly number[]Intercepted TCP ports
blockQuicboolean | nullBlock QUIC on intercepted ports
upstreamCaCertPathsreadonly string[]Extra trust roots for upstream verification
interceptCaCertPathstring | nullCustom intercept CA cert (PEM)
interceptCaKeyPathstring | nullCustom intercept CA key (PEM)

PublishedPort

interface PublishedPort {
  readonly hostPort: number;
  readonly guestPort: number;
  readonly protocol: "tcp" | "udp";
}