# Linux over Tailscale

Your Mac and iPhone can reach Looper on the Linux host. The host cannot start
connections to other tailnet devices. Internet access still works.

## 1. Set the policy

Download the [policy template](/docs/isolated-linux/fleet-policy.json).
Replace the example addresses: `.10` is your Mac, `.11` your iPhone, `.20` the
Linux host, and `.21` another host. All use the `100.64.0` prefix.

Merge the template into your existing policy, preserving unrelated settings.
It allows only **TCP 8765, TCP 8766, and UDP 8766** from your two controllers
to `tag:looper-bot`. Port 8765 also permits pairing; use trusted devices.

- Remove broad allow rules that match the bot. [Grants are additive](https://tailscale.com/docs/reference/syntax/grants).
- Tag each isolated Linux host **only `tag:looper-bot`**. Keep personal devices untagged.
- Run the template's six policy tests before saving.
- After tagged access works, remove any old direct-IP grant to the host.

This policy grants no SSH access. Use the local or hosting-provider console
for setup. On a shared LAN, use a separate VM/VLAN or an egress firewall too:
Tailscale policy controls tailnet traffic.

## 2. Set up Linux

Use an ordinary user with Tailscale authenticated and nftables installed.
Download these files into one directory:

[Firewall](/docs/isolated-linux/looper-ingress.nft) ·
[Firewall service](/docs/isolated-linux/looper-ingress.service) ·
[Looper service override](/docs/isolated-linux/tailscale.conf) ·
[Environment](/docs/isolated-linux/server.env.example)

Replace the Mac and iPhone addresses in the firewall. It allows loopback and
those two devices through `tailscale0`, then drops other Looper ingress.
Keep Looper's wildcard listeners: its local CLI and hooks need loopback.

<details>
<summary>Linux setup commands</summary>

Clear routing roles and disable Tailscale SSH:

```sh
sudo tailscale set --accept-routes=false --advertise-routes= \
  --advertise-exit-node=false --advertise-connector=false \
  --exit-node= --ssh=false --webclient=false
```

Validate and install the firewall. The service expects nft at `/usr/sbin/nft`.

```sh
sudo nft -c -f looper-ingress.nft
sudo install -d -m 755 /etc/nftables.d
sudo install -m 644 looper-ingress.nft /etc/nftables.d/looper-ingress.nft
sudo install -m 644 looper-ingress.service /etc/systemd/system/looper-ingress.service
sudo systemctl daemon-reload
sudo systemctl enable --now looper-ingress.service
```

Download the [Linux archive and checksum](https://downloads.looper.fyi/),
then install without starting the server:

```sh
LOOPER_ARCHIVE='replace-with-downloaded-linux-archive.tar.gz'
sha256sum -c "$LOOPER_ARCHIVE.sha256"
tar -xzf "$LOOPER_ARCHIVE"
bash looper-linux/looper-linux.sh install --no-start
export PATH="$HOME/.local/bin:$PATH"
install -d -m 700 "$HOME/.config/looper"
install -d -m 700 "$HOME/.config/systemd/user/looper.service.d"
install -m 644 tailscale.conf "$HOME/.config/systemd/user/looper.service.d/tailscale.conf"
```

For a new installation, copy `server.env.example` to
`~/.config/looper/server.env` with mode 600. For an existing installation,
merge its three listener settings; retain the store path and other settings.
`--no-start` leaves an existing process running.

```sh
systemctl --user daemon-reload
systemctl --user enable looper.service
systemctl --user restart looper.service
curl --fail --max-time 3 http://127.0.0.1:8765/health
```

The override runs `serve --tailscale`. Its user needs access to the local
Tailscale daemon. For startup after logout, an administrator can run
`sudo loginctl enable-linger "$USER"`.

</details>

If your firewall manager flushes nftables on reload, integrate this file with
that manager. Check `sudo nft list table inet looper_ingress` after reload and
reboot; an active service alone does not prove the rules exist.

## 3. Run Grok and pair

Install and authenticate **Grok Build** as the same user. Back up existing
hooks, then run:

```sh
looper hooks register grok
# From your project directory:
looper grok
```

On iPhone, connect Tailscale. In Looper, choose **Add Computer**, enable
Tailscale, and enter the host's IP or full `.ts.net` name. Choose **Open Server
Pairing Page** and enter the generated four-letter code. For an already paired
host, select its Tailscale route.

Check access from your approved Mac, using the host's real address:

```sh
curl --fail --max-time 3 http://100.64.0.20:8765/health
```

Then verify:

- The phone connects and sends a harmless prompt to the same Grok session.
- HTTP/3 uses UDP 8766 in app diagnostics.
- Bot-to-device and bot-to-bot connections fail against known listening ports.
- Looper and the firewall survive reboot.

[Troubleshooting](/docs/troubleshooting) · [Linux installation](/docs/linux) · [Docs](/docs)
