Exposure Modes
Exposure mode controls how the daemon is reachable over the network. Most setups stay on local mode. You only need a tunnel mode when something external (GitHub webhooks, CI, a second machine) needs to reach the daemon.
Before you begin
Section titled “Before you begin”- Netclaw installed and initialized (
netclaw init). See Installation if needed. - For Tailscale modes:
tailscaledinstalled and running. - For Cloudflare Tunnel:
cloudflaredinstalled and configured.
| Mode | Config value | Required process | Reachability | Risk |
|---|---|---|---|---|
| Local | local | None | Loopback only | Lowest |
| Tailscale Serve | tailscale-serve | tailscaled | Your Tailscale network (called a tailnet), HTTPS | Low |
| Tailscale Funnel | tailscale-funnel | tailscaled | Public internet via Tailscale | High |
| Cloudflare Tunnel | cloudflare-tunnel | cloudflared | Public internet via Cloudflare | High |
The netclaw init wizard covers this at step 9:

Options marked with a warning triangle expose the daemon to the public internet. Tailscale Serve is the recommended remote mode: tailnet-only access, no public exposure.
Configuration
Section titled “Configuration”Set the mode in the Daemon section of ~/.netclaw/config/netclaw.json.
Local (default)
Section titled “Local (default)”No Daemon section needed. The daemon binds to 127.0.0.1:5199 and is only reachable from the same machine.
{}Tailscale Serve
Section titled “Tailscale Serve”{ "Daemon": { "ExposureMode": "tailscale-serve" }}Tailscale Serve creates an HTTPS endpoint on your tailnet that proxies to the daemon’s local port. Only devices on your tailnet can reach it. Then run tailscale serve to proxy your tailnet hostname to 127.0.0.1:5199.
Tailscale Funnel
Section titled “Tailscale Funnel”{ "Daemon": { "ExposureMode": "tailscale-funnel" }}Funnel extends Serve to the public internet. Anyone with the URL can reach the daemon, though netclaw’s device authentication still applies. Configure it with tailscale funnel. Only use Funnel when you need public internet access — Tailscale Serve covers everything else.
Cloudflare Tunnel
Section titled “Cloudflare Tunnel”{ "Daemon": { "ExposureMode": "cloudflare-tunnel" }}Routes traffic through Cloudflare’s network to the daemon. Set up cloudflared with a Cloudflare Tunnel pointed at 127.0.0.1:5199, and pair it with a Cloudflare Access policy to restrict who can connect.
Full Daemon section
Section titled “Full Daemon section”| Field | Type | Default | Notes |
|---|---|---|---|
Host | string | 127.0.0.1 | IP address the daemon binds to |
Port | int | 5199 | TCP port |
ExposureMode | string | local | local, tailscale-serve, tailscale-funnel, or cloudflare-tunnel |
{ "Daemon": { "Host": "127.0.0.1", "Port": 5199, "ExposureMode": "tailscale-serve" }}Case-insensitive — tailscale-serve, TailscaleServe, and TAILSCALE-SERVE all work.
Docker users: if you switch to a tunnel mode, update your container’s port binding to match the Host and Port values here. See Docker Deployment for details.
Environment variables
Section titled “Environment variables”Override any field with NETCLAW_Daemon__ prefixed env vars:
NETCLAW_Daemon__ExposureMode=tailscale-serveNETCLAW_Daemon__Host=127.0.0.1NETCLAW_Daemon__Port=5199Double underscores separate path segments, following the .NET configuration convention.
Restart required
Section titled “Restart required”Host, Port, and ExposureMode require a daemon restart — they aren’t hot-reloaded. Other config changes trigger an automatic restart; the daemon drains active sessions and restarts itself.
# systemdsystemctl --user restart netclaw
# Dockerdocker restart netclawInbound webhooks
Section titled “Inbound webhooks”Tunnel modes make inbound webhooks possible. External services like GitHub or CI systems can trigger autonomous runs via HTTP POST. The netclaw init wizard asks about this right after exposure mode selection:

They do nothing in local mode.
Device pairing
Section titled “Device pairing”Non-local modes require at least one paired device or an alternative remote authentication scheme. Without one, the daemon refuses to start — remote clients have no way to authenticate.
The init wizard handles this: it generates a bootstrap device token when you pick a tunnel mode, writing it to ~/.netclaw/config/devices.json and ~/.netclaw/config/secrets.json. For manual setup, pair a device before starting the daemon:
netclaw daemon pairStartup validation
Section titled “Startup validation”If the mode requires tailscaled or cloudflared and that process isn’t running, you’ll see this in the daemon logs (~/.netclaw/logs/daemon.log or journalctl --user -u netclaw for systemd):
Daemon startup aborted: ExposureMode is 'tailscale-serve' but the requiredtunnel process 'tailscaled' is not running. Start 'tailscaled' before startingNetclaw, or set ExposureMode to 'local' in netclaw.json.If no paired devices exist and no alternative auth scheme is configured:
Daemon startup aborted: ExposureMode is 'tailscale-serve' but no paired devicesexist and no alternative remote authentication scheme is configured. Pair a devicewith 'netclaw daemon pair' or configure another remote auth scheme before startingNetclaw.Both are fatal. The daemon won’t start until you fix the underlying issue.
Troubleshooting
Section titled “Troubleshooting”Start here: netclaw doctor
Section titled “Start here: netclaw doctor”Run netclaw doctor first — it has a dedicated exposure-mode check.

Tunnel process not running
Section titled “Tunnel process not running”Startup aborted with “required tunnel process is not running.”
Start the required process first:
# Tailscale modessudo systemctl start tailscaled
# Cloudflare Tunnelsudo systemctl start cloudflaredOr switch to local mode if you don’t need remote access:
{ "Daemon": { "ExposureMode": "local" }}No paired devices
Section titled “No paired devices”Startup aborted with “no paired devices exist and no alternative remote authentication scheme is configured.”
Pair a device:
netclaw daemon pairOr re-run the init wizard, which generates a bootstrap token automatically when you select a tunnel mode:
netclaw initNon-loopback bind in local mode
Section titled “Non-loopback bind in local mode”netclaw doctor reports a warning: “ExposureMode is ‘local’ but bind address is not loopback.”
The daemon is reachable beyond localhost without tunnel protection. Either bind to loopback:
{ "Daemon": { "Host": "127.0.0.1" }}Or switch to the exposure mode that reflects how the daemon is actually reachable:
{ "Daemon": { "Host": "127.0.0.1", "ExposureMode": "tailscale-serve" }}Related pages
Section titled “Related pages”- Docker Deployment — containerized daemon with loopback port binding
- systemd Service — bare-metal Linux service management
- Webhooks — inbound webhook route configuration
- Models — model slot configuration
netclaw doctor— built-in health check diagnostics
Resources
Section titled “Resources”- Tailscale Serve documentation — set up HTTPS endpoints on your tailnet
- Tailscale Funnel documentation — expose tailnet services to the public internet
- Cloudflare Tunnel documentation — route traffic through Cloudflare to your origin
- Cloudflare Access documentation — identity-aware access policies for tunneled services
- Tailscale download — install Tailscale on your platform