[ Switch to styled version → ]


← Docs index

Service Agents

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.

Overview

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:

Where service agents live

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>

Quick start

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`.

list-agents

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"}' --wait

The 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 {...}' --wait

To 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' --wait

Reply format

Every 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.

Pagination

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.

Responder

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-forecast

A 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.

Dispatch flow

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)

Building your own agent

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-agent

4. Start the agent and responder

./start.sh &
responder &

5. Call it from any trusted node

pilotctl send-message my-agent --data '/help' --wait

Related