> ## Documentation Index
> Fetch the complete documentation index at: https://tesser.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Share gateway

> Open shared boxes at your own domain, behind Cloudflare Access, for people without the CLI

A share gateway gives every shared box its own address on your domain, such
as `https://box-<id>.share.example.com/`, that a teammate, a designer, or a
phone opens in a browser with no Tesser login. The box's first declared port
is served there, and every other port at
`https://box-<id>--<port>.share.example.com/`, so each port is its own site
the way `localhost:<port>` is.

The gateway is a Cloudflare Worker you deploy into your own Cloudflare
account, behind a Cloudflare Access application. Access decides who may open
a box. Each shared box dials the Worker over an outbound websocket, so
nothing reaches into the network the boxes run in, and the Worker holds no
secret.

Opening a sleeping box's address wakes it, with a page that shows the
progress and turns into the app when it answers. Traffic through the gateway
keeps the box awake. A box is served only while it has a live share: `tesser
share <box>` prints the box's gateway address next to the review link, and
`tesser share <box> --revoke`, or the share expiring, takes it off within a
few seconds.

An org running in its own AWS account can run the gateway inside its VPC
instead, behind its VPN; see [Serve shared boxes on your own
domain](/docs/self-hosting#serve-shared-boxes-on-your-own-domain).

## Pick the host

Boxes hang off one host, `<host>`, and each box takes one label in front of
it. Cloudflare's free certificate covers a zone's apex and one level below
it, so:

* A zone used only for the gateway, such as `examplereview.com`, gives boxes
  `box-<id>.examplereview.com` with the free certificate.
* A host one level down in your main zone, such as `share.example.com`, puts
  boxes two levels deep. Their certificate needs [Advanced Certificate
  Manager](https://developers.cloudflare.com/ssl/edge-certificates/advanced-certificate-manager/)
  with Total TLS on the zone.

## Set it up

You need a Cloudflare zone for `<host>` and [Zero
Trust](https://developers.cloudflare.com/cloudflare-one/) on the same account
(the free plan covers 50 users).

1. Point `<host>` and `*.<host>` at Cloudflare with two proxied DNS records.
   The address does not matter; the Worker answers every request:

   | Type | Name | Content | Proxy |
   | - | - | - | - |
   | AAAA | `<host>` | `100::` | Proxied |
   | AAAA | `*.<host>` | `100::` | Proxied |

2. In Zero Trust, add a self-hosted Access application on `*.<host>` with the
   policies that say who may open shared boxes, such as your company's email
   domain. Leave `<host>` itself outside it: boxes dial in there, and the
   Worker checks their tickets. Copy the application's **audience tag**.

3. Deploy the Worker from a project of its own:

   ```sh theme={"theme":"css-variables"}
   npm install @tesser-sh/gateway
   ```

   ```ts index.ts theme={"theme":"css-variables"}
   export { default, GatewayBoxes } from "@tesser-sh/gateway";
   ```

   ```jsonc wrangler.jsonc theme={"theme":"css-variables"}
   {
     "name": "tesser-gateway",
     "main": "index.ts",
     "compatibility_date": "2026-08-31",
     "compatibility_flags": ["enable_request_signal"],
     "workers_dev": false,
     "routes": [
       { "pattern": "<host>/__tesser/*", "zone_name": "<zone>" },
       { "pattern": "*.<host>/*", "zone_name": "<zone>" }
     ],
     "durable_objects": { "bindings": [{ "name": "BOXES", "class_name": "GatewayBoxes" }] },
     "migrations": [{ "tag": "v1", "new_sqlite_classes": ["GatewayBoxes"] }],
     "vars": {
       "GATEWAY_HOST": "<host>",
       "TESSER_URL": "https://api.tesser.sh",
       "TESSER_ORG": "<org id>",
       "ACCESS_TEAM": "<team>.cloudflareaccess.com",
       "ACCESS_AUD": "<audience tag>"
     }
   }
   ```

   ```sh theme={"theme":"css-variables"}
   npx wrangler deploy
   ```

4. Point the org at it. Only an org admin can:

   ```sh theme={"theme":"css-variables"}
   tesser gateway set --url https://<host> \
     --access-team <team>.cloudflareaccess.com --access-aud <audience tag>
   ```

`https://<host>/__tesser/health` answers `ok` once the Worker is live. Share
a box with `tesser share <box>` and open the gateway address it prints.

## How it works

* **Boxes dial out.** While a box is shared, Tesser hands it a ticket it
  signs for your gateway, and the box keeps one websocket open to
  `wss://<host>/__tesser/box`. The Worker checks the ticket against
  `https://api.tesser.sh/.well-known/jwks.json`, so a box can only ever
  answer for itself. A revoke tells the box to hang up.
* **One Durable Object per box** holds that socket and carries every request
  and websocket for the box over it, with flow control on bodies in both
  directions.
* **Viewers come through Access.** The Worker checks the
  `Cf-Access-Jwt-Assertion` header on every request against your team's
  keys and the application's audience tag.
* **Waking needs no secret.** When a viewer opens a sleeping box, the Worker
  passes the viewer's Access token to Tesser, which checks it against the
  team and audience you set and wakes the box.

The dev server sees what it sees on a laptop: each request arrives as
`http://localhost:<port>`, so Vite's `allowedHosts` and Next's
`allowedDevOrigins` need nothing added. The gateway adds a small script to
each page that sends the page's `fetch`, `XMLHttpRequest`, `WebSocket`, and
`EventSource` calls, links, and forms aimed at `localhost:<port>` through the
gateway, and redirects to `localhost:<port>` come back as gateway addresses.
Each port's site keeps its own cookies, where `localhost` shares them across
ports, and a third-party sign-in only returns to a gateway address if the
provider allows it as a callback.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.