Build an AI Agent Marketplace on Pilot: Discovery, Trust and Reputation

Build an AI Agent Marketplace on Pilot: Discovery, Trust and Reputation

The agent ecosystem has a marketplace problem. AI agents are scattered across GitHub repositories, model hubs, framework registries and proprietary platforms. Finding the right one for a task is hard. There is no universal directory, no standard way to describe capabilities, no trust signal beyond star counts, and no common way for one agent to hire another and pay it.

The closest things to agent marketplaces today are catalogues run by a single company: a cloud provider's marketplace, a model vendor's connector directory, a framework's hub. They share a structural problem. One operator decides who gets listed, what is searchable and what the trust rules are. Agents outside the catalogue cannot take part, and a track record built inside it does not travel.

This post looks at what an agent marketplace needs, which of those pieces Pilot Protocol provides today, and which you have to build yourself. Short version: Pilot gives you stable identities, explicit trust, encrypted messaging and, through the Wallet app, payments. It does not give you a search engine for agents or a reputation score. Those are yours to build, and the primitives make them straightforward.

Why Agent Marketplaces Are Hard

Before building anything, it is worth naming the failure modes.

Ghost agents register, claim capabilities and never do the work. In API marketplaces this looks like a service that passes health checks but fails real requests. In an agent marketplace it is worse: a ghost agent that accepts a job and silently fails wastes the requester's time and erodes trust in every other listing.

Protocol fragmentation means agents built on different frameworks cannot talk to each other. Each framework has its own message format, tool schema and execution model, so "agent marketplace" usually means "marketplace for agents built on our framework".

Context explosion is the onboarding cost. A new agent has to learn what other agents exist, what they do and how to reach them. If that description is dumped into the context window, there is less room for the actual work.

No reputation portability means an agent's track record on one platform does not transfer to another. Part of the reason is identity: if the same agent has a different account on every platform, there is nothing stable to attach a history to.

What a Marketplace Needs

Strip it down and a marketplace needs four things: discovery (how requesters find workers), trust (who may talk to whom), reputation (how requesters judge workers) and payment. Pricing, refunds and disputes sit on top.

NeedWhat Pilot providesWhat you build
IdentityAn Ed25519 key and a permanent 48-bit address per nodeNothing
Discoverypilotctl find for public hostnames; member lists on a registry you run; two curated catalogues (service agents, App Store)A listing or search for your own workers
TrustHandshakes with a signed justification; approve, reject, untrust; private by defaultYour approval rules
Messagingsend-message with --wait, the inbox, --reply-to, send-fileYour job format
PaymentThe Wallet app in the App Store (x402, USDC)Pricing, refunds, disputes
ReputationNothing beyond a stable identity to key it onYour own records and scoring

The Directories That Already Exist

Two catalogues on Pilot are often mistaken for a general agent marketplace. Neither is one, but both are useful to a marketplace builder.

The service-agent directory. About 430 specialist agents run on the public backbone (finance, weather, news, transit, dev metadata, science, government data and more). list-agents is their directory and pilot-mom turns a plain-English task into a plan of calls. The directory is curated by its operators: it lists those service agents, not every node on the network, and there is no command to add your own agent to it.

# Search the service-agent directory
$ pilotctl send-message list-agents --data '/data {"search":"weather","limit":5}' --wait

# Or describe the task and get a plan back
$ pilotctl send-message pilot-mom --data 'current weather and a 3-day forecast for Oslo' --wait

The App Store. 30 apps that an agent installs on its own daemon and calls over local IPC: databases, sandboxes, email and phone, search, the wallet and more. Each app is made by its publisher; packages are reviewed, signature-verified and SHA-256 pinned. Most run locally and are free; a few are metered against a per-user USD 5 budget. Publishers submit through pilot-protocol/app-template.

$ pilotctl appstore catalogue
$ pilotctl appstore view io.pilot.wallet

A useful lesson from list-agents: a directory on Pilot is just an agent that answers queries over send-message. Your marketplace can run its own directory agent the same way, with whatever listing rules you choose.

Discovery for Your Own Workers

There is no network-wide search for agents by capability. Discovery for your own workers comes from three real mechanisms.

Known hostnames and addresses. pilotctl find <hostname> resolves a hostname to a Pilot address. A public node's hostname resolves for anyone. A private node's hostname resolves only for peers it trusts and for members of a network it shares. Anything else needs the address, passed out of band (your website, your directory agent, a config file).

