Inter-host OpenClaw session messaging over reachable HTTPS using built-in gateway webhook hooks. Use when: (1) sending a message from this OpenClaw instance to another host's OpenClaw session, (2) checking status/health of a remote OpenClaw peer, (3) managing the peer registry (adding/removing/listing known peers), (4) exchanging bootstrap trust material for new peers, (5) any cross-host agent communication that should NOT go through visible chat channels like Telegram/WhatsApp/Discord. Triggers: "send to <peer>", "message the other host", "antenna send", "antenna status", "antenna peers exchange", "cross-host message", "inter-host relay", "ping <peer>", "peer list".
Resources
9Install
npx skillscat add clawreefantenna/antenna Install via the SkillsCat registry.
Antenna — Inter-Host OpenClaw Messaging (v1.1.0)
Send messages between OpenClaw instances over reachable HTTPS via the built-in /hooks/agent webhook.
Prerequisites
Each participating host needs:
- OpenClaw gateway running with hooks enabled (
hooks.enabled: true) - A reachable HTTPS endpoint for
/hooks/agent - Antenna agent registered in gateway config (
agentssection) hooks.allowedAgentIdsincludes"antenna"hooks.allowedSessionKeyPrefixesincludes"hook:antenna"- Host-specific Antenna config in:
antenna-config.jsonantenna-peers.json
Normal path:
- Run
antenna setupto generate the live runtime files. - Use
antenna-config.example.jsonandantenna-peers.example.jsonas tracked reference templates only.
Notes:
- Peers do not need to share one tailnet or one central hub.
- Tailscale Funnel is a convenient default, but reverse proxies, VPS/domain-hosted HTTPS, Cloudflare Tunnel, and similar paths also work.
Architecture
Messages flow through a script-first relay pipeline:
- Sender runs
antenna-send.shwhich builds an[ANTENNA_RELAY]envelope and POSTs it to the recipient's/hooks/agentendpoint. - Recipient gateway dispatches to the dedicated Antenna agent.
- Antenna agent runs
antenna-relay.shwhich deterministically parses, validates, and formats the message. - Antenna agent calls
sessions_sendto inject the formatted message into the target session. - Message appears persistently in the target conversation thread.
The LLM never performs relay parsing logic; the scripts do.
Trust Model
Antenna trust is layered:
- Peer URL — where to reach that installation
- Hook bearer token — protects webhook ingress
- Per-peer runtime identity secret — authenticates claimed sender identity when configured
- Inbound session allowlist — limits where inbound relay may deliver
- Untrusted-input framing — reminds receiving agents the relayed content may be external
For peer onboarding, Antenna now prefers Layer A encrypted bootstrap exchange using age.
Configuration
Live runtime files are local installation state:
antenna-config.jsonantenna-peers.json
Tracked reference files live beside them:
antenna-config.example.jsonantenna-peers.example.json
Use antenna setup for normal installation; use the *.example.json files for schema reference or manual recovery.
antenna-config.json
{
"max_message_length": 10000,
"default_target_session": "main",
"relay_agent_id": "antenna",
"relay_agent_model": "openai/gpt-5.4",
"local_agent_id": "<your-agent-id>",
"install_path": "<absolute-path-to-this-skill-directory>",
"log_enabled": true,
"log_path": "antenna.log",
"log_max_size_bytes": 10485760,
"log_verbose": false,
"mcs_enabled": false,
"mcs_model": "sonnet",
"allowed_inbound_sessions": ["main", "antenna"],
"allowed_inbound_peers": ["<peer-a>", "<peer-b>"],
"allowed_outbound_peers": ["<peer-a>", "<peer-b>"],
"rate_limit": {
"per_peer_per_minute": 10,
"global_per_minute": 30
}
}Key fields:
relay_agent_model— use a full provider/model ID, not a local aliaslocal_agent_id— used to resolvemain→agent:<id>:maininstall_path— absolute path to this skill directoryallowed_inbound_sessions— inbound delivery allowlistallowed_inbound_peers/allowed_outbound_peers— peer allowlistsrate_limit.*— inbound abuse controls
antenna-peers.json
{
"<your-host-id>": {
"url": "https://<your-reachable-hostname>",
"token_file": "secrets/hooks_token_<your-host-id>",
"peer_secret_file": "secrets/antenna-peer-<your-host-id>.secret",
"exchange_public_key": "age1...",
"agentId": "antenna",
"display_name": "My Host",
"self": true
},
"<remote-peer-id>": {
"url": "https://<remote-reachable-hostname>",
"token_file": "secrets/hooks_token_<remote-peer-id>",
"peer_secret_file": "secrets/antenna-peer-<remote-peer-id>.secret",
"exchange_public_key": "age1...",
"agentId": "antenna",
"display_name": "Remote Host"
}
}Key fields:
url— reachable HTTPS hook base URLtoken_file— bearer token for that peerpeer_secret_file— per-peer runtime identity secretexchange_public_key— peer'sagepublic key for Layer A exchangeself— marks the local host entry
Usage
Send a message
scripts/antenna-send.sh <peer> "Your message here"
antenna msg <peer> "Your message here"
antenna msg <peer> --subject "Config sync" "Here's the block you need..."
antenna msg <peer> --session "agent:<agent-id>:mychannel" "Your message"
echo "Long message body..." | antenna send <peer> --stdin
antenna send <peer> --dry-run "Test message"Peer onboarding / bootstrap exchange
Preferred encrypted flow:
antenna peers exchange keygen
antenna peers exchange pubkey
antenna peers exchange initiate <peer-id> --pubkey <age1...> --print
antenna peers exchange import <bundle-file>
antenna peers exchange reply <peer-id>Optional direct-send convenience:
antenna peers exchange initiate <peer-id> \
--pubkey <age1...> \
--email someone@example.com \
--send-emailLegacy/manual fallback:
antenna peers exchange <peer-id> --export
antenna peers exchange <peer-id> --import <file>
antenna peers exchange <peer-id> --import-value <hex>Notes:
- Secure Layer A requires
ageandage-keygen - Optional direct-send requires
himalaya - Email is convenience transport only, not part of the trust model
- Import shows a preview and asks before allowlist changes unless
--yesis used
Health and status
antenna doctor
antenna uninstall --dry-run
antenna uninstall
antenna peers list
antenna peers test <id>
antenna status
antenna log --tail 50Testing
antenna test <model>
antenna test-suite --tier A
antenna test-suite --model <m>
antenna test-suite --models "<m1>,<m2>"
antenna test-suite --reportInbox (optional approval queue)
When inbox_enabled is true in config, inbound messages from peers not in inbox_auto_approve_peers are queued for review instead of being relayed immediately. Auto-approved peers bypass the queue and relay instantly (current behavior).
antenna inbox # list pending messages (table view)
antenna inbox count # pending count (for heartbeat/cron checks)
antenna inbox show <ref> # full message body for a ref
antenna inbox approve all # approve everything pending
antenna inbox approve 1,3,5-7 # selective approval (commas and ranges)
antenna inbox deny all # reject everything pending
antenna inbox deny 2,4 # selective denial
antenna inbox drain # output delivery JSON for approved, remove denied
antenna inbox clear # purge all processed itemsDelivery flow: antenna inbox drain outputs one JSON line per approved message with sessionKey and message fields. The calling agent (your primary assistant) reads these and calls sessions_send for each. This avoids re-entering the relay agent via /hooks/agent.
Configuration:
{
"inbox_enabled": false,
"inbox_auto_approve_peers": ["trusted-peer-id"],
"inbox_queue_path": "antenna-inbox.json"
}Notes:
- Disabled by default — existing behavior is unchanged
- Auto-approve list lets trusted peers bypass the queue (progressive trust)
- Queue file is local runtime state (gitignored)
- Ref numbers auto-increment and support range selection
- When inbox is enabled, the relay agent only needs
exec(notsessions_send), reducing its required permissions
Heartbeat / cron integration:
Add to your HEARTBEAT.md:
## Antenna inbox check
- Run: `antenna inbox count`
- If > 0: run `antenna inbox list` and mention itOr set up a cron job for automated handling:
Check antenna inbox. If there are pending messages from peers
in [trusted-peer-id], approve and drain them. For anything else,
summarize the queue and ask me.Conversational usage: Ask your assistant "any Antenna messages waiting?" — it can run antenna inbox list, you review, then say "approve 1 and 3, deny 2" and it handles the rest.
Security Notes
- Relay agent is script-first and non-interpreting
- Inbound sessions are allowlisted
- Sender peer must be allowlisted
- Per-peer identity secret can authenticate sender claims
- Tokens and secrets are file-backed and should be
chmod 600 antenna statusaudits secret/token file permissions- Relayed content is framed as potentially untrusted input
- Rate limiting throttles inbound bursts
Troubleshooting
- Gateway won't start: Run
antenna doctor - Want a clean slate: Run
antenna uninstall(use--dry-runfirst if you want a preview) - 401 Unauthorized: wrong hook bearer token
- 403 Forbidden: session prefix/agent restrictions or peer policy mismatch
- Relay rejected: peer not allowlisted, session not allowlisted, or identity secret mismatch
- Encrypted exchange fails immediately:
age/age-keygenmissing - Email send convenience fails:
himalayamissing or no suitable account configured - Message sent but not visible: check
commands.ownerDisplay = "raw"on the receiver; without it, hook-delivered messages are processed but invisible in Control UI - Exec denied / allowlist miss: ensure relay agent instructions use only simple commands (no
$(...), heredocs, or chaining); theantenna-relay-exec.shwrapper exists for this - Repeated approval prompts: ensure Antenna agent has
tools.exec.security: "allowlist",tools.exec.ask: "off", andsandbox: { mode: "off" }in registration
File Inventory
skills/antenna/
├── SKILL.md
├── README.md
├── CHANGELOG.md
├── antenna-config.example.json
├── antenna-peers.example.json
├── antenna-peers.json
├── antenna-config.json
├── antenna.log
├── bin/
│ └── antenna
├── scripts/
│ ├── antenna-send.sh
│ ├── antenna-relay.sh
│ ├── antenna-health.sh
│ ├── antenna-peers.sh
│ ├── antenna-doctor.sh
│ ├── antenna-exchange.sh
│ ├── antenna-relay-exec.sh
│ ├── antenna-inbox.sh
│ ├── antenna-model-test.sh
│ └── antenna-test-suite.sh
├── docs/
│ ├── ANTENNA-RELAY-FSD.md
│ └── SECURITY-ASSESSMENT-v1.0.20.md
└── agent/
├── AGENTS.md
└── TOOLS.mdNotes:
antenna-config.json,antenna-peers.json, andantenna-inbox.jsonare local runtime files (gitignored)antenna-config.example.jsonandantenna-peers.example.jsonare tracked reference templates
Gateway / Agent Registration
On each host:
- agent
antennaregistered in OpenClaw config underagentswith:agentDirandworkspaceboth pointing to the Antennaagent/directorysandbox: { mode: "off" }tools.exec: { security: "allowlist", ask: "off" }- restrictive
tools.deny(block web, browser, image, cron, memory tools)
hooks.allowedAgentIdsincludes"antenna"hooks.allowedSessionKeyPrefixesincludes"hook:antenna"commands.ownerDisplayset to"raw"(required for relay messages to appear in Control UI)- Exec allowlist entries for Antenna agent:
/usr/bin/bash,/usr/bin/echo,/usr/bin/jq,/usr/bin/cat