Egress

Egress is your application's side of the egress gateway. The gateway is the route a backend on Turn Zero Cloud uses to call an external application programming interface (API) with a stored key. The gateway adds the key to the request and never gives it to your process. It removes the key's header from the upstream's response and passes the rest through. So an upstream that echoes the key in its body returns it to your process.

This package's client sends the call, tells you whether a response came from the gateway or from the upstream, and reads a streamed response frame by frame. Call an external API with an API key describes the gateway itself and the two routes to an external destination.

Use it when

Use Egress for every call your backend makes to an API with a stored key: an AI provider, a payments API, or a search API. Store the key once, declare the upstream against the key's name, and call /egress/v0/<upstream>/<path> through the client under your application's platform credential. The provider bills your own provider account. When you rotate the key in custody, no application code changes.

Where your backend calls a provider through the provider's own software development kit (SDK), unchanged, the gateway handles the call without this package. Declare the upstream with settings. Each deploy then gives the SDK its base URL and an egress key, which the gateway swaps for your stored key. That route gives your code no typed refusal. An unchanged SDK shows it.

A destination your backend reaches without a stored key needs an entry in the manifest's egress list. The runtime's ordinary fetch reaches it through the HTTPS tunnel, and that route needs no package. Egress firewall and request limits describes the modes and the refusals.

What it provides

The current package provides:

  • A client, EgressClient, published as the npm package @turnzero/egress. Use the library shows how to copy it into app/lib/egress/, declare it as file:lib/egress, and install it with npm install. The client calls through a transport function your application supplies, which adds your credential. A deployed transport reads the gateway's origin from TURNZERO_CLOUD_GATEWAY_URL and the credential from TURNZERO_CLOUD_TOKEN. Both are among the settings a deployed container receives (Deploy an application).
  • One call per request. call(upstream, path, init) sends the method, the headers, the body, and the query string unchanged. It returns the upstream's own response, whatever its status, when the response has the gateway's marker header x-egress-upstream. The gateway adds that header to every response that came from an upstream.
  • A typed refusal, GatewayRefusal, thrown for every response without the marker header. That covers the gateway's own refusals, the platform's authentication challenge, and a response to a request that never reached the gateway. Each has the refusal's name, its status, its detail, and a kind that says what to do, as the table below lists.
  • A frame reader, readEventStream, for an upstream that responds with an event stream. It returns each frame in order as it arrives and honours the caller's abort signal. It throws egress_error, the one final event the gateway can send inside the stream, as the same typed refusal.
kind What to do
declaration Correct the upstream's declaration.
credential Store the credential the declaration names. Where the refusal is platform_minted_name, the declaration names a credential the platform mints for its own use: store your key under a name of your own and declare the upstream naming it.
bounds Stay within the platform limit the refusal gives.
transport Retry the call.
authentication Refresh your application's own credential. Where the refusal is egress_key_not_admitted, an egress key was sent outside its own upstream: send it only to that upstream.
environment Set the environment on the request.
admission The platform's rate limit refused the call (rate_capped), or the application reached its plan's limit on outbound bytes for the UTC day (egress_daily_bytes_capped). Wait, then call again: for the day limit, after the reset time the refusal's detail names, or move the application to a larger plan.
unrecognized This client version does not recognize the name. Read its error and status.
unmarked The request never reached the gateway. Check the route your transport builds.

A refused tunnel connection raises a different error: the runtime harness's EgressRefusedError, found in the cause of a rejected fetch. Node.js Runtime describes it.

A test double ships at the package's testing subpath, @turnzero/egress/testing. createEgressDouble takes the gateway's place behind the unchanged client, and a handler the test declares responds for each upstream. Each declare call gives the application the upstream is bound to, { application }, as the platform's declaration does. An upstream the platform itself provides, such as the AI allowance, uses its own package's double. Test your application locally shows it in use.

From version 0.13.0, the double reads an upstream name containing a NUL character (U+0000) or an unpaired UTF-16 surrogate as no name, and refuses the call with 404 unknown_upstream, as the gateway does.

The client adds no header of its own. The gateway removes some headers before the request reaches the upstream, among them the forwarding headers a proxy adds and the platform's own. Call an external API with an API key lists them.

A backend running under a minted token or a session, rather than its platform credential, sets the environment once on its transport with the header x-turnzero-cloud-environment. Every gateway call then sends it. The Storage package's withEnvironment wraps a transport used only for storage the same way.

The client adds no retry, no timeout, and no limit of its own. The gateway limits the request size, the response size, and the upstream's time, and refuses a call over each limit by name. The package never switches to another provider. If you want a fallback when one provider responds with a quota error, write it in your application.

You declare an upstream in the manifest's upstreams member where the manifest includes it, and with the declare_upstream action otherwise. An upstream the manifest declares belongs to the manifest, so declare_upstream refuses to change it (manifest_owned_field). Declare in the manifest shows the member. Where your project keeps a Turn Zero Blueprint instance file for the package, record there each upstream your application uses and the name of its credential. The key's value is kept only in custody.

Availability

Version 0.14.1 is the package's current version, and list_library reports the version the platform currently publishes. The entry includes the testing subpath from version 0.3.0.

The client's table gives the kind declaration to five refusal names. The gateway returns them when a declared host resolves, at the time of the call, to an address that is not public. Call an external API with an API key describes the rule and lists the names.

The entry contains the package's product statement, its detailed contract, its integration guide, and the compiled client. The platform runs the egress gateway. You declare keyed upstreams in the manifest or through declare_upstream, and the platform counts use per upstream.

Secrets stores the keys the gateway adds. Logging records what your backend does with an upstream's response. Node.js Runtime routes calls through the tunnel to destinations that use no stored key.