# Connect devices outside your tailnet

Last validated Jun 24, 2026

\[Missing snippet: aperture\_release\_note.mdx]

Aperture runs on a [tailnet][kb-tailnet], and devices in that tailnet can reach it directly. But not every device that needs LLM access can join the tailnet. CI/CD runners, contractor machines, personal devices, and devices in a different tailnet can all connect to Aperture using `ts-unplug`, a tool from the [ts-plug repository][xt-gh-ts-plug] that creates a lightweight tailnet node and proxies local traffic to your Aperture instance.

When you run `ts-unplug` on a device, it joins your tailnet as its own node and opens a local port that forwards traffic to Aperture. The device does not need Tailscale installed. Aperture authenticates requests using the `ts-unplug` node's Tailscale identity, not a shared API key. If each person runs their own `ts-unplug` instance, Aperture attributes activity to individual users.

This connection method is sometimes referred to as bridge mode or standalone mode.

> **Note:**
>
> Bridge mode and standalone mode refer to the same `ts-unplug` workflow described in this topic. This documentation uses bridge mode.

> **Note:**
>
> The `ts-plug` and `ts-unplug` tools are under active development. Refer to the [ts-plug repository][xt-gh-ts-plug] for the latest status and release information.

## Use cases

`ts-unplug` is useful when devices cannot or should not join your tailnet directly.

* **CI/CD runners**: Ephemeral environments like GitHub Actions runners that need LLM access during builds. Use an [auth key][kb-auth-keys] to skip interactive approval.
* **Contractor or vendor machines**: External collaborators who need Aperture access without joining your organization's tailnet.
* **Personal devices**: Team members who prefer not to install Tailscale on personal hardware.
* **Cross-tailnet access**: Devices in a different tailnet that need to reach your Aperture instance.

For devices that are already in your tailnet, connecting directly requires fewer steps and provides the same per-user identity. Refer to [set up LLM clients][kb-use-your-tools] for connection instructions.

## Prerequisites

Before you begin, confirm you have the following:

* **Go toolchain.** `ts-unplug` is built from source. Install [Go][xt-go-install] 1.21 or later.
* **Git.** Required to clone the [ts-plug repository][xt-gh-ts-plug].
* **The FQDN of your Aperture instance.** By default, this is `<aperture-hostname>.<tailnet-id>.ts.net`. Find your tailnet name in the [General](https://login.tailscale.com/admin/settings/general) of the admin console.
* **Permission to approve devices.** You must be an [Owner, Admin, or Network admin](/docs/reference/user-roles/) to approve the `ts-unplug` node in the admin console, unless you use an [auth key][kb-auth-keys] to authenticate automatically.

## Get started

To connect a device to Aperture using `ts-unplug`, build the tool from source, start the proxy, and approve the device in your tailnet.

### Step 1: Build ts-unplug

Clone the [ts-plug repository][xt-gh-ts-plug] and build `ts-unplug`:

```shell
git clone https://github.com/tailscale/ts-plug.git
cd ts-plug
make ts-unplug
```

This creates the binary at `./build/ts-unplug`.

### Step 2: Start ts-unplug

Run `ts-unplug` and point it at your Aperture instance. Replace `<tailnet-id>` with your tailnet name and choose a local port:

```shell
./build/ts-unplug -dir ./state -port 8080 <aperture-hostname>.<tailnet-id>.ts.net
```

On the first run, `ts-unplug` prints a URL to authenticate the new node. Open the URL in a browser to complete authentication.

### Step 3: Approve the device

Approve the new device in the [Machines](https://login.tailscale.com/admin/machines) of the admin console for the tailnet hosting Aperture.

After approval, `ts-unplug` displays `HTTP proxy listening` and you can access Aperture at `http://localhost:8080`.

### Step 4: Verify the connection

After `ts-unplug` is running, confirm the connection works by sending a test request:

```shell
curl -s http://localhost:8080/v1/messages \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-haiku-4-5-20251001",
    "max_tokens": 25,
    "messages": [{"role": "user", "content": "respond with: hello"}]
  }'
```

If the request succeeds, open the [Aperture dashboard][kb-web-dashboard] at `http://localhost:8080/ui/` to find the request in your usage history.

## Next steps

* **Configure tools to use the proxy**

  After you confirm the connection works, configure your tools to use `http://localhost:<port>` as the base URL for API requests. For tool-specific configuration, refer to [set up LLM clients][kb-use-your-tools] and replace `http://<aperture-hostname>` with `http://localhost:<port>` in any configuration example.

* **Manage `ts-unplug` instances**

  Manage your `ts-unplug` instances to maintain security and keep your tailnet organized. Stop the proxy when it's not in use, remove devices from your tailnet when they're no longer needed, and run multiple instances with separate state directories if multiple users need access.

## Limitations

The following limitations apply while `ts-unplug` is in beta:

* `ts-unplug` must be built from source. Pre-built binaries are not available.
* `ts-unplug` listens on `localhost` only. Other devices on the same network cannot access the proxy.
* Each `ts-unplug` instance creates a separate tailnet node. If you need many instances, consider using [auth keys][kb-auth-keys] with appropriate tags and access control policies.

[kb-auth-keys]: /docs/features/access-control/auth-keys

[kb-tailnet]: /docs/concepts/tailnet

[kb-use-your-tools]: /docs/aperture/use-your-tools

[kb-web-dashboard]: /docs/aperture/reference/dashboard

[xt-gh-ts-plug]: https://github.com/tailscale/ts-plug

[xt-go-install]: https://go.dev/doc/install
