Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt

Use this file to discover all available pages before exploring further.

The gateway enables HTTP webhooks for external integrations. Configure the server, pairing flow, and optional tunnel providers.

Basic Gateway Configuration

{
  "gateway": {
    "port": 3000,
    "host": "127.0.0.1",
    "require_pairing": true,
    "allow_public_bind": false,
    "pair_rate_limit_per_minute": 10,
    "webhook_rate_limit_per_minute": 60,
    "idempotency_ttl_secs": 300,
    "paired_tokens": []
  }
}
gateway.port
number
default:"3000"
Port to bind the HTTP server to.
gateway.host
string
default:"127.0.0.1"
Host address to bind to. Use 127.0.0.1 (localhost) for local-only access, or 0.0.0.0 to allow external connections.
gateway.require_pairing
boolean
default:"true"
Require clients to complete pairing flow before sending webhooks. Highly recommended.
gateway.allow_public_bind
boolean
default:"false"
Allow binding to 0.0.0.0 (all interfaces). Must be explicitly enabled for security.
Setting allow_public_bind: true exposes the gateway to your network. Always use require_pairing: true and configure firewall rules.

Pairing Flow

The pairing flow authenticates external clients:
  1. Client requests pairing code: POST /pair/request
  2. NullClaw displays pairing code to the user
  3. User approves pairing
  4. Client exchanges code for token: POST /pair/confirm
  5. Client uses token in Authorization: Bearer <token> header for all requests

Pairing Configuration

gateway.pair_rate_limit_per_minute
number
default:"10"
Rate limit for pairing requests (per IP address).
gateway.webhook_rate_limit_per_minute
number
default:"60"
Rate limit for webhook requests (per token).
gateway.idempotency_ttl_secs
number
default:"300"
TTL for idempotency keys (prevents duplicate webhook processing).
gateway.paired_tokens
array
Pre-authorized bearer tokens (bypasses pairing flow). Use for service-to-service integration.

Example: Pre-authorized Token

{
  "gateway": {
    "require_pairing": true,
    "paired_tokens": [
      "service-token-abc123"
    ]
  }
}
Clients can use Authorization: Bearer service-token-abc123 without pairing.

Tunnel Configuration

Tunnels expose the local gateway to the public internet:
{
  "tunnel": {
    "provider": "cloudflared"
  }
}
tunnel.provider
string
default:"none"
Tunnel provider:
  • none — No tunnel (default)
  • cloudflared — Cloudflare Tunnel
  • ngrok — Ngrok tunnel
  • tailscale — Tailscale Funnel
  • custom — Custom tunnel command

Cloudflare Tunnel

Automatic HTTPS tunnel via Cloudflare:
{
  "tunnel": {
    "provider": "cloudflared"
  },
  "gateway": {
    "port": 3000,
    "require_pairing": true
  }
}
Setup:
  1. Install cloudflared: Download
  2. Run NullClaw with tunnel enabled:
    nullclaw listen
    
  3. NullClaw will print the public HTTPS URL

Ngrok Tunnel

Expose via ngrok:
{
  "tunnel": {
    "provider": "ngrok"
  },
  "gateway": {
    "port": 3000
  }
}
Setup:
  1. Install ngrok: Download
  2. Set auth token: ngrok authtoken <your-token>
  3. Run NullClaw:
    nullclaw listen
    

Tailscale Funnel

Expose via Tailscale:
{
  "tunnel": {
    "provider": "tailscale"
  },
  "gateway": {
    "port": 3000
  }
}
Setup:
  1. Install Tailscale: Download
  2. Enable Funnel: tailscale funnel on
  3. Run NullClaw

Webhook Endpoints

The gateway exposes these endpoints:

POST /pair/request

Request a pairing code. Response:
{
  "code": "ABCD1234",
  "expires_at": "2026-03-01T15:30:00Z"
}

POST /pair/confirm

Exchange pairing code for token. Request:
{
  "code": "ABCD1234"
}
Response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2026-04-01T15:30:00Z"
}

POST /webhook

Send a message to NullClaw. Headers:
Authorization: Bearer <token>
Content-Type: application/json
X-Idempotency-Key: unique-request-id
Request:
{
  "message": "What's the weather today?",
  "session_id": "optional-session-id",
  "metadata": {
    "source": "external-service"
  }
}
Response:
{
  "response": "I can't check the weather directly, but you can...",
  "session_id": "session-abc123"
}

GET /health

Health check endpoint (no auth required). Response:
{
  "status": "ok",
  "version": "0.1.0"
}

Example: Public Gateway with Tunnel

{
  "gateway": {
    "port": 3000,
    "host": "127.0.0.1",
    "require_pairing": true,
    "pair_rate_limit_per_minute": 5,
    "webhook_rate_limit_per_minute": 30
  },
  "tunnel": {
    "provider": "cloudflared"
  }
}
This configuration:
  • Binds gateway to localhost (secure)
  • Requires pairing for all clients
  • Exposes via Cloudflare Tunnel (automatic HTTPS)
  • Rate limits pairing and webhook requests

Example: Service Integration

{
  "gateway": {
    "port": 3000,
    "host": "127.0.0.1",
    "require_pairing": true,
    "paired_tokens": [
      "service-token-production-xyz"
    ]
  }
}
Use for service-to-service integration:
curl -X POST http://localhost:3000/webhook \
  -H "Authorization: Bearer service-token-production-xyz" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: request-$(date +%s)" \
  -d '{
    "message": "Analyze the latest error logs",
    "session_id": "monitoring-service"
  }'

Security Best Practices

Pairing flow prevents unauthorized access. Only disable if using paired_tokens for service accounts.
Bind to 127.0.0.1 (localhost) unless you need external access. Use tunnels for internet exposure.
Set conservative rate limits to prevent abuse:
  • pair_rate_limit_per_minute: 5 (pairing is rare)
  • webhook_rate_limit_per_minute: 30 (adjust based on usage)
Always send X-Idempotency-Key header to prevent duplicate processing of webhooks.
Pre-authorized tokens in paired_tokens don’t expire. Rotate them periodically.
The gateway uses HTTPS when accessed via tunnels (cloudflared, ngrok, tailscale). Local access uses HTTP.