ctx.fetch refuses anything outside the list synchronously, before it opens a socket — including redirect targets. It is a security boundary, not a convenience filter.
Declaring egress
List only the origins you call:http:. Wildcards are a single leading *..
Validation
defineIntegration() rejects invalid entries with the offending entry quoted — the message begins Egress entry "<entry>" is invalid. Entries are https origins only:
Runtime behavior
Denied calls throwEgressDeniedError (from @ekanos/sdk/context) synchronously:
https://api.acme.example/v1/data redirects to https://evil.example, the fetch is denied.
EgressDeniedError carries code: 'EGRESS_DENIED', the refused origin, and an optional reason. The host also refuses an allowlisted name that resolves into a private, loopback, link-local, or other non-public range, on the first request or any redirect hop. Those denials are found after DNS, so they reject the returned promise instead of throwing synchronously, and reason names the category (never the address).
Common patterns
Single provider
Most integrations call one API:Multi-region
If you need multiple regions:Wildcard for CDN
If assets come from a wildcard subdomain:Explicit ports
If the API uses a non-standard port:https://api.acme.example:443 is redundant — 443 is the HTTPS default.
Promotion review
Widening this list is a security-posture change that a human reviewer looks at during promotion. The reviewer reads your declaration against what your handlers actually do, so: Declare only origins you call. Do not addhttps://*.example “just in case” — the reviewer will question it, and it weakens your security posture.
If you call a new origin, add it to the declaration. ctx.fetch denies anything not listed.
Error handling
Special case: OAuth
Both OAuth endpoints must be in your egress list.defineIntegration() rejects the definition otherwise and tells you which origin to add: