---
source: https://a2a.exposed/public-front
---

# Put a secure public front on an A2A agent that lives on your laptop, LAN or tailnet

In public front mode, a2a-exposed puts a Cloudflare Worker in front of an agent that already speaks A2A on a laptop, LAN or tailnet: it serves the agent card rewritten to the public URL, checks each peer's token, and forwards requests through a Cloudflare Tunnel locked by Cloudflare Access.

Use this when the agent already speaks A2A. An agent that needs a task store uses the [task store and wake](/inbox).

## The path

1. A peer calls the public Worker with its own bearer token, issued by [pairing](/security/pairing).
2. The Worker checks that token, then calls your agent with the Worker's own credential.
3. The Worker calls your agent at a Cloudflare Tunnel hostname. Cloudflare Access admits only the Worker's service token.
4. `cloudflared` on your machine delivers the request to the agent.
5. The Worker also sends `UPSTREAM_TOKEN` if your agent expects its own bearer, and `X-A2A-Peer` with the peer's label.

```bash
npx -y a2a-exposed@latest init \
  --hostname agent.example.com \
  --upstream https://agent-upstream.example.com/a2a
```

`--upstream` must be a public Tunnel hostname. A Tailnet, LAN or localhost URL is refused.

## Does this work with Tailscale?

Your agent can live on a tailnet. The Worker reaches the agent through a Cloudflare Tunnel hostname locked by Access. Tailscale is where the agent sits. The tunnel is the transport.

## What changes on the agent card

Interfaces point at the public URL. Name, skills and version come from the upstream card unless you override them with `--agent-*`. These are removed from every string: localhost, private IPs, `*.ts.net`, `*.local`, plain `http`, and the tunnel hostname itself. `signatures` are dropped. `streaming`, push notifications and the extended card are advertised as false.

`UPSTREAM_TOKEN` stays on the Worker. On failure peers get a generic `-32603` 502. You get a reason code from `npx -y a2a-exposed@latest upstream verify` or `status`: `access_credentials_missing`, `access_rejected`, `upstream_auth_missing`, `upstream_auth_rejected`, `tunnel_down`, and others listed in [troubleshooting](/docs/troubleshooting).

## Peer isolation

Each task and context belongs to the peer that created it. A call on another peer's task is refused before it reaches your agent. `ListTasks` is refused. Push notification configs are refused, because the upstream would call a peer's URL from inside your network. Peers poll `GetTask`. Streaming is not proxied.

## What the public front adds

The Worker rewrites the agent card to the public URL, issues a token per peer, and removes private URLs from the card. Cloudflare Tunnel is the private hop. The tunnel recipe is in [docs/tunnel](/docs/tunnel). The threat model is in [security](/security).

Grok Bot is tested end to end. The public-front path is listed on [compatibility](/docs/compatibility).
