Skip to content

Let Runs Reach Your Tailnet ​

Before You Start ​

A Run in a Cell can make outbound connections to your tailnet through a userspace proxy. This provides outbound access only; it doesn't expose a service in the Cell to your tailnet. Tools must use the proxy, including for private URLs supplied as Variables.

Approved subnet routes work through the proxy, so services behind a subnet router can be reached when your access rules allow them. Zozo never uses an exit node. Ordinary public egress remains available.

Generate a Tailscale Auth Key ​

A Tailnet Key holds a Tailscale auth key with all four requirements below. Follow Tailscale's auth key guide for the click-by-click generation steps.

RequirementWhy Zozo needs itTailscale guide
ReusableEvery Cell joins separately, so the same saved key must admit multiple nodes.Auth keys
EphemeralNodes are temporary and are removed after the Cell ends and they go offline, rather than accumulating in your device list.Ephemeral nodes
Pre-approvedWith device approval enabled, each Run must be able to join without waiting for human approval.Auth keys and device approval
TaggedAccess rules apply to the node's tag, so you can limit Runs to specific services.Group devices with tags

Zozo stores the key without validating these properties. Choose a dedicated tag such as tag:zozo-runs, with permissions in your access control policy.

Rotate before the auth key expires

Reusable keys still expire. Once the auth key expires or is revoked, new Cells can't join and their Runs fail before the Runtime starts. Zozo doesn't renew keys automatically. Generate a replacement and save it before expiry; revoke and replace an exposed key immediately.

Add the Tailnet Key to an Environment and Pin It to a Project ​

  1. Open Settings in your Dashboard.
  2. Create or open an Environment.
  3. Paste the auth key into Tailnet Key and select Set Tailnet Key. After saving, only its set status and update time are shown.
  4. Open Projects and select that Environment in your Project's Environment menu.
  5. Start a new Run. Its Cell joins before its Runtime starts.

To rotate the key, enter a replacement in the same field and select Replace Tailnet Key. The replacement applies to future Runs. See Manage a Tailnet Key for removal and recovery. The key is separate from Variables and never enters the Runtime's process environment.

Limit What Runs Can Reach ​

Use a dedicated tag and grant it only the destinations and ports your Projects need, for example a staging web server on port 443 or a test database on port 5432. Projects sharing an Environment share its key and tag permissions. Use separate Environments and tags when they need different access.

Follow Tailscale's tag guide for tag ownership and its access control policy guide for destination and port rules. For services behind a subnet router, follow the subnet router guide to advertise and approve routes and allow access to their destinations. Route approval and access permission are both required.

Reach Non-Web Services ​

After joining, ALL_PROXY, HTTP_PROXY, and HTTPS_PROXY are set to socks5h://127.0.0.1:1055. Clients supporting SOCKS5 with remote DNS can resolve MagicDNS names through the proxy:

sh
curl --proxy "$ALL_PROXY" https://test-server.your-tailnet.ts.net/health

Configure clients that ignore proxy variables

A client that ignores these variables needs explicit SOCKS5 configuration. A private URL alone doesn't provide reach. Configure the proxy and remote DNS in the client, or use a tailnet IP address where remote DNS isn't supported.

For ordinary SSH, use ProxyCommand with a SOCKS5-capable helper. With OpenBSD nc installed in the Cell, this example connects to a server's tailnet IP through the proxy (replace the address and user):

sh
ssh -o 'ProxyCommand=nc -X 5 -x 127.0.0.1:1055 %h %p' user@100.101.102.103

Database clients need the same explicit proxy support. If your client doesn't support SOCKS5, forward the database port through an SSH server that can reach it. With the same helper installed, for example:

sh
ssh -N -o 'ProxyCommand=nc -X 5 -x 127.0.0.1:1055 %h %p' \
  -L 127.0.0.1:15432:db.internal:5432 user@100.101.102.103

While that SSH connection stays open, connect your PostgreSQL client to 127.0.0.1:15432. Here db.internal resolves on the SSH server. Keep normal SSH host verification, database authentication, and any required TLS settings; the proxy supplies network reach, not service credentials.

When a Run Can't Join ​

The Environment and each pinning Project show the latest observed join health. Saving or replacing a key doesn't test it; a new Run must attempt a join before health changes.

StateMeaning and remedy
unknownNo Run has reported a join attempt yet. Start a Run in a Cell to observe it.
okThe Cell joined successfully. If a service is still unreachable, check its access rules, subnet route approval, proxy configuration, and service listener.
expiredTailscale reported an expired key. Generate and save a replacement Tailnet Key, then start another Run.
refusedTailscale refused authentication or permissions. Check for an invalid or revoked key, tag ownership and permissions, and device approval requirements; correct them or replace the key, then retry.
unreachableJoining timed out or failed without an authentication diagnosis. Check connectivity to Tailscale's coordination service, then retry.

Joining must succeed before work starts

A Cell with a saved Tailnet Key that can't join fails the Run with tailnet_join_failed before its Runtime starts. Fix the key or connectivity and start a new Run to resume work.

On a Local Stack ​

A Local Stack runs on your machine with the host Executor. Its saved Tailnet Key is unused, and Runs use the host's own network, including the host's Tailscale connection if configured. The key field and Run show an unused-key notice. Configure private reach on your host for these Runs.

Docs for Zozo: a coding-agent harness.