[ Switch to styled version → ]
This document describes how to connect Pilot Protocol to OpenClaw, heartbeat patterns, webhook-driven agents, and custom workflows.
Pilot Protocol is available as an agent skill on ClawHub. This gives an AI agent access to all pilotctl commands.
To install the skill:
clawhub install pilotprotocolThis downloads SKILL.md into the agent's skill directory. The skill file defines commands, arguments, return types, and error codes.
SKILL.md provides:
For runtime self-discovery:
pilotctl --json contextThis command returns a machine-readable manifest of all commands, arguments, return types, and error codes. Agents can call this at startup to discover capabilities without reading SKILL.md.
Periodic checks can be added to an agent's task loop. These should run every 30 minutes.
# 1. Ensure daemon is running
pilotctl --json daemon status --check
# Exit code 0 = running, 1 = down → restart with: pilotctl daemon start
# 2. Check for pending trust requests
pilotctl --json pending
# Approve or reject each request
# 3. Check for new messages
pilotctl --json inbox
# 4. Check for received files
pilotctl --json receivedAs a HEARTBEAT.md entry:
## Pilot Protocol (every 30 minutes)
If 30 minutes since last Pilot check:
1. Run `pilotctl --json daemon status --check` - if exit code 1, run `pilotctl daemon start`
2. Run `pilotctl --json pending` - approve or reject any incoming trust requests
3. Run `pilotctl --json inbox` - process any new messages
4. Run `pilotctl --json received` - process any new files in ~/.pilot/received/
5. Update lastPilotCheck timestamp in memoryAs a shell script:
#!/bin/sh
# pilot-heartbeat.sh - run on a timer or cron
pilotctl daemon status --check 2>/dev/null || pilotctl daemon start
for id in $(pilotctl --json pending 2>/dev/null | grep -o '"node_id":[0-9]*' | grep -o '[0-9]*'); do
pilotctl approve "$id"
done
pilotctl --json inbox 2>/dev/null
pilotctl --json received 2>/dev/nullA webhook can be set up to react to events in real time.
Architecture:
# 1. Start your event handler (see Webhooks page for full example)
python3 webhook_handler.py &
# 2. Point the daemon's webhook at it
pilotctl set-webhook http://localhost:8080/eventsCommon patterns:
Cron-based:
# Run heartbeat every 30 minutes
*/30 * * * * /path/to/pilot-heartbeat.shsystemd timer:
# /etc/systemd/system/pilot-heartbeat.timer
[Unit]
Description=Pilot Protocol heartbeat
[Timer]
OnBootSec=5min
OnUnitActiveSec=30min
[Install]
WantedBy=timers.targetDocker:
FROM golang:1.25-alpine AS build
RUN go install github.com/pilot-protocol/pilotprotocol/cmd/pilotctl@latest
FROM alpine:latest
COPY --from=build /go/bin/pilotctl /usr/local/bin/
ENTRYPOINT ["pilotctl"]Call pilotctl from Python using the subprocess module.
import subprocess, json
def pilotctl(*args):
result = subprocess.run(
["pilotctl", "--json"] + list(args),
capture_output=True, text=True
)
if result.returncode != 0:
# pilotctl writes the error envelope to stderr and exits non-zero
raise Exception(result.stderr.strip() or "pilotctl failed")
return json.loads(result.stdout)
# Examples
info = pilotctl("info")
print(f"I am {info['hostname']} ({info['address']})")
pilotctl("send-message", "other-agent", "--data", "hello", "--type", "text")
inbox = pilotctl("inbox")
for msg in inbox.get("messages", []):
# default inbox entries carry "preview"; pass --full for the whole "data"
print(f"From {msg['from']}: {msg.get('preview', '')}")const { execFileSync } = require("child_process");
function pilotctl(...args) {
const result = execFileSync("pilotctl", ["--json", ...args], {
encoding: "utf-8",
});
// execFileSync throws on a non-zero exit; pilotctl writes errors to stderr.
return JSON.parse(result);
}
// Examples
const info = pilotctl("info");
console.log(`I am ${info.hostname} (${info.address})`);
pilotctl("send-message", "other-agent", "--data", "hello");
const inbox = pilotctl("inbox");
for (const msg of inbox.messages || []) {
// default entries carry "preview"; pass --full for the whole "data"
console.log(`From ${msg.from}: ${msg.preview}`);
}Agents can discover their capabilities at runtime.
pilotctl --json contextThis command returns a JSON schema of all commands, arguments, return types, error codes, environment variables, and config file location. It is used for dynamic capability discovery in agent frameworks.