- An egress allowlist per machine: the hosts and addresses it may reach. Everything else is blocked.
- Secrets bound to a host: the machine only ever holds a placeholder, and the real value is inserted into requests on their way out, only to the hosts you name.
The allowlist
What an entry can be
Entries are bare hosts and addresses: no scheme, port, or path.
Wildcards are allowed when every host under the domain belongs to one party.
*.npmjs.org only reaches hosts npm runs.
They are refused on domains where anyone can get a subdomain, because that opens a way out to a host an attacker controls:
*.amazonaws.comwould admit every S3 bucket*.vercel.appevery Vercel deployment- likewise
*.cloudfront.net,*.herokuapp.com,*.workers.dev,*.github.io,*.pages.devand other shared-hosting or CDN domains
my-bucket.s3.amazonaws.com. Top-level wildcards like *.com are refused as well.
Addresses must be public. Private, link-local, loopback and multicast ranges are refused, as is anything overlapping them, 0.0.0.0/0 included. IPv6 is refused too: machines have no IPv6 egress.
How it is enforced
Web requests (HTTP and HTTPS) are admitted by the hostname they name, soapi.stripe.com works whichever address it resolves to. Everything else is admitted by destination address, and the addresses of an allowlisted name count as allowlisted too, so a plain curl to an allowlisted host works without you listing its addresses.
What a blocked request sees depends on the protocol: HTTP gets a 403, HTTPS has its connection closed during the handshake, and anything else is dropped. The hosts of any bound secret the machine holds are always admitted, allowlist or not.
A change takes effect within a second, on a running machine, without a restart. The machine can read its own policy but not change it:
Secrets bound to a host
A secret is normally injected into the machine as its real value. Bound to a host, it is not:STRIPE_KEY is an opaque string starting with bxds_. A request to api.stripe.com that carries it, in a header, the query string, the body, or Basic auth, arrives at Stripe with the real key. A request anywhere else carries the useless placeholder. There is nothing to configure in the machine; your code uses the variable as it always did.
- The placeholder is stable. Rotating the value with another
setkeeps the same placeholder, so a running process never sees its environment change. domainsis the full desired set. Setting the secret again without it makes it a plain secret again, injected as its real value.- Wildcards follow the same rules as allowlist entries:
*.stripe.comis fine,*.amazonaws.comis refused.
Forks, snapshots, and isolation
- A fork inherits its source’s allowlist. A confined machine’s fork comes up confined.
- A machine created from a snapshot starts unrestricted. A snapshot carries no allowlist, so set one after the restore if you need it.
- Bound secrets are set once, at the organization level, and apply to every machine in scope, forks and restores included.
--isolatedand the allowlist are independent. Isolation cuts a machine off from the rest of your account; the allowlist cuts it off from the internet. A sandbox for untrusted code usually wants both. See Sandboxes.- On an isolated machine, bound secrets arrive in
execand SSH sessions only, like every account-level variable: nothing is injected at boot.
Where to set it
Both controls are available in the TypeScript and Python SDKs and the gRPC API (SetVmEgressAllow, and the domains field on SetVar). They are deliberately not settable from inside a machine, and the laptop CLI does not expose them yet.