[ Switch to styled version → ]
About 430 always-on data agents on the Pilot Protocol backbone. They are callable by name over an end-to-end encrypted tunnel after a one-time trust handshake.
Service agents are always-on nodes on Pilot Protocol's public backbone, operated by Vulture Labs. About 430 of them are live today. Each one wraps a single public data source and answers with typed JSON: crypto and FX prices, weather and air quality, news, transit and departures, sports, developer package metadata (PyPI, crates.io, CRAN…), science and papers, government statistics, health and drug data, geocoding, aviation (METAR, TAF, ADS-B) and more. They do not use public endpoints, API keys, or load balancers.
Two agents help you find the right one: `list-agents` is the searchable directory, and `pilot-mom` turns a plain-English task into a ready-to-run plan of agent calls.
Service agents treat AI agents and HTTP services as equivalent models: a process that takes requests and produces results.
Agents are:
Service agents register on the backbone (network 0), the global address space shared by all Pilot nodes. To use an agent, a node discovers it in the directory, completes a one-time handshake, and then calls it over the encrypted overlay.
The handshake gates access. A service agent only answers calls after a successful handshake. Many public agents auto-approve handshakes.
pilotctl handshake <agent-name>The usage pattern is discover, handshake, ask for the schema, and query. The `--wait` flag makes `send-message` block until the agent's reply arrives and print it inline; with `--json` the reply is under `data.reply`. If nothing arrives within the wait (30 seconds by default) the command exits non-zero.
# 1. Discover agents: handshake the directory, then query it
pilotctl handshake list-agents
pilotctl send-message list-agents --data '/data {"search":"weather","limit":5}' --wait
# 2. Handshake the specialist you found and read its filters
pilotctl handshake open-meteo-forecast
pilotctl send-message open-meteo-forecast --data '/help' --wait
# 3. Query it
pilotctl send-message open-meteo-forecast --data '/data {"latitude":51.51,"longitude":-0.13,"include_current_weather":true}' --wait
# Same call, machine-readable
pilotctl --json send-message open-meteo-forecast --data '/data {"latitude":51.51,"longitude":-0.13}' --wait | jq -r '.data.reply.data'A reply that arrives after the wait is still saved; read it with `pilotctl inbox --from <agent> --latest`.
The `list-agents` service is the directory of service agents on the backbone. It accepts a `--data` payload as a typed command:
# First 50 entries of the directory
pilotctl send-message list-agents --data '/data' --wait
# Keyword-filtered (ranked: substring + fuzzy + semantic embeddings)
pilotctl send-message list-agents --data '/data {"search":"bitcoin","limit":10}' --wait
# One category
pilotctl send-message list-agents --data '/data {"category":"security"}' --waitThe directory supports keyword search and semantic matching. It ranks agents by substring, fuzzy (Levenshtein), and embedding similarity across their name, category, and description.
After finding an agent's name, it can be called directly:
pilotctl handshake <agent-name>
pilotctl send-message <agent-name> --data '/help' --wait
pilotctl send-message <agent-name> --data '/data {...}' --waitTo skip picking agents yourself, describe the task to `pilot-mom` and run the plan it returns:
pilotctl send-message pilot-mom --data 'current weather in London and the BTC price in GBP' --waitEvery specialist accepts the same commands: `/help` (its filters and an example call), `/data {json filters}` (fetch and return normalized data) and `/summary {json filters}` (fetch and return an LLM-written summary). Other messages are dropped. Replies share one envelope:
{"agent": "<hostname>", "command": "data|help|summary", "ok": true, "data": { ... }}Check `ok` before using the payload; on failure the reply explains what went wrong, often with a corrected call.
Specialists whose upstream API paginates accept `page` and `page_size` filters. The agent merges up to 5 pages (500 records) per call into a single JSON reply.
If more data is available, the reply includes a `pagination` block with information on how to fetch the next page, including a pre-formatted command.
{
"items": [ ... ],
"count": 125,
"pagination": {
"style": "page",
"page_size": 25,
"pages_merged": 5,
"records": 125,
"total": 100000,
"has_more": true,
"next": {
"filters": { "search": "ai", "page": 6, "page_size": 25 },
"command": "/data {\"search\":\"ai\",\"page\":6,\"page_size\":25}",
"send_message": "pilotctl send-message <agent> --data '/data {...}'"
}
}
}The `pagination.next.send_message` command fetches the next page. The agent concatenates result arrays from the upstream API without reshaping them. The `/summary` command provides an LLM digest of the merged data.
The responder is a daemon that runs on the agent's host node. It watches the pilot inbox for incoming messages, dispatches them to a local HTTP service, and sends replies back over the overlay network.
Usage:
responder [-endpoints <path>] [-pilotctl <path>] [-socket <path>] [-inbox-dir <path>] [-history <path>]endpoints.yaml:
The responder reads `~/.pilot/endpoints.yaml` to map commands to local HTTP services. Each entry specifies a `name`, a `link` to the service, and an optional `arg_regex` for validation and parsing.
# ~/.pilot/endpoints.yaml on the open-meteo-forecast node
commands:
- name: open-meteo-forecast
link: http://127.0.0.1:10148/open-meteo-forecastA data agent declares a single endpoint; slash commands such as `/help` and `/data {...}` are all dispatched to it.
Message format:
The responder accepts messages as plain text starting with `/` or as a JSON object.
{"command": "<name>", "body": "<args>"}The responder matches the command against configured endpoints. If `arg_regex` is set, named capture groups from the message body are forwarded as query parameters. If the body does not match the regex, the request is dispatched without query parameters. Plain text messages not starting with `/` are dropped.
Request–reply cycle:
The responder will fail to start if `~/.pilot/endpoints.yaml` is missing or invalid.
The path of a service agent call, from the caller to the responder and back:
pilotctl send-message <agent> --data <body>
│
▼ overlay encrypted (X25519 + AES-256-GCM)
responder on service agent node
│ watches ~/.pilot/inbox/ (event-driven)
│ parses JSON → matches command → validates arg_regex
▼
localhost HTTP service (e.g. http://127.0.0.1:10148/open-meteo-forecast)
│ calls the upstream API, normalizes the result
▼
reply envelope {agent, command, ok, data}
│
▼ overlay back to caller's node
send-message --wait on the calling node prints the reply
(a late reply stays in ~/.pilot/inbox/ — read it with pilotctl inbox)The backbone's agents are built from a template and run behind the responder; both live in a private repository today. The steps below show how each agent is put together, so you can build the same shape on your own node. Any process that reads `pilotctl inbox` and answers with `pilotctl send-message <sender> --reply-to <message_id> --data …` works as a service agent; peers reach it by handshaking your node. Listing an agent in the `list-agents` directory is done by the directory's operators.
1. Lay out the agent
Each agent in the fleet has:
2. Edit the system prompt and tools
# agent/prompts.py
SYSTEM_PROMPT = """
You are MyAgent, a specialized assistant that...
"""3. Register the endpoint
Add an entry to `~/.pilot/endpoints.yaml` on the agent's host node.
commands:
- name: my-agent
link: http://127.0.0.1:8400/my-agent4. Start the agent and responder
./start.sh &
responder &5. Call it from any trusted node
pilotctl send-message my-agent --data '/help' --wait