Why Tunnels?
Webhook-based channels (Telegram, Discord, Slack) require a public HTTPS URL to receive events. Tunnels provide this by:- Establishing an outbound connection from your gateway to a tunnel server
- The tunnel server provides a public HTTPS URL
- Incoming webhook requests are forwarded through the tunnel to your local gateway
Supported Providers
NullClaw supports multiple tunnel providers:Cloudflare Tunnel
Free, unlimited bandwidth, zero-trust security
ngrok
Popular, reliable, generous free tier
Tailscale Funnel
Tailnet-based, secure, funnel mode for public access
Custom
Bring your own tunnel (bore, localtunnel, etc.)
Configuration
Add tunnel configuration to~/.nullclaw/config.json:
Cloudflare Tunnel
Cloudflare Tunnel (formerly Argo Tunnel) provides free, unlimited bandwidth with zero-trust security.Prerequisites
- Cloudflare account (free)
- Install
cloudflaredCLI:
Setup
1
Login to Cloudflare
2
Create tunnel
3
Get tunnel token
eyJ...).4
Add to config
How It Works
NullClaw spawnscloudflared as a child process:
Implementation
Fromsrc/tunnel.zig:
ngrok
ngrok is a popular tunnel service with a generous free tier.Prerequisites
- ngrok account (free): https://ngrok.com/signup
- Install ngrok CLI:
Setup
1
Get auth token
Login to ngrok dashboard and copy your auth token from:
https://dashboard.ngrok.com/get-started/your-authtoken
2
Add to config
How It Works
NullClaw spawns ngrok:Custom Domain
With a paid ngrok plan, you can use a custom domain:Tailscale Funnel
Tailscale Funnel exposes a service on your tailnet to the public internet.Prerequisites
- Tailscale account (free): https://tailscale.com/
- Install Tailscale:
- Login to Tailscale:
Setup
1
Enable funnel mode
Funnel vs Serve
tailscale serve: Exposes to your tailnet only (private)tailscale funnel: Exposes to public internet (requires funnel mode)
"funnel": true for public webhooks.
How It Works
NullClaw spawns:Custom Tunnels
Bring your own tunnel provider (bore, localtunnel, etc.).Configuration
Placeholders
{port}: Replaced with gateway port (e.g.,3000){host}: Replaced withlocalhost
How It Works
NullClaw:- Replaces placeholders in
start_command - Spawns the command as a child process
- Reads stdout for a URL matching
https://...
https://abc123.bore.pub.
No Tunnel (Local Only)
Setprovider to "none" to disable tunnels:
localhost. Webhooks will not work.
Tunnel State
NullClaw tracks tunnel state:Security Considerations
Webhook Secrets
Always configure webhook secret tokens: Telegram:Tunnel Lifecycle
Start
Stop
URL Extraction
NullClaw scans tunnel process output for HTTPS URLs:Troubleshooting
Tunnel binary not found
Ensure the tunnel CLI is in your PATH:URL not extracted
NullClaw reads tunnel process output to find the URL. If extraction fails:- Check tunnel process logs
- Verify the tunnel is actually starting
- Ensure output contains
https://URL
Tunnel disconnects
Free tunnel plans may have:- Idle timeouts
- Bandwidth limits
- Connection limits
Port already in use
If the gateway port is already bound:Implementation Reference
The tunnel system is implemented insrc/tunnel.zig with a vtable-based architecture:
NoneTunnel(no-op)CloudflareTunnelNgrokTunnelTailscaleTunnelCustomTunnel
~/workspace/source/src/tunnel.zig for full implementation.