Outbound calls
Your Function reaches nothing on the internet until the App it belongs to says what it may reach. Nothing is open by default, and nothing is inherited from the workspace. An App with nothing on it blocks every outbound call its code makes.
Two things open a host, and they are not the same.
The egress catalogue is a list of providers we keep. Each one has a key, a name, and the hosts
behind it. Subscribe an App to stripe and its Functions may call api.stripe.com. You do not type
those hosts and you cannot change them. They live with us, so a provider that adds a host reaches
every subscribed App through one edit here rather than an edit on each App.
The egress allowlist is a list of host patterns you write yourself, and it belongs to one App.
api.mycompany.com matches that host and nothing else. *.mycompany.com matches any subdomain of it,
and never the bare mycompany.com nor a lookalike like evil-mycompany.com. An entry authorizes port
443 unless you pin a different one.
An App reaches the union of the two. Both are read when an invocation starts, so a host opened now is reachable on the next request, with no redeploy and no new version. Closing one works the same way, and the call after it is blocked.
What is in the catalogue
Section titled “What is in the catalogue”| Key | Provider | Hosts a subscription grants |
|---|---|---|
openai |
OpenAI | api.openai.com |
anthropic |
Anthropic | api.anthropic.com |
stripe |
Stripe | api.stripe.com |
github |
GitHub | api.github.com |
A host no provider covers goes on the App’s own allowlist. You can write api.stripe.com there
yourself instead of subscribing. The catalogue saves you following a provider that moves its API to
a new name.
S3 is not in the catalogue. The hosts it needs would open every AWS service and every bucket, yours
or anyone’s. Add your own bucket’s host to the allowlist instead, such as
my-bucket.s3.eu-west-1.amazonaws.com.
The allowlist limits where data can go. It does not make a leak impossible. A host that many
customers share, like api.openai.com, api.github.com or an S3 host, still takes data out: code that can call it can send your
data to someone else’s account there.
No command sets either one
Section titled “No command sets either one”There is no wawesome egress command, and no flag on deploy that opens a host. The CLI touches the
lists in two places: scaffolding, and setting a variable a template marked as naming a host.
A template declares the catalogue providers its code calls. npx wawesome init --template <name>
subscribes the App to each of them before the deploy:
[wawesome] ✅ Enabled outbound calls to 'openai'.
A template can also mark a variable as one that names a host, such as the address of your
database. When you answer that prompt, init takes the host out of the address and adds it to the
App’s allowlist:
[wawesome] ✅ Opened outbound calls to 'abcd.supabase.co', from SUPABASE_URL.
Only the host you typed is opened, with no port, so the default HTTPS port only. The value has to be
an https:// address. A connection string like https://user:password@host/db works too. Any other
scheme, an address with its own port, or an IPv6 address is refused. The message names the
variable, and nothing is stored. A blank answer opens nothing. init also lists the variable under
opens_host in wawesome-function.json.
That list is what lets you deploy first and fill the value in later. wawesome env set on a
variable listed there opens its host the same way, with the same refusals:
npx wawesome env set SUPABASE_URL https://abcd.supabase.co
[wawesome] ✅ Set variable 'SUPABASE_URL' on app 'my-app'.
[wawesome] ✅ Opened outbound calls to 'abcd.supabase.co', from SUPABASE_URL.
The CLI only ever adds to the allowlist. It never removes an entry. So pointing the variable at a different project opens the new host, and the old entry stays. The allowlist is the App’s, and the CLI cannot know whether it wrote that entry or you did. Clearing it is yours to do, under Custom Hosts in the dashboard. The CLI tells you when it sees one:
[wawesome] The allowlist entry 'abcd.supabase.co' no longer matches SUPABASE_URL. It stays; remove it under Egress in the dashboard if nothing else uses it.
For a secret variable the CLI cannot read the old value back, so it cannot name the old entry. It says so, and you check Custom Hosts yourself.
That covers the calls the template ships with. A host you add to the code afterwards is not covered, and neither is anything you wrote from scratch.
Everything after that is the dashboard, on the App’s page under Egress. Curated Catalog is where you enable and disable a provider. Custom Hosts is where you add and remove a host pattern.
An agent working over MCP can name this failure but cannot fix it.
diagnose_latest_failure reads the failed run, reports the cause as egress-blocked, and hands
back a link to that tab. Opening a host stays with a person at a screen they signed in to.
When a call is blocked
Section titled “When a call is blocked”Say your handler calls a host nobody opened:
export default {
async fetch(): Promise<Response> {
const models = await fetch('https://api.openai.com/v1/models');
return Response.json(await models.json());
},
};
fetch rejects before the request leaves the sandbox. Nothing is sent and no DNS lookup happens. If
you catch it, this is the error:
TypeError: NetworkError when attempting to fetch resource
If you do not, the run ends there. The caller gets a 500 with an empty body, and
npx wawesome logs <invocation-id> shows the rejection your handler let through, with a hint and
a link to the App’s Egress tab under it:
Error while running request handler: NetworkError when attempting to fetch resource
Stack:
[wawesome] An outbound call was blocked (egress-blocked). Open its host under Egress: https://dashboard.wawesome.io/apps/my-app?tab=egress
The dashboard shows the same hint and link when you open the run. GET /v1/invocations/{id} carries
them under remediation. A run that catches the error and answers normally gets no hint.
The error in your code is still that one line. We do not say which rule stopped the call. A Function that could tell an unopened host from a blocked address could work out the whole rule set by asking one question at a time. The hint is added outside the sandbox, where your code cannot read it.
Open the host
Section titled “Open the host”- Take the host out of the URL your Function called. For
https://api.openai.com/v1/modelsthat isapi.openai.com, not the path and not the whole URL. - Open the App in the dashboard and go to Egress. The address is
https://dashboard.wawesome.io/apps/{app}?tab=egress. The hint above and an MCP diagnosis hand you the same link. - If a catalogue provider covers the host, enable it under Curated Catalog. If none does, add the host under Custom Hosts.
- Invoke the Function again. There is nothing to redeploy.
The same error, other causes
Section titled “The same error, other causes”The message does not change, so check these before you conclude the host is missing.
An http:// URL is blocked. Outbound calls are HTTPS only, and the scheme is checked before the
host is. A host you opened is still blocked over cleartext. A call that is not HTTP at all never had
a path out, and reaching a database covers that one.
A port you did not authorize is blocked. An allowlist entry covers 443 unless you pinned another
port, so https://api.mycompany.com:8443/ needs an entry pinned to 8443.
A host that resolves into a private range is blocked whatever the allowlist says. 127.0.0.1 and
the cloud metadata address 169.254.169.254 are among the addresses no App can reach. A public
hostname pointed at one of them is blocked on the answer rather than on the name.
Reaching a database
Section titled “Reaching a database”Every outbound call your Function makes is an HTTP request. There are no raw TCP sockets. The sandbox refuses one before it reaches the network, so a client that speaks a wire protocol of its own has nothing to connect over. A Postgres or MySQL driver is the usual one. Neither a catalogue provider nor an allowlist entry changes that, because neither is what stopped it.
Most drivers do not get that far. They import node:net, the bundler has nothing to resolve it to,
and the deploy stops at the build.
Reach the database over HTTP instead. Supabase, Turso and Neon each answer over HTTPS, so their client works here and their host goes under Custom Hosts like any other. Where your data goes covers databases in full, and limits covers the rest of what the runtime does and does not give you.
What does not open a host
Section titled “What does not open a host”Setting an environment variable holding an API address does not, unless the template
listed that variable under opens_host. A variable is a value your code reads, and on its own it
authorizes nothing. Only the CLI opens that host. Setting the same variable in the dashboard or
over MCP opens nothing.
Deploying does not, and neither does a new version. Both lists belong to the App. A Function rolled back to last week’s code reaches exactly what the App reaches today.