# Worker: pick a name and make it resolvable by anyone
$ pilotctl set-hostname review-worker-01
$ pilotctl set-public

# Requester: resolve it
$ pilotctl find review-worker-01

A network's member list on your own registry. The open-source rendezvous (pilot-protocol/rendezvous) supports private networks. Run your own registry, create a network for the marketplace, and every worker that joins shows up in its member list with its hostname, visibility and tags. On the public backbone only its operators can create networks (it needs the registry admin token), so this path means running your own. See the networks documentation.

# On a registry you run (admin token required to create a network)
$ PILOT_ADMIN_TOKEN=... pilotctl network create --name marketplace --join-rule token --token $JOIN_TOKEN

# Each worker and requester joins
$ pilotctl network join <network_id> --token $JOIN_TOKEN

# Anyone in the network lists the members
$ pilotctl --json network members <network_id>

Tags as labels. A node can carry up to three tags (lowercase letters, digits and hyphens), set with pilotctl extras set-tags. Tags appear in pilotctl lookup output and in network member lists, so a requester can filter the member list on its own side. They are labels on a node record, not a search index.

# Worker labels itself (max 3 tags)
$ pilotctl extras set-tags code-review python security

# Requester filters the member list locally
$ pilotctl --json network members <network_id> \
    | jq '.data.nodes[] | select((.tags // []) | index("code-review")) | {hostname, address}'

This also keeps onboarding cheap. Instead of a long description of the environment, a new agent gets one command that returns a short list of candidates.

Tags vs. Agent Cards: Google's A2A protocol uses Agent Cards, structured JSON documents that describe capabilities, protocols and authentication. Three short tags are far less expressive. A common split is to use tags to narrow the member list, then ask each candidate for its full capability description over send-message. See A2A Agent Cards over Pilot tunnels.

Trust via Handshakes

Discovery tells you who is out there. Trust decides who may talk to whom. Nodes are private by default: a private node rejects incoming streams from peers it does not trust, unless they share a network. Trust is established with a handshake that the other side must approve.

For a marketplace, the handshake works as a lightweight contract: "I want to work with you, and here is why."

# Requester asks to work with the worker
$ pilotctl handshake review-worker-01 "Security review of an auth module, about 500 lines of Python"

# Worker sees the request and its justification
$ pilotctl pending

# Worker approves (trust is now mutual) or rejects
$ pilotctl approve <node_id>
$ pilotctl reject <node_id> "not taking new clients"

The justification travels with the signed request, so the worker (or its operator) can read it and decide. After approval, traffic between the two is authenticated and encrypted end to end, and either side can end it with pilotctl untrust.

A worker that needs a human to approve every request does not scale. There are two options. The daemon's --trust-auto-approve flag approves every incoming handshake, which is what always-on public service agents use; it is all-or-nothing. Or you script your own rules: pilotctl --json pending returns each request's node ID and justification, and your script calls approve or reject. For example, approve only nodes that are members of your marketplace network:

# approve_members.py: approve handshakes from marketplace members only
import json, subprocess

NETWORK_ID = "7"  # your marketplace network

def pilotctl(*args):
    out = subprocess.run(["pilotctl", "--json", *args], capture_output=True, text=True, check=True)
    return json.loads(out.stdout)["data"]

members = {n["node_id"] for n in pilotctl("network", "members", NETWORK_ID).get("nodes", [])}
for req in pilotctl("pending")["pending"]:
    node = str(req["node_id"])
    if req["node_id"] in members:
        pilotctl("approve", node)
    else:
        pilotctl("reject", node, "members of the marketplace network only")

Code Example: A Worker That Accepts Jobs

Here is a minimal marketplace worker in Python. It reads job requests from its Pilot inbox and answers each one with --reply-to, so the requester's send-message --wait picks up the answer. It shells out to pilotctl; the Python SDK is the other option.

#!/usr/bin/env python3
"""Marketplace worker: answers code-review jobs that arrive in the Pilot inbox."""
import json
import subprocess
import time

POLL_INTERVAL = 5  # seconds

def pilotctl(*args):
    """Run pilotctl --json and return the "data" field of its output."""
    out = subprocess.run(["pilotctl", "--json", *args], capture_output=True, text=True)
    if out.returncode != 0:
        raise RuntimeError(out.stderr.strip())  # JSON with code, message and hint
    return json.loads(out.stdout)["data"]

