- Two or more OpenClaw instances on the same LAN
- Gateway auth tokens for each instance
- Python 3 (for JSON parsing in the shell script)
- curl
- SSH key access between hosts (for tunnel approach)
On each agent that needs to receive requests, enable the chat completions endpoint.
Edit ~/.openclaw/openclaw.json:
⚠️ Do NOT setbind: "lan"— OpenClaw will refuse to start if the gateway is exposed on a non-loopback address without TLS. Credentials and chat data would be sent in plaintext. See Network Transport for secure options.
{
"hooks": {
"enabled": true,
"token": "a-strong-random-token-here",
"path": "/hooks"
}
}Generate a secure token:
openssl rand -hex 32openclaw gateway restartOpenClaw gateways bind to loopback (127.0.0.1) by default. To enable agent-to-agent communication across the LAN, you need a secure transport layer. Three options:
How it works: Create a persistent SSH tunnel that forwards a local port to the remote agent's loopback gateway. Both gateways stay on bind: loopback. The SSH tunnel provides encryption.
Setup: On Agent A's host, create a tunnel to Agent B:
# Forward local port 18790 → Agent B's loopback:18790
ssh -N -L 18790:127.0.0.1:18790 user@agent-b-hostThen in Agent A's peers.json, point to 127.0.0.1:18790 (the local tunnel endpoint).
For a persistent tunnel, use a systemd service:
# ~/.config/systemd/user/lobsterlan-tunnel-agentb.service
[Unit]
Description=LobsterLAN SSH tunnel to Agent B
After=network-online.target
[Service]
ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-i /home/you/.ssh/agentb_key \
-L 18790:127.0.0.1:18790 \
user@agent-b-host
Restart=always
RestartSec=10
[Install]
WantedBy=default.targetsystemctl --user daemon-reload
systemctl --user enable --now lobsterlan-tunnel-agentbFor bidirectional comms, Agent B also needs a tunnel to Agent A (or Agent A opens a reverse tunnel with -R).
Pros: Zero config changes to gateways, encrypted by default, simple. Cons: Requires SSH key access between hosts, one tunnel per peer.
How it works: Use Caddy, nginx, or similar to terminate TLS and proxy requests to the loopback gateway. Both gateways stay on bind: loopback.
Example with Caddy:
# /etc/caddy/Caddyfile
agent-a.local:18800 {
tls internal
reverse_proxy 127.0.0.1:18789
}
Then in the peer's peers.json, use https://agent-a.local:18800 as the host/port.
Note:
tls internaluses Caddy's built-in CA — peers must trust it, or use--insecure(not recommended). For home LANs, self-signed certs with a shared CA work well.
Pros: Proper TLS, can add rate limiting/logging at proxy layer. Cons: TLS cert management, additional service to maintain.
How it works: Both agents join a Tailscale tailnet. Tailscale Serve provides HTTPS endpoints automatically with valid certificates.
# On each agent's host
tailscale serve --bg 18789Then in peers.json, use the Tailscale hostname:
{
"host": "agent-b.tail12345.ts.net",
"port": 443
}OpenClaw also has built-in Tailscale support:
{
"gateway": {
"tailscale": {
"mode": "serve"
}
}
}Pros: Valid TLS certs, works across sites/networks, zero port management. Cons: Requires Tailscale account, external dependency, agents must be on same tailnet.
| Scenario | Recommendation |
|---|---|
| Home LAN, 2-3 agents | SSH Tunnel — simplest, most secure |
| Office with existing reverse proxy | Reverse Proxy — leverage what you have |
| Agents on different networks | Tailscale — only option that works cross-site |
| Paranoid security requirements | SSH Tunnel — smallest attack surface |
On each agent that wants to send requests, create the peer config:
cd ~/.openclaw/workspace/skills/lobsterlan/config
cp peers.example.json peers.jsonEdit peers.json. When using SSH tunnels, point to the local tunnel endpoint:
{
"self": {
"id": "spock",
"name": "Spock",
"host": "127.0.0.1",
"port": 18789
},
"peers": {
"scotty": {
"name": "Scotty",
"host": "127.0.0.1",
"port": 18790,
"gateway_token": "scottys-gateway-auth-token",
"hooks_token": "scottys-hooks-token",
"chat_completions": true,
"webhooks": true
}
}
}Security:
peers.jsoncontains auth tokens. Add it to.gitignore(already done in the repo).
If using SSH tunnels, no additional firewall rules are needed (traffic stays on loopback).
For reverse proxy or Tailscale approaches, ensure each host allows the relevant port from LAN peers only.
# Allow from specific peer
sudo ufw allow from 192.168.1.3 to any port 18789 proto tcp
# Or allow entire LAN subnet
sudo ufw allow from 192.168.1.0/24 to any port 18789 proto tcp# Add to /etc/pf.conf (or use the macOS Firewall GUI)
pass in on en0 proto tcp from 192.168.1.0/24 to any port 18789# Check peer is reachable
./scripts/lobsterlan.sh status scotty
# Ask a question
./scripts/lobsterlan.sh ask scotty "Hello, can you hear me?"
# Delegate a task
./scripts/lobsterlan.sh delegate scotty "Run a health check and report"For agents to talk both ways, each agent needs:
- Its own APIs enabled (Step 1)
- A secure transport to the other agent (Step 2)
- The other agent configured as a peer (Step 3)
- Is the target gateway running?
openclaw statuson the target host - If using SSH tunnels: is the tunnel running?
systemctl --user status lobsterlan-tunnel-* - Is the chat completions endpoint enabled?
- Did you set
bind: "lan"? Change it back to"loopback"— OpenClaw refuses to start with plaintext on non-loopback addresses. Use an SSH tunnel instead. - Run
openclaw doctor --fixto recover.
- Is the
gateway_tokenin peers.json correct? - Check
gateway.auth.tokenon the target host
- Is
gateway.http.endpoints.chatCompletions.enabledset totrue?
- Is
hooks.enabledset totrue? - Is the hooks path correct? (default:
/hooks)
- Default timeout is 120 seconds
- For long tasks, use
delegateinstead ofask - Check if the target agent is busy (max concurrent requests)
{ "gateway": { // Keep bind as "loopback" — do NOT change to "lan" "http": { "endpoints": { "chatCompletions": { "enabled": true } } } } }