# Connecting to a private network (Tailscale and similar)

> How to let an AQ agent reach hosts on your own private network with Tailscale or a similar tool: run it yourself from the repository setup, as your own user, with ephemeral tagged nodes, shields up and a tight ACL.

Published November 12, 2026 · by the AQ team · Canonical: https://aq.dev/docs/private-networks/

Sometimes an agent needs to reach a machine that is not on the internet: a test rig, a lab device, a staging database behind your VPN. AQ does not install or manage Tailscale, ZeroTier or any other overlay network. You run your own, from your repository's setup command, as your own user on the machine, with no sudo. This page shows a safe way to do that with Tailscale; the same rules apply to any similar tool.

## What AQ does and does not do

- Nothing is preinstalled. The setup below downloads Tailscale's official static build into your own directories.
- Everything runs as your OS user, in userspace networking mode. No root, no network interface, no change to the machine's routing.
- On AQ's machines, ordinary users cannot reach the cloud metadata server, and AQ's cloud machines sit on networks that hold none of AQ's internal services, whatever tool runs on them. Your tunnel reaches your network and the public internet.
- AQ watches for tunnels and proxies on its machines. A node that advertises routes or offers itself as an exit node, Tailscale SSH, or a proxy that listens where other users can reach it is flagged for review. On a Free sandbox, a node that advertises routes or an exit node is stopped. On paid machines AQ only reports; it does not stop your processes.

## Safe settings

- **Ephemeral, pre-authorized, tagged nodes.** Each machine joins as an ephemeral node with its own tag, for example `tag:aq`. Use a tag only for AQ; never reuse one another provider's machines already carry.
- **Shields up.** Start with `--shields-up`, so nothing on your tailnet can open a connection to the AQ machine.
- **No undefined, undefined or undefined.** The AQ machine only connects out. AQ flags all three.
- **Userspace networking, no TCP proxy.** Run `tailscaled --tun=userspace-networking`. Reach hosts with `tailscale nc` or `tailscale ssh`, which talk to the daemon over its unix socket. Do not start `--socks5-server` or `--outbound-http-proxy-listen` on a machine others use: those listen on a TCP port, and every other user on the machine could send traffic through your tailnet. If a tool needs a proxy, put it on a unix socket inside a directory only you can read (mode 0700).
- **State in your own directory.** Keep the daemon's socket and state in a 0700 directory under your home, never in `/tmp` or a shared path.

## An OAuth client, saved as an AQ secret

In the Tailscale admin console, create an OAuth client with the `auth_keys` scope and select only your AQ tag. It can then mint auth keys for that tag and nothing else. Save its secret in AQ as a personal or organization [secret](https://aq.dev/docs/secrets/), for example `TS_OAUTH_CLIENT_SECRET`, limited to the repository that needs it. The setup passes it to `tailscale up` with `ephemeral=true&preauthorized=true`, so the node joins without approval and leaves when it goes away.

## Access controls

Give the tag only what it needs. In your tailnet policy file, declare the tag and allow it to reach the rigs on port 22 and nothing more. With no rule pointing at `tag:aq`, nothing on your tailnet can start a connection to it.

```
{
  "tagOwners": {
    "tag:aq": ["autogroup:admin"],
    "tag:rig": ["autogroup:admin"]
  },
  "acls": [
    { "action": "accept", "src": ["tag:aq"], "proto": "tcp", "dst": ["tag:rig:22"] }
  ]
}
```

## Example setup

Add this to the repository's setup command (or a script it calls). It downloads a pinned release, checks it against Tailscale's published checksum, and starts the daemon in your home. The variables at the top are the only things to change.

```
#!/usr/bin/env bash
set -euo pipefail
TS_VERSION=1.104.7
TS_TAG=tag:aq
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
TS_DIR="$HOME/.aq-tailscale"
PKG="tailscale_$TS_VERSION"_"$ARCH"
install -d -m 0700 "$TS_DIR"
cd "$TS_DIR"

if [ ! -x "$PKG/tailscaled" ]; then
  url="https://pkgs.tailscale.com/stable/$PKG.tgz"
  curl -fsSLo ts.tgz "$url"
  echo "$(curl -fsSL "$url.sha256")  ts.tgz" | sha256sum -c -
  tar -xzf ts.tgz
  rm ts.tgz
fi
TS="$TS_DIR/$PKG"
SOCK="$TS_DIR/tailscaled.sock"

if ! "$TS/tailscale" --socket="$SOCK" status >/dev/null 2>&1; then
  nohup "$TS/tailscaled" --tun=userspace-networking \
    --statedir="$TS_DIR/state" --socket="$SOCK" >"$TS_DIR/tailscaled.log" 2>&1 &
  sleep 2
fi

"$TS/tailscale" --socket="$SOCK" up \
  --auth-key="$TS_OAUTH_CLIENT_SECRET?ephemeral=true&preauthorized=true" \
  --advertise-tags="$TS_TAG" --shields-up --hostname="aq-$(hostname -s)"
```

Then reach a rig over SSH through the daemon, with no proxy port:

```
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
TS="$HOME/.aq-tailscale/tailscale_1.104.7_$ARCH"
SOCK="$HOME/.aq-tailscale/tailscaled.sock"
ssh -o ProxyCommand="$TS/tailscale --socket=$SOCK nc %h %p" robot@rig-01
```

The same `ProxyCommand` line works in `~/.ssh/config`, so `git`, `scp` and `rsync` over SSH use it too.

## Teardown

Ephemeral nodes remove themselves from your tailnet 30 to 60 minutes after they go quiet, so a work item that ends, or a machine that sleeps, leaves nothing behind. To remove a node at once, run `tailscale --socket="$SOCK" logout` before stopping the daemon. Revoking the OAuth client stops every new machine from joining.

## Other tools

ZeroTier, WireGuard in userspace, cloudflared and similar tools follow the same rules: run them as your user from the setup, connect out only, keep any proxy on a unix socket in a 0700 directory, and never advertise routes or offer an exit node from an AQ machine. More on how machines are isolated: [security and isolation](https://aq.dev/docs/security-and-isolation/).

---

About AQ: AQ is the multiplayer coding harness where teams build and run bots with every AI coding agent, like Claude Code and Codex: reviewed pull requests, live app previews, Genie and standing jobs, on private cloud machines of their own. Start at [aq.dev](https://aq.dev/).
