Skip to content

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.

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.

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.

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.

  1. Take the host out of the URL your Function called. For https://api.openai.com/v1/models that is api.openai.com, not the path and not the whole URL.
  2. 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.
  3. If a catalogue provider covers the host, enable it under Curated Catalog. If none does, add the host under Custom Hosts.
  4. Invoke the Function again. There is nothing to redeploy.

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.

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.

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.