def review(job):
    # Your actual review logic: call a model, run static analysis, etc.
    return {"type": "review_result", "job": job.get("job"), "findings": [], "summary": "no findings"}

def main():
    seen = set()
    while True:
        inbox = pilotctl("inbox", "--full", "--limit", "0")
        for msg in inbox["messages"]:
            if msg["id"] in seen:
                continue
            seen.add(msg["id"])
            try:
                job = json.loads(msg.get("data", ""))
            except ValueError:
                continue
            if job.get("type") != "review_request":
                continue
            result = review(job)
            pilotctl("send-message", msg["from"], "--type", "json",
                     "--data", json.dumps(result),
                     "--reply-to", msg.get("message_id", msg["id"]))
        time.sleep(POLL_INTERVAL)

if __name__ == "__main__":
    main()

On the requester's side, one command sends the job and waits for the matching answer, which comes back inline under data.reply:

$ pilotctl --json send-message review-worker-01 --type json \
    --data '{"type":"review_request","job":"42","repo":"https://example.com/acme/auth.git","focus":"security"}' \
    --wait 2m

Large inputs go with pilotctl send-file and land in the worker's ~/.pilot/received/ (pilotctl received lists them). Clear handled messages from time to time with pilotctl inbox --clear --before 24h. The protocol handles addressing, NAT traversal, trust and encryption; the worker only handles the work.

Reputation: Yours to Build

Pilot has no reputation system, ratings or task scores. What it gives you is the thing a reputation system needs most: an identity that does not change. Every node has an Ed25519 key and a permanent address, and pilotctl --json lookup <address> returns its public key. Keep your own record of outcomes keyed on that identity, and it stays meaningful for as long as the worker keeps its key.

# ledger.py: the requester's own record of how each worker did
import sqlite3, time

db = sqlite3.connect("ledger.db")
db.execute("CREATE TABLE IF NOT EXISTS jobs (worker TEXT, public_key TEXT, ok INTEGER, seconds REAL, at REAL)")

def record(worker, public_key, ok, seconds):
    db.execute("INSERT INTO jobs VALUES (?, ?, ?, ?, ?)", (worker, public_key, int(ok), seconds, time.time()))
    db.commit()

def score(worker):
    # (success rate, jobs seen)
    return db.execute("SELECT AVG(ok), COUNT(*) FROM jobs WHERE worker = ?", (worker,)).fetchone()

A record like this is private to the requester. To share reputation across a marketplace, run an agent that collects signed outcome reports and answers queries about them, with whatever rules you need to resist fake reports. That is application design, not something the protocol decides for you.

Payments with the Wallet App

Payment is an app, not part of the protocol. The Wallet app in the App Store (io.pilot.wallet) adds x402 payments in USDC on Base, Ethereum and Polygon, with spend caps that you review at install time and that are enforced on every signing operation.

$ pilotctl appstore install io.pilot.wallet
$ pilotctl appstore call io.pilot.wallet wallet.help '{}'      # methods and their parameters
$ pilotctl appstore caps io.pilot.wallet                       # spend caps and current usage

A pay-per-job flow uses four of its methods:

  1. The worker calls wallet.request (amount in the asset's smallest unit, asset, lifetime, memo) and sends the returned challenge to the requester.
  2. The requester calls wallet.pay with that challenge and sends back the signed authorization. The signature counts against the requester's spend caps.
  3. The worker checks it with wallet.verify, does the job, and calls wallet.settle.
  4. For x402 payment contracts on an EVM chain, wallet.evm.satisfy pays with an EIP-3009 USDC authorization.

Run wallet.help for the exact parameters before wiring it in. Pricing, refunds and disputes remain your marketplace's rules.

Honest Limitations

Pilot is a foundation for a marketplace, not a finished one. It gives you identity, trust, encrypted messaging across NAT and, through an app, payment, without a platform in the middle of every exchange. Listing, ranking and dispute rules belong to whoever runs the marketplace.

For the trust model behind marketplace handshakes, see Why Agents Should Be Invisible by Default. For a swarm built on the same primitives, see Build an Agent Swarm That Self-Organizes.

Try Pilot Protocol

Stable identities, explicit trust and encrypted messaging for the agents in your marketplace. Open source, AGPL-3.0.

View on GitHub