drift Docs
Start
What is Drift?
The tour, if you are new here.
Use cases
Whether Drift does your thing.
Getting started
Nothing to deployed, in one command.
Architecture
How a slice is put together.
What it costs
The free grant, four unit prices, two rules.
Build
Canvas
Static sites, same origin as your API.
Tools
Operate
Auth
Route gates, API keys and your account.
Security
Boundaries, sandboxing and hardening.
Troubleshooting
Error codes
What went wrong, and what to do about it.
Legal
Acceptable use
What a slice may not be used for.
Data processing
The DPA, and every sub-processor.

Edge Egress

Calling Stripe, Slack or an SMTP host is ordinary work for a function. What a slice may dial is its egress posture, declared in the egress block of edge:

Driftfile
edge {
  egress {
    mode = "allowlist" # open | allowlist, left out is open
    hosts = [
      "api.stripe.com", # every port on this host
      "hooks.slack.com",
      "smtp.sendgrid.net:587", # only this port
    ]
  }
}
Mode What the slice may reach
openAny public host, on any port. The default.
allowlistOnly the declared hosts. A bare host admits every port on it; host:port admits that one port. An allowlist with no hosts reaches nothing outside the slice.

How it is enforced

The allowlist is enforced, not a hint. drift apply sends the declaration to the platform, which resolves every declared host to its current IPv4 addresses and renders each one into the slice's own outbound network rule, so a connection to an address that is not on it is refused at the network. A host that resolves into a private range is never granted, whatever it is called. Name resolution itself is allowed only to the cluster's own resolver, so port 53 to anywhere else is closed too.

A wildcard (*.example.com) cannot be resolved to an address, so it cannot become a rule, and it is refused rather than silently dropped. The Driftfile reader refuses it offline (drift file lint, drift plan and drift apply all read the file the same way), with the entry's position:

Driftfile:13:3: Invalid edge.egress: edge.egress: hosts/0: '*.example.com' does not match pattern '^[^*]+$'

The platform refuses it again if one arrives some other way, with egress host "*.example.com" is a wildcard, which this platform cannot render into a NetworkPolicy rule; list the concrete hostnames instead.

drift plan shows the posture as one line: + egress allowlist api.stripe.com for a slice that does not exist yet, ~ egress open → allowlist api.stripe.com for a change, and egress unchanged (allowlist) when the live posture already matches. The apply reports what it did: ✓ egress allowlist applied (3 hosts), or · egress mode open when the block declares open. When the live posture already matches the file it prints nothing. A refusal fails the apply rather than leaving the slice on its old posture while the deploy reports success.

Leaving the egress block out leaves the posture as it is, like every other part of edge. A slice an earlier apply put on an allowlist stays on it until a file declares mode = "open". An egress block with no mode is open.

What no posture opens

Whatever the mode, the slice runtime checks every connection a function makes before the kernel carries it out, and refuses two kinds outright:

  • Private and reserved addresses: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local 169.254.0.0/16 (the cloud metadata endpoint among them), CGNAT 100.64.0.0/10 and 0.0.0.0/8, the IPv6 unique-local and link-local ranges, and any of those written as an IPv4-mapped IPv6 address. A function cannot reach the platform's internal services, or another slice, or anything on a private network, whatever the Driftfile says.
  • The slice's own control port, on loopback. The rest of loopback stays open, because that is where a function reaches Backbone, Deed and the link relay.

The same ranges are excepted from the slice's network rule in open mode, so each refusal stands on two independent mechanisms.

What a refused call looks like

Refused by What the function sees
The allowlist (a public host not on the list)The connection is refused: a connection refused dial error
The runtime (a private address, or the control port)The connect call fails with operation not permitted (EPERM)

How an error surfaces differs by language: Go returns an error, Python, Node.js and Ruby raise, and PHP and Rust answer with a status of 0. See Outbound HTTP.

The Go SDK's drift.HTTPRequest wraps a refused dial in an EgressDeniedError carrying the host it was for, whose message names the host and points at the hosts of the egress block, so a handler can branch with errors.As instead of parsing a message:

Go
var denied *drift.EgressDeniedError
if errors.As(err, &denied) {
    return 502, "Bad Gateway", map[string]string{"host": denied.Host}, nil
}

It is a best-effort signal rather than a certain one. It fires on connection refused, which a host that is allowlisted but down produces as well, and it covers only calls made through that SDK helper: a Go handler that opens its own http.Client sees the plain dial error, as every other language's SDK always does.

From the CLI

There is deliberately no add or remove: the Driftfile holds the list, and drift apply reconciles it. These commands inspect it and refresh it.

Command What it does
drift atomic egress listThe slice's mode and declared hosts
drift atomic egress refreshResolve every declared host again, re-apply the rule, and print the addresses it now holds
drift atomic egress test <host>Whether <host> is on the declared list: an exact match of the hostname, any port, with no DNS lookup

A refresh, with documentation addresses standing in for the real ones:

Shell
$ drift atomic egress refresh
Mode: allowlist

Declared hosts:
  - api.stripe.com
  - smtp.sendgrid.net:587

Resolved to 3 IP/port entries (active in the egress allowlist):
  - 203.0.113.10/32
  - 203.0.113.11/32
  - 198.51.100.25/32:587

Last resolved: 2026-10-05T09:14:02Z

list prints the first two parts and no addresses. For an open slice both print Mode: open and Every public host on any port is reachable. Private CIDRs are blocked unconditionally.

Shell
$ drift atomic egress test api.stripe.com
✔  api.stripe.com matches "api.stripe.com" on the allowlist.

$ drift atomic egress test api.example.com
✗  api.example.com is NOT on the allowlist.
    Add it to the egress hosts in your Driftfile's edge block and run 'drift apply'.

A miss also exits non-zero. On an open slice, test prints Slice egress mode is "open" — every public host is reachable.

Limits

Limit Value
Address familyIPv4 only. An IPv6-only host is not reachable on an allowlist.
When names resolveWhen the list is applied or refreshed, not per request. Reach for drift atomic egress refresh when an allowlisted CDN's addresses move.
A host that does not resolveGets no rule, so it is unreachable until a refresh resolves it
WildcardsRefused
Private and reserved rangesRefused in both modes