Skip to content

Pairing Remote Devices

Your netclaw daemon runs on one machine but you want to use the CLI from another — a laptop, a second server, a container you can’t shell into. Pairing takes two commands: generate a time-limited code on the daemon, exchange it from the remote device, and the client gets a bearer token for all future requests.

  • Netclaw installed on both machines (netclaw init completed on the daemon host). The remote device only needs the netclaw binary — you don’t need to run netclaw init on it. Pairing replaces init for client-only machines.
  • The daemon’s exposure mode set to something other than local — remote pairing doesn’t work over loopback. The default daemon port is 5199; make sure the chosen proxy or tunnel path can reach the daemon.
  • Network connectivity between the two machines (same tailnet, tunnel, or direct)

By default, the daemon only listens on loopback. You need to change this before any remote device can connect.

During netclaw init, Step 9 handles this:

Network exposure mode selection during netclaw init

ModeConfig valueRequiresWho can reach it
LocallocalNothingLoopback only (default)
Reverse Proxyreverse-proxyReverse proxy + trusted proxy configWhatever the proxy exposes
Tailscale Servetailscale-servetailscaled runningSame tailnet
Tailscale Funneltailscale-funneltailscaled runningPublic internet
Cloudflare Tunnelcloudflare-tunnelcloudflared runningInternet via Cloudflare Tunnel

To change the mode after init, edit ~/.netclaw/config/netclaw.json:

{
"Daemon": {
"ExposureMode": "tailscale-serve"
}
}

For tunnel setup details — Tailscale Serve commands, Cloudflare Tunnel configuration, and how each mode binds the daemon — see Exposure Modes.

Restart the daemon after changing exposure mode (netclaw daemon stop && netclaw daemon start, or systemctl restart netclaw if you’re using systemd).

On the machine running the daemon:

Terminal window
netclaw daemon pair

Output:

Pairing code: ABCD-EF23
Expires at: 14:32:15 (local time)
On the remote device, run:
netclaw pair http://my-server:5199

Codes expire after 5 minutes and are single-use. Generating a new code replaces any previous one — only one active at a time.

The character set (23456789ABCDEFGHJKLMNPQRSTUVWXYZ) deliberately excludes 0/O/1/I/L to avoid misreads.

If the daemon runs in a container, docker exec is still the simplest daemon-host path:

Terminal window
docker exec -it <container-name> netclaw daemon pair

The daemon also logs pairing codes at Information level:

Terminal window
docker logs <container-name> | grep "Pairing code"

Use docker logs when you only need the code. Use docker exec when you want to pair or manage devices from inside the container.

On the remote machine:

Terminal window
netclaw pair http://my-server:5199

For Tailscale, Cloudflare Tunnel, and reverse-proxy modes, the endpoint URL differs — check Exposure Modes for the correct format for each mode.

The CLI prompts for two things:

Pairing code (XXXX-XXXX): ABCD-EF23
Device name [my-laptop]:

Device name defaults to the machine’s hostname. Hit Enter to accept the default, or type a custom name.

On success:

  • The CLI saves the bearer token to ~/.netclaw/config/secrets.json
  • The daemon endpoint is written to ~/.netclaw/client/config.json
  • netclaw chat, netclaw status, and all other remote commands work from here

If a device with the same name already exists on the daemon, the exchange returns HTTP 409. Revoke the old device first (see below).

You can pair as many devices as you need.

From the newly paired device:

Terminal window
netclaw status

If you get system status back, pairing worked. The CLI attaches the bearer token automatically from here on.

Run these from the daemon host.

Terminal window
netclaw daemon devices

Shows Name, Created, and Last Used for each paired device. Prints No paired devices. if none exist.

Terminal window
netclaw daemon devices revoke <name>

After revocation, the device gets 401 on its next request. Use this when a device is lost, retired, or you need to re-pair with the same name.

  • Raw tokens never hit disk on the daemon side — it stores a SHA256 hash with a per-device salt in ~/.netclaw/config/devices.json (file permissions 600 on Linux)
  • Clients keep the raw token in ~/.netclaw/config/secrets.json
  • In local mode, loopback connections still work without bearer auth
  • In remote-auth-required modes, bearer auth may still be required even on a loopback control-plane endpoint

When the CLI connects, it checks these in order:

  1. NETCLAW_DAEMON_ENDPOINT environment variable
  2. ~/.netclaw/client/config.json (written by netclaw pair)
  3. Daemon bind config (Daemon.Host + Daemon.Port) if available
  4. Default: http://127.0.0.1:5199

If the daemon bind host is a wildcard like 0.0.0.0, the CLI normalizes it to a connectable local endpoint instead of trying to connect to the wildcard address.

Override with the env var when you need to switch between multiple daemons without re-pairing.

The pairing endpoint has three layers of brute-force protection:

LayerBehavior
Rate limiter5 attempts/minute per IP
Fail2ban-style guard10 failures in 15 min blocks the IP for 15 min
No-code-pending gateReturns 404 when no code is active

With a 32-character alphabet, 8-character codes, 5-minute expiry, and 5 attempts/minute, that makes brute-force guessing impractical.

”Authentication failed: the daemon rejected the bearer token”

Section titled “”Authentication failed: the daemon rejected the bearer token””

The token was revoked or the daemon’s device store was reset. Re-pair:

Terminal window
netclaw pair <endpoint>

If the daemon is already running, run netclaw daemon pair on the daemon host to get a fresh code.

If the device store was lost and a non-local daemon now fails startup, temporarily switch Daemon.ExposureMode to local or restore devices.json and secrets.json from backup. Then start the daemon, run netclaw daemon pair, and switch back.

Codes last 5 minutes. Generate a new one with netclaw daemon pair and try again.

A device with that name is already paired. Either pick a different name during pairing, or revoke the existing one first:

Terminal window
netclaw daemon devices revoke <name>

Check the basics:

  1. Is the daemon running? (netclaw daemon start on the host)
  2. Is the exposure mode set to something other than local?
  3. Can you reach the daemon through the intended endpoint or proxy path?
  4. Is a firewall blocking port 5199?

If the daemon uses reverse-proxy, also confirm the daemon itself is not still bound to loopback and that TrustedProxies includes the proxy’s source IP or CIDR.

Run netclaw doctor on the daemon host — it includes exposure-mode health checks.

Wait 15 minutes, or fix the issue from a different IP. Too many wrong codes temporarily block your IP for 15 minutes.

  • netclaw chat — start talking to your agent from the paired device
  • netclaw status — check daemon connectivity and endpoint URL
  • netclaw doctor — diagnose exposure mode and connectivity issues
  • Exposure Modes — all Daemon.ExposureMode options and tunnel setup
  • netclaw init — the setup wizard handles bootstrap pairing during non-local setup