From bb1038c97f73e1ab159cc1b4b8866880ea2e57c3 Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Wed, 24 Jun 2026 14:49:16 -0700 Subject: [PATCH 1/3] plain: regenerate all twins, add drift guard, close content gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plain (machine-UI) mirrors had drifted badly: the only CI guard (check:plain) verifies 1:1 file existence but never content, and the content generator (regen-plain.mjs) was manual-only and never wired into CI — so twins silently rotted whenever a marketing page was edited. Changes: - Regenerate all 37 manifest twins from current sources via regen-plain.mjs. - Add app-store and publish to the regen manifest (were required by the coverage guard but had no generator entry) and generate their twins. - Content-drift guard: regen-plain now stamps each twin with its source path + SHA-256; check-plain-coverage now fails when a source changed without regeneration. Turns silent drift into a red CI check. - Hand-patch ~12 twins where the summarizer dropped material facts (commands, CLI flags, config keys, numeric specs, per-app metadata). Verified: check:plain green (39 stamped twins in sync), astro build green (190 pages). skills/index.astro stays unstamped — it renders live from upstream JSON at build time. --- scripts/check-plain-coverage.mjs | 56 +- scripts/regen-plain.mjs | 32 +- src/pages/plain/app-store.astro | 76 +-- src/pages/plain/docs/app-store.astro | 114 ++--- src/pages/plain/docs/cli-reference.astro | 477 ++++++++++-------- .../plain/docs/comparison-networking.astro | 341 ++++++++----- src/pages/plain/docs/comparison.astro | 132 +++-- src/pages/plain/docs/concepts.astro | 143 +++--- src/pages/plain/docs/configuration.astro | 86 ++-- src/pages/plain/docs/consent.astro | 165 ++---- src/pages/plain/docs/diagnostics.astro | 22 +- src/pages/plain/docs/enterprise-audit.astro | 34 +- .../plain/docs/enterprise-blueprints.astro | 38 +- .../plain/docs/enterprise-identity.astro | 63 +-- .../plain/docs/enterprise-policies.astro | 20 +- src/pages/plain/docs/enterprise-rbac.astro | 56 +- src/pages/plain/docs/enterprise.astro | 50 +- src/pages/plain/docs/error-codes.astro | 88 ++-- src/pages/plain/docs/firewalls.astro | 63 +-- src/pages/plain/docs/gateway.astro | 46 +- src/pages/plain/docs/getting-started.astro | 110 ++-- src/pages/plain/docs/go-sdk.astro | 90 ++-- src/pages/plain/docs/index.astro | 98 ++-- src/pages/plain/docs/integration.astro | 44 +- src/pages/plain/docs/messaging.astro | 36 +- src/pages/plain/docs/motd.astro | 40 +- src/pages/plain/docs/networks.astro | 108 ++-- src/pages/plain/docs/pubsub.astro | 63 +-- src/pages/plain/docs/python-sdk.astro | 52 +- src/pages/plain/docs/research.astro | 56 +- src/pages/plain/docs/sdk-parity.astro | 72 +-- src/pages/plain/docs/service-agents.astro | 39 +- src/pages/plain/docs/services.astro | 24 +- src/pages/plain/docs/troubleshooting.astro | 164 +++--- src/pages/plain/docs/trust.astro | 10 +- src/pages/plain/docs/webhooks.astro | 50 +- src/pages/plain/index.astro | 143 +++--- src/pages/plain/mcp.astro | 47 +- src/pages/plain/p2p.astro | 45 +- src/pages/plain/plans.astro | 69 +-- src/pages/plain/publish.astro | 66 +-- 41 files changed, 1836 insertions(+), 1692 deletions(-) diff --git a/scripts/check-plain-coverage.mjs b/scripts/check-plain-coverage.mjs index eb260d2..a769ca7 100644 --- a/scripts/check-plain-coverage.mjs +++ b/scripts/check-plain-coverage.mjs @@ -17,8 +17,9 @@ // // Exit code: 0 when coverage is complete, 1 when any page is missing/orphaned. -import { readdir } from 'node:fs/promises'; +import { readdir, readFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; +import { createHash } from 'node:crypto'; import { dirname, join, basename } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -52,6 +53,34 @@ async function astroSlugs(relDir) { ); } +const PLAIN_DIR = 'src/pages/plain'; + +// Recursively collect every plain .astro file (repo-relative paths). +async function walkAstro(relDir) { + const out = []; + const entries = await readdir(join(REPO_ROOT, relDir), { withFileTypes: true }); + for (const e of entries) { + const rel = `${relDir}/${e.name}`; + if (e.isDirectory()) out.push(...(await walkAstro(rel))); + else if (e.name.endsWith('.astro')) out.push(rel); + } + return out; +} + +// Same hashing as scripts/regen-plain.mjs sourceHash(): raw utf8, sha256, hex. +function sha256(text) { + return createHash('sha256').update(text, 'utf8').digest('hex'); +} + +// Pull the regen provenance stamp out of a generated plain file, if present. +// Returns { source, hash } or null for hand-written / data-driven plain pages. +function readStamp(plainText) { + const src = plainText.match(/^\/\/ plain-source:\s*(.+)$/m); + const hash = plainText.match(/^\/\/ plain-source-sha256:\s*([0-9a-f]{64})$/m); + if (!src || !hash) return null; + return { source: src[1].trim(), hash: hash[1] }; +} + async function main() { const errors = []; @@ -79,7 +108,28 @@ async function main() { } } - const checked = MAIN_PAIRS.length + human.size; + // 3. Content drift: every plain file that carries a regen provenance stamp + // must match the CURRENT hash of its source. A mismatch means the source + // was edited but the plain twin was never regenerated — the silent staleness + // the structural checks above can't see. Files with no stamp (data-driven + // skills/setups pages, hand-written plain pages) are skipped here. + let stamped = 0; + for (const rel of await walkAstro(PLAIN_DIR)) { + const stamp = readStamp(await readFile(join(REPO_ROOT, rel), 'utf8')); + if (!stamp) continue; + stamped += 1; + const srcAbs = join(REPO_ROOT, stamp.source); + if (!existsSync(srcAbs)) { + errors.push(`Stale plain page: ${rel} was generated from ${stamp.source}, which no longer exists (remove the plain page or restore the source)`); + continue; + } + const current = sha256(await readFile(srcAbs, 'utf8')); + if (current !== stamp.hash) { + errors.push(`Stale plain page: ${stamp.source} changed since ${rel} was generated — re-run \`node scripts/regen-plain.mjs\` and commit the result`); + } + } + + const checked = MAIN_PAIRS.length + human.size + stamped; if (errors.length) { console.error('✗ Plain (machine UI) coverage check failed:\n'); for (const e of errors) console.error(` - ${e}`); @@ -90,7 +140,7 @@ async function main() { process.exit(1); } - console.log(`✓ Plain coverage OK — ${checked} required pairs present, no orphans.`); + console.log(`✓ Plain coverage OK — ${checked} required pairs present, no orphans, ${stamped} stamped twins in sync with source.`); } main().catch((err) => { diff --git a/scripts/regen-plain.mjs b/scripts/regen-plain.mjs index c410285..dc1c609 100644 --- a/scripts/regen-plain.mjs +++ b/scripts/regen-plain.mjs @@ -15,7 +15,8 @@ // GEMINI_API_KEY=... node scripts/regen-plain.mjs [page-slug ...] // // If no page-slug is passed, all pages in the manifest are regenerated. -// Valid slugs: index, p2p, mcp, plans +// Valid slugs: index, p2p, mcp, plans, app-store, publish, plus every +// docs/ auto-discovered from src/pages/docs/. // // Data-driven pages (skills/) are NOT regenerated here — they render // directly from the upstream JSON at build time. @@ -28,9 +29,18 @@ // Exit code: 0 on success, 1 on any failure. import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'; +import { createHash } from 'node:crypto'; import { dirname, join, basename } from 'node:path'; import { fileURLToPath } from 'node:url'; +// Stable content hash of a marketing source file. Stamped into the generated +// plain twin so scripts/check-plain-coverage.mjs can detect when the source +// changed but the twin was never re-run (silent content drift). MUST match the +// hashing in check-plain-coverage.mjs exactly (raw utf8 bytes, sha256, hex). +export function sourceHash(sourceText) { + return createHash('sha256').update(sourceText, 'utf8').digest('hex'); +} + const __dirname = dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = join(__dirname, '..'); @@ -80,6 +90,20 @@ const MANIFEST = { canonical: 'https://pilotprotocol.network/plain/plans/', description: 'Backbone is open and free. Private networks and enterprise are managed early-access.', }, + 'app-store': { + source: 'src/pages/app-store.astro', + dest: 'src/pages/plain/app-store.astro', + title: 'App Store — Pilot Protocol', + canonical: 'https://pilotprotocol.network/plain/app-store/', + description: 'Agent-native apps on the Pilot Protocol network. Install with one command; publish your own from your browser or by PR.', + }, + publish: { + source: 'src/pages/publish.astro', + dest: 'src/pages/plain/publish.astro', + title: 'Publish an app — Pilot Protocol', + canonical: 'https://pilotprotocol.network/plain/publish/', + description: 'Publish your existing HTTP API on the Pilot Protocol app store. Describe it once; we generate, sign, and verify an agent-first adapter.', + }, }; // Extract title/description from DocLayout props in a doc .astro file. @@ -258,7 +282,7 @@ function renderBlock(block) { return ''; } -function renderAstro(meta, payload, key) { +function renderAstro(meta, payload, key, srcHash) { const sections = (payload.sections || []) .map((s) => { const body = (s.body || []).map(renderBlock).filter(Boolean).join('\n'); @@ -287,6 +311,8 @@ function renderAstro(meta, payload, key) { return `--- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: ${meta.source} +// plain-source-sha256: ${srcHash} ${layoutImport} --- @@ -315,7 +341,7 @@ async function regen(slug) { if (process.env.DEBUG === '1') { console.log(`[${slug}] payload sections=${payload.sections?.length}, first-body-len=${payload.sections?.[0]?.body?.length ?? 0}`); } - const rendered = renderAstro(entry, payload, slug); + const rendered = renderAstro(entry, payload, slug, sourceHash(sourceAstro)); if (DRY_RUN) { console.log(`\n--- ${destPath} ---\n${rendered}\n--- END ---\n`); diff --git a/src/pages/plain/app-store.astro b/src/pages/plain/app-store.astro index 5a2c175..4392fb4 100644 --- a/src/pages/plain/app-store.astro +++ b/src/pages/plain/app-store.astro @@ -1,55 +1,57 @@ --- +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/app-store.astro +// plain-source-sha256: 7f8bc4643f72dd6aea9ea03b0f36da03c9aeb78abec136804f13fab62c0e7046 import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

The Pilot App Store.

+

App Store

-

The App Store is where agents find tools built specifically for them, and where builders publish their own. Every app is an agent-native interface — JSON in, JSON out — discoverable by the agents on the Pilot Protocol network. Install with one command, manage from one namespace, and submit your own apps anytime.

+

The App Store is a catalogue of tools for AI agents on the Pilot Protocol network. Apps are installed with a single command and managed from one namespace.

-

Install and use an app

+

How it works

+

Agents interact with the App Store through their native protocol.

    -
  • Browse the catalogue: pilotctl appstore catalogue
  • -
  • Inspect before installing: pilotctl appstore view <id>
  • -
  • Install (the daemon spawns it locally): pilotctl appstore install <id>
  • -
  • Discover its methods: pilotctl appstore call <id> <ns>.help '{}'
  • -
  • Call a method: pilotctl appstore call <id> <method> '<json>'
  • +
  • Discover: Agents search and filter for apps by capability.
  • +
  • Install: An app is installed with a single command. The daemon fetches the app, verifies its signature, requests permissions, and spawns it.
  • +
  • Run: Apps run in the agent's context with native protocol access.
- -

What an app is

-
    -
  • A thin, stateless adapter plus a signed manifest.
  • -
  • The daemon fetches the bundle, re-verifies its signature and binary sha256 on every spawn, and brokers JSON-in/JSON-out calls over a unix socket.
  • -
  • The heavy backend (your API) lives wherever it already runs; the adapter forwards each method to it.
  • -
  • Apps declare the permissions they need (network, file I/O, protocols); the agent grants or denies at install. No ambient authority.
  • -
- -

Publish your own app

-

Two flows, same result: a reviewed, signed, catalogued app any agent can install.

- -

Browser (no code):

+
pilotctl appstore install <id>
+ +

Featured apps

+

The following apps are available in the catalogue.

+

AEGIS (io.pilot.aegis): A runtime firewall for AI agents. It intercepts prompt injection, jailbreaks, homoglyph attacks, and infrastructure-impersonation at every agent input surface, including inbox messages, tool results, skill files, and memory notes, before the agent reads them. It is an 880 KB Rust binary that runs fully offline, with 90% recall and 95% precision on a held-out corpus. 4 methods. 2.3 MB download. Category: security. v0.1.4, Apache-2.0. By Pilot Protocol.

+
pilotctl appstore install io.pilot.aegis
+

Cosift (io.pilot.cosift): Provides grounded web search, retrieval, and research for agents. It performs keyword and semantic search, document retrieval, and LLM-grounded answers over a crawled web corpus and returns results as structured JSON. Every method is discoverable at runtime via cosift.help. 8 methods. 4.6 MB download. Category: search. v0.1.2, MIT. By Pilot Protocol.

+
pilotctl appstore install io.pilot.cosift
+

Sixtyfour (io.pilot.sixtyfour): People- and company-intelligence for agents. It provides contact discovery (find email / find phone), reverse lookups from an email or phone, full person and company enrichment, and an agentic QA researcher, with every field source-backed and returned as structured JSON. Methods are discoverable via sixtyfour.help. 12 methods. 4.9 MB download. Category: intel. v0.1.0, Proprietary. By Sixtyfour.

+
pilotctl appstore install io.pilot.sixtyfour
+

Smol Machines (io.pilot.smolmachines): Provides fast, hardware-isolated Linux microVMs on demand with sub-second boot times and real hypervisor isolation. It can be used to safely run untrusted or AI-generated code, GPU tasks, or headless browser automation in a disposable sandbox. Driven through smolmachines.exec, with smolmachines.help for discovery. VM boot under 1 second. 5 MB download. Category: compute. v1.2.0, Apache-2.0. By Smol Machines.

+
pilotctl appstore install io.pilot.smolmachines
+

Wallet (io.pilot.wallet): Enables on-overlay USDC payments using x402 and EIP-3009 settlement across Base, Ethereum, and Polygon; one address works on all three USDC mainnets. Spend caps declared in the manifest are reviewed at install time and enforced on every signing operation. 3 USDC chains. 8.7 MB download. Category: payments. v0.3.3, AGPL-3.0. By Pilot Protocol.

+
pilotctl appstore install io.pilot.wallet
+

Ideon (io.telepat.ideon-free): An adapter for article generation. The `ideon-free.generate(idea)` method returns a job ID, and `ideon-free.poll(jobId)` returns the finished markdown article. It is a thin adapter over Ideon's `ideon_write`, with no payment required. 2 methods. Free. Category: writing. v0.3.1. By Telepat.

+
pilotctl appstore install io.telepat.ideon-free
+

Slipstream (io.pilot.slipstream): Polymarket smart-money intelligence for agents. It provides leaderboards of the wallets that win, live signals and trade tape, a market scanner, and opportunity scores over an Ed25519-signed API that rejects unsigned callers. Every method is discoverable at runtime via slipstream.help. 9 methods. 4.5 MB download. Category: finance. v1.0.0, MIT. By Pilot Protocol.

+
pilotctl appstore install io.pilot.slipstream
+ +

For builders

+

Existing HTTP APIs can be wrapped in a thin, signed adapter and published to the catalogue. There are two ways to publish.

    -
  • Go to https://pilotprotocol.network/publish
  • -
  • Describe your HTTP API: id (io.pilot.<name>), version, methods, backend URL, and any auth headers.
  • -
  • The server generates, signs, and verifies the adapter. A reviewer approves it onto the store.
  • -
  • No Go, no git, no manifest to write.
  • +
  • Publish in browser: A wizard generates, signs, and verifies the adapter after the app's ID, methods, backend URL, and auth headers are provided. A team then reviews it for the store.
  • +
  • Publish by PR: The `pilot-app` toolkit turns a `pilot.app.yaml` into a signed adapter bundle. A pull request is opened to `pilot-protocol/app-template` for verification, review, and release.
- -

PR (from the terminal):

+

App bundles have the following properties:

    -
  • Install the toolkit: go install github.com/pilot-protocol/app-template/cmd/pilot-app@latest
  • -
  • Generate a spec: pilot-app example > pilot.app.yaml, then edit it to point at your API and list your methods.
  • -
  • Scaffold and package: pilot-app init -o ./my-app, then make package in the project.
  • -
  • Submit: pilot-app submit -C . --prepare /path/to/app-template-fork, then open a PR to pilot-protocol/app-template.
  • -
  • CI verifies the bundle; a maintainer reviews; on merge, automation releases it and adds the catalogue entry.
  • +
  • Verified: Each adapter is sha256-pinned and signed by its publisher. The daemon re-verifies the signature and binary hash each time it spawns the app.
  • +
  • Scoped: Apps declare necessary permissions, such as network access or file I/O. The agent grants or denies these permissions at install time.
-

Requirements to publish

+

Related

    -
  • An existing HTTP API the adapter can forward to. CLI/binary apps are coming soon.
  • -
  • A valid email for submission and review notifications (browser flow).
  • -
  • Secrets are never collected; operators supply them at install time.
  • -
  • Every submission is reviewed before it appears in the store.
  • +
  • Publish an app
  • +
  • App Store Docs
diff --git a/src/pages/plain/docs/app-store.astro b/src/pages/plain/docs/app-store.astro index ea79c10..67aa8e1 100644 --- a/src/pages/plain/docs/app-store.astro +++ b/src/pages/plain/docs/app-store.astro @@ -1,5 +1,7 @@ --- -// Plain mirror of /docs/app-store. Keep in sync with src/pages/docs/app-store.astro. +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/app-store.astro +// plain-source-sha256: 190c0c81699c5824103a2986c35297634d3b81a33e96923988e53b296e89a451 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,29 +10,21 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

App Store

-

Installable agent apps that run locally on your daemon as typed IPC services - JSON in, JSON out, auto-spawned on install. The loop is: discover, install, call.

+

The App Store is for installable capability apps. An app is a binary with a manifest that runs locally on the daemon as a typed IPC service.

Overview

-

Where list-agents is the phonebook for live data on the overlay, the App Store is for installable capability apps. An app is a small binary plus a signed manifest.json. The daemon fetches it from the catalogue, verifies it, spawns the binary, hands it a unix socket, and brokers IPC calls to it. Each method is a typed call - JSON in, JSON out.

-

Apps are:

+

The App Store provides installable capability apps. An app is a binary plus a signed manifest.json. The daemon fetches, verifies, and supervises the app. It spawns the binary, provides a unix socket, and brokers IPC calls. Each app method is a typed call with JSON input and output.

    -
  • Local: they run on your own daemon, one process per host. The heavy lifting (an index, a chain, an LLM) lives wherever the app's backend is; the installed binary is a thin, stateless adapter.
  • -
  • Typed: every method maps to a JSON request/response. No browser, no REST plumbing.
  • -
  • Signature-verified: the manifest pins the binary's sha256 and carries an ed25519 signature; the daemon re-checks both on every spawn.
  • -
  • Grant-scoped: the manifest declares exactly what the app may do (network, file I/O); the user accepts at install time. No ambient authority.
  • -
  • Auto-spawned: once installed, the daemon's supervisor keeps the app running. No manual start.
  • -
-

The whole loop an agent runs is: discover, install, call.

- -

Available apps

-

Live in the catalogue today - install either with a single command:

-
    -
  • Cosift (io.pilot.cosift, v0.1.2, MIT) - grounded web search, retrieval, and research for agents: keyword + semantic search, document retrieval, and LLM-grounded answers over a crawled web corpus, returned as structured JSON. 8 methods, discoverable at runtime via cosift.help. pilotctl appstore install io.pilot.cosift
  • -
  • Wallet (io.pilot.wallet, v0.3.3, AGPL-3.0) - on-overlay USDC payments: x402 + EIP-3009 settlement across Base, Ethereum, and Polygon, one address on all three USDC mainnets, with manifest-declared spend caps enforced on every signing operation. pilotctl appstore install io.pilot.wallet
  • +
  • Local: They run on the local daemon, one process per host. The installed binary is a thin, stateless adapter.
  • +
  • Typed: Every method maps to a JSON request/response.
  • +
  • Signature-verified: The manifest pins the binary's sha256 and carries an ed25519 signature. The daemon re-checks both on every spawn.
  • +
  • Grant-scoped: The manifest declares what the app may do. The user accepts these grants at install time.
  • +
  • Auto-spawned: Once installed, the daemon's supervisor keeps the app running.
+

The agent execution loop is: discover, install, then call.

Using apps

-

Discovery and install go through the catalogue - a signed list the daemon fetches. Install verifies the bundle and the daemon auto-spawns it; call is then the workhorse.

+

Discovery and installation use the catalogue, a signed list fetched by the daemon. Installation verifies the bundle, and the daemon auto-spawns it. The `call` command executes app methods.

# 1. Discover what's installable
 pilotctl appstore catalogue
 
@@ -43,28 +37,28 @@ pilotctl appstore status io.pilot.cosift
 
 # 4. Call a method - JSON in, JSON out on stdout
 pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft consensus","k":"5"}'
-

No config step. A well-built app ships with sane defaults, so install then call is all an agent needs. Apps may read an optional config.json next to their manifest for overrides (e.g. a self-hosted backend).

+

Apps ship with default configurations. An optional `config.json` file, placed next to the manifest, can be used for overrides.

-

Discovery and the help convention

-

pilotctl appstore list and status surface a flat list of method names - enough to know what exists, not how to call it. The convention for richer discovery is a <app>.help method: a single local call (no backend round-trip) that returns every method with its parameters, a kind (utility / status / meta), and an expected-latency class.

+

Discovery & the help convention

+

`pilotctl appstore list` and `status` show method names. A `<app>.help` method provides detailed discovery, returning methods, parameters, kind, and latency class.

pilotctl appstore call io.pilot.cosift cosift.help '{}'
-

The latency class lets an agent pick the cheapest method for its need before spending a slow one:

+

The latency class helps agents select methods based on expected performance.

    -
  • fast - under ~1s: status or cheap retrieval.
  • -
  • med - ~1-5s: an LLM rerank or a single-pass synthesis.
  • -
  • slow - ~5-30s: multi-step work (e.g. research that plans, retrieves, and synthesizes).
  • +
  • fast: under ~1s for status or cheap retrieval.
  • +
  • med: ~1-5s for an LLM rerank or a single-pass synthesis.
  • +
  • slow: ~5-30s for multi-step work.
-

Each method entry also carries a measured, warm round-trip estimate, so an agent can budget a call end-to-end (agent → daemon → app → backend → back).

+

Each method includes a measured, warm round-trip time estimate for call budgeting.

Lifecycle

-
pilotctl appstore restart io.pilot.cosift          # respawn (e.g. after writing a config.json)
-pilotctl appstore audit io.pilot.cosift            # supervisor log: spawn / exit / verify-fail
+
pilotctl appstore restart io.pilot.cosift    # respawn (e.g. after writing a config.json)
+pilotctl appstore audit io.pilot.cosift      # supervisor log: spawn / exit / verify-fail
 pilotctl appstore install io.pilot.cosift --force  # upgrade to a new version
 pilotctl appstore uninstall io.pilot.cosift --yes
-

Upgrades key on the version: the supervisor respawns an app when its app_version changes. Bump the version for every new build, or a re-release of the same version won't roll running nodes onto the new binary.

+

The supervisor respawns an app when its `app_version` changes. The version must be incremented for new builds to trigger an upgrade on running nodes.

Building an app

-

An app is a binary that listens on the socket the daemon hands it and speaks the app-store IPC protocol. The manifest declares its identity, the methods it exposes, the pinned binary, and the grants it needs.

+

An app is a binary that communicates over a socket using the app-store IPC protocol. A manifest file declares its identity, methods, binary hash, and required grants.

{
   "id": "io.pilot.cosift",
   "app_version": "0.1.2",
@@ -81,15 +75,15 @@ pilotctl appstore uninstall io.pilot.cosift --yes
"protection": "shareable", "store": { "publisher": "ed25519:...", "signature": "..." } }
-

The binary registers one handler per method and serves them over the socket. In Go, that's the app-store/pkg/ipc contract:

+

The binary registers a handler for each method. The Go implementation uses the `app-store/pkg/ipc` package.

d := ipc.NewDispatcher()
 d.Register("cosift.search", func(ctx, req) (json.RawMessage, error) { ... })
 // ... one Register per exposed method ...
 ipc.Serve(ctx, conn, d)   // on the --socket the daemon supplies
-

The daemon spawns the binary with a fixed set of lifecycle flags (--socket, --manifest, --addr, --db, --identity, --cap-state). An app must accept all of them (even if it ignores most) or it will fail to start. Method names in the code must match the manifest's exposes list, or the daemon won't broker them.

+

The daemon spawns the binary with a fixed set of flags: `--socket`, `--manifest`, `--addr`, `--db`, `--identity`, and `--cap-state`. The app must accept these flags. Method names in the code must match the manifest's `exposes` list.

Publishing an app

-

Three steps: sign, release, and add one catalogue entry by PR.

+

Publishing involves three steps: signing the manifest, creating a release, and adding an entry to the catalogue via a pull request.

# 1. One-time: generate a publisher keypair (keep the private key safe)
 pilotctl appstore gen-key publisher.key
 
@@ -99,7 +93,7 @@ tar -czf io.pilot.cosift-0.1.2.tar.gz -C bundle .
 
 # 3. Attach the tarball to a GitHub release
 gh release create cosift-v0.1.2 io.pilot.cosift-0.1.2.tar.gz
-

Then add one entry to catalogue.json (pinning the tarball's sha256) and open a PR. Once merged, pilotctl appstore install <id> resolves it everywhere:

+

An entry is added to `catalogue.json` with the tarball's sha256. After the PR is merged, the app can be installed by its ID.

{
   "id": "io.pilot.cosift",
   "version": "0.1.2",
@@ -107,49 +101,41 @@ gh release create cosift-v0.1.2 io.pilot.cosift-0.1.2.tar.gz
"bundle_url": "https://github.com/.../releases/download/cosift-v0.1.2/io.pilot.cosift-0.1.2.tar.gz", "bundle_sha256": "<sha256 of the tarball>" } -

The catalogue itself is signed. After editing catalogue.json, re-sign it so the daemon and pilotctl will trust it:

+

The catalogue file is signed. After editing, it must be re-signed.

pilotctl appstore sign-catalogue --key catalog-signing.key catalogue/catalogue.json
-

This writes a detached catalogue.json.sig (commit both). pilotctl verifies it against the embedded catalogue key before trusting any entry.

-

Three integrity layers protect every install: the catalogue carries a detached ed25519 signature (a substituted app list fails), the catalogue pins each tarball sha256 (a swapped CDN byte fails), and the manifest pins the binary sha256 under an ed25519 signature - the last two re-checked at every spawn.

+

This command creates a `catalogue.json.sig` file. Both files must be committed. `pilotctl` verifies the signature against an embedded key.

+

There are three integrity layers: a signed catalogue, a pinned tarball sha256 in the catalogue, and a pinned binary sha256 in the signed manifest. The daemon re-checks the manifest signature and binary hash at every spawn.

Catalogue vs sideload

-

There are two install paths, with different trust:

+

There are two installation paths.

    -
  • Catalogue (install <id>): the reviewed path. The bundle is signature-verified and installs with the grants its manifest declares, including net.dial. This is how any app that needs the network is distributed.
  • -
  • Sideload (install <dir> --local): for local development. The manifest is clamped to a small sandbox: fs.read/fs.write under $APP and audit.log only. No net.dial, no inter-app calls, no hooks. A net-using app must go through the catalogue.
  • +
  • Catalogue (`install <id>`): A signature-verified bundle that installs with the grants declared in its manifest, including network access.
  • +
  • Sideload (`install <dir> --local`): For local development. Installs with a restricted sandbox (local filesystem access under `$APP` and audit logging). Network access, inter-app calls, and hooks are disabled.
-

To stage a release locally before publishing, point $PILOT_APPSTORE_CATALOG_URL at a file:// catalogue and install by id - the same code path as production, with your own tarball.

- -

Security model and hardening

-

The app store is deny-by-default at every layer. Trust flows from a signed catalogue, through a signed manifest, to a sandboxed and continuously-verified child process.

+

To stage a release locally, the `$PILOT_APPSTORE_CATALOG_URL` environment variable can be pointed to a local `file://` catalogue.

-

Signed catalogue (fail-closed)

-

The catalogue is signed with a dedicated ed25519 key whose public half is compiled into pilotctl and the daemon. pilotctl fetches both catalogue.json and a detached catalogue.json.sig and verifies the signature before trusting any entry. An unsigned, missing-signature, or tampered catalogue is refused - a compromised host or CDN cannot point installs at hostile bundle URLs without forging the signature. The signing key can be rotated at build time:

+

Security model & hardening

+

The app store is deny-by-default. Trust is established through a signed catalogue and a signed manifest, leading to a sandboxed and verified child process.

+

Signed catalogue (fail-closed): The catalogue is signed with an ed25519 key. The public key is compiled into `pilotctl` and the daemon. `pilotctl` fetches `catalogue.json` and `catalogue.json.sig` and verifies the signature before use. Tampered or unsigned catalogues are rejected. The signing key can be rotated at build time.

go build -ldflags \
   "-X .../internal/catalogtrust.publicKeyB64=<new-b64-pubkey>" \
   ./cmd/pilotctl ./cmd/daemon
- -

Broker authorization (exposes + grants)

-

Apps never dial each other's sockets directly - every call goes through the daemon's broker, which enforces two gates before any dispatch:

+

Broker authorization (exposes + grants): All calls go through the daemon's broker, which enforces authorization.

    -
  • Exposes gate: a method is dispatchable only if the target app lists it in its manifest exposes set. That list is the app's entire callable surface; anything else is refused, even for the daemon itself.
  • -
  • Grant gate: when one app calls another, the caller must declare a matching ipc.call grant targeting <app>.<method> (exact, <app>.*, or *). No grant, no call.
  • +
  • Exposes gate: A method is dispatchable only if the target app lists it in its manifest `exposes` set.
  • +
  • Grant gate: When one app calls another, the caller must declare a matching `ipc.call` grant.
- -

Supervisor hardening

-

The supervisor that spawns and watches each app applies defence-in-depth:

+

Supervisor hardening: The app supervisor applies several security measures.

    -
  • Launch-time re-verification (TOCTOU): immediately before exec, the binary is re-checked: rejected if it became a symlink, and its sha256 must still match the pinned hash. A binary swapped between install-scan and launch is caught before it runs.
  • -
  • Exponential backoff: a binary that fails verification is retried with capped exponential backoff (not a fixed interval), and a crash-looping app is suspended after too many failures in a window until an operator restarts it.
  • -
  • Resource limits: on Linux each app is spawned with RLIMIT_NOFILE and an RLIMIT_AS address-space cap, bounding fd and memory abuse.
  • -
  • Audit log rotation: every lifecycle event (spawn, exit, verify-fail, suspend, resume) is written to a per-app JSONL audit log that rotates across a bounded number of generations, so a crash-looping app can't fill the disk while recent forensics are retained.
  • +
  • Launch-time re-verification (TOCTOU): Immediately before `exec`, the binary is re-checked against its pinned sha256 hash.
  • +
  • Exponential backoff: A binary that fails verification or a crash-looping app is retried with capped exponential backoff.
  • +
  • Resource limits: On Linux each app is spawned with `RLIMIT_NOFILE` and an `RLIMIT_AS` address-space cap.
  • +
  • Audit log rotation: Lifecycle events are written to a per-app JSONL audit log that rotates.
- -

Extension hooks

-

Apps may register hooks on daemon primitives (declared in the manifest), but the hook surface is bounded: per-app rate limiting caps how often the daemon will dispatch into an app's hooks, and the number of dynamic hook registrations per app is capped - so a hostile hook can't become a DoS amplifier.

+

Extension hooks: Apps can register hooks on daemon primitives. The hook surface is limited by per-app rate limiting and a cap on the number of dynamic hook registrations.

Worked example: io.pilot.cosift

-

The cosift app is a stateless adapter to a search / answer / research API over a multi-million-document web corpus. It exposes three utility methods and several status/discovery ones:

+

The `io.pilot.cosift` app is a stateless adapter for a search, answer, and research API. It exposes utility, status, and discovery methods.

# Discover the surface + latencies
 pilotctl appstore call io.pilot.cosift cosift.help '{}'
 
@@ -159,8 +145,8 @@ pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft leader ele
 # answer / chat (med) - grounded synthesis with citations
 pilotctl appstore call io.pilot.cosift cosift.answer '{"q":"What is HNSW?"}'
 
-# research (slow) - plan -> multi-retrieval -> report
+# research (slow) - plan -> multi-retrieval -> report
 pilotctl appstore call io.pilot.cosift cosift.research '{"q":"compare raft and paxos"}'
-

Its source is a public reference for app authors: a tiny adapter, a manifest, and the publish flow above.

+

The source code for this app is available as a public reference.

diff --git a/src/pages/plain/docs/cli-reference.astro b/src/pages/plain/docs/cli-reference.astro index 382b478..9637b28 100644 --- a/src/pages/plain/docs/cli-reference.astro +++ b/src/pages/plain/docs/cli-reference.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/cli-reference.astro +// plain-source-sha256: 16cb0f65e823dd84c78fdebb208f7d4fa119b7782b7fd10ad2b90c5563f01c2d import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,221 +10,244 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

CLI Reference

-

A complete reference for `pilotctl`. All commands support `--json` for structured output.

+

A complete reference for the pilotctl command-line interface. All commands support the --json flag for structured output.

Global flags

pilotctl --json <command> [args...]
-

Use `--json` with any command for structured output:

+

Use the --json flag with any command for structured output.

  • Success: {"status":"ok","data":{...}}
  • Error: {"status":"error","code":"...","message":"...","hint":"..."}
-

Self-discovery

+ +

Self-discovery

pilotctl --json context
-

Returns the full command schema. Use this to discover capabilities at runtime.

+

Returns the full command schema. This can be used to discover capabilities at runtime.

-

Bootstrap

-

init

+

init

pilotctl init [--registry <addr>] [--beacon <addr>] [--hostname <name>] [--socket <path>]
-

Creates `~/.pilot/config.json` with registry, beacon, socket, and hostname settings.

-

Returns: `config_path`, `registry`, `beacon`, `socket`, `hostname`

-

config

+

Creates ~/.pilot/config.json with registry, beacon, socket, and hostname settings.

+

Returns: config_path, registry, beacon, socket, hostname

+ +

config

pilotctl config                          # Show current config
 pilotctl config --set registry=host:9000  # Update a key
-

`config` with no args returns the full current config. `--set` returns the updated key and value.

+

The config command with no arguments returns the full current configuration. The --set flag returns the updated key and value.

-

Quickstart

-

quickstart

+

quickstart

pilotctl quickstart [--json]
-

A guided 3-step getting-started walkthrough. Run it after each step; it detects whether the daemon is already running and guides you to the next action.

+

A guided 3-step getting-started walkthrough. It detects if the daemon is running and guides to the next action.

Step 1 — Start the daemon

pilotctl daemon start
-

If the daemon isn't running, quickstart prints the start command with a description. If it's already running, it proceeds to step 2.

+

If the daemon is not running, quickstart prints the start command. If it is running, it proceeds to step 2.

Step 2 — Discover specialist agents

pilotctl send-message list-agents \
     --data '/data {"search":"","limit":10}' --wait
-

Queries the public directory for ~436 specialist agents covering weather, crypto, news, sports, transit, science, and more. Filter by keyword: `weather`, `crypto`, `news`, `sports`, `joke`.

+

Queries the public directory for specialist agents. Filter by keyword: weather, crypto, news, sports, joke.

Step 3 — Handshake + query a specialist

pilotctl send-message <hostname> --data '/help' --wait
 pilotctl handshake <hostname>
 pilotctl send-message <hostname> --data '/data {"<filter>":"<value>"}' --wait
-

The canonical 3-command pattern:

+

A common 3-command pattern:

    -
  • Learn the API: send `/help` to see the specialist's schema.
  • -
  • Establish trust: handshake the specialist.
  • -
  • Query it: send `/data` with a filter to get a structured JSON reply.
  • +
  • Learn the API: send /help to see the specialist's schema
  • +
  • Establish trust: handshake the specialist
  • +
  • Query it: send /data with a filter to get a structured JSON reply
-

Returns (--json): `quickstart` [{`step`, `title`, `command`, `description`, `done`}].

+

Returns (--json): quickstart [{step, title, command, description, done}].

-

Daemon lifecycle

-

daemon start

+

daemon start

pilotctl daemon start [--registry <addr>] [--beacon <addr>] [--listen <addr>]
   [--identity <path>] [--email <addr>] [--hostname <name>]
   [--public] [--no-encrypt] [--foreground] [--log-level <level>] [--log-format <fmt>]
   [--socket <path>] [--config <path>] [--webhook <url>]
   [--admin-token <token>] [--networks <ids>]
-

Starts as a background process. It blocks until registered, prints status, then exits. Use `--foreground` to run in the current process.

-

`--email` is optional. If omitted, the daemon synthesises one from the public-key fingerprint (`<fingerprint>@nodes.pilotprotocol.network`). When supplied, it persists to `~/.pilot/config.json` and is not needed on subsequent starts. `--trust-auto-approve` auto-accepts every incoming handshake.

-

Returns: `node_id`, `address`, `pid`, `socket`, `hostname`, `log_file`

-

daemon stop

+

Starts as a background process. It blocks until registered, prints status, then exits. Use --foreground to run in the current process.

+

The --email flag is optional. If omitted, the daemon synthesises one from the public-key fingerprint (<fingerprint>@nodes.pilotprotocol.network). When supplied, it persists to ~/.pilot/config.json. The --trust-auto-approve flag auto-accepts every incoming handshake.

+

Returns: node_id, address, pid, socket, hostname, log_file

+ +

daemon stop

pilotctl daemon stop
-

Returns: `pid`. Includes `forced` (bool) if the daemon required SIGKILL.

-

daemon status

+

Returns: pid. Includes forced (bool) if the daemon required SIGKILL.

+ +

daemon status

pilotctl daemon status [--check]
-

`--check` mode is silent and exits 0 if responsive, 1 otherwise.

-

Returns: `running`, `responsive`, `pid`, `pid_file`, `socket`, `node_id`, `address`, `hostname`, `uptime_secs`, `peers`, `connections`

+

--check mode: silent, exits 0 if responsive, 1 otherwise.

+

Returns: running, responsive, pid, pid_file, socket, node_id, address, hostname, uptime_secs, peers, connections

-

Identity & Discovery

-

info

+

info

pilotctl info
-

Returns: `node_id`, `address`, `hostname`, `uptime_secs`, `connections`, `ports`, `peers`, `encrypt`, `bytes_sent`, `bytes_recv`, per-connection stats, peer list with encryption status.

-

set-hostname

+

Returns: node_id, address, hostname, uptime_secs, connections, ports, peers, encrypt, bytes_sent, bytes_recv, per-connection stats, peer list with encryption status.

+ +

set-hostname

pilotctl set-hostname <name>

Names must be lowercase alphanumeric with hyphens, 1–63 characters.

-

Returns: `hostname`, `node_id`

-

clear-hostname

+

Returns: hostname, node_id

+ +

clear-hostname

pilotctl clear-hostname
-

Clears the user-set hostname. The node keeps its internal hostname `pilot-XXXXXXXX`.

-

Returns: `hostname`

-

find

+

Clears the user-set hostname. The node keeps its internal hostname pilot-XXXXXXXX.

+

Returns: hostname

+ +

find

pilotctl find <hostname>

Discovers a node by hostname. Requires mutual trust.

-

Returns: `hostname`, `node_id`, `address`, `public`

-

set-public / set-private

+

Returns: hostname, node_id, address, public

+ +

set-public / set-private

pilotctl set-public      # Make this node visible to all
 pilotctl set-private     # Hide this node (default)
-

Routes through the daemon, which signs the request. Returns: `node_id`, `visibility`

+

Routes through the daemon, which signs the request. Returns: node_id, visibility

-

Communication

-

connect

+

connect

pilotctl connect <address|hostname> [port] --message "<msg>" [--timeout <dur>]
-

Dials the target, sends the message, reads one response, and exits. Default port is 1000 (stdio).

-

Returns: `target`, `port`, `sent`, `response`

-

send

+

Dials the target, sends the message, reads one response, and exits. Default port: 1000 (stdio).

+

Returns: target, port, sent, response

+ +

send

pilotctl send <address|hostname> <port> --data "<msg>" [--timeout <dur>]
-

Returns: `target`, `port`, `sent`, `response`

-

recv

+

Returns: target, port, sent, response

+ +

recv

pilotctl recv <port> [--count <n>] [--timeout <dur>]
-

Listens on a port, accepts incoming connections, and collects messages. Default count is 1.

-

Returns: `messages` [{`seq`, `port`, `data`, `bytes`}], `timeout` (bool)

-

send-file

+

Listens on a port, accepts incoming connections, and collects messages. Default count: 1.

+

Returns: messages [{seq, port, data, bytes}], timeout (bool)

+ +

send-file

pilotctl send-file <address|hostname> <filepath>
-

Sends via data exchange (port 1001). Saved to `~/.pilot/received/` on the target.

-

Returns: `filename`, `bytes`, `destination`, `ack`

-

send-message

+

Sends via data exchange (port 1001). Saved to ~/.pilot/received/ on the target.

+

Returns: filename, bytes, destination, ack

+ +

send-message

pilotctl send-message <address|hostname> --data "<text>" [--type text|json|binary]
               [--count <n>] [--reuse-conn] [--wait [<dur>]] [--trace] [--no-auto-handshake]
-

Sends a typed message via data exchange (port 1001). Default type is `text`.

+

Sends a typed message via data exchange (port 1001). Default type: text.

    -
  • `--count <n>` — send N times (default 1).
  • -
  • `--reuse-conn` — reuse the underlying connection across `--count` sends.
  • -
  • `--wait [<dur>]` — block until a reply lands in `~/.pilot/inbox/`. Default 30s.
  • -
  • `--no-auto-handshake` — skip the implicit trust handshake with known agents.
  • -
  • `--trace` — print per-step timing on stderr.
  • +
  • --count <n> — send N times (default 1).
  • +
  • --reuse-conn — reuse the underlying connection across --count sends.
  • +
  • --wait [<dur>] — block until a reply lands in ~/.pilot/inbox/. Default 30s.
  • +
  • --no-auto-handshake — skip the implicit trust handshake with known agents.
  • +
  • --trace — print per-step timing on stderr.
-

Returns: `target`, `type`, `bytes`, `ack`

-

dgram

+

Returns: target, type, bytes, ack

+ +

dgram

pilotctl dgram <address|hostname> <port> --data "<msg>"

Sends a single unreliable datagram.

-

listen

+ +

listen

pilotctl listen <port> [--count <n>] [--timeout <dur>]
-

Listens for datagrams. Without `--count`, it streams NDJSON indefinitely.

-

Returns: `messages` [{`src_addr`, `src_port`, `data`, `bytes`}], `timeout` (bool)

-

broadcast

+

Listens for datagrams. Without --count, it streams NDJSON indefinitely.

+

Returns: messages [{src_addr, src_port, data, bytes}], timeout (bool)

+ +

broadcast

pilotctl broadcast <network_id> <message> [--port <port>]

Sends a best-effort datagram to every member of the network.

-

Returns: `network_id`, `port`, `bytes`

-

subscribe

+

Returns: network_id, port, bytes

+ +

subscribe

pilotctl subscribe <address|hostname> <topic> [--count <n>] [--timeout <dur>]
-

Subscribes to an event stream (port 1002). Use `*` for all topics. Without `--count`, it streams NDJSON.

-

Returns: `events` [{`topic`, `data`, `bytes`}], `timeout` (bool)

-

publish

+

Subscribes to an event stream (port 1002). Use * for all topics. Without --count, it streams NDJSON.

+

Returns: events [{topic, data, bytes}], timeout (bool)

+ +

publish

pilotctl publish <address|hostname> <topic> --data "<message>"
-

Returns: `target`, `topic`, `bytes`

-

Pipe mode

+

Returns: target, topic, bytes

+ +

Pipe mode

echo "hello" | pilotctl connect <address|hostname> [port] [--timeout <dur>]
-

Without `--message`, the command reads from stdin, sends it, and reads one response.

+

Without --message, the command reads from stdin, sends it, and reads one response.

-

Trust management

-

handshake

+

handshake

pilotctl handshake <node_id|address|hostname> [justification]
-

Returns: `status`, `node_id`

-

pending

+

Returns: status, node_id

+ +

pending

pilotctl pending

Pending requests persist across daemon restarts.

-

Returns: `pending` [{`node_id`, `justification`, `received_at`}]

-

approve

+

Returns: pending [{node_id, justification, received_at}]

+ +

approve

pilotctl approve <node_id>
-

Returns: `status`, `node_id`

-

reject

+

Returns: status, node_id

+ +

reject

pilotctl reject <node_id> [reason]
-

Returns: `status`, `node_id`

-

trust

+

Returns: status, node_id

+ +

trust

pilotctl trust
-

Returns: `trusted` [{`node_id`, `mutual`, `network`, `approved_at`}]

-

untrust

+

Returns: trusted [{node_id, mutual, network, approved_at}]

+ +

untrust

pilotctl untrust <node_id>
-

Returns: `node_id`

+

Returns: node_id

-

Webhooks

-

set-webhook

+

set-webhook

pilotctl set-webhook <url>

Persists to config and applies immediately to a running daemon.

-

Returns: `webhook`, `applied` (bool)

-

clear-webhook

+

Returns: webhook, applied (bool)

+ +

clear-webhook

pilotctl clear-webhook
-

Returns: `webhook`, `applied` (bool)

+

Returns: webhook, applied (bool)

-

Tags

-

set-tags

+

set-tags

pilotctl set-tags <tag1> [tag2] [tag3]

Maximum 3 tags. Lowercase alphanumeric with hyphens, 1–32 characters each.

-

Returns: `node_id`, `tags`

-

clear-tags

+

Returns: node_id, tags

+ +

clear-tags

pilotctl clear-tags
-

Returns: `tags` (empty array)

+

Returns: tags (empty array)

-

Mailbox

-

received

+

received

pilotctl received [--clear]
-

Lists files in `~/.pilot/received/`. Use `--clear` to delete all.

-

Returns: `files` [{`name`, `bytes`, `modified`, `path`}], `total`, `dir`

-

inbox

+

Lists files in ~/.pilot/received/. Use --clear to delete all.

+

Returns: files [{name, bytes, modified, path}], total, dir

+ +

inbox

pilotctl inbox [--clear] [--trace]
-

Lists messages in `~/.pilot/inbox/`. Use `--clear` to delete all. Use `--trace` for relative age and byte-count per message.

-

Returns: `messages` [{`type`, `from`, `data` (base64), `bytes`, `received_at`}], `total`, `dir`

+

Lists messages in ~/.pilot/inbox/. Use --clear to delete all. Use --trace for relative age and byte-count per message.

+

Returns: messages [{type, from, data (base64), bytes, received_at}], total, dir

-

Networks

+

network list

Private networks provide group-level connectivity with a permission model.

-

network list

pilotctl network list
-

Lists all networks the node is a member of.

-

Returns: `networks` [{`id`, `name`, `join_rule`, `members`}]

-

network join

+

Lists all networks your node is a member of.

+

Returns: networks [{id, name, join_rule, members}]

+ +

network join

pilotctl network join <network_id> [--token <token>]
-

Join a network. Use `--token` for token-gated networks.

-

network leave

+

Join a network. Use --token for token-gated networks.

+ +

network leave

pilotctl network leave <network_id>

Leave a network.

-

network members

+ +

network members

pilotctl network members <network_id>
-

Returns: `nodes` [{`node_id`, `hostname`, `public`}]

-

network invite

+

Returns: nodes [{node_id, hostname, public}]

+ +

network invite

pilotctl network invite <network_id> <node_id>

Invite another node to a network.

-

network invites

+ +

network invites

pilotctl network invites

List pending invitations from other nodes.

-

Returns: `invites` [{`network_id`, `inviter_id`, `timestamp`}]

-

network accept

+

Returns: invites [{network_id, inviter_id, timestamp}]

+ +

network accept

pilotctl network accept <network_id>

Accept a pending invite and join the network.

-

network reject

+ +

network reject

pilotctl network reject <network_id>

Decline a pending invite.

Service Agents

-

Service agents are always-on responders on a dedicated overlay network. They are discovered via `list-agents` and queried using `send-message`. The agent treats the `--data` payload as a typed command and replies to the inbox.

+

Service agents are always-on responders on a dedicated overlay network. They are discovered via list-agents and communicated with using send-message. The agent treats the --data payload as a typed command.

# 1. Discover what's online (list-agents is the directory)
 pilotctl handshake list-agents
 pilotctl send-message list-agents --data '/data {"search":"weather","limit":5}' --wait
@@ -234,174 +259,194 @@ pilotctl send-message noaa-weather --data '/data {"airport":"KSFO"}' -
 
 # 3. Read the reply that --wait blocked for
 jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
-

The `--wait [<dur>]` flag (default 30s) makes `send-message` block until the reply lands in `~/.pilot/inbox/`.

+

The --wait flag makes send-message block until the reply is received in ~/.pilot/inbox/.

-

Diagnostics

-

health

+

health

pilotctl health
-

A quick daemon health check.

-

Returns: `status`, `uptime_seconds`, `connections`, `peers`, `bytes_sent`, `bytes_recv`

-

ping

+

Daemon health check.

+

Returns: status, uptime_seconds, connections, peers, bytes_sent, bytes_recv

+ +

ping

pilotctl ping <address|hostname> [--count <n>] [--timeout <dur>]
-

Sends echo probes (port 7). Default is 4 pings.

-

Returns: `target`, `results` [{`seq`, `bytes`, `rtt_ms`, `error`}], `timeout` (bool)

-

traceroute

+

Sends echo probes (port 7). Default: 4 pings.

+

Returns: target, results [{seq, bytes, rtt_ms, error}], timeout (bool)

+ +

traceroute

pilotctl traceroute <address> [--timeout <dur>]
-

Returns: `target`, `setup_ms`, `rtt_samples` [{`rtt_ms`, `bytes`}]

-

bench

+

Returns: target, setup_ms, rtt_samples [{rtt_ms, bytes}]

+ +

bench

pilotctl bench <address|hostname> [<size_mb>] [--timeout <dur>]
-

Throughput benchmark via echo port. Default is 1 MB.

-

Returns: `target`, `sent_bytes`, `recv_bytes`, `send_duration_ms`, `total_duration_ms`, `send_mbps`, `total_mbps`

-

peers

+

Throughput benchmark via echo port. Default: 1 MB.

+

Returns: target, sent_bytes, recv_bytes, send_duration_ms, total_duration_ms, send_mbps, total_mbps

+ +

peers

pilotctl peers [--search <query>]

Lists currently connected peers and their connection quality. Real endpoints are redacted by the daemon.

-

Returns: `peers` [{`node_id`, `encrypted`, `authenticated`, `path` (`direct` | `relay`)}], `total`, `relay_peer_count`, `encrypted_peers`, `authenticated_peers`

-

connections

+

Returns: peers [{node_id, encrypted, authenticated, path (direct | relay)}], total, relay_peer_count, encrypted_peers, authenticated_peers

+ +

connections

pilotctl connections
-

Returns: `connections` [{`id`, `local_port`, `remote_addr`, `remote_port`, `state`, `cong_win`, `in_flight`, `srtt_ms`, `unacked`, `ooo_buf`, `peer_recv_win`, `recv_win`}], `total`

-

disconnect

+

Returns: connections [{id, local_port, remote_addr, remote_port, state, cong_win, in_flight, srtt_ms, unacked, ooo_buf, peer_recv_win, recv_win}], total

+ +

disconnect

pilotctl disconnect <conn_id>
-

Returns: `conn_id`

+

Returns: conn_id

-

Managed Networks

-

Operator commands for networks that run an automated evaluation cycle. Subcommands are `status`, `cycle`, and `reconcile`.

-

managed status

+

managed status

+

Operator commands for networks that run an automated evaluation cycle.

pilotctl managed status [--net <id>]
-

Show managed-network status for this node. `--net 0` (the default) returns the global view.

-

managed cycle

+

Show managed-network status for this node. --net 0 (the default) returns the global view.

+ +

managed cycle

pilotctl managed cycle --force [--net <id>]
-

Force a managed-network evaluation cycle. Prunes low-scoring peers and fills vacancies. `--force` is required.

-

Returns: `pruned`, `filled`, `peers`

-

managed reconcile

+

Force a managed-network evaluation cycle. Prunes low-scoring peers and fills vacancies. --force is required.

+

Returns: pruned, filled, peers

+ +

managed reconcile

pilotctl managed reconcile --net <id>
-

Poll the registry and refresh local peer state for a specific managed network. `--net` is required.

-

Returns: `peers`

+

Poll the registry and refresh local peer state for a specific managed network. --net is required.

+

Returns: peers

-

Member Tags

-

Per-member metadata tags inside a managed network. This is distinct from the node-level `set-tags` command.

-

member-tags set

+

member-tags set

+

Per-member metadata tags inside a managed network. This is distinct from the node-level set-tags command.

pilotctl member-tags set --net <id> --node <id> --tags tag1,tag2
-

Set the tag list on a member, replacing any prior value.

-

member-tags get

+

Set the tag list on a member. Replaces any prior value.

+ +

member-tags get

pilotctl member-tags get --net <id> [--node <id>]
-

Read member tags. Omit `--node` to get every member's tags in the network.

+

Read member tags. Omit --node to get every member's tags in the network.

-

Network Policies

+

policy get

Local policy engine for automating network behavior.

-

policy get

pilotctl policy get --net <id>

Retrieve the active policy for a network.

-

policy set

+ +

policy set

pilotctl policy set --net <id> --file <path>
 pilotctl policy set --net <id> --inline '<json>'
-

Apply a policy document to a network.

-

policy validate

+

Apply a policy document to a network. It is validated and compiled locally before applying.

+ +

policy validate

pilotctl policy validate --file <path>
 pilotctl policy validate --inline '<json>'

Validate a policy document without applying it. Returns rule count and compilation status.

-

policy test

+ +

policy test

pilotctl policy test --file <path> --event '<json>'

Test a policy against a simulated event. Returns whether the event would be allowed or denied.

-

Enterprise Admin

-

audit

+

audit

pilotctl audit [--network <id>]
-

Queries the audit trail for a network (default: backbone network 0). Requires admin token. Returns: `entries`

-

audit-export

+

Queries the audit trail for a network (default: backbone network 0). Requires admin token. Returns: entries

+ +

audit-export

pilotctl audit-export <get|set|disable> [options]

Configure external audit log export. Subcommands:

    -
  • `get` — show current export config.
  • -
  • `set --format <json|splunk_hec|syslog_cef> --endpoint <URL> [...]` — configure export.
  • -
  • `disable` — turn export off.
  • +
  • get — show current export config.
  • +
  • set --format <json|splunk_hec|syslog_cef> --endpoint <URL> [...] — configure export.
  • +
  • disable — turn export off.

Requires admin token.

-

provision

+ +

provision

pilotctl provision <blueprint.json>

Provision a network from a JSON blueprint file. Requires admin token.

-

deprovision

+ +

deprovision

pilotctl deprovision <network-name>

Look up a network by name and delete it. Requires admin token.

-

provision-status

+ +

provision-status

pilotctl provision-status

Shows provisioning status. Requires admin token.

-

idp

+ +

idp

pilotctl idp <get|set> [options]

Get or set the identity provider configuration. Subcommands:

    -
  • `get` — show current IdP config.
  • -
  • `set --type <oidc|saml|entra_id|ldap|webhook> --url <URL> [...]` — configure IdP.
  • +
  • get — show current IdP config.
  • +
  • set --type <oidc|saml|entra_id|ldap|webhook> --url <URL> [...] — configure IdP.

Requires admin token.

-

directory-sync

+ +

directory-sync

pilotctl directory-sync <directory.json> [--network <id>] [--remove-unlisted]
-

Sync a directory of node-to-identity mappings into a network. Requires admin token.

-

directory-status

+

Sync a directory of node-to-identity mappings into a network. Use --remove-unlisted to disable nodes not in the file. Requires admin token.

+ +

directory-status

pilotctl directory-status <network_id>

Shows directory sync status for a network. Requires admin token.

-

Registry

-

register

+

register

pilotctl register [listen_addr]
-

Returns: `node_id`, `address`, `public_key`

-

lookup

+

Returns: node_id, address, public_key

+ +

lookup

pilotctl lookup <node_id>
-

Returns: `node_id`, `address`, `real_addr`, `public`, `hostname`

-

deregister

+

Returns: node_id, address, real_addr, public, hostname

+ +

deregister

pilotctl deregister
-

Routes through the daemon (signed). Returns: `status`

-

rotate-key

+

Routes through the daemon for a signed request. Returns: status

+ +

rotate-key

pilotctl rotate-key
-

Generates a new keypair for this node and re-registers it. The daemon signs the rotation and replaces `~/.pilot/identity.json`. Existing trust links are preserved.

-

Returns: `node_id`, new `public_key`

-

set-public / set-private

+

Generates a new keypair for this node and re-registers it. The daemon signs the rotation and replaces ~/.pilot/identity.json. Existing trust links are preserved. The old private key is destroyed.

+

Returns: node_id, new public_key

+ +

set-public / set-private

pilotctl set-public
 pilotctl set-private
-

Toggles whether this node appears in the public directory. Returns: `node_id`, `visibility`

-

trusted

+

Toggles whether this node appears in the public directory. Returns: node_id, visibility

+ +

trusted

pilotctl trusted
-

Lists nodes in the embedded trusted-agents directory that are auto-approved on first contact.

+

Lists nodes in the embedded trusted-agents directory that are auto-approved on first contact. This is different from `pilotctl trust`, which shows live trust state.

-

Meta

-

version

+

version

pilotctl version

Prints the build version string.

-

updates

+ +

updates

pilotctl updates [--count <n>] [--scope <name>]
-

Reads the published changelog feed and prints recent entries (default: 5).

-

skills

+

Reads the published changelog feed and prints recent entries (default: 5). The --scope flag filters by tag.

+ +

skills

pilotctl skills [status|paths|check|disable|enable]
-

Manages the `SKILL.md` files the daemon installs for each detected agent tool (Claude Code, OpenClaw, PicoClaw, OpenHands, Hermes). Defaults to `status`.

+

Manages the SKILL.md files the daemon installs for each detected agent tool. Without a subcommand, it defaults to status.

    -
  • `status` — show install state per detected tool.
  • -
  • `paths` — print install paths only, one per line.
  • -
  • `check` — run one reconcile pass immediately.
  • -
  • `disable` — remove every file the daemon has ever written and persist an opt-out in `~/.pilot/config.json` so future ticks are no-ops. Files in pilot-owned subdirs (`~/.<tool>/skills/pilot-protocol/`, `~/.pilot/bin/`, plugin install dirs) are deleted; files co-inhabited with the user (CLAUDE.md, AGENTS.md, AGENT.md, SOUL.md) only have the pilot marker block stripped — the file itself is never deleted. OpenClaw `openclaw.json` is restored from the `.pilot-bak` snapshot when present, otherwise inverse-merged.
  • -
  • `enable` — re-enables injection and runs one reconcile pass.
  • +
  • status — show install state per detected tool.
  • +
  • paths — print install paths only, one per line.
  • +
  • check — run one reconcile pass immediately.
  • +
  • disable — remove every file the daemon has written and persist an opt-out.
  • +
  • enable — re-enables injection and runs one reconcile pass.
-

Gateway

-

gateway start

+

gateway start

pilotctl extras gateway start [--subnet <cidr>] [--ports <list>] [<pilot-addr>...]
-

Maps pilot addresses to local IPs on a private subnet (default: `10.4.0.0/16`). Requires root.

-

Returns: `pid`, `subnet`, `mappings` [{`local_ip`, `pilot_addr`}]

-

gateway stop

+

Maps pilot addresses to local IPs on a private subnet (default: 10.4.0.0/16). Requires root.

+

Returns: pid, subnet, mappings [{local_ip, pilot_addr}]

+ +

gateway stop

pilotctl extras gateway stop
-

Returns: `pid`

-

gateway map

+

Returns: pid

+ +

gateway map

pilotctl extras gateway map <pilot-addr> [local-ip]
-

Returns: `local_ip`, `pilot_addr`

-

gateway unmap

+

Returns: local_ip, pilot_addr

+ +

gateway unmap

pilotctl extras gateway unmap <local-ip>
-

Returns: `unmapped`

-

gateway list

+

Returns: unmapped

+ +

gateway list

pilotctl extras gateway list
-

Returns: `mappings` [{`local_ip`, `pilot_addr`}], `total`

+

Returns: mappings [{local_ip, pilot_addr}], total

Related

diff --git a/src/pages/plain/docs/comparison-networking.astro b/src/pages/plain/docs/comparison-networking.astro index 893bfd7..710cea5 100644 --- a/src/pages/plain/docs/comparison-networking.astro +++ b/src/pages/plain/docs/comparison-networking.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/comparison-networking.astro +// plain-source-sha256: dc3c10f9b59822aa870fe792117c0753d846d79cc7e3b59d36a492189208f1ef import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,148 +10,259 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

Pilot Protocol vs Tailscale vs ZeroTier vs Nebula

-

Overlay networks compared. All create virtual addresses, encrypt traffic, and traverse NATs. The difference is who they are built for.

+

This document compares Pilot Protocol with other overlay networks like Tailscale, ZeroTier, Nebula, and libp2p. It outlines their design goals, features, and typical use cases.

Overview

+

Pilot Protocol, Tailscale, ZeroTier, and Nebula are all overlay networks. They create virtual addresses, encrypt traffic, and traverse NATs. They are built for different purposes.

    -
  • Pilot Protocol - An overlay network built for autonomous AI agents. Provides virtual addresses, port-based services, a bilateral trust model, peer discovery with tags, and built-in application services (data exchange and pub/sub). No external Go dependencies.
  • -
  • Tailscale - A VPN mesh built on WireGuard for connecting human users and servers. Manages device access through an admin console, integrates with SSO/OIDC, and provides Magic DNS.
  • -
  • ZeroTier - A virtual Ethernet switch that creates flat L2 networks. Devices join a network ID and get an IP. Managed through a central controller.
  • -
  • Nebula - Slack's overlay network for connecting servers at scale. Certificate-based identity, firewall rules in config files, designed for infrastructure teams.
  • -
  • libp2p - A modular networking stack for peer-to-peer applications. Provides transport, discovery, and pubsub primitives. Used by IPFS, Ethereum, and Filecoin.
  • +
  • Pilot Protocol: An overlay network for autonomous AI agents. It provides virtual addresses, port-based services, a bilateral trust model, peer discovery with tags, and built-in application services (data exchange and pub/sub). It has no external Go dependencies.
  • +
  • Tailscale: A VPN mesh built on WireGuard for connecting human users and servers. It manages device access through an admin console, integrates with SSO/OIDC, and provides Magic DNS.
  • +
  • ZeroTier: A virtual Ethernet switch that creates flat L2 networks. Devices join a network ID and get an IP. It is managed through a central controller.
  • +
  • Nebula: Slack's overlay network for connecting servers at scale. It uses certificate-based identity and firewall rules in config files.
  • +
  • libp2p: A modular networking stack for peer-to-peer applications. It provides transport, discovery, and pubsub primitives.

vs Tailscale

-

Tailscale is a WireGuard-based mesh VPN with strong NAT traversal and a polished admin experience, designed for connecting users and servers under centralized access control. Pilot Protocol is designed for autonomous agents that generate their own identity and negotiate trust without an admin.

-
    -
  • Designed for: Pilot = AI agents; Tailscale = users and servers
  • -
  • Addressing: Pilot = 48-bit virtual (N:NNNN.HHHH.LLLL); Tailscale = 100.x.y.z (CGNAT range)
  • -
  • Encryption: Pilot = X25519 + AES-256-GCM; Tailscale = WireGuard (ChaCha20-Poly1305)
  • -
  • NAT traversal: Pilot = STUN + hole-punch + relay; Tailscale = DERP relay servers
  • -
  • Trust model: Pilot = bilateral handshake (agent-initiated); Tailscale = centralized ACLs (admin console)
  • -
  • Discovery: Pilot = registry + tags + hostnames; Tailscale = Magic DNS + admin console
  • -
  • Identity: Pilot = Ed25519 key pair (self-generated); Tailscale = SSO/OIDC provider
  • -
  • Application services: Pilot = echo, data exchange, pub/sub; Tailscale = Taildrop, Funnel, Serve
  • -
  • Admin experience: Pilot = CLI + config files; Tailscale = polished admin console
  • -
  • Account required: Pilot = No; Tailscale = Yes (Google, Microsoft, GitHub, etc.)
  • -
  • Transport: Pilot = UDP with custom reliable transport; Tailscale = WireGuard (kernel or userspace)
  • -
  • License: Pilot = AGPL-3.0; Tailscale = BSD-3 (client), proprietary (coordination)
  • -
  • Self-hostable: Pilot = Yes (rendezvous server); Tailscale = Yes (Headscale, community project)
  • -
-

Key difference: Tailscale is built for human-managed networks - sign in with an identity provider, an admin defines ACL policies, and devices get IP addresses on a WireGuard mesh. Pilot Protocol is built for agent-managed networks - agents generate their own cryptographic identity, negotiate trust directly with peers, and get built-in services for data exchange and task delegation. If agents run on machines already on a Tailscale network, Pilot tunnels run over it.

+

Tailscale is a WireGuard-based mesh VPN designed for connecting users and servers under centralized access control. Pilot Protocol is designed for autonomous agents that generate their own identity and negotiate trust without an administrator.

+

Pilot Protocol features:

+
    +
  • Designed for: AI agents
  • +
  • Addressing: 48-bit virtual (N:NNNN.HHHH.LLLL)
  • +
  • Encryption: X25519 + AES-256-GCM
  • +
  • NAT traversal: STUN + hole-punch + relay
  • +
  • Trust model: Bilateral handshake (agent-initiated)
  • +
  • Discovery: Registry + tags + hostnames
  • +
  • Identity: Ed25519 key pair (self-generated)
  • +
  • Application services: Echo, data exchange, pub/sub
  • +
  • Admin experience: CLI + config files
  • +
  • Account required: No
  • +
  • Transport: UDP with custom reliable transport
  • +
  • License: AGPL-3.0
  • +
  • Self-hostable: Yes (rendezvous server)
  • +
+

Tailscale features:

+
    +
  • Designed for: Users and servers
  • +
  • Addressing: 100.x.y.z (CGNAT range)
  • +
  • Encryption: WireGuard (ChaCha20-Poly1305)
  • +
  • NAT traversal: DERP relay servers
  • +
  • Trust model: Centralized ACLs (admin console)
  • +
  • Discovery: Magic DNS + admin console
  • +
  • Identity: SSO/OIDC provider
  • +
  • Application services: Taildrop, Funnel, Serve
  • +
  • Admin experience: Admin console
  • +
  • Account required: Yes (Google, Microsoft, GitHub, etc.)
  • +
  • Transport: WireGuard (kernel or userspace)
  • +
  • License: BSD-3 (client), proprietary (coordination)
  • +
  • Self-hostable: Yes (Headscale, community project)
  • +
+

The key difference is the intended user. Tailscale is for human-managed networks where an admin defines ACL policies. Pilot Protocol is for agent-managed networks where agents generate their own cryptographic identity and negotiate trust directly with peers.

vs ZeroTier

-

ZeroTier creates virtual Ethernet segments (L2). Any device can join a network by ID and get an IP. Pilot Protocol operates at L3/L4 with port-based service multiplexing and agent-native features.

-
    -
  • Layer: Pilot = L3/L4 (network + transport); ZeroTier = L2 (virtual Ethernet)
  • -
  • Addressing: Pilot = 48-bit virtual addresses; ZeroTier = 10-character node ID + IP
  • -
  • Ports: Pilot = 16-bit ports with well-known assignments; ZeroTier = standard IP ports
  • -
  • Trust: Pilot = bilateral handshake; ZeroTier = network controller approval
  • -
  • Discovery: Pilot = tags, hostnames, registry search; ZeroTier = network member list
  • -
  • Application services: Pilot = echo, data exchange, pub/sub; ZeroTier = none
  • -
  • Account required: Pilot = No; ZeroTier = Yes (for managed networks)
  • -
  • Self-hosted: Pilot = Yes (rendezvous server); ZeroTier = Yes (controller)
  • -
  • Free tier: Pilot = unlimited (open source); ZeroTier = 25 devices
  • -
  • License: Pilot = AGPL-3.0; ZeroTier = BSL 1.1
  • -
-

Key difference: ZeroTier emulates Ethernet - it gives you a flat network and you build everything else on top. Pilot Protocol provides a complete agent networking stack: addressing, transport, discovery, trust, and application-layer services out of the box.

+

ZeroTier creates virtual Ethernet segments (L2). Pilot Protocol operates at L3/L4 with port-based service multiplexing and agent-native features.

+

Pilot Protocol features:

+
    +
  • Layer: L3/L4 (network + transport)
  • +
  • Addressing: 48-bit virtual addresses
  • +
  • Ports: 16-bit ports with well-known assignments
  • +
  • Trust: Bilateral handshake
  • +
  • Discovery: Tags, hostnames, registry search
  • +
  • Application services: Echo, data exchange, pub/sub
  • +
  • Account required: No
  • +
  • Self-hosted: Yes (rendezvous server)
  • +
  • Free tier: Unlimited (open source)
  • +
  • License: AGPL-3.0
  • +
+

ZeroTier features:

+
    +
  • Layer: L2 (virtual Ethernet)
  • +
  • Addressing: 10-character node ID + IP
  • +
  • Ports: Standard IP ports
  • +
  • Trust: Network controller approval
  • +
  • Discovery: Network member list
  • +
  • Application services: None
  • +
  • Account required: Yes (for managed networks)
  • +
  • Self-hosted: Yes (controller)
  • +
  • Free tier: 25 devices
  • +
  • License: BSL 1.1
  • +
+

The key difference is abstraction. ZeroTier emulates Ethernet. Pilot Protocol provides a complete agent networking stack with addressing, transport, discovery, trust, and application-layer services.

vs Nebula

-

Nebula is Slack's overlay network for infrastructure. It uses certificate-based identity and config-file firewall rules. Pilot Protocol uses dynamic trust negotiation and agent-driven discovery.

-
    -
  • Designed for: Pilot = AI agents; Nebula = server infrastructure
  • -
  • Identity: Pilot = Ed25519 (self-generated); Nebula = X.509 certificates (CA-signed)
  • -
  • Trust: Pilot = dynamic (runtime handshake); Nebula = static (certificate + config)
  • -
  • Firewall: Pilot = per-port accept rules; Nebula = YAML config rules
  • -
  • Discovery: Pilot = registry + tags + hostnames; Nebula = Lighthouse nodes + static hosts
  • -
  • Configuration: Pilot = JSON config + CLI flags; Nebula = YAML files + CA PKI
  • -
  • Application services: Pilot = echo, data exchange, pub/sub; Nebula = none
  • -
  • PKI required: Pilot = No; Nebula = Yes (nebula-cert CA)
  • -
  • NAT traversal: Pilot = STUN + hole-punch + relay; Nebula = Lighthouse + punch
  • -
  • License: Pilot = AGPL-3.0; Nebula = MIT
  • -
-

Key difference: Nebula requires a PKI setup - run a certificate authority, sign certificates for each node, and distribute them manually. Pilot Protocol agents generate their own identity and negotiate trust at runtime. Pilot suits dynamic agent populations where nodes come and go; Nebula excels for static infrastructure with known hosts.

+

Nebula uses certificate-based identity and config-file firewall rules. Pilot Protocol uses dynamic trust negotiation and agent-driven discovery.

+

Pilot Protocol features:

+
    +
  • Designed for: AI agents
  • +
  • Identity: Ed25519 (self-generated)
  • +
  • Trust: Dynamic (runtime handshake)
  • +
  • Firewall: Per-port accept rules
  • +
  • Discovery: Registry + tags + hostnames
  • +
  • Configuration: JSON config + CLI flags
  • +
  • Application services: Echo, data exchange, pub/sub
  • +
  • PKI required: No
  • +
  • NAT traversal: STUN + hole-punch + relay
  • +
  • License: AGPL-3.0
  • +
+

Nebula features:

+
    +
  • Designed for: Server infrastructure
  • +
  • Identity: X.509 certificates (CA-signed)
  • +
  • Trust: Static (certificate + config)
  • +
  • Firewall: YAML config rules
  • +
  • Discovery: Lighthouse nodes + static hosts
  • +
  • Configuration: YAML files + CA PKI
  • +
  • Application services: None
  • +
  • PKI required: Yes (nebula-cert CA)
  • +
  • NAT traversal: Lighthouse + punch
  • +
  • License: MIT
  • +
+

The key difference is identity management. Nebula requires a PKI setup with a certificate authority. Pilot Protocol agents generate their own identity and negotiate trust at runtime, making it suited for dynamic agent populations.

vs libp2p

-

libp2p is a modular networking toolkit used by IPFS, Ethereum, and Polkadot. It provides building blocks; Pilot Protocol provides a complete, opinionated stack.

-
    -
  • Approach: Pilot = complete stack (single binary); libp2p = modular toolkit (assemble yourself)
  • -
  • Dependencies: Pilot = stdlib only; libp2p = heavy (multiple crates/modules)
  • -
  • Addressing: Pilot = 48-bit virtual addresses; libp2p = Multiaddr + PeerID
  • -
  • Transport: Pilot = UDP with reliable streams; libp2p = TCP, QUIC, WebSocket, WebRTC
  • -
  • Discovery: Pilot = central registry + tags; libp2p = DHT (Kademlia) + mDNS
  • -
  • Trust: Pilot = mutual handshake with approval; libp2p = connection-level (no trust model)
  • -
  • Complexity: Pilot = one binary, one config file; libp2p = multiple protocols to configure
  • -
  • Primary use case: Pilot = AI agent networking; libp2p = blockchain and decentralized apps
  • -
  • Setup time: Pilot = minutes; libp2p = hours to days
  • -
-

Key difference: libp2p is a toolkit - choose transports, discovery mechanisms, and security protocols, then wire them together. Pilot Protocol is opinionated and complete: one binary, no external dependencies, built-in services, and a trust model designed for agents. Use libp2p for maximum flexibility in a blockchain or decentralized application; use Pilot for agents talking to each other in minutes.

+

libp2p is a modular networking toolkit. Pilot Protocol is a complete, opinionated stack.

+

Pilot Protocol features:

+
    +
  • Approach: Complete stack (single binary)
  • +
  • Dependencies: Stdlib only
  • +
  • Addressing: 48-bit virtual addresses
  • +
  • Transport: UDP with reliable streams
  • +
  • Discovery: Central registry + tags
  • +
  • Trust: Mutual handshake with approval
  • +
  • Complexity: One binary, one config file
  • +
  • Primary use case: AI agent networking
  • +
  • Setup time: Minutes
  • +
+

libp2p features:

+
    +
  • Approach: Modular toolkit (assemble yourself)
  • +
  • Dependencies: Heavy (multiple crates/modules)
  • +
  • Addressing: Multiaddr + PeerID
  • +
  • Transport: TCP, QUIC, WebSocket, WebRTC
  • +
  • Discovery: DHT (Kademlia) + mDNS
  • +
  • Trust: Connection-level (no trust model)
  • +
  • Complexity: Multiple protocols to configure
  • +
  • Primary use case: Blockchain and decentralized apps
  • +
  • Setup time: Hours to days
  • +
+

The key difference is scope. libp2p is a toolkit to assemble a networking stack. Pilot Protocol is a complete stack in one binary with no external dependencies and built-in services.

Feature matrix

+

Pilot Protocol:

    -
  • Agent-native design: Pilot = Yes; Tailscale = No; ZeroTier = No; Nebula = No; libp2p = No
  • -
  • Account required: Pilot = No; Tailscale = Yes; ZeroTier = Yes; Nebula = No; libp2p = No
  • -
  • PKI/CA required: Pilot = No; Tailscale = No; ZeroTier = No; Nebula = Yes; libp2p = No
  • -
  • Stdlib-only (no external deps): Pilot = Yes; Tailscale = No; ZeroTier = No; Nebula = No; libp2p = No
  • -
  • NAT traversal: Pilot = STUN+relay; Tailscale = DERP relay; ZeroTier = root servers; Nebula = Lighthouse; libp2p = AutoNAT+relay
  • -
  • E2E encryption: Pilot = AES-256-GCM; Tailscale = WireGuard; ZeroTier = ChaCha20; Nebula = AES-256-GCM; libp2p = Noise/TLS
  • -
  • Trust model: Pilot = bilateral; Tailscale = central ACL; ZeroTier = controller; Nebula = certificates; libp2p = none
  • -
  • Peer discovery: Pilot = registry+tags; Tailscale = Magic DNS; ZeroTier = controller; Nebula = Lighthouse; libp2p = DHT+mDNS
  • -
  • Port multiplexing: Pilot = 16-bit ports; Tailscale = IP ports; ZeroTier = IP ports; Nebula = IP ports; libp2p = protocol IDs
  • -
  • Built-in services: Pilot = echo, data exchange, event stream; Tailscale = none; ZeroTier = none; Nebula = none; libp2p = pubsub only
  • -
  • Pub/Sub: Pilot = Yes; Tailscale = No; ZeroTier = No; Nebula = No; libp2p = Yes
  • -
  • Self-hosted: Pilot = Yes; Tailscale = Headscale; ZeroTier = Yes; Nebula = Yes; libp2p = Yes
  • -
  • License: Pilot = AGPL-3.0; Tailscale = BSD-3/Prop.; ZeroTier = BSL 1.1; Nebula = MIT; libp2p = MIT/Apache
  • +
  • Agent-native design: Yes
  • +
  • Account required: No
  • +
  • PKI/CA required: No
  • +
  • Stdlib-only (no external deps): Yes
  • +
  • NAT traversal: STUN+relay
  • +
  • E2E encryption: AES-256-GCM
  • +
  • Trust model: Bilateral
  • +
  • Peer discovery: Registry+tags
  • +
  • Port multiplexing: 16-bit ports
  • +
  • Built-in services: Echo, Data Exchange, Event Stream
  • +
  • Pub/Sub: Yes
  • +
  • Self-hosted: Yes
  • +
  • License: AGPL-3.0
- -

When to use what

- -

Use Pilot Protocol when:

+

Tailscale:

    -
  • You are building with AI agents that need to find, trust, and communicate with each other
  • -
  • You want lightweight networking with no accounts, no PKI, no cloud platform to manage
  • -
  • You need built-in application services (data exchange and pub/sub) out of the box
  • -
  • Agents need to dynamically discover peers by tags, hostnames, or capabilities
  • -
  • You want agents to negotiate trust at runtime without a central authority
  • +
  • Agent-native design: No
  • +
  • Account required: Yes
  • +
  • PKI/CA required: No
  • +
  • Stdlib-only (no external deps): No
  • +
  • NAT traversal: DERP relay
  • +
  • E2E encryption: WireGuard
  • +
  • Trust model: Central ACL
  • +
  • Peer discovery: Magic DNS
  • +
  • Port multiplexing: IP ports
  • +
  • Built-in services: None
  • +
  • Pub/Sub: No
  • +
  • Self-hosted: Headscale
  • +
  • License: BSD-3/Prop.
- -

Use Tailscale when:

+

ZeroTier:

    -
  • You need a VPN mesh for human users and their devices
  • -
  • You want SSO integration (Google, Microsoft, Okta)
  • -
  • You need centralized access control managed by an admin
  • -
  • You want an admin console and commercial support
  • +
  • Agent-native design: No
  • +
  • Account required: Yes
  • +
  • PKI/CA required: No
  • +
  • Stdlib-only (no external deps): No
  • +
  • NAT traversal: Root servers
  • +
  • E2E encryption: ChaCha20
  • +
  • Trust model: Controller
  • +
  • Peer discovery: Controller
  • +
  • Port multiplexing: IP ports
  • +
  • Built-in services: None
  • +
  • Pub/Sub: No
  • +
  • Self-hosted: Yes
  • +
  • License: BSL 1.1
- -

Use ZeroTier when:

+

Nebula:

    -
  • You need a flat L2 network that "just works" for up to 25 devices (free tier)
  • -
  • You want virtual Ethernet between devices across the internet
  • -
  • You need broad platform support (runs on nearly everything)
  • +
  • Agent-native design: No
  • +
  • Account required: No
  • +
  • PKI/CA required: Yes
  • +
  • Stdlib-only (no external deps): No
  • +
  • NAT traversal: Lighthouse
  • +
  • E2E encryption: AES-256-GCM
  • +
  • Trust model: Certificates
  • +
  • Peer discovery: Lighthouse
  • +
  • Port multiplexing: IP ports
  • +
  • Built-in services: None
  • +
  • Pub/Sub: No
  • +
  • Self-hosted: Yes
  • +
  • License: MIT
- -

Use Nebula when:

+

libp2p:

    -
  • You are connecting servers in a known, static infrastructure
  • -
  • You already have a PKI or are comfortable running a certificate authority
  • -
  • You need fine-grained firewall rules defined in config files
  • -
  • You want MIT-licensed software with proven scale (50,000+ hosts at Slack)
  • +
  • Agent-native design: No
  • +
  • Account required: No
  • +
  • PKI/CA required: No
  • +
  • Stdlib-only (no external deps): No
  • +
  • NAT traversal: AutoNAT+relay
  • +
  • E2E encryption: Noise/TLS
  • +
  • Trust model: None
  • +
  • Peer discovery: DHT+mDNS
  • +
  • Port multiplexing: Protocol IDs
  • +
  • Built-in services: Pubsub only
  • +
  • Pub/Sub: Yes
  • +
  • Self-hosted: Yes
  • +
  • License: MIT/Apache
-

Use libp2p when:

+

When to use what

+

Use Pilot Protocol when:

    -
  • You are building a blockchain, decentralized storage, or Web3 application
  • -
  • You need maximum protocol flexibility and transport agnosticism
  • -
  • You want DHT-based fully decentralized discovery (no central server at all)
  • -
  • You are willing to invest time assembling and configuring the stack
  • +
  • Building with AI agents that need to find, trust, and communicate with each other.
  • +
  • A lightweight network with no accounts, PKI, or cloud platform is needed.
  • +
  • Built-in application services like data exchange and pub/sub are required.
  • +
  • Agents need to dynamically discover peers by tags, hostnames, or capabilities.
  • +
  • Agents need to negotiate trust at runtime without a central authority.
- -

The short version: Tailscale, ZeroTier, and Nebula give you a network. Pilot Protocol gives agents a network with identity, trust, discovery, and services built in. If your nodes are humans or servers, use a VPN. If your nodes are agents, use Pilot.

- -

Related

+

Use Tailscale when:

+
    +
  • A VPN mesh for human users and their devices is needed.
  • +
  • SSO integration (Google, Microsoft, Okta) is required.
  • +
  • Centralized access control managed by an admin is needed.
  • +
  • An admin console and commercial support are desired.
  • +
+

Use ZeroTier when:

+
    +
  • A flat L2 network is needed for up to 25 devices on the free tier.
  • +
  • Virtual Ethernet between devices across the internet is the goal.
  • +
  • Broad platform support is required.
  • +
+

Use Nebula when:

+
    +
  • Connecting servers in a known, static infrastructure.
  • +
  • A PKI is already in place or running a certificate authority is acceptable.
  • +
  • Fine-grained firewall rules defined in config files are needed.
  • +
  • MIT-licensed software with proven scale (50,000+ hosts at Slack) is preferred.
  • +
+

Use libp2p when:

    -
  • vs MCP / A2A / ACP
  • -
  • Research
  • +
  • Building a blockchain, decentralized storage, or Web3 application.
  • +
  • Maximum protocol flexibility and transport agnosticism are required.
  • +
  • DHT-based fully decentralized discovery is needed.
  • +
  • Time can be invested to assemble and configure the stack.
diff --git a/src/pages/plain/docs/comparison.astro b/src/pages/plain/docs/comparison.astro index 639343f..3c3f7f8 100644 --- a/src/pages/plain/docs/comparison.astro +++ b/src/pages/plain/docs/comparison.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/comparison.astro +// plain-source-sha256: 33495d3a3aa5263e7ecb22dcfbc2d8e08458899d2b9be40b6e0db2b4c14be960 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,100 +10,97 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

Pilot Protocol vs MCP vs A2A vs ACP

-

Four protocols at different layers of the agent communication stack. They are often complementary rather than competing.

+

This document compares four protocols for AI agent communication: Pilot Protocol, MCP, A2A, and ACP. It describes their functions, layers of operation, and how they can be used together.

Overview

+

The AI agent ecosystem has multiple protocols addressing different communication needs. They operate at different layers of the stack and are often complementary.

    -
  • Pilot Protocol - A network-layer overlay that gives each agent a permanent virtual address, encrypted UDP tunnels, NAT traversal, and a trust model. "TCP/IP for agents."
  • -
  • MCP (Model Context Protocol) - Anthropic's protocol for connecting LLMs to tools and data sources. Defines how a model calls external functions and retrieves context.
  • -
  • A2A (Agent-to-Agent) - Google's protocol for agent interoperability. Defines agent cards, task lifecycle, and message exchange between agents.
  • -
  • ACP (Agent Communication Protocol) - BeeAI's protocol for multi-agent orchestration. Defines how agents discover each other, exchange messages, and coordinate tasks within a runtime.
  • +
  • Pilot Protocol: A network-layer overlay that gives each agent a permanent virtual address, encrypted UDP tunnels, NAT traversal, and a trust model.
  • +
  • MCP (Model Context Protocol): Anthropic's protocol for connecting LLMs to tools and data sources. It defines how a model calls external functions and retrieves context.
  • +
  • A2A (Agent-to-Agent): Google's protocol for agent interoperability. It defines agent cards, task lifecycle, and message exchange between agents.
  • +
  • ACP (Agent Communication Protocol): BeeAI's protocol for multi-agent orchestration. It defines how agents discover each other, exchange messages, and coordinate tasks within a runtime.

vs MCP (Model Context Protocol)

MCP connects an LLM to tools and data sources. Pilot Protocol connects agents to each other.

    -
  • Layer: Pilot = Network (L3/L4); MCP = Application (L7)
  • -
  • Purpose: Pilot = agent-to-agent connectivity; MCP = model-to-tool connectivity
  • -
  • Transport: Pilot = encrypted UDP tunnels; MCP = stdio, HTTP+SSE
  • -
  • Addressing: Pilot = 48-bit virtual addresses; MCP = named tool endpoints
  • -
  • NAT traversal: Pilot = built-in (STUN + relay); MCP = not applicable
  • -
  • Trust model: Pilot = mutual handshake + crypto; MCP = implicit (local process)
  • -
  • Discovery: Pilot = registry + tags + DNS; MCP = tool manifests
  • -
  • Multi-agent: Pilot = native (any-to-any); MCP = hub-and-spoke (host to server)
  • +
  • Layer: Pilot Protocol (Network (L3/L4)), MCP (Application (L7)).
  • +
  • Purpose: Pilot Protocol (Agent-to-agent connectivity), MCP (Model-to-tool connectivity).
  • +
  • Transport: Pilot Protocol (Encrypted UDP tunnels), MCP (stdio, HTTP+SSE).
  • +
  • Addressing: Pilot Protocol (48-bit virtual addresses), MCP (Named tool endpoints).
  • +
  • NAT traversal: Pilot Protocol (Built-in (STUN + relay)), MCP (Not applicable).
  • +
  • Trust model: Pilot Protocol (Mutual handshake + crypto), MCP (Implicit (local process)).
  • +
  • Discovery: Pilot Protocol (Registry + tags + DNS), MCP (Tool manifests).
  • +
  • Multi-agent: Pilot Protocol (Native (any-to-any)), MCP (Hub-and-spoke (host ↔ server)).
-

Key difference: MCP is designed for a single model interacting with local tools. Pilot Protocol is designed for distributed agents communicating across networks. An MCP server could run on top of a Pilot tunnel to expose tools to remote agents.

+

MCP is designed for a single model interacting with local tools. Pilot Protocol is for distributed agents communicating across networks. An MCP server can run on top of a Pilot tunnel to expose tools to remote agents.

vs A2A (Agent-to-Agent)

-

A2A defines the application-level contract between agents - agent cards, task lifecycle, and message schemas. Pilot Protocol provides the network-level connectivity that moves those messages.

+

A2A defines the application-level contract between agents, such as agent cards, task lifecycle, and message schemas. Pilot Protocol provides the network-level connectivity for those messages.

    -
  • Layer: Pilot = Network (L3/L4); A2A = Application (L7)
  • -
  • Purpose: Pilot = connectivity + encryption + trust; A2A = task lifecycle + interop
  • -
  • Transport: Pilot = encrypted UDP tunnels; A2A = HTTP/JSON-RPC
  • -
  • Discovery: Pilot = registry + tags + DNS; A2A = agent cards (/.well-known/agent.json)
  • -
  • NAT traversal: Pilot = built-in (STUN + relay); A2A = requires public endpoints or VPN
  • -
  • Security: Pilot = X25519+AES-GCM per tunnel; A2A = delegated to HTTP/TLS
  • -
  • Addressing: Pilot = permanent virtual addresses; A2A = URLs
  • -
  • Offline support: Pilot = inbox queuing; A2A = polling / push notifications
  • +
  • Layer: Pilot Protocol (Network (L3/L4)), A2A (Application (L7)).
  • +
  • Purpose: Pilot Protocol (Connectivity + encryption + trust), A2A (Task lifecycle + interop).
  • +
  • Transport: Pilot Protocol (Encrypted UDP tunnels), A2A (HTTP/JSON-RPC).
  • +
  • Discovery: Pilot Protocol (Registry + tags + DNS), A2A (Agent cards (/.well-known/agent.json)).
  • +
  • NAT traversal: Pilot Protocol (Built-in (STUN + relay)), A2A (Requires public endpoints or VPN).
  • +
  • Security: Pilot Protocol (X25519+AES-GCM per tunnel), A2A (Delegated to HTTP/TLS).
  • +
  • Addressing: Pilot Protocol (Permanent virtual addresses), A2A (URLs).
  • +
  • Offline support: Pilot Protocol (Inbox queuing), A2A (Polling / push notifications).
-

Key difference: A2A assumes agents are reachable via HTTP URLs. Pilot Protocol makes agents reachable even behind NATs, firewalls, or without public IPs. A2A agent cards can advertise Pilot addresses, and A2A JSON-RPC messages can travel over Pilot tunnels.

+

A2A assumes agents are reachable via HTTP URLs. Pilot Protocol makes agents reachable behind NATs, firewalls, or without public IPs. A2A agent cards can advertise Pilot addresses, and A2A JSON-RPC messages can travel over Pilot tunnels.

vs ACP (Agent Communication Protocol)

-

ACP focuses on multi-agent orchestration within a runtime. Pilot Protocol focuses on the network layer beneath.

+

ACP focuses on multi-agent orchestration within a runtime. Pilot Protocol focuses on the network layer.

    -
  • Layer: Pilot = Network (L3/L4); ACP = Application / Runtime (L7)
  • -
  • Purpose: Pilot = cross-network connectivity; ACP = local runtime orchestration
  • -
  • Scope: Pilot = internet-scale (any network); ACP = single runtime / cluster
  • -
  • Transport: Pilot = encrypted UDP tunnels; ACP = HTTP/REST
  • -
  • Discovery: Pilot = global registry + tags; ACP = local agent directory
  • -
  • Trust: Pilot = mutual handshake + crypto identity; ACP = runtime-level access control
  • -
  • NAT traversal: Pilot = built-in; ACP = not applicable (local)
  • +
  • Layer: Pilot Protocol (Network (L3/L4)), ACP (Application / Runtime (L7)).
  • +
  • Purpose: Pilot Protocol (Cross-network connectivity), ACP (Local runtime orchestration).
  • +
  • Scope: Pilot Protocol (Internet-scale (any network)), ACP (Single runtime / cluster).
  • +
  • Transport: Pilot Protocol (Encrypted UDP tunnels), ACP (HTTP/REST).
  • +
  • Discovery: Pilot Protocol (Global registry + tags), ACP (Local agent directory).
  • +
  • Trust: Pilot Protocol (Mutual handshake + crypto identity), ACP (Runtime-level access control).
  • +
  • NAT traversal: Pilot Protocol (Built-in), ACP (Not applicable (local)).
-

Key difference: ACP orchestrates agents within a single runtime environment. Pilot Protocol connects agents across different machines, networks, and organizations. ACP agents can use Pilot tunnels to communicate with agents in remote runtimes.

+

ACP orchestrates agents within a single runtime environment. Pilot Protocol connects agents across different machines, networks, and organizations. ACP agents can use Pilot tunnels to communicate with agents in remote runtimes.

Feature matrix

    -
  • Permanent agent identity: Pilot = Yes; MCP = No; A2A = agent cards; ACP = agent ID
  • -
  • Virtual addressing: Pilot = 48-bit; MCP = No; A2A = No; ACP = No
  • -
  • End-to-end encryption: Pilot = X25519+AES-GCM; MCP = No; A2A = TLS; ACP = No
  • -
  • NAT traversal: Pilot = STUN+relay; MCP = N/A; A2A = No; ACP = N/A
  • -
  • Mutual trust model: Pilot = Yes; MCP = No; A2A = No; ACP = No
  • -
  • Peer discovery: Pilot = registry+tags; MCP = tool manifest; A2A = agent cards; ACP = directory
  • -
  • Pub/Sub: Pilot = built-in; MCP = No; A2A = No; ACP = No
  • -
  • Task delegation: Pilot = app-level (via service agents); MCP = No; A2A = Yes; ACP = Yes
  • -
  • Tool calling: Pilot = via services; MCP = Yes; A2A = No; ACP = No
  • -
  • Streaming: Pilot = Yes; MCP = SSE; A2A = SSE; ACP = SSE
  • -
  • Offline/async: Pilot = inbox; MCP = No; A2A = polling; ACP = No
  • -
  • Dependencies: Pilot = stdlib only; MCP = SDK; A2A = HTTP stack; ACP = runtime
  • -
  • Transport: Pilot = UDP; MCP = stdio/HTTP; A2A = HTTP; ACP = HTTP
  • +
  • Permanent agent identity: Pilot (Yes), MCP (No), A2A (Agent cards), ACP (Agent ID).
  • +
  • Virtual addressing: Pilot (48-bit), MCP (No), A2A (No), ACP (No).
  • +
  • End-to-end encryption: Pilot (X25519+AES-GCM), MCP (No), A2A (TLS), ACP (No).
  • +
  • NAT traversal: Pilot (STUN+relay), MCP (N/A), A2A (No), ACP (N/A).
  • +
  • Mutual trust model: Pilot (Yes), MCP (No), A2A (No), ACP (No).
  • +
  • Peer discovery: Pilot (Registry+tags), MCP (Tool manifest), A2A (Agent cards), ACP (Directory).
  • +
  • Pub/Sub: Pilot (Built-in), MCP (No), A2A (No), ACP (No).
  • +
  • Task delegation: Pilot (App-level (via service agents)), MCP (No), A2A (Yes), ACP (Yes).
  • +
  • Tool calling: Pilot (Via services), MCP (Yes), A2A (No), ACP (No).
  • +
  • Streaming: Pilot (Yes), MCP (SSE), A2A (SSE), ACP (SSE).
  • +
  • Offline/async: Pilot (Inbox), MCP (No), A2A (Polling), ACP (No).
  • +
  • Dependencies: Pilot (Stdlib only), MCP (SDK), A2A (HTTP stack), ACP (Runtime).
  • +
  • Transport: Pilot (UDP), MCP (stdio/HTTP), A2A (HTTP), ACP (HTTP).

When to use what

- -

Use Pilot Protocol when you need:

+

Use Pilot Protocol for:

  • Agents communicating across networks, NATs, or organizations
  • Permanent agent identity that survives restarts and migrations
  • End-to-end encryption without relying on TLS termination
  • A trust model where agents explicitly approve peers
  • -
  • Lightweight networking with minimal infrastructure (no HTTP server, no cloud platform to manage)
  • +
  • Lightweight networking with minimal infrastructure
- -

Use MCP when you need:

+

Use MCP for:

  • An LLM to call external tools (databases, APIs, file systems)
  • Local tool integration within a single application
  • Standardized tool discovery and invocation
- -

Use A2A when you need:

+

Use A2A for:

  • Interoperability between agents from different vendors
  • Structured task lifecycle (submit, progress, complete)
  • Agent capability discovery via agent cards
- -

Use ACP when you need:

+

Use ACP for:

  • Multi-agent orchestration within a single runtime
  • Local agent coordination and workflow management
  • @@ -109,33 +108,28 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

Using them together

-

These protocols are designed for different layers and combine naturally.

- -

Pilot + MCP

-

Run an MCP server on one machine and expose it over a Pilot tunnel. Remote agents connect to the MCP server's Pilot address - no public IP needed, encrypted end-to-end, trust-gated access.

+

These protocols are designed for different layers and can be combined.

+

Pilot + MCP

+

Run an MCP server on one machine and expose it over a Pilot tunnel. Remote agents can connect to the MCP server's Pilot address without a public IP, with end-to-end encryption and trust-gated access.

# Agent A runs an MCP server, exposed on Pilot port 80
 # Agent B connects from across the internet
 pilotctl connect <agent-a-address> 80
 # MCP JSON-RPC flows over the encrypted Pilot tunnel
- -

Pilot + A2A

-

Agents advertise A2A agent cards with their Pilot address. Task requests and responses travel over Pilot tunnels instead of public HTTP endpoints. NAT traversal, encryption, and trust come for free.

+

Pilot + A2A

+

Agents advertise A2A agent cards with their Pilot address. Task requests and responses travel over Pilot tunnels instead of public HTTP endpoints.

# Agent card includes Pilot address instead of URL
 {
   "name": "research-agent",
   "pilot_address": "1:0001.0000.0042",
   "skills": [{"name": "web-research"}]
 }
- -

Pilot + ACP

-

ACP runtimes on different machines connect via Pilot tunnels. Agents in runtime A can discover and communicate with agents in runtime B as if they were local - the Pilot tunnel handles routing, encryption, and NAT traversal.

- -

The short version: MCP, A2A, and ACP define what agents say. Pilot Protocol defines how they reach each other.

+

Pilot + ACP

+

ACP runtimes on different machines connect via Pilot tunnels. Agents in one runtime can discover and communicate with agents in another runtime as if they were local.

Related

diff --git a/src/pages/plain/docs/concepts.astro b/src/pages/plain/docs/concepts.astro index 8eaad2e..ce4ce26 100644 --- a/src/pages/plain/docs/concepts.astro +++ b/src/pages/plain/docs/concepts.astro @@ -1,123 +1,104 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/concepts.astro +// plain-source-sha256: 4d87a2b28057d902153b429b4fa1fa548a49bec288aff931e6a58ba3ca586295 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- +

← Docs index

Core Concepts

-

How Pilot Protocol addresses, transports, encrypts, traverses NATs, and establishes trust.

+

This document describes addressing, transport, encryption, NAT traversal, and the trust model in Pilot Protocol.

Addressing

- -

Every agent on the network gets a 48-bit virtual address with two parts: a 16-bit network prefix and a 32-bit node address.

- -

Addresses are displayed in hex format: N:NNNN.HHHH.LLLL

- +

Each agent on the network has a 48-bit virtual address with two parts: a 16-bit network prefix and a 32-bit node address.

+

Addresses are displayed in hex format: N:NNNN.HHHH.LLLL

    -
  • N — network ID in decimal (0 = public backbone)
  • -
  • NNNN — same network ID in hex (for readability)
  • -
  • HHHH.LLLL — 32-bit node address (two groups of 4 hex digits) uniquely assigned by the registry on registration
  • +
  • N - network ID in decimal (0 = public backbone)
  • +
  • NNNN - same network ID in hex
  • +
  • HHHH.LLLL - 32-bit node address assigned by the registry on registration
-

Examples:

    -
  • 0:0000.0000.0001 — node 1 on network 0
  • -
  • 0:0000.0000.0005 — node 5 on network 0
  • +
  • 0:0000.0000.0001 - node 1 on network 0
  • +
  • 0:0000.0000.0005 - node 5 on network 0
- -

Agents can also register hostnames — human-readable names like my-agent. Most commands accept either an address or a hostname. If one is not set, the node is still assigned an internal hostname of the form pilot-XXXXXXXX, where the suffix is the first 4 bytes of SHA-256(public_key) encoded as hex (8 hex characters).

- -

Special addresses

+

Agents can also register human-readable hostnames. Most commands accept either an address or a hostname. If a hostname is not set, the node is assigned an internal hostname of the form pilot-XXXXXXXX, where the suffix is the first 4 bytes of SHA-256(public_key) encoded as hex.

+

Special addresses:

    -
  • 0:0000.0000.0000 — unassigned / wildcard
  • -
  • 0:0000.FFFF.FFFF — broadcast (all nodes on network 0)
  • +
  • 0:0000.0000.0000 - unassigned / wildcard
  • +
  • 0:0000.FFFF.FFFF - broadcast (all nodes on network 0)

Transport

- -

Pilot Protocol provides reliable streams (TCP-equivalent) over UDP tunnels. The transport layer includes:

- +

Pilot Protocol provides reliable streams over UDP tunnels. The transport layer includes:

    -
  • Sliding window — controls how many packets can be in-flight simultaneously
  • -
  • Congestion control (AIMD) — additive-increase, multiplicative-decrease to avoid network congestion
  • -
  • Flow control — advertised receive window prevents overwhelming slow receivers
  • -
  • Nagle algorithm — coalesces small writes into larger packets for efficiency
  • -
  • Auto segmentation — large sends are automatically split into MTU-sized segments
  • -
  • Zero-window probing — detects when a receiver's window opens back up
  • -
  • SACK — selective acknowledgments for efficient loss recovery
  • +
  • Sliding window
  • +
  • Congestion control (AIMD)
  • +
  • Flow control
  • +
  • Nagle algorithm
  • +
  • Auto segmentation
  • +
  • Zero-window probing
  • +
  • SACK (selective acknowledgments)
- -

The transport also supports datagrams — unreliable, unordered messages for scenarios where reliability isn't needed.

- -

Connection lifecycle

+

The transport also supports datagrams, which are unreliable, unordered messages.

+

Connection lifecycle:

    -
  • Keepalive probes — sent every 30 seconds to detect dead connections
  • -
  • Idle timeout — connections without activity for 120 seconds are cleaned up
  • -
  • Graceful shutdown — FIN packets ensure both sides know when a connection ends
  • +
  • Keepalive probes are sent every 30 seconds.
  • +
  • Idle connections time out after 120 seconds.
  • +
  • Graceful shutdown uses FIN packets.

Encryption

- -

Traffic is encrypted by default. The encryption stack:

- +

Traffic is encrypted by default. The encryption stack includes:

    -
  • X25519 — Diffie-Hellman key exchange for per-tunnel shared secrets
  • -
  • AES-256-GCM — authenticated encryption for all tunnel traffic
  • -
  • Ed25519 — digital signatures for identity and trust operations
  • -
  • Random nonce prefix — each secure connection uses a unique nonce prefix to prevent replay
  • +
  • X25519 for Diffie-Hellman key exchange.
  • +
  • AES-256-GCM for authenticated encryption.
  • +
  • Ed25519 for digital signatures.
  • +
  • A random nonce prefix for each secure connection to prevent replay.
- -

Every node has a persistent Ed25519 identity keypair stored at ~/.pilot/identity.json. The public key is registered with the registry and used for trust handshake signing.

+

Each node has a persistent Ed25519 identity keypair stored at ~/.pilot/identity.json. The public key is registered and used for signing trust handshakes.

NAT Traversal

- -

The daemon automatically discovers its public endpoint and handles NAT traversal:

- -
    -
  1. STUN discovery — the daemon queries the beacon server to learn its public IP and port, and determines the NAT type
  2. -
  3. Direct connection — for Full Cone NATs, the STUN-discovered endpoint works for all peers
  4. -
  5. Hole-punching — for Restricted/Port-Restricted Cone NATs, the beacon coordinates simultaneous UDP packets from both peers to punch through the NAT
  6. -
  7. Relay fallback — for Symmetric NATs (where each destination gets a different port mapping), traffic is relayed through the beacon server
  8. -
- -

NAT type is detected automatically. No configuration needed. Cloud VMs with static IPs can skip STUN with the --endpoint host:port flag.

+

The daemon automatically discovers its public endpoint and handles NAT traversal. The process is as follows:

+
    +
  • STUN discovery: The daemon queries the beacon server to learn its public IP, port, and NAT type.
  • +
  • Direct connection: For Full Cone NATs, the discovered endpoint is used for all peers.
  • +
  • Hole-punching: For Restricted or Port-Restricted Cone NATs, the beacon coordinates simultaneous UDP packets from both peers.
  • +
  • Relay fallback: For Symmetric NATs, traffic is relayed through the beacon server.
  • +
+

NAT type is detected automatically. Cloud VMs with static IPs can skip STUN with the --endpoint host:port flag.

Trust Model

- -

Agents are private by default. No other agent can discover, resolve, or communicate with you until you explicitly establish mutual trust.

- -

The trust flow:

-
    -
  1. Agent A sends a handshake request to Agent B (with a justification message)
  2. -
  3. The request is relayed through the registry (signed with Ed25519)
  4. -
  5. Agent B sees the request in pending and can approve or reject it
  6. -
  7. Once both sides trust each other, they can communicate directly
  8. -
- -

Auto-approval: if both agents independently send handshake requests to each other, trust is established automatically — no manual approval needed.

- -

Trust persists across daemon restarts. Trust can be revoked at any time with untrust.

+

Agents are private by default. Communication requires mutual trust.

+

The trust flow is:

+
    +
  • Agent A sends a handshake request to Agent B.
  • +
  • The request is relayed through the registry and signed with Ed25519.
  • +
  • Agent B receives the pending request and can approve or reject it.
  • +
  • Once both sides trust each other, they can communicate directly.
  • +
+

If two agents independently send handshake requests to each other, trust is established automatically.

+

Trust persists across daemon restarts and can be revoked with the `untrust` command.

Well-known Ports

-
    -
  • 7 — Echo: liveness probes, latency measurement, throughput benchmarks
  • -
  • 53 — Nameserver: DNS-style hostname resolution
  • -
  • 80 — HTTP: web endpoints (use with the gateway)
  • -
  • 443 — Secure: end-to-end encrypted channel (X25519 + AES-256-GCM)
  • -
  • 444 — Handshake: trust negotiation protocol
  • -
  • 1000 — Stdio: text streams between agents (default for connect)
  • -
  • 1001 — Data Exchange: typed frames: text, JSON, binary, file transfer
  • -
  • 1002 — Event Stream: pub/sub broker with topic filtering and wildcards
  • +
  • 7 (Echo): Liveness probes, latency measurement, throughput benchmarks
  • +
  • 53 (Nameserver): DNS-style hostname resolution
  • +
  • 80 (HTTP): Web endpoints
  • +
  • 443 (Secure): End-to-end encrypted channel (X25519 + AES-256-GCM)
  • +
  • 444 (Handshake): Trust negotiation protocol
  • +
  • 1000 (Stdio): Text streams between agents
  • +
  • 1001 (Data Exchange): Typed frames: text, JSON, binary, file transfer
  • +
  • 1002 (Event Stream): Pub/sub broker with topic filtering and wildcards

Related

diff --git a/src/pages/plain/docs/configuration.astro b/src/pages/plain/docs/configuration.astro index 5593bbd..e8aef9f 100644 --- a/src/pages/plain/docs/configuration.astro +++ b/src/pages/plain/docs/configuration.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/configuration.astro +// plain-source-sha256: 6c78bd54c1e66879bdb44349d6bd3414efcdd6f2f7a69f1423baa5bb75cfdd0f import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -20,7 +22,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro'; "socket": "/tmp/pilot.sock", "webhook": "http://localhost:8080/events" } -

CLI flags override environment variables, which override config file values. The config file is created by pilotctl init and can be updated with pilotctl config --set.

+

CLI flags override environment variables, which override config file values. The config file is created by `pilotctl init` and can be updated with `pilotctl config --set`.

Config commands

Initialize

@@ -32,18 +34,31 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
pilotctl config --set registry=host:9000
 pilotctl config --set hostname=new-name
+

Automatic updates

+

Pilot includes a `pilot-updater` sidecar that can update binaries to the latest stable release. Automatic updates are disabled by default.

+

Check the status

+
pilotctl update status
+

Shows whether automatic updates are enabled and the current version.

+

Enable / disable

+
pilotctl update enable
+pilotctl update disable
+

The setting is stored in ~/.pilot/auto-update.json and is read by the updater on its next check. When enabled, the updater installs new stable releases on its interval (default hourly) after verifying release checksums.

+

Update once, manually

+
pilotctl update
+

Runs a single check-and-install. This works regardless of the automatic-update setting. Pin to a specific release with `--pin v1.12.1`.

+

Environment variables

    -
  • PILOT_SOCKET: Path to the daemon IPC socket. Default: /tmp/pilot.sock.
  • -
  • PILOT_REGISTRY: Registry server address. Default: 34.71.57.205:9000.
  • -
  • PILOT_BEACON: Beacon server address (used for STUN, NAT punch, and relay). Default: 34.71.57.205:9001.
  • -
  • PILOT_DAEMON_BIN: Override path to the pilot-daemon binary. Default: auto-discovered.
  • -
  • PILOT_GATEWAY_BIN: Override path to the pilot-gateway binary. Default: auto-discovered.
  • -
  • PILOT_HOSTNAME: Hostname to set during install. If unset, the node is assigned an internal hostname of the form pilot-XXXXXXXX. Default: none.
  • -
  • PILOT_ADMIN_TOKEN: Admin token for enterprise operations. Default: none.
  • -
  • PILOT_HOME: Directory where pilot stores identity, config, and received files. Default: ~/.pilot.
  • -
  • PILOT_EMAIL: Email address used for first-time daemon registration. Default: none.
  • -
  • PILOT_RC: Path to a shell RC snippet sourced by the daemon on startup. Default: none.
  • +
  • PILOT_SOCKET: Path to the daemon IPC socket (Default: /tmp/pilot.sock)
  • +
  • PILOT_REGISTRY: Registry server address (Default: 34.71.57.205:9000)
  • +
  • PILOT_BEACON: Beacon server address (used for STUN, NAT punch, and relay) (Default: 34.71.57.205:9001)
  • +
  • PILOT_DAEMON_BIN: Override path to the `pilot-daemon` binary (Default: auto-discovered)
  • +
  • PILOT_GATEWAY_BIN: Override path to the `pilot-gateway` binary (Default: auto-discovered)
  • +
  • PILOT_HOSTNAME: Hostname to set during install. If unset, the node is assigned an internal hostname. (Default: none)
  • +
  • PILOT_ADMIN_TOKEN: Admin token for enterprise operations (Default: none)
  • +
  • PILOT_HOME: Directory where pilot stores identity, config, and received files (Default: ~/.pilot)
  • +
  • PILOT_EMAIL: Email address used for first-time daemon registration (Default: none)
  • +
  • PILOT_RC: Path to a shell RC snippet sourced by the daemon on startup (Default: none)

Directory structure

@@ -61,7 +76,7 @@ pilotctl config --set hostname=new-name updater.log # Auto-updater log file

Daemon flags

-

These flags forward from pilotctl daemon start to the underlying pilot-daemon binary.

+

These flags forward from `pilotctl daemon start` to the underlying `pilot-daemon` binary.

  • --registry <addr>: Registry server address
  • --beacon <addr>: Beacon server address (STUN)
  • @@ -83,31 +98,30 @@ pilotctl config --set hostname=new-name
  • --networks <ids>: Comma-separated network IDs to auto-join on startup
  • --wait <dur>: How long to wait for the daemon to become ready (default 15s)
- -

pilot-daemon-only tuning flags

-

The following flags are consumed only when invoking the pilot-daemon binary directly. They are not forwarded by pilotctl daemon start.

+

pilot-daemon-only tuning flags

+

The following flags accept tuning values but are only consumed when invoking the `pilot-daemon` binary directly. They are not forwarded by `pilotctl daemon start`.

    -
  • -keepalive <dur>: Keepalive probe interval. Default: 30s.
  • -
  • -idle-timeout <dur>: Idle connection timeout. Default: 120s.
  • -
  • -syn-rate-limit <n>: Max SYN packets per second. Default: 100.
  • -
  • -max-conns-per-port <n>: Max connections per port. Default: 1024.
  • -
  • -max-conns-total <n>: Max total connections. Default: 4096.
  • -
  • -time-wait <dur>: TIME_WAIT duration. Default: 10s.
  • -
  • -registry-tls: Enable TLS for registry connection. Default: false.
  • -
  • -registry-fingerprint <hex>: SHA-256 fingerprint of registry TLS cert. Default: none.
  • -
  • -no-echo: Disable the built-in echo service (port 7). Default: false.
  • -
  • -no-dataexchange: Disable the built-in data exchange (port 1001). Default: false.
  • -
  • -dataexchange-b64: Include a lossless data_b64 field alongside data in inbox messages. Default: false.
  • -
  • -no-eventstream: Disable the built-in event stream (port 1002). Default: false.
  • -
  • -relay-only: Hide real_addr from peers; reach only via beacon-relay. Default: false.
  • -
  • -transport <mode>: Tunnel transport — `udp` (default, today's behavior) or `compat` (opt-in, tunnels Pilot packets over WSS to the beacon for daemons in UDP-blocked environments). See "Running pilot behind a firewall".
  • -
  • -compat-beacon <url>: WSS endpoint used when -transport=compat. Default: wss://beacon.pilotprotocol.network/v1/compat
  • -
  • -tls-trust <mode>: Compat-mode TLS trust — `system` (default; OS trust store, matches the public beacon's Let's Encrypt cert) or `pinned` (Pilot CA root embedded in the daemon binary). Default will flip back to `pinned` in a future release once the production Pilot root ships embedded.
  • -
  • -fake-listen-addr <addr>: Advertise this listen_addr to the registry instead of the real one. Default: none.
  • +
  • -keepalive <dur>: Keepalive probe interval (Default: 30s)
  • +
  • -idle-timeout <dur>: Idle connection timeout (Default: 120s)
  • +
  • -syn-rate-limit <n>: Max SYN packets per second (Default: 100)
  • +
  • -max-conns-per-port <n>: Max connections per port (Default: 1024)
  • +
  • -max-conns-total <n>: Max total connections (Default: 4096)
  • +
  • -time-wait <dur>: TIME_WAIT duration (Default: 10s)
  • +
  • -registry-tls: Enable TLS for registry connection (Default: false)
  • +
  • -registry-fingerprint <hex>: SHA-256 fingerprint of registry TLS cert (Default: none)
  • +
  • -no-echo: Disable the built-in echo service (port 7) (Default: false)
  • +
  • -no-dataexchange: Disable the built-in data exchange (port 1001) (Default: false)
  • +
  • -dataexchange-b64: Include a lossless `data_b64` field alongside `data` in inbox messages. (Default: false)
  • +
  • -no-eventstream: Disable the built-in event stream (port 1002) (Default: false)
  • +
  • -relay-only: Hide real_addr from peers; reach only via beacon-relay (Default: false)
  • +
  • -transport <mode>: Tunnel transport. 'udp' (default) binds a UDP socket. 'compat' tunnels Pilot packets over WSS to the beacon for daemons in UDP-blocked environments. (Default: udp)
  • +
  • -compat-beacon <url>: WSS endpoint used when -transport=compat (Default: wss://beacon.pilotprotocol.network/v1/compat)
  • +
  • -tls-trust <mode>: Compat-mode TLS trust store. 'system' (default) uses the OS trust store. 'pinned' uses the Pilot CA root embedded in the daemon binary. (Default: system)
  • +
  • -fake-listen-addr <addr>: Advertise this listen_addr to the registry instead of the real one (Default: none)

Logging

-

The daemon uses structured logging via Go's slog package. Logs are written to ~/.pilot/pilot.log.

+

The daemon uses structured logging. Logs are written to ~/.pilot/pilot.log.

# Debug logging
 pilotctl daemon start --log-level debug
 
@@ -116,10 +130,10 @@ pilotctl daemon start --log-format json
 
 # View logs
 tail -f ~/.pilot/pilot.log
-

Log levels: debug, info (default), warn, error

+

Log levels: `debug`, `info` (default), `warn`, `error`

Setup manifests

-

When a setup skill configures an agent for a role, it writes a setup manifest to ~/.pilot/setups/<slug>.json. This file persists the role configuration.

+

When a setup skill configures this agent for a specific role, it writes a setup manifest to ~/.pilot/setups/<slug>.json. This file persists the role configuration so the agent knows its identity, which skills to use, which peers exist, and what data flows to expect.

{
   "setup": "fleet-health-monitor",
   "setup_name": "Fleet Health Monitor",
@@ -158,7 +172,7 @@ tail -f ~/.pilot/pilot.log
  • role_name (string): Human-readable role name
  • hostname (string): This agent's hostname
  • description (string): What this agent does in context
  • -
  • skills (map): Skill name to contextual description of what it does in this role
  • +
  • skills (map): Skill name → contextual description of what it does in this role
  • peers (array): All peer agents in the setup (even indirect ones)
  • data_flows (array): Directional data flows (send/receive) with port and topic
  • handshakes_needed (array): Hostnames of peers that need direct trust (handshakes)
  • diff --git a/src/pages/plain/docs/consent.astro b/src/pages/plain/docs/consent.astro index a7b022d..2426a2a 100644 --- a/src/pages/plain/docs/consent.astro +++ b/src/pages/plain/docs/consent.astro @@ -1,18 +1,20 @@ --- -// Plain mirror of /docs/consent. Keep in sync with src/pages/docs/consent.astro. +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/consent.astro +// plain-source-sha256: dbc34432b6d6acb9d15188dbdda57bbba803c661dc27c6b43c05dd203056ac50 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- - +

    ← Docs index

    -

    Consent and Privacy Controls

    +

    Consent & Privacy Controls

    -

    Four features ship on by default: telemetry, broadcasts, reviews, and skill injection. Each is transparent about what it does, who benefits, and what you are accepting. This page describes each in plain terms.

    +

    Four features are on by default. This page explains what each feature does, who benefits, and what is being accepted.

    How opt-out works

    -

    All four features are on by default. Each comes with a data and trust cost. Disabling any of them does not affect core messaging, peer routing, or tunnel encryption.

    -

    The three consent flags live in ~/.pilot/config.json under a consent key:

    +

    All four features are on by default. Disabling any of them does not affect core messaging, peer routing, or tunnel encryption.

    +

    Three consent flags are located in ~/.pilot/config.json under a `consent` key:

    {
       "consent": {
         "telemetry":  true,
    @@ -20,33 +22,19 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
         "reviews":    true
       }
     }
    -

    Set any of them to false to opt out. Skill injection has its own key and CLI - see Skill injection below.

    +

    Set any value to `false` to opt out. Skill injection has its own configuration key and CLI.

    Telemetry

    -

    Risk level: Low.

    - -

    What it does

    -

    When you browse or install apps from the app store, a small signed event is sent to telemetry.pilotprotocol.network. Three events are emitted:

    -
      -
    • catalogue_viewed: fired once when you run pilotctl appstore catalogue. No payload beyond a timestamp and your identity signature.
    • -
    • appstore_view: fired when you run pilotctl appstore view <app-id>. Payload: the app ID you viewed.
    • -
    • app_installed: fired after a successful pilotctl appstore install <app-id>. Payload: app ID, version installed, and install source (catalogue or local).
    • -
    -

    Each event is signed with your daemon's Ed25519 identity key before transmission. The telemetry server verifies the signature and rejects unsigned or tampered events.

    - -

    Who benefits

    +

    When browsing or installing apps from the app store, a small signed event is sent to telemetry.pilotprotocol.network. Three events are emitted:

      -
    • You: Popular, well-maintained apps surface to the top of the catalogue; abandoned or low-quality apps do not. The result is a curated catalogue driven by what agents actually use - not advertising.
    • -
    • App developers: Real usage data without building their own analytics pipeline. A developer can tell whether an app is getting traction and prioritize accordingly.
    • -
    • The network: A richer, actively-maintained app ecosystem. Telemetry is the quality signal that keeps the catalogue healthy.
    • +
    • catalogue_viewed: Fired once when `pilotctl appstore catalogue` is run. It has no payload beyond a timestamp and an identity signature.
    • +
    • appstore_view: Fired when `pilotctl appstore view <app-id>` is run. The payload is the app ID that was viewed.
    • +
    • app_installed: Fired after a successful `pilotctl appstore install <app-id>`. The payload includes the app ID, version installed, and install source.
    - -

    Your risk profile

    -

    What is received: The app ID, the action type, and a signature from your Ed25519 key. Your Ed25519 public key is a pseudonymous identifier - it has no name or email unless you registered with one via -email. Your IP address is visible to the telemetry server during the TLS connection, as with any HTTPS request.

    -

    What is not received: Message contents, agent conversation data, peer history, or any data about what your agent is actually doing.

    -

    Who should turn this off: Users in high-sensitivity environments with strict no-telemetry policies, users who do not want any third party to know which apps they have looked at, or users running automated pipelines where even low-volume outbound telemetry is undesirable.

    - -

    Commands

    +

    Each event is signed with the daemon's Ed25519 identity key before transmission. The telemetry server verifies the signature and rejects unsigned or tampered events.

    +

    Telemetry data is used to surface popular and well-maintained apps in the catalogue. It provides app developers with usage data. This is intended to improve the app ecosystem.

    +

    The telemetry server receives the app ID, the action type, and a signature from an Ed25519 key. The Ed25519 public key is a pseudonymous identifier; it has no name or email unless registered with one via `-email`. An IP address is visible to the telemetry server during the TLS connection. Message contents, agent conversation data, peer history, or any data about agent activity is not sent.

    +

    This feature can be turned off for high-sensitivity environments with no-telemetry policies, or for users who do not want a third party to know which apps they have viewed.

    # These commands trigger telemetry events when consent is on
     pilotctl appstore catalogue                 # → catalogue_viewed event
     pilotctl appstore view io.pilot.cosift      # → appstore_view event (carries app ID)
    @@ -55,27 +43,14 @@ pilotctl appstore install io.pilot.cosift   # → app_installed event (carries a
     # Opt out — no dial, no buffer, no goroutine spawned
     # Set in ~/.pilot/config.json:
     # {"consent": {"telemetry": false}}
    -

    Takes effect immediately for CLI commands. Restart the daemon for daemon-side events.

    +

    Opting out takes effect immediately for CLI commands. The daemon must be restarted for daemon-side events.

    Broadcasts

    -

    Risk level: Medium.

    - -

    What it does

    -

    Network administrators holding a valid admin token can send a single datagram to every agent in a network simultaneously - without iterating over each peer. When your daemon receives a broadcast, it checks the admin token, then forwards the datagram payload to your agent on the specified port.

    - -

    Who benefits

    -
      -
    • You (if you run a fleet): Coordinate all your agents with a single command instead of O(N) individual messages. Push a config refresh, trigger a controlled shutdown, signal a version upgrade - all at once. This is the only O(1) mechanism for fleet-wide coordination in a large peer mesh.
    • -
    • Fleet operators: Enterprise deployments and service meshes depend on broadcast for operational control - incident response, rolling restarts, policy pushes.
    • -
    • The network: Rapid, authenticated fan-out enables faster response to security advisories and protocol updates across large deployments.
    • -
    - -

    Your risk profile

    -

    What you are accepting: Any party holding the network's admin token can deliver arbitrary data to your agent. The admin token is set by whoever configured the network - usually the person who started the daemon with -admin-token. Your agent code is responsible for handling the incoming payload correctly.

    -

    The realistic threat: If the admin token is compromised - leaked in a config file, rotated without your knowledge, or held by a bad actor - an attacker could send broadcast payloads to your agent. Broadcasts are authenticated (no valid token, no delivery) but the authentication is only as strong as your token handling practices.

    -

    Who should turn this off: Users joining networks whose administrators they do not know or trust. Users running solo (no fleet, no admin) who will never send or receive broadcasts - turning it off eliminates an attack surface that offers no benefit. Users in high-security environments where any unsolicited inbound data channel is unacceptable.

    - -

    Commands

    +

    Network administrators with a valid admin token can send a single datagram to every agent in a network simultaneously. When a daemon receives a broadcast, it checks the admin token, then forwards the datagram payload to the agent on the specified port.

    +

    Broadcasts allow for fleet-wide coordination with a single command for tasks like configuration refreshes or controlled shutdowns. This is the only O(1) mechanism for fleet-wide coordination.

    +

    Accepting broadcasts means any party holding the network's admin token can deliver arbitrary data to an agent. The admin token is set by the network configurator, usually the person who started the daemon with `-admin-token`. The agent code is responsible for handling the incoming payload.

    +

    If the admin token is compromised, an attacker could send broadcast payloads to an agent. Broadcasts are authenticated, but the authentication depends on token handling practices.

    +

    This feature can be turned off by users joining networks with unknown or untrusted administrators, or by solo users who will not send or receive broadcasts.

    # Send a broadcast (requires admin token — you are the operator)
     pilotctl broadcast <net-id> <dst-port> <data> --admin-token <token>
     
    @@ -83,32 +58,18 @@ pilotctl broadcast <net-id> <dst-port> <data> --admin-token &l
     # Opt out — incoming datagrams are silently dropped before reaching your agent
     # Set in ~/.pilot/config.json:
     # {"consent": {"broadcasts": false}}
    -

    Restart the daemon for the change to take effect. The sender receives no error when you opt out - by design, to avoid a noisy failure cascade across large fleets.

    +

    The daemon must be restarted for the change to take effect. The sender receives no error when a recipient has opted out.

    Reviews

    -

    Risk level: Low.

    - -

    What it does

    -

    Two review-prompt behaviours are consent-gated, plus an explicit review command:

    -
      -
    • Post-send-message nudge: after roughly 5% of successful pilotctl send-message calls, a short prompt appears on stderr suggesting you leave a review of Pilot itself. Pressing Enter or running your next command skips it. The prompt never appears in stdout and never corrupts --json output.
    • -
    • App call intercept: after roughly 5% of successful pilotctl appstore call invocations, the output is replaced by a review prompt for the called app. Your call result is shown first; the prompt appears after.
    • -
    • Explicit command: pilotctl review <subject> lets you submit a review at any time, independently of the above prompts.
    • -
    - -

    Who benefits

    +

    Three review-related behaviors are gated by consent:

      -
    • You: Community reviews surface quality signals before you install. A 2-star app with "crashes on arm64" is more useful than no rating. You are also the producer of that signal for others.
    • -
    • App developers: Direct feedback from real users - actual text from people using the app, not just a traffic spike or star count.
    • -
    • The network: Review signals drive catalogue ranking. Well-reviewed apps get visibility; broken ones get deprioritized. The catalogue improves as reviews accumulate.
    • +
    • Post-send-message nudge: After about 5% of successful `pilotctl send-message` calls, a prompt appears on stderr suggesting a review of Pilot.
    • +
    • App call intercept: After about 5% of successful `pilotctl appstore call` invocations, the output is replaced by a review prompt for the called app after the call result is shown.
    • +
    • Explicit command: `pilotctl review <subject>` allows submitting a review at any time.
    - -

    Your risk profile

    -

    What is received when you submit a review: The subject (an app ID or the string pilot), an optional star rating (1-5), and optional free-text you typed. No session context, no command history, no automatically-captured data - everything in a review is what you explicitly provided.

    -

    The main operational risk: The 5% intercept replaces pilotctl appstore call stdout with a review prompt. If you run appstore call in a shell script or pipeline, this can corrupt your output. Disable reviews if you run pilotctl in automation.

    -

    Who should turn this off: Users running pilotctl in scripts or CI pipelines where stdout must be clean. Users who do not want any unsolicited prompts during normal operation.

    - -

    Commands

    +

    Reviews provide quality signals for apps in the catalogue and give developers user feedback. This data drives catalogue ranking.

    +

    When a review is submitted, the subject (an app ID or `pilot`), an optional star rating, and optional free-text are sent. Only explicitly provided data is included.

    +

    The 5% intercept replaces `pilotctl appstore call` stdout with a review prompt, which can corrupt output in shell scripts or pipelines. Reviews should be disabled if `pilotctl` is used in automation.

    # Submit a review explicitly
     pilotctl review pilot                              # review Pilot itself (no rating)
     pilotctl review pilot --rating 5                  # with a star rating
    @@ -121,37 +82,22 @@ pilotctl --json appstore call io.pilot.cosift cosift.help '{}'
     # Opt out — no prompts, no intercepts, no review data sent
     # Set in ~/.pilot/config.json:
     # {"consent": {"reviews": false}}
    -

    Takes effect immediately for all CLI commands.

    +

    Opting out takes effect immediately for all CLI commands.

    Skill injection

    -

    Risk level: Medium.

    - -

    What it does

    -

    The daemon writes a SKILL.md file and a short heartbeat directive into the well-known configuration directories of supported agent toolchains - Claude Code (~/.claude/CLAUDE.md), Cursor (.cursor/rules), OpenHands, OpenClaw, and Hermes. This is what makes those agents discover Pilot tools automatically and reach for Pilot first instead of falling back to slower, less-structured tools like web_search or curl.

    -

    The injector writes only inside a clearly-delimited marker block (<!-- pilot-inject:start --> / <!-- pilot-inject:end -->). It never touches content outside that block. On every tick it re-fetches the latest skill content and rewrites its own block.

    - -

    Who benefits

    -
      -
    • You: Zero-configuration integration. Your agents automatically know Pilot is available and use it for tasks it handles better - messaging peers, querying live-data specialists, calling app store apps. Without injection you would manually copy skill content into every agent's config and keep it updated.
    • -
    • The network: Every additional agent that defaults to Pilot for peer messaging adds traffic and value to the mesh. Higher usage means more diverse specialist agents, more apps, and a richer ecosystem.
    • -
    • Skill developers: Publishing a skill to the catalog is enough for distribution. Injection is the mechanism that puts new skills in front of users automatically.
    • -
    - -

    Your risk profile

    - -

    What you are accepting: The injector fetches content at runtime from the public pilot-protocol/pilot-skills repository and writes it to your agent's config directory. In auto mode, this happens every 15 minutes. The injected content influences what tools your agent reaches for and what instructions it follows.

    -

    The realistic threat: If the pilot-skills repository is compromised, injected content could modify your agent's behavior - potentially instructing it to use Pilot tools in unintended ways, or expanding the set of actions it takes. This is a supply-chain risk: you are trusting the integrity of a third-party repository. The injector does not execute anything, but agent toolchains do execute the injected instructions.

    -

    Mitigation: In manual mode (the default on fresh install), content is only applied when you explicitly trigger it with pilotctl update - giving you full control over when updates land. You can inspect what would be injected by reading the skills repo before updating.

    -

    Who should turn this off: Users with strict control requirements over their agent configs who prefer to manage skill content manually. Users in environments where any external write to agent config directories is a compliance violation. Users who have already managed their CLAUDE.md and do not want it modified.

    - -

    Three modes

    +

    The daemon writes a `SKILL.md` file and a heartbeat directive into the configuration directories of supported agent toolchains, including Claude Code, Cursor, OpenHands, OpenClaw, and Hermes. This allows agents to discover Pilot tools automatically.

    +

    The injector writes only inside a `<!-- pilot-inject:start -->` / `<!-- pilot-inject:end -->` marker block. It re-fetches the latest skill content and rewrites its block on every tick.

    +

    This feature provides zero-configuration integration for agents. It is intended to increase network traffic and simplify skill distribution for developers.

    +

    The injector fetches content from the public `pilot-protocol/pilot-skills` repository and writes it to the agent's config directory. In `auto` mode, this happens every 15 minutes. The injected content influences agent behavior.

    +

    If the `pilot-skills` repository is compromised, injected content could modify an agent's behavior. This is a supply-chain risk. The injector does not execute anything, but agent toolchains execute the injected instructions.

    +

    In `manual` mode, which is the default, content is only applied when explicitly triggered with `pilotctl update`.

    +

    This feature can be turned off by users who prefer to manage skill content manually or operate in environments where external writes to agent config directories are a compliance violation.

    +

    There are three modes:

      -
    • manual (default on fresh install): Skills are installed once when the daemon first starts. Updated only when you explicitly run pilotctl update or pilotctl skills check. No background ticker. Best for users who want Pilot tools available but want to review updates before they land.
    • -
    • auto: A reconcile pass runs every 15 minutes. Skills are always current with whatever is in the skill repository. Best for users who trust the skill repository and want always-current skills without manual intervention.
    • -
    • disabled: No injection. No updates. Existing injected files are removed immediately on mode switch. Best for users who manage their agent config themselves, or who are not using any supported toolchain.
    • +
    • manual (default on fresh install): Skills are installed once when the daemon first starts. They are updated only when `pilotctl update` or `pilotctl skills check` is run.
    • +
    • auto: A reconcile pass runs every 15 minutes to keep skills current with the skill repository.
    • +
    • disabled: No injection occurs. Existing injected files are removed.
    - -

    Commands

    # Check current mode, see which files are managed and their paths
     pilotctl skills status
     
    @@ -174,20 +120,11 @@ pilotctl skills disable all
     # Works in auto and manual. In disabled mode, outputs a message but does nothing.
     pilotctl update
     pilotctl skills check    # alias for the same operation
    -

    Mode changes take effect immediately - no daemon restart required. The mode is stored in ~/.pilot/config.json under skill_inject.mode.

    - -

    Everything injected is open source: pilot-protocol/skillinject (the injector code), pilot-protocol/pilot-skills (the content that gets injected).

    +

    Mode changes take effect immediately and do not require a daemon restart. The mode is stored in `~/.pilot/config.json` under `skill_inject.mode`. The injector code and the injected content are open source.

    Sandbox mode (daemon hardening)

    -

    Not a privacy feature - a security hardening tool.

    - -

    What it does

    -

    The -sandbox flag on the pilotd daemon restricts all filesystem access to a single confinement directory. Any path flag (-config, -identity, -socket) that resolves to a location outside the sandbox directory causes a fatal error at startup - before the daemon reads or writes anything. Unset path flags are automatically redirected to their counterpart inside the sandbox directory.

    - -

    Who benefits and why

    -

    Sandbox mode does not change what data is collected or sent. It limits the blast radius if the daemon binary is compromised. A process confined to ~/.pilot cannot write to your agent configs, your SSH keys, or your project files - even if an attacker achieves code execution inside the daemon. It is defense-in-depth: independent of network-level security, relevant when you deploy the daemon in shared or less-trusted environments.

    - -

    Commands

    +

    The `-sandbox` flag on the `pilotd` daemon restricts all filesystem access to a single confinement directory. Any path flag that resolves to a location outside the sandbox directory causes a fatal error at startup. Unset path flags are automatically redirected to their counterpart inside the sandbox directory.

    +

    Sandbox mode is a security hardening tool, not a privacy feature. It limits the blast radius if the daemon binary is compromised. It is relevant when deploying the daemon in shared or less-trusted environments.

    # Confine daemon to ~/.pilot (default sandbox-dir)
     pilotd -sandbox
     
    @@ -203,10 +140,10 @@ pilotd -sandbox -sandbox-dir /opt/pilot-data \
     # -config  → <sandbox-dir>/config.json
     # -identity → <sandbox-dir>/identity.json
     # -socket  → <sandbox-dir>/pilot.sock
    -

    Network paths (registry, beacon, telemetry endpoint) are unaffected by sandbox mode.

    +

    Network paths are unaffected by sandbox mode.

    Disable everything at once

    -

    To opt out of all four features in a single config edit:

    +

    To opt out of all four features, edit `~/.pilot/config.json`:

    {
       "consent": {
         "telemetry":  false,
    @@ -215,12 +152,12 @@ pilotd -sandbox -sandbox-dir /opt/pilot-data \
       },
       "skill_inject": {"mode": "disabled"}
     }
    -

    Set in ~/.pilot/config.json and restart the daemon. Peer routing, tunnel encryption, and peer-to-peer messaging are unaffected.

    +

    Restart the daemon after setting the configuration. Peer routing, tunnel encryption, and peer-to-peer messaging are unaffected.

    Related

    diff --git a/src/pages/plain/docs/diagnostics.astro b/src/pages/plain/docs/diagnostics.astro index 5dfcbec..56f0b88 100644 --- a/src/pages/plain/docs/diagnostics.astro +++ b/src/pages/plain/docs/diagnostics.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/diagnostics.astro +// plain-source-sha256: 5e027742676ada5ee4eb82e64c115ea9619b4910c260cdb3fd97d0428c1eb01d import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,12 +10,12 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Diagnostics

    -

    This page describes tools for measuring latency, throughput, and inspecting network state.

    +

    Tools for measuring latency, throughput, and inspecting network state.

    health

    Checks daemon vitals.

    pilotctl health
    -

    Returns: status, uptime_seconds, connections, peers, bytes_sent, bytes_recv.

    +

    Returns: `status`, `uptime_seconds`, `connections`, `peers`, `bytes_sent`, `bytes_recv`

    # Example output
     Daemon Health
       Status:      ok
    @@ -29,7 +31,7 @@ Daemon Health
     pilotctl ping other-agent --count 10
     pilotctl ping 0:0000.0000.0005 --timeout 30s

    Uses the built-in echo service on port 7. The default is 4 pings.

    -

    Returns: target, results ([{seq, bytes, rtt_ms, error}]), and timeout (bool).

    +

    Returns: `target`, `results` [{`seq`, `bytes`, `rtt_ms`, `error`}], `timeout` (bool)

    # Example output
     PING 0:0000.0000.037D
       seq=0 bytes=6 time=513.952ms
    @@ -40,14 +42,14 @@ PING 0:0000.0000.037D
     

    traceroute

    Measures connection setup time and RTT samples.

    pilotctl traceroute 0:0000.0000.0005
    -

    Returns: target, setup_ms, rtt_samples ([{rtt_ms, bytes}]).

    +

    Returns: `target`, `setup_ms`, `rtt_samples` [{`rtt_ms`, `bytes`}]

    bench

    Measures throughput by sending data through the echo service.

    pilotctl bench other-agent          # 1 MB (default)
     pilotctl bench other-agent 10       # 10 MB
     pilotctl bench other-agent 50
    -

    Returns: target, sent_bytes, recv_bytes, send_duration_ms, total_duration_ms, send_mbps, total_mbps.

    +

    Returns: `target`, `sent_bytes`, `recv_bytes`, `send_duration_ms`, `total_duration_ms`, `send_mbps`, `total_mbps`

    # Example output
     BENCH 0:0000.0000.037D - sending 1.0 MB via echo port
       Sent:     1.0 MB in 3.7s (0.3 MB/s)
    @@ -57,17 +59,17 @@ BENCH 0:0000.0000.037D - sending 1.0 MB via echo port
     

    Lists connected peers.

    pilotctl peers
     pilotctl peers --search "web-server"  # Filter by tag or query
    -

    Returns: peers ([{node_id, encrypted, authenticated, path (direct | relay)}]), total, plus the encrypted_peers, authenticated_peers, and relay_peer_count aggregates. Real endpoints are redacted by the daemon before reaching a client.

    +

    Returns: `peers` [{`node_id`, `encrypted`, `authenticated`, `path` (`direct` | `relay`)}], `total`, plus the `encrypted_peers` / `authenticated_peers` / `relay_peer_count` aggregates. Real endpoints are redacted by the daemon before they reach any client.

    connections

    Lists active connections with transport stats.

    pilotctl connections
    -

    Returns detailed per-connection information: connection ID, local/remote port, state, bytes sent/received, segments, retransmissions, and transport diagnostics such as CWND, SRTT, and SACK stats.

    +

    Returns detailed per-connection information: connection ID, local/remote port, state, bytes sent/received, segments, retransmissions, and transport diagnostics - CWND (congestion window: TCP send rate limit), SRTT (smoothed round-trip time), and SACK (selective acknowledgements) stats.

    info

    -

    Provides full daemon status.

    +

    Shows full daemon status.

    pilotctl info
    -

    Returns: node_id, address, hostname, uptime_secs, connections, ports, peers, encrypt, bytes_sent, bytes_recv, per-connection stats, and a peer list with encryption status.

    +

    Returns: `node_id`, `address`, `hostname`, `uptime_secs`, `connections`, `ports`, `peers`, `encrypt`, `bytes_sent`, `bytes_recv`, per-connection stats, peer list with encryption status.

    disconnect

    Closes a specific connection by ID.

    @@ -76,7 +78,7 @@ pilotctl connections # Close it pilotctl disconnect 42
    -

    Returns: conn_id.

    +

    Returns: `conn_id`

    Related

      diff --git a/src/pages/plain/docs/enterprise-audit.astro b/src/pages/plain/docs/enterprise-audit.astro index 89b513d..518bfd2 100644 --- a/src/pages/plain/docs/enterprise-audit.astro +++ b/src/pages/plain/docs/enterprise-audit.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise-audit.astro +// plain-source-sha256: 76104ae3c1af932b7b5e5f954b4ac2375a30a422adf713bc1b1aea3ce1bc0b96 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,14 +10,14 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Audit & Compliance

      -

      The registry generates structured audit events for every state change. These events can be queried via an API, and can be exported to external SIEM systems, webhooks, or other endpoints.

      +

      This document describes structured audit events, which are generated for every state change in the registry. It covers event structure, querying, and exporting to external systems.

      Overview

      -

      Every state change in the registry generates a structured audit event. Events are emitted as SIEM-ingestible JSON, stored in an in-memory ring buffer for API queries, and can be forwarded to external systems through an audit export pipeline.

      -

      The audit system operates at the registry level, capturing events across all networks. Enterprise features add more event types, such as RBAC changes, policy updates, and directory sync.

      +

      Every state change in the registry generates a structured audit event. Events are emitted via slog as SIEM-ingestible JSON, stored in an in-memory ring buffer for API queries, and can be forwarded to external systems through the audit export pipeline.

      +

      The audit system runs at the registry level and captures events across all networks. Enterprise features add more event types, such as RBAC changes, policy updates, and directory sync.

      Audit events

      -

      Each audit event contains the following fields:

      +

      Each audit event contains several fields.

      • timestamp: RFC 3339 timestamp of the event
      • action: Event type (e.g., node.registered, member.promoted)
      • @@ -78,7 +80,7 @@ pilotctl audit --network <network_id>
    "network_id": 1, "admin_token": "your-admin-token" } -

    The command returns an array of audit events named `entries`, newest first. The `network_id` filter is optional.

    +

    The command returns an array of audit events named `entries`, newest first. The `network_id` filter is optional. If omitted or set to 0, all events are returned.

    The ring buffer is in-memory only and does not persist across registry restarts. For persistent audit trails, use audit export.

    Audit export

    @@ -90,20 +92,20 @@ pilotctl audit --network <network_id> "token": "your-hec-token", "admin_token": "your-admin-token" } -

    Three export formats are supported:

    +

    Three export formats are supported.

    • Splunk HEC (splunk_hec): For Splunk HTTP Event Collector.
    • CEF / Syslog (cef): For ArcSight, QRadar, or any CEF-compatible SIEM.
    • JSON (json): For any HTTP endpoint accepting JSON payloads.
    -

    Delivery guarantees:

    +

    Delivery guarantees are based on the following parameters.

    • Buffer size: 1,024 events
    • Retry attempts: 3
    • Retry strategy: Exponential backoff
    • Delivery: Asynchronous (non-blocking)
    -

    Events are buffered and delivered asynchronously. If the export endpoint is unavailable, events are retried up to 3 times with exponential backoff. Events that exceed the retry limit are dropped, but remain in the in-memory ring buffer.

    +

    Events are buffered and delivered asynchronously. If the export endpoint is unavailable, events are retried with exponential backoff up to 3 times. Events that exceed the retry limit are dropped, but they remain in the in-memory ring buffer for API queries.

    Splunk HEC

    Splunk HEC (HTTP Event Collector) integration sends events in Splunk’s native format.

    @@ -114,17 +116,17 @@ pilotctl audit --network <network_id> "token": "your-hec-token", "admin_token": "your-admin-token" } -

    Events are formatted as Splunk HEC JSON payloads. The HEC token is sent in the `Authorization` header.

    +

    Events are formatted as Splunk HEC JSON payloads with the `event` field containing the audit data. The HEC token is sent in the `Authorization` header.

    CEF / Syslog

    -

    Common Event Format (CEF) output is compatible with SIEM systems that accept CEF-formatted syslog.

    +

    Common Event Format (CEF) output is compatible with ArcSight, QRadar, and other SIEM systems that accept CEF-formatted syslog.

    {
       "command": "set_audit_export",
       "format": "cef",
       "endpoint": "https://siem.example.com/api/events",
       "admin_token": "your-admin-token"
     }
    -

    Events are formatted as CEF strings with Pilot Protocol vendor and product identifiers.

    +

    Events are formatted as CEF strings with the Pilot Protocol vendor and product identifiers, severity mapping, and extension fields containing the audit context.

    JSON export

    Generic JSON export sends the raw audit event as a JSON POST to any HTTP endpoint.

    @@ -134,17 +136,17 @@ pilotctl audit --network <network_id> "endpoint": "https://logs.example.com/ingest", "admin_token": "your-admin-token" } -

    The payload is the audit event object, with the same structure returned by `get_audit_log`.

    +

    The payload is the audit event object, which has the same structure returned by `get_audit_log`. This format is for custom integrations, log aggregators, or data pipelines.

    Webhooks & DLQ

    Webhooks deliver audit events to HTTP endpoints. Each webhook invocation includes a unique event ID for deduplication.

    -

    Failed webhook deliveries are retried with exponential backoff. After all retries are exhausted, the event is moved to a dead-letter queue (DLQ) for manual inspection.

    -

    The DLQ can be queried with the following command:

    +

    Failed webhook deliveries are retried with exponential backoff. After all retries are exhausted, the event is moved to a dead-letter queue (DLQ) for manual inspection and replay.

    +

    The DLQ can be queried with the following command.

    {
       "command": "get_webhook_dlq",
       "admin_token": "your-admin-token"
     }
    -

    This returns an array of failed webhook events named `entries`.

    +

    This returns `entries`, an array of failed webhook events with original payload, error, and timestamps.

    Webhooks can be configured via the `set_audit_export` command or through the `webhooks` field in a blueprint.

    Metrics

    @@ -156,7 +158,7 @@ pilotctl audit --network <network_id>
  • pilot_webhook_deliveries_total (Counter): Total webhook delivery attempts
  • pilot_webhook_dlq_size (Gauge): Current number of events in the dead-letter queue
  • -

    These can be scraped from the registry’s metrics endpoint.

    +

    These metrics can be scraped from the registry’s metrics endpoint to set up alerts for delivery failures or DLQ growth.

    Related

      diff --git a/src/pages/plain/docs/enterprise-blueprints.astro b/src/pages/plain/docs/enterprise-blueprints.astro index 74442ca..a1f517a 100644 --- a/src/pages/plain/docs/enterprise-blueprints.astro +++ b/src/pages/plain/docs/enterprise-blueprints.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise-blueprints.astro +// plain-source-sha256: 814c33e6f99bc733004c4843b41579f71b28cff3154aa2e24a101faab3c1696b import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,11 +10,11 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Blueprints

      -

      A blueprint is a JSON document that describes an enterprise network. It is applied with one command to provision the network's name, join rule, policies, identity provider, webhooks, audit export, role pre-assignments, and admin token.

      +

      A blueprint is a single JSON document that describes an entire enterprise network. It is applied with one command to provision the network in a deterministic sequence.

      Overview

      -

      A blueprint is a single JSON document that describes an entire enterprise network: its name, join rule, policies, identity provider, webhooks, audit export, role pre-assignments, and admin token. Applying it with one command causes the registry to provision everything in a deterministic sequence.

      -

      Blueprints are designed for infrastructure-as-code workflows. They can be stored in version control, with changes reviewed in pull requests and applied through CI/CD pipelines.

      +

      A blueprint is a single JSON document that describes an entire enterprise network: its name, join rule, policies, identity provider, webhooks, audit export, role pre-assignments, and admin token. Apply it with one command and the registry provisions everything in a deterministic sequence.

      +

      Blueprints are designed for infrastructure-as-code workflows. They can be stored in version control, reviewed in pull requests, and applied through CI/CD pipelines.

      Blueprint format

      {
      @@ -49,15 +51,15 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
       
       

      Fields reference

        -
      • name (string, required): Network name. Lowercase alphanumeric with hyphens. Used for idempotent lookup.
      • -
      • join_rule (string): One of `open`, `invite_only`, or `token`. Defaults to `open`.
      • -
      • enterprise (bool): Enable enterprise features. Defaults to `false`.
      • -
      • policy (object): Network policy: `max_members`, `allowed_ports`, `description`.
      • -
      • identity_provider (object): IDP configuration: `type`, `url`, `client_id`.
      • -
      • webhooks (array): Webhook endpoints: `url` and `events` filter.
      • -
      • audit_export (object): Export config: `format`, `endpoint`, `token`.
      • -
      • roles (object): Map of node ID (string) to role (`admin` or `member`). Pre-assigns RBAC roles.
      • -
      • network_admin_token (string): Per-network admin token for delegated administration.
      • +
      • `name` (string, required): Network name. Lowercase alphanumeric with hyphens. Used for idempotent lookup.
      • +
      • `join_rule` (string, optional): One of `open`, `invite_only`, or `token`. Defaults to `open`.
      • +
      • `enterprise` (bool, optional): Enable enterprise features. Defaults to `false`.
      • +
      • `policy` (object, optional): Network policy: `max_members`, `allowed_ports`, `description`.
      • +
      • `identity_provider` (object, optional): IDP configuration: `type`, `url`, `client_id`.
      • +
      • `webhooks` (array, optional): Webhook endpoints: `url` and `events` filter.
      • +
      • `audit_export` (object, optional): Export config: `format`, `endpoint`, `token`.
      • +
      • `roles` (object, optional): Map of node ID (string) to role (`admin` or `member`). Pre-assigns RBAC roles.
      • +
      • `network_admin_token` (string, optional): Per-network admin token for delegated administration.

      Apply sequence

      @@ -74,7 +76,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Each step is independent. If a later step fails, earlier steps are not rolled back. The result includes which actions were taken and which failed.

      Idempotency

      -

      Blueprints are idempotent. Applying the same blueprint twice produces the same result. The registry looks up the network by `name`:

      +

      Blueprints are idempotent. Applying the same blueprint twice produces the same result. The registry looks up the network by `name`.

      • If a network with that name exists, it is updated, not duplicated.
      • If no network with that name exists, a new one is created.
      • @@ -82,7 +84,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

        This makes blueprints safe to apply repeatedly in CI/CD pipelines. Re-applying after a partial failure completes the remaining steps without duplicating already-completed ones.

        Validation

        -

        The registry validates the blueprint before applying any changes:

        +

        The registry validates the blueprint before applying any changes.

        • `name` is required and must match the network name rules (lowercase alphanumeric with hyphens).
        • `join_rule` must be one of `open`, `invite_only`, or `token`.
        • @@ -90,7 +92,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
        • `policy.allowed_ports` must have 100 or fewer entries.
        • `policy.description` must be 256 or fewer characters.
        • `identity_provider.type` must be a valid provider type (`oidc`, `saml`, `entra_id`, `ldap`, `webhook`).
        • -
        • `roles` values must be `admin` or `member`. Ownership is assigned to the creator and cannot be set here.
        • +
        • `roles` values must be `admin` or `member`. Ownership is assigned to the creator.

        If validation fails, no changes are made and the error is returned immediately.

        @@ -106,7 +108,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro'; "admin_token": "your-admin-token" }

      The blueprint is the only payload field besides `admin_token`. Ownership of a newly-created network is assigned to the registry node that the admin token authorizes.

      -

      Result format:

      +

      The result format is:

      {
         "network_id": 5,
         "name": "prod-fleet",
      @@ -119,14 +121,14 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
         ]
       }

      The `created` field indicates whether a new network was created (`true`) or an existing one was updated (`false`).

      -

      For programmatic use, a blueprint can be loaded from a JSON file using a helper.

      +

      For programmatic use, a blueprint can be loaded from a JSON file using a helper in `pkg/registry/wire`.

      // Go SDK — pkg/registry/wire has the typed loader
       import "github.com/pilot-protocol/pilotprotocol/pkg/registry/wire"
       import "github.com/pilot-protocol/pilotprotocol/pkg/registry"
       
       bp, err := wire.LoadBlueprint("network.json")        // *wire.NetworkBlueprint
       result, err := client.ProvisionNetwork(bp.ToMap(), adminToken)
      -

      `wire.LoadBlueprint` reads and validates the JSON file, returning a typed struct. `registry.Client.ProvisionNetwork` takes the blueprint as a map and the admin token. The network owner is the node authenticated on the client connection.

      +

      `wire.LoadBlueprint` reads and validates the JSON file. `registry.Client.ProvisionNetwork` takes the blueprint as a map and the admin token. The network owner is the node authenticated on the client connection.

      Status query

      To inspect the provisioning state of a network:

      diff --git a/src/pages/plain/docs/enterprise-identity.astro b/src/pages/plain/docs/enterprise-identity.astro index 9c95db4..3b72060 100644 --- a/src/pages/plain/docs/enterprise-identity.astro +++ b/src/pages/plain/docs/enterprise-identity.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise-identity.astro +// plain-source-sha256: 39c65ccedc45f986b5ac9f6e94a60a96f5e6de9c33ae32384bddea675e45c39b import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,19 +10,19 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Identity & SSO

      -

      This document describes integration with external identity providers (IDPs) for centralized authentication. It covers supported providers, configuration, JWT validation, JWKS caching, external IDs, and directory sync.

      +

      Enterprise networks can integrate with external identity providers (IDPs) to centralize authentication. Agents present tokens from an IDP, and the registry validates them before granting access.

      Overview

      -

      Enterprise networks can integrate with external identity providers (IDPs) to centralize authentication. Agents can present tokens from an organization's IDP, such as OIDC, SAML, Entra ID, LDAP, or a custom webhook. The registry validates these tokens before granting access.

      +

      Enterprise networks can integrate with external identity providers (IDPs) to centralize authentication. Instead of relying on built-in Ed25519 keys, agents can present tokens from an organization’s IDP (OIDC, SAML, Entra ID, LDAP, or a custom webhook). The registry validates these tokens before granting access.

      Identity integration is configured per-network. Each network can have its own IDP configuration.

      Supported providers

        -
      • OIDC (type: oidc): OpenID Connect for standard JWT-based SSO. Works with Auth0, Okta, Google, etc.
      • -
      • SAML (type: saml): Security Assertion Markup Language for XML-based enterprise SSO.
      • -
      • Entra ID (type: entra_id): Microsoft Entra ID (Azure AD) for native integration with Microsoft environments.
      • -
      • LDAP (type: ldap): Lightweight Directory Access Protocol for on-premises directory servers.
      • -
      • Webhook (type: webhook): Custom HTTP callback to a verification endpoint for non-standard systems.
      • +
      • OIDC (type `oidc`): OpenID Connect for standard JWT-based SSO. Works with Auth0, Okta, Google, etc.
      • +
      • SAML (type `saml`): Security Assertion Markup Language for XML-based enterprise SSO.
      • +
      • Entra ID (type `entra_id`): Microsoft Entra ID (Azure AD) integration.
      • +
      • LDAP (type `ldap`): Lightweight Directory Access Protocol for on-premises directory servers.
      • +
      • Webhook (type `webhook`): A custom HTTP callback for verification.

      Configuration

      @@ -38,7 +40,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro'; "command": "get_idp_config", "admin_token": "your-admin-token" } -

      An audit event (idp.configured) is emitted when the IDP is set or changed.

      +

      An audit event (`idp.configured`) is emitted when the IDP is set or changed.

      JWT validation

      The registry includes built-in JWT validation supporting two algorithms:

      @@ -46,21 +48,21 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
    • RS256: Uses an RSA public key from a JWKS. For standard OIDC/OAuth2 providers.
    • HS256: Uses a shared secret. For simple integrations and internal services.
    -

    Validate a token with the `validate_token` protocol command.

    +

    Validate a token with the `validate_token` protocol command:

    {
       "command": "validate_token",
       "token": "eyJhbGciOiJSUzI1NiIs...",
       "admin_token": "your-admin-token"
     }
    -

    The command returns `valid` (boolean), `claims` (the decoded JWT claims if valid), or `error` (a string describing the validation failure).

    +

    The command returns `valid` (boolean), `claims` (the decoded JWT claims if valid), or `error` (a string describing the failure).

    The validator checks the following claims:

    • Signature: Verified against the JWKS public key (RS256) or shared secret (HS256).
    • -
    • Expiration (exp): Token must not be expired.
    • -
    • Not-before (nbf): Token must not be used before its valid time.
    • -
    • Issued-at (iat): Checked for reasonableness.
    • -
    • Issuer (iss): Must match the configured IDP URL.
    • -
    • Audience (aud): Must match the configured client ID.
    • +
    • Expiration (`exp`): Token must not be expired.
    • +
    • Not-before (`nbf`): Token must not be used before its valid time.
    • +
    • Issued-at (`iat`): Checked for reasonableness.
    • +
    • Issuer (`iss`): Must match the configured IDP URL.
    • +
    • Audience (`aud`): Must match the configured client ID.

    JWKS caching

    @@ -69,13 +71,13 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
  • Cache TTL: 5 minutes
  • Max response size: 64 KB
  • Key matching: By `kid` (Key ID) header in the JWT
  • -
  • Refresh: Automatic on cache expiry; on-demand if `kid` is not found in the cached set
  • +
  • Refresh: Automatic on cache expiry, or on-demand if a `kid` is not found in the cached set.
  • If the JWKS endpoint is unreachable and the cache has expired, validation fails. The registry does not fall back to accepting unverified tokens.

    Security

    -

    The validator enforces the expected algorithm based on configuration to prevent algorithm confusion attacks. The `alg` header in the JWT must match the configured algorithm.

    -

    A 60-second clock skew tolerance is applied to all time-based claims (exp, nbf, iat) to accommodate minor clock differences between the IDP and the registry.

    +

    The validator prevents algorithm confusion attacks by enforcing the expected algorithm based on configuration. The `alg` header in the JWT must match the configured algorithm.

    +

    A 60-second clock skew tolerance is applied to all time-based claims (`exp`, `nbf`, `iat`) to accommodate minor clock differences.

    Webhook identity

    For systems that do not support OIDC or SAML, a webhook identity provider can be configured. The registry sends the agent’s credentials to an HTTP endpoint, which returns an approval or rejection.

    @@ -85,7 +87,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro'; "url": "https://auth.example.com/verify-agent", "admin_token": "your-admin-token" } -

    The endpoint receives a POST with the agent’s identity information and must return a JSON response indicating authorization status.

    +

    The endpoint receives a POST with the agent’s identity information and must return a JSON response indicating whether the agent is authorized.

    External IDs

    Agents can be mapped to external identity systems using external IDs.

    @@ -104,8 +106,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    External IDs are free-form strings, such as email addresses, UPNs, or LDAP DNs. They are stored in the registry and included in audit events. An `identity.external_id_set` audit event is emitted on change.

    Directory sync

    -

    Directory sync pushes entries from an external directory to the registry, managing network members.

    -

    The sync operation is performed with the `directory_sync` command.

    +

    Directory sync pushes entries from an external directory to the registry, which provisions and deprovisions network members.

    {
       "command": "directory_sync",
       "network_id": 1,
    @@ -125,28 +126,28 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
       "remove_unlisted": true,
       "admin_token": "your-admin-token"
     }
    -

    The sync operation performs the following actions:

    +

    The sync operation performs these actions:

    • Matches entries to a registered node by `node_id`.
    • -
    • Joins nodes to the network if they are not already members.
    • -
    • Maps roles (`admin` or `member`). The `owner` role cannot be assigned via sync.
    • -
    • Sets external IDs for identity mapping.
    • -
    • Applies optional capability tags.
    • +
    • Joins nodes to the network if not already members.
    • +
    • Maps the `role` field to an RBAC role (admin or member). The owner role cannot be assigned via sync.
    • +
    • Sets the `external_id` for identity mapping.
    • +
    • Applies the optional `tags` field to the node.
    • Removes members not in the entries list if `remove_unlisted` is true.
    -

    Directory sync supports role pre-assignment. When a new member is added through sync, they receive their assigned role immediately.

    -

    The sync status can be queried.

    +

    Directory sync supports role pre-assignment. When a new member is added through sync, they receive their assigned role immediately instead of defaulting to the member role.

    +

    To query sync status:

    {
       "command": "get_directory_status",
       "network_id": 1,
       "admin_token": "your-admin-token"
     }
    -

    The command returns the last sync timestamp, number of entries processed, and any errors. A `directory.synced` audit event is emitted after each sync.

    +

    This command returns the last sync timestamp, number of entries processed, and any errors. A `directory.synced` audit event is emitted after each sync.

    Related

    diff --git a/src/pages/plain/docs/enterprise-policies.astro b/src/pages/plain/docs/enterprise-policies.astro index 6b0f81c..5fa1b12 100644 --- a/src/pages/plain/docs/enterprise-policies.astro +++ b/src/pages/plain/docs/enterprise-policies.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise-policies.astro +// plain-source-sha256: f2ad028f4f372017790f60569c2506d1d9be958fa19ee7e05f0a80fa70a4327b import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -12,12 +14,12 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Overview

    Network policies let owners and admins enforce constraints on enterprise networks. Policies control how many agents can join, which ports are accessible, and what metadata is attached to the network.

    -

    Policies use merge-on-update semantics: only the fields sent are changed, and unmentioned fields keep their current values. This makes partial updates safe, as setting one field does not reset others.

    +

    Policies use merge-on-update semantics. Only fields included in an update request are changed; unmentioned fields keep their current values. This allows for safe partial updates.

    MaxMembers

    Caps the total number of agents that can be members of the network at any given time. The owner counts toward the cap.

      -
    • Join attempt at capacity: Rejected with “membership limit reached”.
    • +
    • Join attempt at capacity: Rejected with “membership limit reached”
    • Invite accept at capacity: Rejected. The invite remains pending but the agent cannot join until a slot opens.
    • Value of 0: No limit (default).
    • Reducing below current count: Allowed. Existing members are not kicked, but no new members can join.
    • @@ -32,14 +34,14 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
    • Maximum entries: 100 ports per policy.
    pilotctl network policy <network_id> --allowed-ports 80,443,1001
    -

    To reset the port whitelist and allow all ports again, set an empty list directly via the registry RPC `set_network_policy` with `"allowed_ports": []`.

    +

    To reset the port whitelist and allow all ports, set an empty list via the registry RPC `set_network_policy` with `"allowed_ports": []`.

    Port policies are enforced at the connection acceptance layer. The daemon checks the destination port against the network’s allowed ports list before accepting the connection.

    Description

    A free-text metadata field for the network. It can be used for human-readable context such as purpose, team name, environment, or compliance notes.

      -
    • Maximum length: 256 characters.
    • -
    • Default: Empty string.
    • +
    • Maximum length: 256 characters
    • +
    • Default: Empty string
    pilotctl network policy <network_id> --description "Production fleet - US East region"
    @@ -57,7 +59,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro'; }, "admin_token": "your-admin-token" } -

    Merge-on-update: only include the fields to change. Omitted fields are preserved.

    +

    Due to merge-on-update semantics, only include the fields to change. Omitted fields are preserved.

    To get a policy:

    pilotctl network policy <network_id>

    The protocol command is `get_network_policy`. It returns the current policy for the network.

    @@ -70,12 +72,12 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Persistence

    Policies are stored as part of the network record in the registry. They persist across registry restarts via the atomic JSON snapshot system. When the registry loads from a snapshot, all network policies are restored.

    -

    Policy state is also included in HA replication snapshots, so standby registries (started via rendezvous -standby <primary:port> -repl-token <token>) have the same policies as the primary.

    +

    Policy state is also included in HA replication snapshots, so standby registries (started via `rendezvous -standby <primary:port> -repl-token <token>`) have the same policies as the primary.

    Related

    diff --git a/src/pages/plain/docs/enterprise-rbac.astro b/src/pages/plain/docs/enterprise-rbac.astro index a1dc3eb..257943c 100644 --- a/src/pages/plain/docs/enterprise-rbac.astro +++ b/src/pages/plain/docs/enterprise-rbac.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise-rbac.astro +// plain-source-sha256: 16f7d236e24300012ddd9ab97670e82eab5259ecbcdb71e85671bd7de8ed49be import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -13,27 +15,27 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Roles

    Enterprise networks have three roles, ordered by privilege:

      -
    • owner: Full control over the network. One owner per network. Assigned when a user creates the network or receives an ownership transfer.
    • +
    • owner: Full control over the network. One owner per network. Assigned when the network is created, or ownership is transferred.
    • admin: Can manage members but cannot delete the network or transfer ownership. Assigned when promoted by the owner.
    • -
    • member: Standard network access. Can communicate with all other members. This is the default role upon joining.
    • +
    • member: Standard network access. Can communicate with all other members. Assigned upon joining the network (default role).

    When enterprise mode is enabled on a network, the creator is automatically assigned the owner role. All existing members receive the member role.

    Permissions matrix

      -
    • Communicate with members: Owner, Admin, Member
    • -
    • List members: Owner, Admin, Member
    • -
    • Invite agents: Owner, Admin
    • -
    • Kick members: Owner, Admin
    • -
    • Promote to admin: Owner
    • -
    • Demote admin to member: Owner
    • -
    • Set network policies: Owner, Admin
    • -
    • Transfer ownership: Owner
    • -
    • Delete the network: Owner
    • -
    • Rename the network: Owner, Admin
    • -
    • Toggle enterprise mode: Owner
    • +
    • Communicate with members: Owner (Yes), Admin (Yes), Member (Yes)
    • +
    • List members: Owner (Yes), Admin (Yes), Member (Yes)
    • +
    • Invite agents: Owner (Yes), Admin (Yes), Member (No)
    • +
    • Kick members: Owner (Yes), Admin (Yes), Member (No)
    • +
    • Promote to admin: Owner (Yes), Admin (No), Member (No)
    • +
    • Demote admin to member: Owner (Yes), Admin (No), Member (No)
    • +
    • Set network policies: Owner (Yes), Admin (Yes), Member (No)
    • +
    • Transfer ownership: Owner (Yes), Admin (No), Member (No)
    • +
    • Delete the network: Owner (Yes), Admin (No), Member (No)
    • +
    • Rename the network: Owner (Yes), Admin (Yes), Member (No)
    • +
    • Toggle enterprise mode: Owner (Yes), Admin (No), Member (No)
    -

    Admins can kick members but not other admins or the owner. The owner can kick any member.

    +

    Admins can kick members but not other admins or the owner. The owner can kick anyone.

    Managing roles

    To promote a member to admin:

    @@ -54,18 +56,18 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    The current owner becomes an admin. The target must be a current member of the network. This is an atomic operation.

    Per-network admin tokens

    -

    The global admin token controls registry-level operations. Enterprise networks also support per-network admin tokens that grant admin-level access to a specific network without global privileges.

    -

    Per-network admin tokens can be set during blueprint provisioning via the `network_admin_token` field. They authorize the holder to perform admin-level operations on that specific network.

    +

    Enterprise networks support per-network admin tokens that grant admin-level access to a specific network without the global privileges of the global admin token.

    +

    Per-network admin tokens can be set during blueprint provisioning via the `network_admin_token` field. They authorize the holder to perform admin-level operations like kick, invite, and set policies on that specific network.

    Invite flow

    -

    Enterprise networks support a consent-based invite flow. Owners and admins send invitations that the target agent must accept.

    +

    Enterprise networks use a consent-based invite flow. Owners and admins send invitations that the target agent must accept.

    To send an invite:

    pilotctl network invite <network_id> <target_node_id>
    -

    The protocol command is `invite_to_network`. The inviter must be an owner or admin. The target receives the invitation.

    -

    To check for invites:

    +

    The protocol command is `invite_to_network`. The inviter must be an owner or admin. The target receives the invitation in their inbox.

    +

    To check the inbox for pending invitations:

    pilotctl network invites

    The protocol command is `get_invites`. It returns pending invitations with network name, inviter ID, and expiry timestamp.

    -

    To accept or reject an invite:

    +

    To accept or reject an invitation:

    pilotctl network accept <network_id>
     pilotctl network reject <network_id>

    The protocol command is `respond_to_invite`. Accepting joins the agent to the network with the member role. Rejecting removes the invitation.

    @@ -73,8 +75,8 @@ pilotctl network reject <network_id>
    • TTL: 30 days from creation.
    • Inbox cap: 100 pending invitations per agent.
    • -
    • Duplicate protection: An agent cannot be invited if they have a pending invite for the same network.
    • -
    • Membership check: An agent cannot be invited if they are already a member.
    • +
    • Duplicate protection: An agent cannot be invited if they already have a pending invite for the same network.
    • +
    • Membership check: An agent who is already a member cannot be invited.
    • MaxMembers enforcement: Accepting an invite is rejected if the network is at capacity.
    • Expired cleanup: Expired invites are automatically pruned when the inbox is queried.
    @@ -85,12 +87,12 @@ pilotctl network reject <network_id>
  • Global admin token: The registry-level admin token set with `--admin-token`. Has full access to all operations across all networks.
  • Per-network admin token: Scoped to a single network. Grants admin-level operations on that network only.
  • RBAC role: The agent’s role in the specific network (owner, admin, member). Checked for all network-scoped operations.
  • -
  • Ed25519 signature: Protocol commands that modify state are signed with the agent’s private key to prevent spoofing.
  • +
  • Ed25519 signature: Protocol commands that modify state (set-hostname, set-visibility, deregister, promote, demote, kick) are signed with the agent’s private key to prevent spoofing.
  • -

    Each layer is checked in order. If any layer grants the required permission, the operation proceeds.

    +

    Each layer is checked in order. If any layer grants the required permission, the operation proceeds. For example, the global admin token can promote a member even without being the network owner.

    Agent keys support rotation and expiry:

      -
    • Key rotation: `rotate_key` replaces the agent’s Ed25519 public key. The new key is used for all subsequent signed operations.
    • +
    • Key rotation: `rotate_key` replaces the agent’s Ed25519 public key.
    • Key expiry: `set_key_expiry` sets a deadline after which the agent’s key is considered expired. Expired agents are blocked from heartbeating.
    • Clear expiry: `set_key_expiry` with no expiry date clears the deadline.
    @@ -98,8 +100,8 @@ pilotctl network reject <network_id>

    Related

    diff --git a/src/pages/plain/docs/enterprise.astro b/src/pages/plain/docs/enterprise.astro index 405a357..88f6330 100644 --- a/src/pages/plain/docs/enterprise.astro +++ b/src/pages/plain/docs/enterprise.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/enterprise.astro +// plain-source-sha256: ae5d261bbcb7641ff23b6e0f525ffad7d24d5f6f2bf477a8c9fe83bb553861b2 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -11,30 +13,30 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Enterprise features provide role-based access, identity provider integration, policies, audit logging, and declarative provisioning for production networks.

    Overview

    -

    Enterprise features extend standard networks with controls for production deployments. These include role-based access control, identity provider integration, membership policies, structured audit logging, and declarative provisioning through blueprints.

    -

    Standard networks treat membership as a binary boundary. Enterprise networks add layers for role-based access control (RBAC), identity and directory sync, port policies, audit logging, and declarative setup via blueprints.

    +

    Enterprise features extend standard networks with controls for production deployments: role-based access control, identity provider integration, membership policies, structured audit logging, and declarative provisioning through blueprints.

    +

    Standard networks treat membership as a binary boundary. Enterprise networks add layers for role-based access control (RBAC), identity and directory sync, port policies, audit logging, and blueprints for setup.

    Enable enterprise

    -

    Enterprise features are enabled on a per-network basis at creation time.

    +

    Enterprise features are gated per-network and enabled at creation time.

    pilotctl network create --name prod-fleet --enterprise

    Enabling enterprise on a network promotes the creator to the owner role and unlocks all enterprise features for that network.

    Feature summary

      -
    • RBAC: Three-tier roles (owner, admin, member) with distinct permissions. Allows promotion, demotion, kicking members, and transferring ownership.
    • +
    • RBAC: Three-tier roles (owner, admin, member) with distinct permissions. Allows for promotion, demotion, kicking, and ownership transfer.
    • Invites: A consent-based flow for agents to join networks. Invites have a 30-day TTL and an inbox cap of 100.
    • Identity & SSO: Integration with OIDC, SAML, Entra ID, LDAP, and webhook identity providers. Supports JWT validation with RS256 and HS256.
    • -
    • Directory sync: Push entries from AD, Entra ID, or LDAP to automatically provision members, map roles, and remove unlisted agents.
    • -
    • Network policies: Enforce membership caps, port whitelists, and network descriptions.
    • -
    • Audit: Provides structured audit events in slog JSON format. Includes an in-memory ring buffer and export to Splunk HEC, CEF/Syslog, or JSON endpoints.
    • +
    • Directory sync: Pushes AD/Entra ID/LDAP entries to automatically provision members, map roles, and remove unlisted agents.
    • +
    • Network policies: Enforces membership caps, port whitelists, and network descriptions.
    • +
    • Audit: Provides structured audit events (slog JSON) in an in-memory ring buffer. Can export to Splunk HEC, CEF/Syslog, or JSON endpoints.
    • Webhooks: Event-driven notifications with retry, a dead-letter queue, and Prometheus metrics.
    • -
    • Blueprints: Declarative JSON documents that provision an entire network, including its name, policies, identity provider, webhooks, audit export configuration, and roles.
    • -
    • Key lifecycle: Rotate agent keys, set expiry dates, and block expired agents from heartbeating.
    • +
    • Blueprints: Declarative JSON documents that provision an entire network, including its name, policies, identity providers, webhooks, audit export configuration, and roles.
    • +
    • Key lifecycle: Supports rotating agent keys, setting expiry dates, and blocking expired agents from heartbeating.

    Enterprise gating

    -

    Some features require enterprise mode on the network, while others are available for all networks.

    -

    Features requiring enterprise mode:

    +

    Some features require enterprise mode on the network. Others work for all networks.

    +

    Features that require an enterprise network:

    • RBAC roles (promote, demote, kick)
    • Ownership transfer
    • @@ -54,17 +56,27 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
    • Tags & discovery
    • Trust & handshakes
    -

    Attempting an enterprise operation on a non-enterprise network returns an error. The flag is toggled by the registry's set_network_enterprise RPC, also available via the Go SDK's registry.Client.SetNetworkEnterprise. Membership is preserved when toggling the flag.

    +

    Attempting an enterprise operation on a non-enterprise network returns an error. The flag is toggled by the registry's set_network_enterprise RPC or the Go SDK's registry.Client.SetNetworkEnterprise. Membership is preserved across the toggle.

    + +

    What’s next

    +

    Documentation is available for specific features:

    +
      +
    • RBAC & Access Control: roles, permissions, invites, and the authorization chain.
    • +
    • Identity & SSO: connect OIDC, SAML, Entra ID, or LDAP; validate JWTs; sync directories.
    • +
    • Network Policies: membership caps, port whitelists, and metadata.
    • +
    • Audit & Compliance: structured logging, export to SIEMs, webhooks.
    • +
    • Blueprints: provision entire networks from a single JSON document.
    • +

    Related

    diff --git a/src/pages/plain/docs/error-codes.astro b/src/pages/plain/docs/error-codes.astro index 3b67d02..be0bd6e 100644 --- a/src/pages/plain/docs/error-codes.astro +++ b/src/pages/plain/docs/error-codes.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/error-codes.astro +// plain-source-sha256: 79062429dd7f3161854b079be6488aa04616cfb084da7c1bc4c0f7fbc3540220 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,80 +10,80 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Error Codes

    -

    Reference for protocol, registry, transport, IPC, network, trust, and CLI errors, with resolution steps.

    +

    A reference for protocol, registry, transport, IPC, network, trust, and CLI errors.

    Protocol errors

      -
    • invalid checksum: Packet CRC32 does not match. Data corrupted in transit. Resolution: Usually transient. If persistent, check for MTU issues or middlebox interference.
    • -
    • packet too short: Received data is smaller than the 34-byte header. Resolution: Likely a truncated packet. Check network path for fragmentation.
    • -
    • invalid magic bytes: Tunnel frame does not start with 0x50494C54 ("PILT"). Resolution: Non-Pilot traffic on the tunnel port. Check firewall rules.
    • -
    • encrypted packet but no key: Received an encrypted packet from a peer with no established key. Resolution: Keys may have desynchronized after restarts. Restart both peers or re-establish trust.
    • +
    • `invalid checksum`: Packet CRC32 does not match. Data corrupted in transit. Usually transient. If persistent, check for MTU issues or middlebox interference.
    • +
    • `packet too short`: Received data is smaller than the 34-byte header. Likely a truncated packet. Check network path for fragmentation.
    • +
    • `invalid magic bytes`: Tunnel frame does not start with 0x50494C54 (“PILT”). Non-Pilot traffic on the tunnel port. Check firewall rules.
    • +
    • `encrypted packet but no key`: Received an encrypted packet from a peer with no established key. Keys may have desynchronized after restarts. Restart both peers or re-establish trust.

    Registry errors

      -
    • network creation is disabled: Registry has network creation turned off. Resolution: Enable network creation in registry configuration.
    • -
    • network name required: Empty name passed to create-network. Resolution: Provide a network name.
    • -
    • network name too long: Name exceeds 63 characters. Resolution: Shorten the network name to 63 characters or fewer.
    • -
    • network name must be lowercase alphanumeric with hyphens: Name contains invalid characters. Resolution: Use only lowercase letters, digits, and hyphens. Must start and end with alphanumeric.
    • -
    • network name is reserved: Attempted to use a reserved name (e.g., backbone). Resolution: Choose a different network name.
    • -
    • network already exists: A network with this name already exists. Resolution: Use a unique name or join the existing network.
    • -
    • network ID space exhausted: All 65,535 network IDs are allocated. Resolution: Delete unused networks to free IDs.
    • -
    • network not found: The specified network ID does not exist. Resolution: Verify the network ID with pilotctl network list.
    • -
    • node already registered: This node is already registered in the backbone. Resolution: Use pilotctl deregister first, or check for stale identity at ~/.pilot/identity.json.
    • -
    • invalid email: The --email flag (or email config key) is malformed. Resolution: Use a valid local@domain address, or omit the flag and let the daemon synthesise <fingerprint>@nodes.pilotprotocol.network.
    • -
    • invalid admin token: The admin token does not match the registry's configured token. Resolution: Check the admin token in your configuration.
    • -
    • hostname already taken: Another node has already registered this hostname. Resolution: Choose a different hostname.
    • +
    • `network creation is disabled`: Registry has network creation turned off. Enable network creation in registry configuration.
    • +
    • `network name required`: Empty name passed to create-network. Provide a network name.
    • +
    • `network name too long`: Name exceeds 63 characters. Shorten the network name to 63 characters or fewer.
    • +
    • `network name must be lowercase alphanumeric with hyphens`: Name contains invalid characters. Use only lowercase letters, digits, and hyphens. Must start and end with alphanumeric.
    • +
    • `network name is reserved`: Attempted to use a reserved name (e.g., `backbone`). Choose a different network name.
    • +
    • `network already exists`: A network with this name already exists. Use a unique name or join the existing network.
    • +
    • `network ID space exhausted`: All 65,535 network IDs are allocated. Delete unused networks to free IDs.
    • +
    • `network not found`: The specified network ID does not exist. Verify the network ID with `pilotctl network list`.
    • +
    • `node already registered`: This node is already registered in the backbone. Use `pilotctl deregister` first, or check for stale identity at `~/.pilot/identity.json`.
    • +
    • `invalid email`: The `--email` flag (or `email` config key) is malformed. Use a valid `local@domain` address, or omit the flag and let the daemon synthesise `<fingerprint>@nodes.pilotprotocol.network`.
    • +
    • `invalid admin token`: The admin token does not match the registry’s configured token. Check the admin token in your configuration.
    • +
    • `hostname already taken`: Another node has already registered this hostname. Choose a different hostname.

    Transport errors

      -
    • connection refused: Target node rejected the connection (untrusted and not in a shared network). Resolution: Establish trust with pilotctl handshake or add both agents to the same network.
    • -
    • connection timed out: No response from the target within the timeout period. Resolution: Check that the target is online, reachable, and has a tunnel to this node. Try pilotctl ping.
    • -
    • idle timeout: Connection closed after 120 seconds of inactivity. Resolution: Send data or keepalive probes to keep the connection alive.
    • -
    • send buffer full: Flow control window is zero; the receiver is not consuming data. Resolution: Wait for the receiver to process pending data, or check for a stuck receiver.
    • +
    • `connection refused`: Target node rejected the connection (untrusted and not in a shared network). Establish trust with `pilotctl handshake` or add both agents to the same network.
    • +
    • `connection timed out`: No response from the target within the timeout period. Check that the target is online, reachable, and has a tunnel to this node. Try `pilotctl ping`.
    • +
    • `idle timeout`: Connection closed after 120 seconds of inactivity. Send data or keepalive probes to keep the connection alive.
    • +
    • `send buffer full`: Flow control window is zero; the receiver is not consuming data. Wait for the receiver to process pending data, or check for a stuck receiver.

    IPC errors

      -
    • daemon is not running: Cannot connect to the IPC socket at /tmp/pilot.sock. Resolution: Start the daemon: pilotctl daemon start
    • -
    • network: missing sub-command: The network IPC command had no sub-command byte. Resolution: Use a valid subcommand: list, join, leave, members, invite.
    • -
    • network join: missing network_id: Join command did not include a network ID. Resolution: Provide a valid 16-bit network ID.
    • -
    • topic too long: Pub/sub topic name exceeds the maximum length. Resolution: Shorten the topic name.
    • -
    • payload too large: Event stream payload exceeds the maximum size. Resolution: Reduce the payload size or split into multiple messages.
    • -
    • frame too large: Data exchange frame exceeds the maximum size. Resolution: Reduce the frame size.
    • +
    • `daemon is not running`: Cannot connect to the IPC socket at `/tmp/pilot.sock`. Start the daemon: `pilotctl daemon start`
    • +
    • `network: missing sub-command`: The network IPC command had no sub-command byte. Use a valid subcommand: list, join, leave, members, invite.
    • +
    • `network join: missing network_id`: Join command did not include a network ID. Provide a valid 16-bit network ID.
    • +
    • `topic too long`: Pub/sub topic name exceeds the maximum length. Shorten the topic name.
    • +
    • `payload too large`: Event stream payload exceeds the maximum size. Reduce the payload size or split into multiple messages.
    • +
    • `frame too large`: Data exchange frame exceeds the maximum size. Reduce the frame size.

    Network errors

      -
    • invalid token for network: The join token does not match the network's configured token. Resolution: Verify the token with the network admin.
    • -
    • invite-only networks require invite flow: Tried to join an invite-only network with a token. Resolution: Ask the admin to run pilotctl network invite, then accept with pilotctl network accept.
    • -
    • node already in network: The agent is already a member. Resolution: No action needed - the agent is already joined.
    • -
    • cannot leave the backbone network: Attempted to leave network 0 (the backbone). Resolution: The backbone cannot be left. Use pilotctl deregister to fully unregister.
    • -
    • node is not a member of network: Tried to remove a node that is not in the network. Resolution: Verify network membership with pilotctl network members.
    • -
    • cannot delete the backbone network: Attempted to delete network 0. Resolution: The backbone is permanent and cannot be deleted.
    • -
    • free networks are limited to 3 agents: Free-tier network has reached the 3-agent limit. Resolution: Upgrade to a Private Network plan or remove an existing agent first.
    • +
    • `invalid token for network`: The join token does not match the network’s configured token. Verify the token with the network admin.
    • +
    • `invite-only networks require invite flow`: Tried to join an invite-only network with a token. Ask the admin to run `pilotctl network invite`, then accept with `pilotctl network accept`.
    • +
    • `node already in network`: The agent is already a member. No action needed.
    • +
    • `cannot leave the backbone network`: Attempted to leave network 0 (the backbone). The backbone cannot be left. Use `pilotctl deregister` to fully unregister.
    • +
    • `node is not a member of network`: Tried to remove a node that is not in the network. Verify network membership with `pilotctl network members`.
    • +
    • `cannot delete the backbone network`: Attempted to delete network 0. The backbone is permanent and cannot be deleted.
    • +
    • `free networks are limited to 3 agents`: Free-tier network has reached the 3-agent limit. Upgrade to a Private Network plan or remove an existing agent first.

    Trust errors

      -
    • handshake already pending: A handshake request to this peer is already in progress. Resolution: Wait for the peer to approve or reject, or check with pilotctl pending.
    • -
    • already trusted: Mutual trust already exists with this peer. Resolution: No action needed.
    • -
    • cannot resolve hostname: The hostname does not resolve to any registered agent. Resolution: Verify the hostname is correct and the target agent is registered.
    • +
    • `handshake already pending`: A handshake request to this peer is already in progress. Wait for the peer to approve or reject, or check with `pilotctl pending`.
    • +
    • `already trusted`: Mutual trust already exists with this peer. No action needed.
    • +
    • `cannot resolve hostname`: The hostname does not resolve to any registered agent. Verify the hostname is correct and the target agent is registered.

    CLI errors

      -
    • not_running: Daemon is not running or socket is unreachable. Resolution: Start the daemon: pilotctl daemon start
    • -
    • connection_failed: Cannot reach the registry or a peer. Resolution: Check network connectivity and registry address. Try PILOT_REGISTRY=host:port.
    • -
    • invalid_argument: A CLI argument is missing or malformed. Resolution: Check command syntax with pilotctl (no args) for usage.
    • -
    • not_found: The requested resource (peer, hostname, connection) was not found. Resolution: Verify the identifier exists and is reachable.
    • +
    • `not_running`: Daemon is not running or socket is unreachable. Start the daemon: `pilotctl daemon start`
    • +
    • `connection_failed`: Cannot reach the registry or a peer. Check network connectivity and registry address. Try `PILOT_REGISTRY=host:port`.
    • +
    • `invalid_argument`: A CLI argument is missing or malformed. Check command syntax with `pilotctl` (no args) for usage.
    • +
    • `not_found`: The requested resource (peer, hostname, connection) was not found. Verify the identifier exists and is reachable.

    Related

    diff --git a/src/pages/plain/docs/firewalls.astro b/src/pages/plain/docs/firewalls.astro index 6341b1c..a70db74 100644 --- a/src/pages/plain/docs/firewalls.astro +++ b/src/pages/plain/docs/firewalls.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/firewalls.astro +// plain-source-sha256: ec382b6644a66b61a17c34ccf60b6a26816c109df1dc757311e3d1efdaa7561d import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,21 +10,21 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Running pilot behind a firewall

    -

    When UDP is blocked, compat mode tunnels Pilot packets over HTTPS/WSS to the beacon — same overlay, different wire.

    +

    When UDP is blocked, compat mode tunnels Pilot packets over HTTPS/WSS to the beacon. This uses the same overlay network but a different wire protocol.

    When you need compat mode

    -

    The default Pilot daemon binds a public UDP socket and reaches peers either directly or via beacon hole-punch. That model works on home ISPs, cloud VMs (GCP, AWS, Azure), and most corporate networks. It does NOT work on:

    +

    The default Pilot daemon binds a public UDP socket. This model does not work in certain environments.

      -
    • Container PaaS without UDP exposure: Render, Railway, Vercel, Fly.io's per-app DNS routing, AWS Lambda — these platforms either don't route inbound UDP at all, or hide your daemon behind symmetric NAT that defeats hole-punch.
    • -
    • Locked-down corporate networks where outbound UDP is firewalled but HTTPS to arbitrary internet hosts is allowed.
    • -
    • Airport / hotel / conference wifi that blocks everything but TCP/443. As of v1.10.3, compat mode runs entirely on TCP/443 (no TCP/9000) — this case is fully covered.
    • +
    • Container PaaS without UDP exposure, such as Render, Railway, Vercel, Fly.io's per-app DNS routing, or AWS Lambda. These platforms may not route inbound UDP or may use symmetric NAT.
    • +
    • Corporate networks where outbound UDP is firewalled but HTTPS to internet hosts is allowed.
    • +
    • Wireless networks that block all traffic except TCP/443. As of v1.10.3, compat mode runs entirely on TCP/443.
    -

    If your daemon registers fine but heartbeats keep failing, or queries to specialists time out with relay-retransmit errors flooding the log, you're likely in one of these environments. Compat mode is the answer: same daemon binary, same overlay, just a different wire from your daemon to the beacon.

    +

    If the daemon registers but heartbeats fail, or if queries to specialists time out with relay-retransmit errors, compat mode may be required. It uses the same daemon binary and overlay network, but changes the wire protocol from the daemon to the beacon.

    Enabling compat mode

    -

    Compat mode is opt-in via one CLI flag on the standalone `pilot-daemon` binary:

    +

    Compat mode is enabled with a CLI flag on the standalone pilot-daemon binary.

    pilot-daemon -transport=compat
    -

    That's it — every other flag has a working default. As of v1.10.3 the daemon uses a single outbound TCP/443 connection for everything: the beacon WSS bridge AND the registry. Explicit form:

    +

    As of v1.10.3, the daemon uses a single outbound TCP/443 connection for the beacon WSS bridge and the registry. The explicit command form is:

    pilot-daemon \
       -transport=compat \
       -compat-beacon=wss://beacon.pilotprotocol.network/v1/compat \
    @@ -31,43 +33,42 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
       -registry-tls -registry-trust=system

    Flag semantics:

      -
    • `-transport=compat` switches the tunnel transport from UDP to WSS. Default is `udp`; the daemon falls back to UDP behavior if this flag is unset.
    • -
    • `-compat-beacon <url>` is the beacon's WSS endpoint. The default points at the managed public beacon (`wss://beacon.pilotprotocol.network/v1/compat`); self-hosted deployments override it.
    • -
    • `-tls-trust <mode>` defaults to `system`. See TLS trust below.
    • -
    • `-registry <host:port>` auto-defaults to `registry.pilotprotocol.network:443` in compat mode. This overrides the standard `34.71.57.205:9000` UDP-mode default; a custom registry host:port can still be passed for self-hosting.
    • -
    • `-registry-tls` plus `-registry-trust=system` are auto-enabled in compat mode. The registry channel is TLS-terminated on the same TCP/443 listener as the beacon WSS bridge (nginx SNI-routes the two hostnames behind one port).
    • +
    • -transport=compat — Switches the tunnel transport from UDP to WSS. The default is udp.
    • +
    • -compat-beacon <url> — The beacon's WSS endpoint. The default is wss://beacon.pilotprotocol.network/v1/compat.
    • +
    • -tls-trust <mode> — Controls TLS trust. Defaults to system.
    • +
    • -registry <host:port> — Defaults to registry.pilotprotocol.network:443 in compat mode. This overrides the standard 34.71.57.205:9000 UDP-mode default. A custom registry host:port can still be passed for self-hosted deployments.
    • +
    • -registry-tls + -registry-trust=system — Auto-enabled in compat mode. The registry channel is TLS-terminated on the same TCP/443 listener as the beacon WSS bridge.
    -

    The daemon also automatically forces `relay_only=true` when compat is enabled — peers will route to it via the beacon's relay path instead of trying direct UDP, which would fail anyway.

    +

    The daemon automatically forces relay_only=true when compat mode is enabled.

    How it works

    -

    The compat-mode daemon opens outbound connections to only TCP/443 — multiplexed by SNI through a single nginx listener on the rendezvous host:

    +

    The compat-mode daemon opens outbound connections only to TCP/443, multiplexed by SNI through a single nginx listener on the rendezvous host.

      -
    • `beacon.pilotprotocol.network:443` — long-lived WebSocket Secure connection that carries the data plane (peer-to-peer Pilot frames, wrapped as binary WS messages).
    • -
    • `registry.pilotprotocol.network:443` — TLS connection pool for the registry RPC channel (registration, heartbeats, resolve, hostname lookup). Same wire protocol as the legacy UDP-mode `tcp:9000`, just wrapped in TLS.
    • +
    • beacon.pilotprotocol.network:443 — A long-lived WebSocket Secure connection for the data plane, carrying Pilot frames as binary WS messages.
    • +
    • registry.pilotprotocol.network:443 — A TLS connection pool for the registry RPC channel (registration, heartbeats, resolve, hostname lookup).
    -

    nginx pre-reads the TLS ClientHello's SNI field and routes registry traffic to its TLS terminator (which proxies plain bytes to the existing registry server), while beacon traffic terminates on the existing WSS-aware vhost. No code change on the registry side; the daemon's `-registry-trust=system` path verifies the public Let's Encrypt cert.

    -

    After the TLS handshake, the daemon completes an Ed25519 challenge so the beacon can authenticate it against the registry's stored pubkey. From that point on, every Pilot UDP packet the daemon would have sent becomes one binary WS frame; every inbound binary WS frame becomes one Pilot packet returned by Recv.

    -

    The beacon transparently bridges between UDP peers and WSS peers. From a specialist's point of view, a compat-mode daemon looks identical to a symmetric-NAT peer — the beacon's existing relay logic delivers packets without any change to the specialist code.

    -

    End-to-end Ed25519 trust is unchanged. TLS provides the encrypted channel between daemon and beacon; Ed25519 still protects peer-to-peer identity and payload integrity, exactly as in UDP mode.

    +

    After the TLS handshake, the daemon completes an Ed25519 challenge for authentication by the beacon. Pilot UDP packets that the daemon would have sent become binary WS frames, and inbound binary WS frames become Pilot packets.

    +

    The beacon bridges between UDP peers and WSS peers. From a specialist's perspective, a compat-mode daemon is identical to a symmetric-NAT peer. The beacon's existing relay logic delivers packets without changes to specialist code.

    +

    End-to-end Ed25519 trust is unchanged. TLS provides the encrypted channel between the daemon and beacon, while Ed25519 protects peer-to-peer identity and payload integrity.

    TLS trust + corp proxies

    -

    The public beacon at `beacon.pilotprotocol.network` currently presents a Let's Encrypt certificate (terminated by nginx on the rendezvous host). With `-tls-trust=system` (the default), the daemon verifies that cert against the OS trust store — the same way browsers do — and connects normally. The daemon logs a clear startup warning that this trust mode does not protect against TLS-intercepting proxies on the path.

    -

    A future release will ship the daemon binary with the Pilot Protocol root CA cert embedded. At that point the default flips to `-tls-trust=pinned`, which verifies the beacon's leaf cert against only this root — no public CA bug, DNS hijack, or Let's Encrypt issuance mistake can MITM your daemon. The escape hatch for that future state (for daemons behind TLS-intercepting corp proxies like Fortinet, Zscaler, BlueCoat, Palo Alto, Cisco Umbrella) will be to pass `-tls-trust=system` explicitly.

    -

    End-to-end Ed25519 protects peer identity and payload integrity in both trust modes. A TLS-intercepting proxy can read or censor relay traffic on the wire but cannot forge a specialist's signed reply.

    +

    The public beacon at beacon.pilotprotocol.network presents a Let's Encrypt certificate. With the default -tls-trust=system, the daemon verifies this certificate against the OS trust store. This trust mode does not protect against TLS-intercepting proxies.

    +

    A future release will embed the Pilot Protocol root CA certificate in the daemon binary. The default will change to -tls-trust=pinned, which verifies the beacon's certificate against only this root. To use the daemon behind a TLS-intercepting corporate proxy, it will be necessary to pass -tls-trust=system explicitly.

    +

    End-to-end Ed25519 protects peer identity and payload integrity in both trust modes. A TLS-intercepting proxy can read or censor relay traffic but cannot forge a specialist's signed reply.

    Limits

      -
    • Latency: Compat-mode traffic always traverses the beacon — there is no direct path. Expect ~50-200 ms one-way to the nearest beacon region, plus the beacon to specialist hop. UDP mode reaches specialists directly when NAT allows it.
    • -
    • Bandwidth: Compat traffic costs the beacon roughly 2x the bytes of UDP relay (TCP/TLS framing overhead, and the beacon pays for both legs). Sustained heavy throughput is better served by a UDP-capable environment.
    • -
    • Hostile-state DPI: Networks that block arbitrary outbound TLS (Great Firewall, some government deployments) will block compat mode. Domain fronting / ECH / Snowflake-style obfuscation is out of scope.
    • -
    • One beacon per session: The daemon holds one WSS connection at a time. On disconnect it reconnects with exponential backoff, optionally selecting a different beacon hostname from the configured list.
    • +
    • Latency: Compat-mode traffic always traverses the beacon. Expect ~50-200 ms one-way latency to the nearest beacon region, plus the beacon-to-specialist hop.
    • +
    • Bandwidth: Compat traffic costs the beacon approximately twice the bytes of UDP relay due to TCP/TLS framing overhead. UDP-capable environments are better for sustained heavy throughput.
    • +
    • Hostile-state DPI: Networks that block arbitrary outbound TLS will block compat mode.
    • +
    • One beacon per session: The daemon holds one WSS connection at a time. It reconnects with exponential backoff on disconnect.
    -

    For the full design — transport matrix, PKI, wire format, rollout plan — see docs/SPEC-compat-mode.md in the repo.

    +

    The full design is available in docs/SPEC-compat-mode.md in the repository at https://github.com/pilot-protocol/pilotprotocol/blob/main/docs/SPEC-compat-mode.md.

    Related

    diff --git a/src/pages/plain/docs/gateway.astro b/src/pages/plain/docs/gateway.astro index 127455c..c9e2a7a 100644 --- a/src/pages/plain/docs/gateway.astro +++ b/src/pages/plain/docs/gateway.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/gateway.astro +// plain-source-sha256: 3a972dbe82137d80cd624f94d7150c0ee05aa7c36a63705ff249ed9d05ac837f import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,21 +10,21 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Gateway

    -

    The gateway allows connecting to a service on a remote pilot node using any TCP client, such as curl or a browser. The local connection port must match the remote service's listening port, as ports are not translated.

    +

    The gateway allows connecting to a service on a remote pilot node using any TCP client. The local connection port must match the remote service's listening port, as ports are not translated.

    How it works

    -

    The gateway enables connections to a TCP service on a remote pilot node using standard tools like curl, a browser, or netcat.

    -

    Starting the gateway with a pilot address performs the following actions:

    +

    The gateway connects to a TCP service on a remote pilot node using standard tools like curl, a browser, or netcat.

    +

    When started with a pilot address, the gateway:

    • Picks a local IP from a private subnet (default 10.4.0.0/16) and adds it as a loopback alias on the network interface.
    • Starts TCP listeners on that IP for every specified port.
    • -
    • When a connection is received, it is tunneled through the encrypted pilot overlay to the remote machine.
    • +
    • Tunnels incoming connections through the encrypted pilot overlay to the remote machine.
    -

    On the remote side, the incoming pilot connection arrives at the same port number. If the gateway is started on port 8080, the remote machine needs a service actually listening on port 8080; the gateway does not translate ports.

    +

    The incoming pilot connection arrives at the same port number on the remote side. The gateway does not translate ports.

    sudo is required. Adding the loopback alias requires root on both macOS and Linux, regardless of the port used.

    Access a remote server

    -

    This section describes connecting to a server running on a peer's machine.

    +

    This section describes how to reach a server running on a peer's machine.

    Example: Reach a peer running a web server on port 80.

    # 1. Trust the peer first (required)
     pilotctl handshake agent-alpha
    @@ -36,15 +38,15 @@ curl http://10.4.0.1/
     
     # 4. Stop when done
     sudo pilotctl extras gateway stop
    -

    The first pilot address mapped gets 10.4.0.1, the second gets 10.4.0.2, and so on.

    -

    Multiple peers at once:

    +

    The first mapped pilot address is assigned 10.4.0.1, the second 10.4.0.2, and so on.

    +

    To map multiple peers at once:

    sudo pilotctl extras gateway start --ports 80,8080 0:0000.0000.037D 0:0000.0000.0002
     # First peer  → http://10.4.0.1/  and  http://10.4.0.1:8080/
     # Second peer → http://10.4.0.2/  and  http://10.4.0.2:8080/

    Expose your own server on pilotprotocol network

    -

    To allow a trusted peer to reach a service on your machine, run the server. No gateway setup is needed on the server side. The peer runs the gateway on their end to connect.

    -

    Your machine (the server):

    +

    To allow a trusted peer to reach a service on a local machine, run the server. No gateway setup is needed on the server side. The peer runs the gateway on their end to connect.

    +

    On the server machine:

    # Start your server on whatever port you want
     python3 -m http.server 8080
     # nginx, caddy, your app - anything that listens on a TCP port
    @@ -55,7 +57,7 @@ pilotctl info
     

    When the peer sends a handshake, approve it:

    pilotctl pending            # see incoming requests
     pilotctl approve <node_id>
    -

    Peer's machine (the client):

    +

    On the peer's (client) machine:

    # --ports 8080 must match the port your server is actually on
     pilotctl handshake 0:0000.0000.xxxx
     sudo pilotctl extras gateway start --ports 8080 0:0000.0000.xxxx
    @@ -63,29 +65,29 @@ curl http://10.4.0.1:8080/

    No port forwarding, VPN, or firewall changes are needed on the server side. The pilot overlay handles the traversal.

    Manage mappings

    -

    List current mappings:

    +

    To list current mappings:

    pilotctl extras gateway list
    -

    Add a mapping to a running gateway:

    +

    To add a mapping to a running gateway:

    pilotctl extras gateway map 0:0000.0000.0007           # auto-assign local IP
     pilotctl extras gateway map 0:0000.0000.0007 10.4.0.5  # assign a specific IP
    -

    Remove a mapping:

    +

    To remove a mapping:

    pilotctl extras gateway unmap 10.4.0.1
    -

    Stop the gateway:

    +

    To stop the gateway:

    sudo pilotctl extras gateway stop

    Notes & limits

      -
    • Ports are not translated. The port specified in --ports is the port the remote machine must be listening on. The gateway forwards traffic to the same port number on the other side.
    • -
    • sudo required. Loopback alias creation requires root on macOS and Linux, regardless of port number.
    • -
    • TCP only. The gateway does not support UDP.
    • -
    • Trust required. Mutual trust with the remote node is required. Run `pilotctl handshake` first.
    • -
    • Default ports. If --ports is omitted, the gateway listens on: 7, 80, 443, 1000, 1001, 1002, 8080, 8443.
    • -
    • Cleanup. Loopback aliases are removed automatically on `gateway stop` or `gateway unmap`.
    • +
    • Ports are not translated. The port specified in --ports is the port the remote machine must be listening on.
    • +
    • sudo is required for loopback alias creation on macOS and Linux, regardless of port number.
    • +
    • The gateway supports TCP only, not UDP.
    • +
    • Mutual trust with the remote node is required. Run pilotctl handshake first.
    • +
    • If --ports is omitted, the gateway listens on ports: 7, 80, 443, 1000, 1001, 1002, 8080, 8443.
    • +
    • Loopback aliases are removed automatically on gateway stop or gateway unmap.

    Related

    diff --git a/src/pages/plain/docs/getting-started.astro b/src/pages/plain/docs/getting-started.astro index cd4c434..b8ca06c 100644 --- a/src/pages/plain/docs/getting-started.astro +++ b/src/pages/plain/docs/getting-started.astro @@ -1,49 +1,44 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/getting-started.astro +// plain-source-sha256: f52b9e4651cdb4391a57e151218bc86a35e1e2d4de5c9cc1f67fe8bc32233ac6 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- +

    ← Docs index

    Getting Started

    -

    Install the daemon, register your agent, and connect to your first peer.

    +

    Install the Pilot Protocol daemon, register an agent, and connect to a peer.

    Choose your transport

    - -

    The installer picks a working default. This section matters mainly for managed runtimes or locked-down networks. Pilot has two transports that speak the same overlay protocol; only the wire from the daemon to the rest of the network differs.

    - +

    The installer picks a working default transport. This section is for deployments to managed runtimes or locked-down networks.

    +

    Pilot has two transports. They use the same overlay protocol; only the wire transport from the daemon to the network differs.

      -
    • UDP (default). The daemon binds a public UDP socket and reaches peers directly when NAT allows, otherwise via the beacon. Best latency and throughput. Works on home / office networks, cloud VMs (AWS EC2, GCP GCE, Azure VM), and most container PaaS that expose UDP.
    • -
    • Compat (-transport=compat). The daemon opens one outbound TCP/443 connection to the beacon (HTTPS / WSS). Every Pilot frame rides that one socket. Slightly higher latency, but works where UDP is blocked or the daemon is behind symmetric NAT.
    • +
    • UDP (default). The daemon binds a public UDP socket and reaches peers directly when NAT allows, or via the beacon. This provides the best latency and throughput. It works on home or office networks, cloud VMs (AWS EC2, GCP GCE, Azure VM), and most container platforms that expose UDP.
    • +
    • Compat (-transport=compat). The daemon opens one outbound TCP/443 connection to the beacon (HTTPS / WSS). All Pilot frames use that socket. This has slightly higher latency but works in environments that block UDP or hide the daemon behind symmetric NAT.
    - -

    Pick by environment:

    +

    Choose by environment:

      -
    • Home / office network, cloud VM (EC2, GCE, Azure) → UDP. Nothing to set.
    • -
    • Container PaaS with no inbound UDP (Render, Railway, Vercel, Fly.io per-app routing) → -transport=compat.
    • -
    • Locked-down corporate wifi blocking outbound UDP but allowing HTTPS to arbitrary hosts → -transport=compat.
    • -
    • Airport / hotel / conference wifi that only lets TCP/443 out → -transport=compat.
    • -
    • Serverless (Lambda, Cloud Functions, Edge Workers) → currently not supported; the runtime tears the process down between invocations.
    • -
    • Hostile-state DPI (Great Firewall, some government networks) blocking arbitrary outbound TLS → out of scope today.
    • +
    • Home / office network, cloud VM (EC2, GCE, Azure) → UDP.
    • +
    • Container PaaS with no inbound UDP (Render, Railway, Vercel, Fly.io's per-app routing) → -transport=compat.
    • +
    • Locked-down corporate wifi blocking outbound UDP but allowing HTTPS to arbitrary hosts → -transport=compat.
    • +
    • Airport / hotel / conference wifi that only lets TCP/443 out → -transport=compat.
    • +
    • Serverless (Lambda, Cloud Functions, Edge Workers) → currently not supported.
    • +
    • Hostile-state DPI (Great Firewall, some government networks) → out of scope.
    - -

    If unsure, run UDP first. If pilotctl info shows an address but no heartbeats and no peers after a minute, switch to compat mode and try again. See the Firewalls & Compat Mode reference for flags, TLS trust, and limits.

    +

    If unsure, run UDP first. If `pilotctl info` shows an address but no heartbeats and no peers after a minute, switch to compat mode.

    Installation

    - -

    Run the one-line installer. It detects the platform, downloads pre-built binaries, writes ~/.pilot/config.json, adds ~/.pilot/bin to PATH, and sets up system services (systemd on Linux, launchd on macOS) for the daemon and auto-updater. Future releases are applied automatically in the background.

    +

    The one-line installer detects the platform, downloads pre-built binaries, writes `~/.pilot/config.json`, adds `~/.pilot/bin` to the PATH, and sets up system services (systemd on Linux, launchd on macOS) for the daemon and auto-updater. Future releases are applied automatically.

    curl -fsSL https://pilotprotocol.network/install.sh | sh
    - -

    You will be prompted for an email address on first install. To skip the prompt:

    +

    To skip the prompt for an email address on first install:

    curl -fsSL https://pilotprotocol.network/install.sh | PILOT_EMAIL=you@example.com PILOT_HOSTNAME=my-agent sh
    - -

    Homebrew (macOS / Linux)

    +

    To install with Homebrew (macOS / Linux):

    brew tap pilot-protocol/pilot
     brew install pilotprotocol
    - -

    From source

    -

    Requires Go 1.25+. The binary names must be pilot-daemon and pilot-gatewaypilotctl looks for them as siblings under those exact names (or via $PILOT_DAEMON_BIN / $PILOT_GATEWAY_BIN).

    +

    To install from source, Go 1.25+ is required. The binary names must be `pilot-daemon` and `pilot-gateway`. `pilotctl` looks for them as siblings under those exact names, or via `$PILOT_DAEMON_BIN` / `$PILOT_GATEWAY_BIN`.

    git clone https://github.com/pilot-protocol/pilotprotocol.git
     cd pilotprotocol
     go build -o ~/.pilot/bin/pilotctl      ./cmd/pilotctl
    @@ -51,73 +46,60 @@ go build -o ~/.pilot/bin/pilot-daemon  ./cmd/daemon
     go build -o ~/.pilot/bin/pilot-gateway ./cmd/gateway

    Start the daemon

    -

    The system service starts automatically after install. To start it manually on first run:

    pilotctl daemon start --email you@example.com --hostname my-agent

    Both flags are optional:

      -
    • If --hostname is omitted, the node is assigned an internal hostname of the form pilot-XXXXXXXX — the suffix is the first 4 hex bytes of SHA-256(public_key).
    • -
    • If --email is omitted, the daemon synthesises one from the public-key fingerprint (<fingerprint>@nodes.pilotprotocol.network). It can be replaced with a real address later by setting email in ~/.pilot/config.json and restarting.
    • +
    • If `--hostname` is omitted, the node is assigned an internal hostname of the form `pilot-XXXXXXXX`, where the suffix is the first 4 hex bytes of `SHA-256(public_key)`.
    • +
    • If `--email` is omitted, the daemon synthesises one from the public-key fingerprint (`<fingerprint>@nodes.pilotprotocol.network`). It can be replaced later by setting `email` in `~/.pilot/config.json` and restarting.
    -

    Once supplied, --email is persisted to config and not needed on subsequent starts.

    - +

    Once supplied, `--email` is persisted to config and not needed on subsequent starts.

    # Output:
     starting daemon (pid 12345).....
     Daemon running (pid 12345)
       Address:  0:0000.0000.xxxx
       Socket:   /tmp/pilot.sock
       Logs:     ~/.pilot/pilot.log
    - -

    The address (0:0000.0000.xxxx) is the permanent identity on the network. It stays the same across restarts.

    - +

    The address (e.g., `0:0000.0000.xxxx`) is a permanent identity on the network and stays the same across restarts.

    Subsequent starts (email already in config):

    pilotctl daemon start
    - -

    Behind a firewall? If the daemon can't reach peers, the network may be blocking UDP. Start it with -transport=compat to route everything over a single outbound TCP/443 connection. See Firewalls & Compat Mode for the full flag set, supported environments, and how the WSS bridge works.

    - -

    Guided quickstart: run pilotctl quickstart for an interactive walkthrough that detects daemon state and guides through discovery, trust, and a first specialist query. See the CLI Reference quickstart section.

    +

    If the daemon cannot reach peers, the network may be blocking UDP. Start it with `-transport=compat` to route everything over a single outbound TCP/443 connection.

    +

    Run `pilotctl quickstart` for an interactive walkthrough that detects daemon state and guides through discovery, trust, and a first specialist query.

    Check your identity

    -
    pilotctl info
    -

    Shows node ID, address, hostname, uptime, peers, active connections, encryption status, and traffic stats.

    - +

    This command shows node ID, address, hostname, uptime, peers, active connections, encryption status, and traffic stats.

    pilotctl daemon status
    -

    Quick check: is the daemon running?

    +

    This command checks if the daemon is running.

    Demo: connect to agent-alpha

    - -

    agent-alpha is a public demo node that auto-approves all handshake requests.

    - -

    1. Establish trust

    +

    `agent-alpha` is a public demo node that auto-approves all handshake requests.

    +

    1. Establish trust

    pilotctl handshake agent-alpha
    -

    Sends a trust request. agent-alpha auto-approves it within a few seconds.

    - -

    2. Verify it worked

    +

    This sends a trust request. agent-alpha auto-approves it within a few seconds.

    +

    2. Verify it worked

    pilotctl trust
    -

    agent-alpha should appear in the list with mutual: yes.

    - -

    3. Ping it

    +

    agent-alpha should appear in the list with `mutual: yes`.

    +

    3. Ping it

    pilotctl ping agent-alpha
    -

    Sends echo probes through the overlay and reports round-trip times.

    - -

    4. Browse its website via the gateway

    -

    The gateway maps agent-alpha's pilot address to a local IP so it can be reached with curl or a browser. Gateway is an operator surface — call it via pilotctl extras gateway (or invoke the pilot-gateway binary directly).

    +

    This sends echo probes through the overlay and reports round-trip times.

    +

    4. Browse its website via the gateway

    +

    The gateway maps agent-alpha's pilot address to a local IP. It is invoked via `pilotctl extras gateway` or the `pilot-gateway` binary directly.

    sudo pilotctl extras gateway start --ports 80 0:0000.0000.037D
     curl http://10.4.0.1/
    -

    agent-alpha runs a web server on port 80. The gateway tunnels the request through the encrypted pilot overlay to that port on the remote machine. sudo is required because the gateway adds a loopback alias to the network interface.

    - +

    agent-alpha is running a web server on port 80. The gateway tunnels the request through the encrypted pilot overlay to that port on the remote machine. `sudo` is required because the gateway adds a loopback alias to the network interface.

    When done:

    sudo pilotctl extras gateway stop
    -

    Next steps

    +

    Related

    diff --git a/src/pages/plain/docs/go-sdk.astro b/src/pages/plain/docs/go-sdk.astro index 800b1f8..e302a8d 100644 --- a/src/pages/plain/docs/go-sdk.astro +++ b/src/pages/plain/docs/go-sdk.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/go-sdk.astro +// plain-source-sha256: b177105c10327c2468ac80036f9a23dc25b05b752db7b1b9a6dcd19efe15f3f4 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -12,9 +14,9 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Installation

    go get github.com/pilot-protocol/pilotprotocol
    -

    The SDK is in pkg/driver.

    +

    The SDK is in pkg/driver. Import it as:

    import "github.com/pilot-protocol/pilotprotocol/pkg/driver"
    -

    The SDK requires Go 1.25+ and a running daemon. The driver communicates with the daemon over a Unix socket at /tmp/pilot.sock.

    +

    Requires Go 1.25+ and a running daemon. The driver communicates with the daemon over a Unix socket at /tmp/pilot.sock.

    Quick start

    package main
    @@ -52,82 +54,82 @@ func main() {
     

    Driver

    The Driver is the main entry point. It connects to the local daemon via IPC and provides methods for all protocol operations.

    func Connect(socketPath string) (*Driver, error)
    -

    Creates a new driver connected to the local daemon. Pass "" for the default socket path (/tmp/pilot.sock). A custom path can be provided, or the PILOT_SOCKET environment variable can be set.

    +

    Creates a new driver connected to the local daemon. Pass "" for the default socket path (/tmp/pilot.sock). Override with a custom path or set the PILOT_SOCKET environment variable.

    func (d *Driver) Dial(addr string) (*Conn, error)
    -

    Opens a stream connection to a remote address and port. The address format is "N:XXXX.YYYY.YYYY:PORT". It returns a *Conn that implements net.Conn.

    +

    Opens a stream connection to a remote address and port. Address format: "N:XXXX.YYYY.YYYY:PORT". Returns a *Conn that implements net.Conn.

    func (d *Driver) DialAddr(dst protocol.Addr, port uint16) (*Conn, error)
    -

    This function is like Dial but takes a parsed protocol.Addr and port number.

    +

    Like Dial but takes a parsed protocol.Addr and port number directly.

    func (d *Driver) Listen(port uint16) (*Listener, error)

    Binds a port and returns a *Listener that accepts incoming connections.

    func (d *Driver) Info() (map[string]interface{}, error)

    Returns the daemon's status: node ID, address, hostname, uptime, peers, connections, encryption status, and traffic stats.

    func (d *Driver) Health() (map[string]interface{}, error)
    -

    A lightweight health check that returns basic status.

    +

    Lightweight health check. Returns basic status without the full info payload.

    func (d *Driver) Close() error

    Disconnects from the daemon and releases resources.

    Conn

    Conn implements the standard net.Conn interface. It can be used with any Go library that works with net.Conn, including net/http, bufio, io.Copy, and TLS wrappers.

      -
    • Read(b []byte) (int, error) - Read data from the connection. Blocks until data arrives or deadline expires.
    • -
    • Write(b []byte) (int, error) - Write data to the connection.
    • -
    • Close() error - Close the connection (sends FIN to remote).
    • -
    • LocalAddr() net.Addr - Returns the local pilot address.
    • -
    • RemoteAddr() net.Addr - Returns the remote pilot address.
    • -
    • SetDeadline(t time.Time) error - Sets both read and write deadlines.
    • -
    • SetReadDeadline(t time.Time) error - Sets the read deadline. A zero value means no deadline.
    • +
    • Read(b []byte) (int, error): Read data from the connection. Blocks until data arrives or deadline expires.
    • +
    • Write(b []byte) (int, error): Write data to the connection.
    • +
    • Close() error: Close the connection (sends FIN to remote).
    • +
    • LocalAddr() net.Addr: Returns the local pilot address.
    • +
    • RemoteAddr() net.Addr: Returns the remote pilot address.
    • +
    • SetDeadline(t time.Time) error: Sets both read and write deadlines.
    • +
    • SetReadDeadline(t time.Time) error: Sets the read deadline. A zero value means no deadline.

    Listener

    Listener accepts incoming connections on a bound port. It follows the standard net.Listener pattern.

      -
    • Accept() (net.Conn, error) - Blocks until a new connection arrives. Returns a *Conn.
    • -
    • Close() error - Stops accepting connections and unblocks any pending Accept call.
    • -
    • Addr() net.Addr - Returns the bound pilot address.
    • +
    • Accept() (net.Conn, error): Blocks until a new connection arrives. Returns a *Conn.
    • +
    • Close() error: Stops accepting connections and unblocks any pending Accept call.
    • +
    • Addr() net.Addr: Returns the bound pilot address.

    Datagrams

    -

    Datagrams are unreliable, connectionless packets used for fire-and-forget data or broadcast to network members.

    +

    Unreliable, connectionless packets. Use for fire-and-forget data or broadcast to network members.

    func (d *Driver) SendTo(dst protocol.Addr, port uint16, data []byte) error

    Sends an unreliable datagram to the given address and port.

    func (d *Driver) Broadcast(netID uint16, port uint16, data []byte, adminToken string) error

    Fans a datagram out to every member of netID. Requires an admin token. Delivery is best-effort.

    func (d *Driver) RecvFrom() (*Datagram, error)
    -

    Receives the next incoming datagram, blocking until a datagram arrives.

    +

    Receives the next incoming datagram. Blocks until a datagram arrives.

    Trust & handshakes

      -
    • Handshake(nodeID uint32, justification string) - Send a trust request to a remote node.
    • -
    • ApproveHandshake(nodeID uint32) - Approve a pending trust request.
    • -
    • RejectHandshake(nodeID uint32, reason string) - Reject a pending trust request.
    • -
    • PendingHandshakes() - List pending trust requests.
    • -
    • WaitForTrust(nodeID uint32, timeoutMs uint32) - Block until mutual trust with nodeID is live or the timeout elapses.
    • -
    • TrustedPeers() - List all trusted peers.
    • -
    • RevokeTrust(nodeID uint32) - Remove a peer from the trusted set.
    • +
    • Handshake(nodeID uint32, justification string): Send a trust request to a remote node.
    • +
    • ApproveHandshake(nodeID uint32): Approve a pending trust request.
    • +
    • RejectHandshake(nodeID uint32, reason string): Reject a pending trust request.
    • +
    • PendingHandshakes(): List pending trust requests.
    • +
    • WaitForTrust(nodeID uint32, timeoutMs uint32): Block until mutual trust with nodeID is live or the timeout elapses.
    • +
    • TrustedPeers(): List all trusted peers.
    • +
    • RevokeTrust(nodeID uint32): Remove a peer from the trusted set.

    All trust methods return (map[string]interface{}, error) with JSON-decoded response data.

    Admin methods

      -
    • SetHostname(hostname string) - Set or clear the daemon's hostname.
    • -
    • SetVisibility(public bool) - Set visibility on the registry (public or private).
    • -
    • SetTags(tags []string) - Set capability tags (max 3).
    • -
    • SetWebhook(url string) - Set or clear the webhook URL. An empty string disables it.
    • -
    • RotateKey() - Generate a new Ed25519 keypair and re-register with the registry. Replaces ~/.pilot/identity.json.
    • -
    • ResolveHostname(hostname string) - Resolve a hostname to node info.
    • -
    • Deregister() - Remove this node from the registry.
    • -
    • Disconnect(connID uint32) - Close a connection by ID.
    • +
    • SetHostname(hostname string): Set or clear the daemon's hostname.
    • +
    • SetVisibility(public bool): Set visibility on the registry (public or private).
    • +
    • SetTags(tags []string): Set capability tags (max 3).
    • +
    • SetWebhook(url string): Set or clear the webhook URL. Empty string disables.
    • +
    • RotateKey(): Generate a new Ed25519 keypair and re-register with the registry. Replaces ~/.pilot/identity.json in place.
    • +
    • ResolveHostname(hostname string): Resolve a hostname to node info.
    • +
    • Deregister(): Remove this node from the registry.
    • +
    • Disconnect(connID uint32): Close a connection by ID.

    Networks

      -
    • NetworkList() - List all networks known to the registry.
    • -
    • NetworkJoin(networkID uint16, token string) - Join a network. Pass an empty string for open networks.
    • -
    • NetworkLeave(networkID uint16) - Leave a network.
    • -
    • NetworkMembers(networkID uint16) - List all members of a network.
    • -
    • NetworkInvite(networkID uint16, targetNodeID uint32) - Invite a node to a network (requires admin token).
    • -
    • NetworkPollInvites() - Check for pending network invites.
    • -
    • NetworkRespondInvite(networkID uint16, accept bool) - Accept or reject a network invite.
    • +
    • NetworkList(): List all networks known to the registry.
    • +
    • NetworkJoin(networkID uint16, token string): Join a network. Pass empty string for open networks.
    • +
    • NetworkLeave(networkID uint16): Leave a network.
    • +
    • NetworkMembers(networkID uint16): List all members of a network.
    • +
    • NetworkInvite(networkID uint16, targetNodeID uint32): Invite a node to a network (requires admin token).
    • +
    • NetworkPollInvites(): Check for pending network invites.
    • +
    • NetworkRespondInvite(networkID uint16, accept bool): Accept or reject a network invite.

    Examples

    @@ -170,13 +172,13 @@ defer ln.Close() http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from pilot!") }))
    -

    Because Listener implements net.Listener and Conn implements net.Conn, the standard net/http server works.

    +

    Because Listener implements net.Listener and Conn implements net.Conn, the standard net/http server works out of the box.

    Related

    diff --git a/src/pages/plain/docs/index.astro b/src/pages/plain/docs/index.astro index 37aa2d4..2f01a67 100644 --- a/src/pages/plain/docs/index.astro +++ b/src/pages/plain/docs/index.astro @@ -1,77 +1,57 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/index.astro +// plain-source-sha256: 55e7d4ca00ddf87b2a10844266b3de221e4e7cc57458beff67a4d14b3826a2a6 import PlainLayout from '../../../layouts/PlainLayout.astro'; ---

    Pilot Protocol Documentation

    -

    Pilot Protocol gives AI agents a permanent address, encrypted channels, and a trust model. This documentation covers guides, a CLI reference, and integration tutorials. New users can start with the Getting Started guide to install the daemon, register an agent, and send a first message in under 5 minutes.

    +

    Pilot Protocol provides AI agents with a permanent address, encrypted channels, and a trust model.

    -

    Flow

    -

    The usage flow is: install, register, initiate trust, then use services (gateway services, data exchange, pub/sub) via the CLI or SDKs. For deeper understanding, see Core Concepts, Diagnostics, and the comparison with MCP.

    +

    Overview

    +

    The Getting Started guide covers installing the daemon, registering an agent, and sending a first message.

    +

    The canonical protocol specification is the IETF draft draft-teodor-pilot-protocol-01. The Research page lists all drafts and preprints.

    All Documentation

    - -

    Documentation

    -
      -
    • Overview — Documentation home.
    • -
    • Getting Started — Install, start the daemon, and connect to your first peer.
    • -
    • Core Concepts — Addressing, transport, encryption, NAT traversal, and the trust model.
    • -
    • CLI Reference — Complete reference for all pilotctl commands, flags, and return values.
    • -
    • Go SDK — Build services, custom agents, and integrations using the driver package.
    • -
    • Python SDK — Build services, custom agents, and integrations with Python.
    • -
    • SDK Parity — Feature comparison between the Go and Python SDKs.
    • -
    - -

    Features

    -
      -
    • Messaging — Connect, send messages, transfer files, and use the inbox.
    • -
    • Trust & Handshakes — The mutual trust model: handshake, approve, reject, auto-approval.
    • -
    • Networks — Private networks: group-level connectivity, join rules, and the permission model.
    • -
    • Built-in Services — Echo, data exchange, and event stream, running out of the box.
    • -
    • Pub/Sub — Subscribe to topics, publish events, wildcard filtering.
    • -
    • Webhooks — Receive real-time HTTP notifications for daemon events.
    • -
    • Gateway — Bridge IP traffic to the overlay: use curl, browsers, any TCP tool.
    • -
    • Service Agents — Build and run service agents on the network.
    • -
    • App Store — Install and build agent apps as typed IPC capabilities.
    • -
    • Consent & Privacy — Consent and privacy controls for the daemon.
    • -
    - -

    Enterprise

    - - -

    Operations

    -
      -
    • Firewalls & Compat Mode — Running behind firewalls and using compat mode.
    • -
    • Diagnostics — Ping, traceroute, bench, connections, and peer inspection.
    • -
    • Configuration — Config files, environment variables, directory structure, and daemon flags.
    • -
    • Message of the Day — Daemon message-of-the-day configuration.
    • -
    • Integration — OpenClaw, heartbeat patterns, webhook-driven agents, and custom workflows.
    • -
    - -

    Reference

    - - -

    Compare

      -
    • vs MCP / A2A / ACP — How Pilot Protocol compares to MCP, A2A, and ACP, and when to use them together.
    • -
    • vs Tailscale / ZeroTier / Nebula — How Pilot Protocol compares to overlay networking tools.
    • +
    • Getting Started: Install, start the daemon, and connect to your first peer.
    • +
    • Core Concepts: Addressing, transport, encryption, NAT traversal, and the trust model.
    • +
    • CLI Reference: Complete reference for all pilotctl commands, flags, and return values.
    • +
    • Go SDK: Build services, custom agents, and integrations using the driver package.
    • +
    • Messaging: Connect, send messages, transfer files, and use the inbox.
    • +
    • Trust & Handshakes: The mutual trust model: handshake, approve, reject, auto-approval.
    • +
    • Networks: Private networks - group-level connectivity, join rules, and the permission model.
    • +
    • Built-in Services: Echo, data exchange, and event stream - built-in services running out of the box.
    • +
    • Pub/Sub: Subscribe to topics, publish events, wildcard filtering.
    • +
    • Webhooks: Receive real-time HTTP notifications for daemon events.
    • +
    • Gateway: Bridge IP traffic to the overlay - use curl, browsers, any TCP tool.
    • +
    • Diagnostics: Ping, traceroute, bench, connections, and peer inspection.
    • +
    • Configuration: Config files, environment variables, directory structure, and daemon flags.
    • +
    • Integration: OpenClaw, heartbeat patterns, webhook-driven agents, and custom workflows.
    • +
    • vs MCP / A2A / ACP: How Pilot Protocol compares to MCP, A2A, and ACP - and when to use them together.
    • +
    • Research: Papers and preprints - agent social structures, network analysis, protocol design.
    -

    Research

    +

    Related

    diff --git a/src/pages/plain/docs/integration.astro b/src/pages/plain/docs/integration.astro index dca16d5..e9479f8 100644 --- a/src/pages/plain/docs/integration.astro +++ b/src/pages/plain/docs/integration.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/integration.astro +// plain-source-sha256: ba807e733f4a0fc2163b1c8e126b9991f22c71c2a5e4b762e656f3e4c6be31f1 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -11,23 +13,23 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Connect Pilot Protocol to OpenClaw, heartbeat patterns, webhook-driven agents, and custom workflows.

    OpenClaw / ClawHub

    -

    Pilot Protocol is available as an agent skill on ClawHub. Install it to give an AI agent access to all pilotctl commands.

    -

    To install the skill:

    +

    Pilot Protocol is available as an agent skill on ClawHub. Install it to give your AI agent access to all pilotctl commands.

    +

    Install the skill

    clawhub install pilotprotocol
    -

    This downloads SKILL.md into the agent's skill directory. The skill file defines every command, its arguments, return types, and error codes.

    -

    SKILL.md provides:

    +

    This downloads SKILL.md into your agent's skill directory. The skill file defines every command, its arguments, return types, and error codes - enabling the agent to discover and use the protocol autonomously.

    +

    What SKILL.md provides

    • Complete command reference with --json output format
    • Error codes with retry guidance (hint field)
    • Heartbeat checklist for periodic monitoring
    • Typical workflow examples
    -

    For runtime self-discovery, agents can call this command at startup to discover capabilities without reading SKILL.md:

    +

    Runtime self-discovery

    pilotctl --json context
    -

    It returns a machine-readable manifest of all commands, arguments, return types, and error codes.

    +

    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.

    Heartbeat pattern

    -

    Add periodic checks to an agent's task loop. Run these every 30 minutes:

    +

    Add periodic checks to your agent's task loop to stay responsive on the network. Run these every 30 minutes:

    # 1. Ensure daemon is running
     pilotctl --json daemon status --check
     # Exit code 0 = running, 1 = down → restart with: pilotctl daemon start
    @@ -41,7 +43,7 @@ pilotctl --json inbox
     
     # 4. Check for received files
     pilotctl --json received
    -

    As a HEARTBEAT.md entry:

    +

    As 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`
    @@ -49,7 +51,7 @@ If 30 minutes since last Pilot check:
     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 memory
    -

    As a shell script:

    +

    As a shell script

    #!/bin/sh
     # pilot-heartbeat.sh - run on a timer or cron
     pilotctl daemon status --check 2>/dev/null || pilotctl daemon start
    @@ -60,8 +62,8 @@ pilotctl --json inbox 2>/dev/null
     pilotctl --json received 2>/dev/null

    Webhook-driven agents

    -

    Set up a webhook to react to events in real time.

    -

    Architecture:

    +

    Set up a webhook, then react to events in real time.

    +

    Architecture

    • Start an HTTP server that receives webhook events
    • Configure the daemon to POST events to your server
    • @@ -72,7 +74,7 @@ python3 webhook_handler.py & # 2. Point the daemon's webhook at it pilotctl set-webhook http://localhost:8080/events
    -

    Common patterns:

    +

    Common patterns

    • Auto-approve handshakes - on handshake.received, automatically approve if the justification matches criteria
    • Process incoming messages - on message.received, parse the message and dispatch a task
    • @@ -81,10 +83,10 @@ pilotctl set-webhook http://localhost:8080/events

    Custom workflows

    -

    Cron-based:

    +

    Cron-based

    # Run heartbeat every 30 minutes
     */30 * * * * /path/to/pilot-heartbeat.sh
    -

    systemd timer:

    +

    systemd timer

    # /etc/systemd/system/pilot-heartbeat.timer
     [Unit]
     Description=Pilot Protocol heartbeat
    @@ -95,7 +97,7 @@ OnUnitActiveSec=30min
     
     [Install]
     WantedBy=timers.target
    -

    Docker:

    +

    Docker

    FROM golang:1.25-alpine AS build
     RUN go install github.com/pilot-protocol/pilotprotocol/cmd/pilotctl@latest
     
    @@ -136,31 +138,31 @@ function pilotctl(...args) {
       });
       const data = JSON.parse(result);
       if (data.status === "error") {
    -    throw new Error(`\${data.code}: \${data.message}`);
    +    throw new Error(`${data.code}: ${data.message}`);
       }
       return data.data || {};
     }
     
     // Examples
     const info = pilotctl("info");
    -console.log(`I am \${info.hostname} (\${info.address})`);
    +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 || []) {
    -  console.log(`From \${msg.from}: \${msg.data}`);
    +  console.log(`From ${msg.from}: ${msg.data}`);
     }

    Self-discovery

    Agents can discover their full capabilities at runtime without reading SKILL.md:

    pilotctl --json context
    -

    This returns a complete JSON schema of all commands, arguments, return types, error codes, environment variables, and config file location.

    +

    Returns a complete JSON schema of all commands, arguments, return types, error codes, environment variables, and config file location. Use this for dynamic capability discovery in agent frameworks.

    Related

    diff --git a/src/pages/plain/docs/messaging.astro b/src/pages/plain/docs/messaging.astro index 1f6840e..0bc8fec 100644 --- a/src/pages/plain/docs/messaging.astro +++ b/src/pages/plain/docs/messaging.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/messaging.astro +// plain-source-sha256: 8ee044dff00eccb9a522a8aca32506a1a467db9fcb6d1350fcd46ecf60b45829 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,7 +10,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Messaging

    -

    This document describes methods for sending messages, transferring files, piping data, and inspecting the inbox between agents.

    +

    Pilot Protocol provides commands to send messages, transfer files, pipe data, and inspect the inbox.

    Communication models

    Pilot Protocol provides four ways to move data between agents.

    @@ -16,15 +18,15 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
  • Stream (`connect` / `send`): Uses any port (default 1000) for interactive request-response and piping data. Delivery is synchronous and bidirectional. Data is not stored.
  • Data Exchange (`send-message` / `send-file`): Uses port 1001 for typed messages and file transfer. Delivery is asynchronous and stored on arrival in `~/.pilot/inbox/` and `~/.pilot/received/`.
  • Pub/Sub (`subscribe` / `publish`): Uses port 1002 for fan-out events and monitoring. Delivery is real-time to active subscribers. Data is not stored.
  • -
  • Datagram (`dgram` / SDK): Uses any port for fire-and-forget packets. Delivery is unreliable with no connection. Data is not stored.
  • +
  • Datagram (`dgram` / SDK): Uses any port for fire-and-forget packets. Delivery is unreliable and connectionless. Data is not stored.
  • This page covers stream, data exchange, and datagram models.

    Prerequisites

    -

    For messaging to work, both agents must have mutual trust via handshake or belong to the same network. Connection attempts are silently dropped otherwise.

    +

    Before messaging, both agents must have mutual trust via handshake or belong to the same network. Without this, connection attempts are silently dropped.

    connect

    -

    Opens a stream connection to the target on port 1000 (stdio), sends the message, reads one response, and exits.

    +

    Opens a stream connection to the target on port 1000 (stdio), sends a message, reads one response, and exits.

    pilotctl connect other-agent --message "hello"
    # Connect on a specific port
     pilotctl connect other-agent 3000 --message "status?"
    @@ -34,7 +36,7 @@ pilotctl connect other-agent --message "ping" --timeout 10s

    Returns: `target`, `port`, `sent`, `response`

    send & recv

    -

    These commands target a specific service on a known port. They are functionally identical to `connect`, but the port must be specified explicitly.

    +

    This command is functionally identical to `connect`, but the port must be specified explicitly.

    To send to a specific port, open a connection, send the data, read one response, and exit.

    pilotctl send other-agent 1000 --data "hello from my-agent"

    To receive messages:

    @@ -46,14 +48,14 @@ pilotctl recv 1000 --count 5 --timeout 60s

    Returns: `messages` [{`seq`, `port`, `data`, `bytes`}], `timeout` (bool)

    Pipe mode

    -

    Without `--message`, `connect` reads from stdin, which can be used for feeding structured input like files or command output to a remote agent.

    +

    Without `--message`, `connect` reads from stdin, which can be used for piping data from other commands.

    echo "hello" | pilotctl connect other-agent
     cat query.json | pilotctl connect other-agent 3000
     echo '{"action":"status"}' | pilotctl connect other-agent 1000
    -

    Stdin must have data piped to it. Interactive terminal input is not supported.

    +

    Stdin must have data piped to it; interactive terminal input is not supported.

    send-message

    -

    This command sends structured, typed messages that are persisted to the recipient's inbox and survive disconnections. It uses the data exchange protocol on port 1001. Messages are saved to `~/.pilot/inbox/` on the target.

    +

    Sends structured, typed messages that are persisted to the recipient's inbox. It uses the data exchange protocol on port 1001. Messages are saved to `~/.pilot/inbox/` on the target.

    # Text message (default)
     pilotctl send-message other-agent --data "task complete"
     
    @@ -63,7 +65,7 @@ pilotctl send-message other-agent --data '{"task":"analyze","input":"data.c
     # Binary message
     pilotctl send-message other-agent --data "binary-payload" --type binary

    Returns: `target`, `type`, `bytes`, `ack`

    -

    Each message is stored as a JSON file in `~/.pilot/inbox/`:

    +

    Each message is stored as a JSON file in `~/.pilot/inbox/`.

    {
       "type": "JSON",
       "from": "0:0000.0000.0005",
    @@ -71,8 +73,8 @@ pilotctl send-message other-agent --data "binary-payload" --type binary
    -

    The `data` field is the raw payload coerced to a JSON string. The `type` field reflects the frame type sent (TEXT, JSON, BINARY, FILE, or TRACE). `received_at` is RFC3339Nano in the daemon's local timezone.

    -

    For binary payloads, starting the daemon with `-dataexchange-b64` adds a `data_b64` field to the inbox JSON with the lossless base64 encoding of the bytes.

    +

    The `data` field is the raw payload coerced to a JSON string. The `type` field reflects the frame type (TEXT, JSON, BINARY, FILE, or TRACE). `received_at` is RFC3339Nano in the daemon's local timezone.

    +

    For binary payloads, start the daemon with `-dataexchange-b64`. The inbox JSON will then carry an additional `data_b64` field with the base64 encoding of the payload. This flag is off by default.

    {
       "type": "BINARY",
       "from": "0:0000.0000.0005",
    @@ -81,7 +83,6 @@ pilotctl send-message other-agent --data "binary-payload" --type binary
    -

    The flag is off by default.

    send-file

    Transfers files to another agent. Files are delivered as typed frames with filename metadata and saved to `~/.pilot/received/` on the target.

    @@ -91,7 +92,7 @@ pilotctl send-file other-agent ./data.json

    The maximum file size is 16 MB.

    Inbox & received

    -

    Messages and files are stored locally and can be inspected.

    +

    Messages and files are stored locally for inspection.

    To check received files, which are saved to `~/.pilot/received/`:

    pilotctl received          # List received files
     pilotctl received --clear  # Delete all received files
    @@ -100,19 +101,18 @@ pilotctl received --clear # Delete all received files pilotctl inbox --clear # Delete all messages

    dgram

    -

    Sends a single UDP-style packet to a port on the target. Delivery is not guaranteed. For reliable delivery, use `send-message` or `send`.

    +

    Sends a single UDP-style packet to a port on the target. Delivery is not guaranteed. The receiver uses `pilotctl listen <port>`.

    pilotctl dgram other-agent 1234 --data "tick"
    -

    The receiver can use `pilotctl listen <port>` to receive the packet.

    broadcast

    -

    Sends a datagram to every member of a network. The daemon fans the message out to each known member. Delivery is best-effort (UDP-style).

    +

    Sends a datagram to every member of a network. The daemon fans the message out to each known member. Delivery is best-effort.

    pilotctl broadcast <network_id> <message> [--port <port>]

    Returns the network ID, port used, and the number of bytes sent.

    Related

      -
    • Mutual trust
    • -
    • Network
    • +
    • Trust
    • +
    • Networks
    • Pub/Sub
    • Built-in Services
    • Python SDK
    • diff --git a/src/pages/plain/docs/motd.astro b/src/pages/plain/docs/motd.astro index 2943c9c..d04b03c 100644 --- a/src/pages/plain/docs/motd.astro +++ b/src/pages/plain/docs/motd.astro @@ -1,4 +1,7 @@ --- +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/motd.astro +// plain-source-sha256: 14ae95ae5ae727fb314bb9b1b9128b61426dced0cac0294c9423d65ecde8f4cc import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -7,38 +10,53 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Message of the Day

      -

      The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can surface for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use cases: maintenance windows, incident updates, breaking-change heads-ups. Messages are managed centrally by the Pilot Protocol team; there is nothing to set up to receive them. When no message is active for the current UTC day, output is unchanged.

      +

      A short notice from the Pilot Protocol team, shown ahead of every pilotctl command for one UTC day at a time.

      +

      What it is

      +

      The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can surface for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. It is used for maintenance windows, incident updates, and breaking-change heads-ups. Messages are managed centrally by the Pilot Protocol team.

      pilotctl info
       Message of the day: overlay maintenance 22:00 UTC — expect ~5min blips
       
       <normal pilotctl info output>
      +

      When no message is active for the current UTC day, output is unchanged.

      How it works

      -

      The daemon is the only component that touches the network. A background loop fetches the central feed every interval (default 15m), selects the entry dated for the current UTC day, holds it in memory, and mirrors it to ~/.pilot/motd.json. pilotctl reads only that local mirror — one file read, no network, no IPC — and re-validates the UTC day on read, so a stale mirror never shows yesterday's message. No new binary ships; the poll is a goroutine inside pilot-daemon.

      +

      The work is split between the daemon and pilotctl. This keeps pilotctl fast and avoids network calls when a command runs.

      +
        +
      • The daemon is the only component that touches the network. A background loop fetches the central feed every --motd-interval (default 15m), selects the entry for the current UTC day, holds it in memory, and mirrors it to ~/.pilot/motd.json.
      • +
      • pilotctl reads only the local mirror. It re-validates the UTC day on read, so a stale mirror from a daemon that was offline across midnight will not show a message from the previous day.
      • +
      +
      central feed  ──poll──▶  pilot-daemon  ──mirror──▶  ~/.pilot/motd.json
      + (Pilot team)             (only net I/O)              the local variable
      +                                                           │ read (no net, no IPC)
      +                                                           ▼
      +                              pilotctl <any command>  ──▶  prepends banner
      +

      The poll is a goroutine inside pilot-daemon.

      Output: text vs JSON

      -

      Text mode prepends a "Message of the day: <text>" line, then the normal output. In --json mode the standard envelope carries a top-level important_update field instead (prepending text would break parsing). The same field is added to error envelopes, and the daemon surfaces the current value as motd inside pilotctl info.

      +

      Text mode prepends a `Message of the day: <text>` line and a blank line, then the normal command output.

      +

      `--json` mode does not prepend text. Instead the standard envelope carries a top-level `important_update` field.

      pilotctl --json info
       # { "status": "ok", "data": { ... }, "important_update": "overlay maintenance 22:00 UTC" }
      +

      The same field is added to error envelopes. The daemon also surfaces the current value as `motd` inside `pilotctl info`.

      Configuration

      -

      Operators running a daemon can tune or disable polling:

      +

      Operators running a daemon can tune or disable polling.

      pilot-daemon --motd-feed-url <url>     # feed location; empty disables polling entirely
       pilot-daemon --motd-interval 15m       # how often to re-fetch (default 15m)
      -

      The PILOT_MOTD_URL environment variable overrides the feed URL. The mirror lives next to the daemon identity (normally ~/.pilot/motd.json), which is where pilotctl looks.

      +

      The `PILOT_MOTD_URL` environment variable overrides the feed URL. The mirror lives next to the daemon identity, normally `~/.pilot/motd.json`.

      -

      Rules and semantics

      +

      Rules & semantics

        -
      • UTC days: a message is active only on its UTC calendar day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
      • -
      • Self-clearing: when the active message is withdrawn, the banner disappears on its own within one poll interval — no action needed on your machine.
      • -
      • Fail-safe: network errors and malformed responses are non-fatal; the daemon keeps its last good mirror, so a transient fetch failure never blocks a command.
      • +
      • UTC days. A message is active only on its UTC calendar day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
      • +
      • Self-clearing. When the active message is withdrawn, the banner disappears on its own within one poll interval.
      • +
      • Fail-safe. Network errors and malformed responses are non-fatal. The daemon keeps its last good mirror and logs at debug level, so a transient fetch failure never blocks a command.

      Related

      diff --git a/src/pages/plain/docs/networks.astro b/src/pages/plain/docs/networks.astro index 52272aa..71efba5 100644 --- a/src/pages/plain/docs/networks.astro +++ b/src/pages/plain/docs/networks.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/networks.astro +// plain-source-sha256: 446e13a94c8a92cfa802b5653924b21dddb32deab0fd2e19401bc128fe3f5d01 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,112 +10,124 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Networks

      -

      Networks are a group-level access control primitive in Pilot Protocol. A network grants connectivity to all its members at once, an alternative to establishing trust between every pair of agents.

      +

      Networks are a group-level access control primitive in Pilot Protocol. A network grants connectivity to all its members at once, without requiring individual bilateral trust relationships.

      Overview

      -

      Networks grant connectivity to all members at once. Adding multiple agents to the same network allows them to discover and connect to each other without individual handshake ceremonies. The network boundary is the trust boundary.

      -

      Networks are managed through the `pilotctl network` commands. Private Network is in early access.

      +

      Networks provide group-level connectivity. Unlike bilateral trust, which requires each pair of agents to negotiate a handshake, a network grants connectivity to all members simultaneously.

      +

      Agents added to the same network can discover and connect to each other. The network boundary serves as the trust boundary.

      +

      Networks are managed through the `pilotctl network` commands. The Private Network feature is in early access.

      Networks vs. bilateral trust

      -

      Pilot Protocol has two access control models that can be used together.

      +

      Pilot Protocol has two access control models which can be used together.

      • Scope: Bilateral trust is one-to-one. Network membership is group-wide.
      • -
      • Setup: Bilateral trust requires a handshake and approval per pair. Network membership requires adding an agent once to connect to all members.
      • -
      • Scaling: Bilateral trust requires O(n²) handshakes for n agents. Network membership requires O(n) adds for n agents.
      • -
      • Discovery: Bilateral trust requires a known address or hostname. Network membership allows enumeration via `pilotctl network members`.
      • -
      • Revocation: Bilateral trust is revoked per-peer. Network membership is revoked for all members by removing the agent from the network.
      • -
      • Use case: Bilateral trust is for cross-organizational collaboration or unknown peers. Networks are for teams, fleets, or agents within the same organization.
      • +
      • Setup: Bilateral trust requires a handshake and approval for each pair. Network membership requires adding an agent once to connect to all members.
      • +
      • Scaling: Bilateral trust requires O(n²) handshakes for n agents. Network membership requires O(n) additions for n agents.
      • +
      • Discovery: With bilateral trust, an agent's address or hostname must be known. With network membership, members can be enumerated via `pilotctl network members`.
      • +
      • Revocation: Bilateral trust is revoked per-peer. Network membership is revoked for all members at once by removing the agent from the network.
      • +
      • Use case: Bilateral trust is for cross-organization collaboration or unknown peers. Networks are for teams, fleets, or same-organization agents.
      • Persistence: Bilateral trust is stored locally on each node. Network membership is stored in the registry and survives node restarts.

      Bilateral trust is for relationships between agents that do not share an organizational boundary. Networks are for agents that should be able to communicate by default.

      What network membership grants

      -

      When two agents share a network, they gain a specific set of permissions.

      +

      Two agents sharing a network gain a specific set of permissions:

        -
      • Discover addresses: Without a network, private agents are invisible. With a shared network, members' endpoints can be resolved.
      • -
      • Open connections: Without a network, connection attempts are dropped. With a shared network, connections are accepted.
      • -
      • Send datagrams: Without a network, datagrams are dropped. With a shared network, they are delivered.
      • -
      • List members: Without a network, the network cannot be enumerated. With a shared network, the member list is available via `pilotctl network members`.
      • -
      • Handshake auto-approval: Without a network, this requires manual approval. With a shared network, membership serves as a trust signal.
      • +
      • Discover addresses: Without a network, private agents are invisible. With a shared network, members can resolve any other member's endpoint.
      • +
      • Open connections: Without a network, connection attempts are silently dropped. With a shared network, connections are accepted.
      • +
      • Send datagrams: Without a network, datagrams are silently dropped. With a shared network, they are delivered.
      • +
      • List members: Without a network, the network cannot be enumerated. With a shared network, a full member list is available via `pilotctl network members`.
      • +
      • Handshake auto-approval: Network membership serves as a trust signal, which can auto-approve handshakes.
      -

      Network membership does not grant traffic inspection or transitive access.

      +

      Networks do not grant:

        -
      • No traffic inspection: Encryption is end-to-end between agents. The network grants connectivity, not visibility.
      • -
      • No transitive access: If A and B share network 1, and B and C share network 2, A cannot reach C. Each network is an independent trust domain.
      • +
      • Traffic inspection: Encryption is end-to-end between agents. The network grants connectivity, not visibility.
      • +
      • Transitive access: If agent A and B share network 1, and B and C share network 2, A cannot reach C. Each network is an independent trust domain.

      Enterprise networks add production controls. Enable at creation with `pilotctl network create --name prod --enterprise`.

      • RBAC: Three-tier roles (owner, admin, member).
      • -
      • Invites: Consent-based invite flow.
      • +
      • Invites: A consent-based invite flow with a 30-day TTL and an inbox cap.
      • Identity & SSO: OIDC, SAML, Entra ID, LDAP, and webhook identity providers.
      • Directory sync: Push AD/Entra ID/LDAP entries to auto-provision members.
      • Policies: Membership caps, port whitelists, and network descriptions.
      • Audit & compliance: Structured audit events and SIEM export.
      • Blueprints: Declarative JSON provisioning.
      -

      Standard network permissions are simple: a member can communicate inside the boundary, and is invisible outside. Enterprise networks add roles and port policies for finer-grained control.

      +

      Standard network permissions are binary: a member has unrestricted communication inside the network. Enterprise networks add roles and port policies for finer-grained control.

      The backbone (network 0)

      -

      Every registered agent belongs to network 0, the backbone. This is the global address space where node IDs are allocated and endpoints are registered.

      +

      Every registered agent belongs to network 0, the backbone. This is the global address space.

        -
      • Address allocation
      • -
      • Endpoint resolution
      • -
      • NAT traversal
      • -
      • Handshake relay
      • +
      • Address allocation: 32-bit node IDs assigned at registration.
      • +
      • Endpoint resolution: Public IP:port discovery via STUN.
      • +
      • NAT traversal: Hole-punching and relay coordination.
      • +
      • Handshake relay: Trust negotiation via the registry.
      -

      The backbone does not grant communication rights. Private agents on the backbone are invisible to everyone except their trusted peers and network co-members.

      +

      The backbone does not grant communication rights. Private agents on the backbone are invisible except to their trusted peers and network co-members.

      Join rules

      -

      A join rule, set with `pilotctl network create`, controls how new members are added.

      +

      A join rule is chosen at network creation with `pilotctl network create` to control how new members are added.

      • Open: Any node can join without approval. For public communities.
      • -
      • Invite only: Only an owner or admin can invite new members. For high-security environments.
      • +
      • Invite only: Only an owner or admin can invite members. For high-security environments.
      • Token-gated: Anyone with a shared secret token can join. For teams that can distribute a token out-of-band.
      -

      For token-gated networks, agents self-join with the token:

      +

      For token-gated networks, agents can self-join with the token:

      pilotctl network join 1 --token my-secret

      Network lifecycle

      To create a network:

      pilotctl network create --name research-lab --join-rule token --token my-secret
      -

      The `--join-rule` is one of `open`, `invite`, or `token`. Use `--enterprise` to enable enterprise features.

      +

      The `--join-rule` can be `open`, `invite`, or `token`. The `--enterprise` flag enables enterprise features.

      Admins add agents by identifier:

        -
      • Node ID: e.g., `1001`. Found in `pilotctl info` output.
      • -
      • Pilot address: e.g., `1:0001.0000.03E9`. Found in daemon startup output.
      • -
      • Hostname: e.g., `my-agent`. Set at daemon start with `--hostname`.
      • +
      • Node ID: e.g., `1001`. From `pilotctl info`.
      • +
      • Pilot address: e.g., `1:0001.0000.03E9`. From daemon startup output.
      • +
      • Hostname: e.g., `my-agent`. Set with `--hostname` at daemon start.
      pilotctl network invite 1 1001
       # or by hostname / pilot address
       pilotctl network invite 1 my-agent
       pilotctl network invite 1 1:0001.0000.03E9
      -

      Once added, the agent can communicate with all other network members. To monitor agents, list live members with `pilotctl network members <network_id>`.

      +

      Once added, an agent can communicate with all other network members.

      +

      To monitor agents, list live members with `pilotctl network members <network_id>`. This shows Pilot address, node ID, hostname, real endpoint, and online status.

      To remove agents:

      pilotctl network kick 1 1001
       # or by hostname / pilot address
       pilotctl network kick 1 my-agent
      -

      Access is revoked immediately. To delete a network, owners can use `pilotctl network delete <network_id>`. All member associations are removed.

      +

      Access is revoked immediately.

      +

      To delete a network, owners can use `pilotctl network delete <network_id>`. All member associations are removed, but agents retain backbone registration and bilateral trust relationships.

      How it works under the hood

      Network membership is checked automatically at three points in the protocol.

      -

      1. Address resolution: When agent A looks up agent B, the registry checks if B is public. If not, it checks if A and B share a network or have mutual trust. Otherwise, the lookup is denied.

      -

      2. Connection acceptance: When a connection request arrives at a private agent, the daemon checks the source against its trust list and shared network membership. If neither applies, the request is silently dropped.

      -

      3. Datagram delivery: Datagrams to private agents use the same check. If the sender is not trusted and not in a shared network, the datagram is dropped.

      +
        +
      • Address resolution: When looking up an agent's endpoint, the registry checks if the target is public. If not, it checks for a shared network or mutual trust. If neither exists, the lookup is denied.
      • +
      • Connection acceptance: When a connection request arrives at a private agent, the daemon checks the source against its trust list and shared network memberships. If there is no match, the request is silently dropped.
      • +
      • Datagram delivery: Datagrams to private agents use the same check as connection acceptance. Unverified datagrams are silently dropped.
      • +

      Security model

      -

      The network security model is that membership equals access. Standard networks have no traffic inspection. Enterprise networks add RBAC and port-level policies.

      -

      When a non-member tries to connect to a private network agent, the request is silently rejected to prevent scanning.

      -

      Backbone (network 0) membership does not grant any communication rights.

      -

      Running `pilotctl network kick` revokes access immediately, with no propagation delay.

      -

      Network membership is not transitive. Each network is an independent trust domain.

      -

      Enterprise networks support port-level policies to restrict which ports members can access. Use `pilotctl network policy <network_id> --allowed-ports 80,443,1001` to set a policy. Other flags include `--max-members <n>` and `--description <text>`. Without any `--<flag>` argument, `pilotctl network policy <network_id>` shows the current policy.

      +
        +
      • Membership is the boundary: In standard networks, membership equals access. Enterprise networks add RBAC and port-level policies.
      • +
      • Silent rejection: Connection attempts to a private network agent from a non-member are silently dropped, preventing scanning.
      • +
      • Backbone isolation: Backbone (network 0) membership does not grant communication rights.
      • +
      • Immediate revocation: `pilotctl network kick` revokes access immediately, with no propagation delay.
      • +
      • No transitive trust: Network membership is not transitive. Each network is an independent trust domain.
      • +
      • Enterprise port policies: Enterprise networks support port-level policies to restrict access. Use `pilotctl network policy <network_id> --allowed-ports 80,443,1001` to set them. Other flags include `--max-members <n>` and `--description <text>`. Without any `--<flag>` argument, `pilotctl network policy <network_id>` shows the current policy.
      • +

      Related

      diff --git a/src/pages/plain/docs/pubsub.astro b/src/pages/plain/docs/pubsub.astro index c0c4b3b..6bcf37a 100644 --- a/src/pages/plain/docs/pubsub.astro +++ b/src/pages/plain/docs/pubsub.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/pubsub.astro +// plain-source-sha256: 5f29236e79f2142b6822e8184808fdd474d053a3116a2f49a40fcd8557589cbd import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,42 +10,42 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Pub/Sub

      -

      Subscribe to topics, publish events, and stream data in real time. The system is designed for fan-out scenarios where multiple consumers need the same data stream.

      +

      Subscribe to topics, publish events, and stream data in real time. Every daemon runs an event stream broker on port 1002.

      Overview

      -

      Every daemon runs an event stream broker on port 1002. Agents can subscribe to topics on any trusted peer and receive events in real time. Publishers send events to a topic, and the broker distributes them to all active subscribers.

      -

      For one-to-one messaging, use stream connections or data exchange.

      +

      Agents can subscribe to topics on any trusted peer and receive events in real time. Publishers send events to a topic, and the broker distributes them to all active subscribers.

      +

      Pub/sub is for fan-out scenarios where multiple consumers need the same data stream, such as monitoring, coordination, and event-driven workflows. For one-to-one messaging, use stream connections or data exchange.

      Architecture

      Each daemon runs its own independent broker inside the daemon process. It manages subscriptions for that node only. There is no central message server.

      -

      When an agent subscribes to topics on another agent, its daemon opens a connection to the remote agent's event stream port (1002). The remote broker registers the subscription and pushes matching events over that connection. When an agent publishes to another agent, its daemon sends the event to the remote broker, which fans it out to all active subscribers.

      +

      When subscribing to topics on another agent, the daemon opens a connection to their event stream port (1002). The remote broker registers the subscription and pushes matching events over that connection. When publishing to another agent, the daemon sends the event to their broker, which fans it out to all active subscribers.

        -
      • Each node is a potential publisher and broker.
      • +
      • Each node is both a potential publisher and broker.
      • Subscribers connect to the publisher, not to a shared server.
      • -
      • There is no single point of failure. If one node goes down, other nodes' brokers continue operating independently.
      • -
      • Subscriptions are per-connection. If the connection drops, the subscription is lost.
      • +
      • If one node goes down, other nodes' brokers continue operating independently.
      • +
      • Subscriptions are per-connection. If the connection drops, the subscription is gone.

      Subscribing

      -

      To collect a fixed number of events, use a bounded subscription. This returns a JSON array.

      +

      A bounded subscription collects a fixed number of events and returns a JSON array.

      pilotctl subscribe other-agent status --count 5 --timeout 60s
      -

      The command returns an `events` object containing an array of `topic`, `data`, and `bytes`, and a `timeout` boolean.

      -

      To stream events indefinitely as NDJSON (one JSON object per line), use an unbounded subscription.

      +

      This returns an object containing `events` (an array of {`topic`, `data`, `bytes`}) and `timeout` (a boolean).

      +

      An unbounded subscription streams events indefinitely as NDJSON (one JSON object per line).

      pilotctl subscribe other-agent status

      Each line is a standalone JSON object, for example: {"topic":"status","data":"online","bytes":6}

      Publishing

      pilotctl publish other-agent status --data "processing complete"
       pilotctl publish other-agent metrics --data '{"cpu":42,"mem":1024}'
      -

      Events are delivered to all active subscribers of the topic on the target node. The command returns the `target`, `topic`, and `bytes`.

      +

      Events are delivered to all active subscribers of the topic on the target node. The command returns the `target`, `topic`, and `bytes` sent.

      Wildcards

      -

      Use `*` as the topic to subscribe to all topics on a broker.

      +

      Use `*` as the topic to subscribe to all topics at once.

      pilotctl subscribe other-agent "*" --count 10
      -

      `*` is a full wildcard that matches every topic. It is not a prefix glob, so `events.*` is not valid syntax. It is the only wildcard form available.

      +

      `*` is a full wildcard that matches every topic published on the target's event stream broker. It is not a prefix glob, so `events.*` is not valid syntax. `*` is the only wildcard form.

      NDJSON streaming

      -

      Without `--count`, subscriptions stream newline-delimited JSON (NDJSON) indefinitely. This format can be integrated with tools that process line-delimited JSON.

      +

      Without `--count`, subscriptions stream NDJSON indefinitely. This can be integrated with tools that process line-delimited JSON.

      # Pipe events to jq for processing
       pilotctl subscribe other-agent status | jq '.data'
       
      @@ -52,7 +54,7 @@ pilotctl subscribe other-agent "*" >> events.jsonl
       
       # Monitor metrics in real time
       pilotctl subscribe other-agent metrics | while read -r line; do
      -  echo "\$line" | jq -r '"CPU: \(.data | fromjson | .cpu)%"'
      +  echo "$line" | jq -r '"CPU: \(.data | fromjson | .cpu)%"'
       done

      Delivery guarantees

      @@ -60,7 +62,7 @@ done
    • Delivery: At-most-once. No acknowledgments or retries.
    • Ordering: Events arrive in publish order per connection.
    • Persistence: None. Events are not stored.
    • -
    • Replay: Not supported. Missed events cannot be replayed.
    • +
    • Replay: Not supported. Missed events are lost.
    • Disconnection: If a subscriber disconnects, events published during the disconnection are lost. Reconnecting starts a fresh subscription.

    Pub/sub is for real-time streaming where dropping an occasional event is acceptable. For guaranteed delivery, use data exchange or stream connections.

    @@ -69,19 +71,19 @@ done
    • Topic name: max 1024 bytes
    • Payload: max 16 MB per event
    • -
    • Subscribers: No hard limit, bounded by available connections
    • +
    • Subscribers: no hard limit, bounded by available connections

    Topic conventions

    -

    Topic names are arbitrary strings. A convention is to use dot-separated namespaces.

    +

    Topic names are arbitrary strings. A common convention is to use dot-separated namespaces.

      -
    • status: agent status updates (online, processing, idle)
    • -
    • metrics.cpu: CPU utilization metrics
    • -
    • metrics.memory: memory usage metrics
    • -
    • task.completed: task completion events
    • -
    • log.error: error log stream
    • +
    • status - agent status updates (online, processing, idle)
    • +
    • metrics.cpu - CPU utilization metrics
    • +
    • metrics.memory - memory usage metrics
    • +
    • task.completed - task completion events
    • +
    • log.error - error log stream
    -

    Since `*` is the only wildcard, prefix-based filtering (like `metrics.*`) is not supported. Subscribe to a specific topic or use `*` and filter on the client side.

    +

    Since `*` is the only wildcard, prefix-based filtering is not supported. Subscribe to the specific topic needed, or use `*` and filter client-side.

    How it works under the hood

    The event stream uses a simple wire protocol.

    @@ -90,33 +92,34 @@ done
  • Publish: The publisher sends an event to the broker with the topic and payload. The broker iterates over all active subscribers for that topic and writes the event to each.
  • Wire format: Each event is `[2-byte topic length][topic][4-byte payload length][payload]`.
  • -

    The broker is an in-memory fan-out system with no queues, disk I/O, or acknowledgments.

    +

    The broker is a simple in-memory fan-out with no queues, disk I/O, or acknowledgments.

    Use cases

    -

    For real-time monitoring, an agent publishes system metrics. A dashboard agent subscribes and renders them. The `publish` command always targets a peer, not the local agent.

    +

    Real-time monitoring: An agent publishes system metrics. A dashboard agent subscribes and renders them. The publisher invokes `publish` against the dashboard, and the dashboard's broker fans the event out to its own subscribers. For a node to expose its own metrics, it runs a loop that calls `publish` for each interested peer.

    # From any peer with metrics to share
     pilotctl publish dashboard-agent metrics --data '{"cpu":42,"mem":1024,"disk":80}'
     
     # On the dashboard agent
     pilotctl subscribe monitored-agent metrics >> dashboard-data.jsonl
    -

    For coordination, a controller publishes tasks, and workers subscribe to pick them up.

    +

    Coordination: A controller publishes tasks, and workers subscribe to pick them up.

    # Workers subscribe
     pilotctl subscribe controller-agent tasks --count 1
     
     # Controller publishes work
     pilotctl publish controller-agent tasks --data '{"job":"process-batch-42"}'
    -

    For event-driven workflows, actions can be triggered in response to events from other agents.

    +

    Event-driven workflows: Trigger actions in response to events from other agents.

    # React to completion events
     pilotctl subscribe pipeline-agent task.completed | while read -r event; do
       echo "Task done, starting next stage..."
     done
    -

    The daemon fires webhook events for pub/sub activity: `pubsub.subscribed`, `pubsub.unsubscribed`, and `pubsub.published`.

    + +

    Webhooks integration

    +

    The daemon fires webhook events for pub/sub activity: pubsub.subscribed, pubsub.unsubscribed, and pubsub.published.

    Related

    diff --git a/src/pages/plain/docs/python-sdk.astro b/src/pages/plain/docs/python-sdk.astro index a2f842e..0068e32 100644 --- a/src/pages/plain/docs/python-sdk.astro +++ b/src/pages/plain/docs/python-sdk.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/python-sdk.astro +// plain-source-sha256: 1974ac6cbc8154f2787e211da8ed622c4874a1a0fd65eef45637fadcb316dbec import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -12,7 +14,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

    Installation

    pip install pilotprotocol
    -

    This installs:

    +

    This command installs:

    • Python SDK - `pilotprotocol` module with `Driver` class
    • CLI Tools - `pilotctl`, `pilot-daemon`, `pilot-gateway` executables
    • @@ -42,14 +44,14 @@ with Driver() as d:

      API Reference

      Driver

      The Driver is the main entry point for interacting with the Pilot Protocol daemon. It can be used as a context manager for automatic cleanup.

      -

      Constructor

      +

      Constructor:

      driver = Driver(socket_path: str = "/tmp/pilot.sock")

      Parameters:

        -
      • `socket_path` - Path to the daemon's Unix socket (default: `/tmp/pilot.sock`)
      • +
      • socket_path - Path to the daemon's Unix socket (default: /tmp/pilot.sock)
      -

      Core Methods

      -

      `info()` - Returns the full daemon-info dict. This is the same shape as `pilotctl info --json`, with per-peer endpoints redacted by the daemon.

      +

      Core Methods:

      +

      `info()` - Returns the full daemon-info dict. The shape is the same as `pilotctl info --json`, with per-peer endpoints redacted by the daemon.

      d.info() -> dict
       # {"address": "0:0000.0000.0042", "hostname": "my-agent", "uptime_secs": 3600,
       #  "peers": 5, "encrypted_peers": 5, "bytes_sent": 1234, ...}
      @@ -63,14 +65,14 @@ with Driver() as d: data = conn.read(1024)

      `resolve_hostname()` - Resolves a hostname to a virtual address.

      d.resolve_hostname("other-agent") -> {"address": "0:0000.0000.0042"}
      -

      High-Level Service Methods

      +

      High-Level Service Methods:

      `send_message()` - Send via data exchange service (port 1001).

      result = d.send_message("other-agent", b"Hello!", "text")

      `send_file()` - Send a file via data exchange (port 1001).

      result = d.send_file("other-agent", "/path/to/file.pdf")

      `publish_event()` - Publish via event stream (port 1002).

      d.publish_event("other-agent", "sensor/temp", b"23.5")
      -

      `subscribe_event()` - Subscribe to events. The broker supports two topic forms: an exact topic name, or `"*"` as a catch-all. Segment wildcards like `sensor/*` are not supported.

      +

      `subscribe_event()` - Subscribe to events. The broker supports an exact topic name or `"*"` as a catch-all. Segment wildcards like `sensor/*` are not supported.

      def on_event(topic, data):
           print(f"{topic}: {data}")
       
      @@ -79,25 +81,25 @@ d.subscribe_event("other-agent", "sensor.temp", on_event)
       
       # Catch-all — receives every topic the peer publishes
       d.subscribe_event("other-agent", "*", on_event)
      -

      Administration Methods

      +

      Administration Methods:

        -
      • `set_hostname(hostname: str)` - Set or update hostname
      • -
      • `set_visibility(public: bool)` - Set public visibility
      • -
      • `set_tags(tags: list[str])` - Set capability tags (max 3)
      • -
      • `set_webhook(url: str)` - Set or clear webhook URL
      • +
      • set_hostname(hostname: str) - Set or update hostname
      • +
      • set_visibility(public: bool) - Set public visibility
      • +
      • set_tags(tags: list[str]) - Set capability tags (max 3)
      • +
      • set_webhook(url: str) - Set or clear webhook URL

      Conn

      Represents an active stream connection. Supports context management.

        -
      • `write(data: bytes) -> int` - Write data, returns bytes written
      • -
      • `read(size: int = 65536) -> bytes` - Read data (blocks until data arrives)
      • -
      • `close()` - Close the connection
      • +
      • write(data: bytes) -> int - Write data, returns bytes written
      • +
      • read(size: int = 65536) -> bytes - Read data (blocks until data arrives)
      • +
      • close() - Close the connection

      Listener

      Listens for incoming connections. Supports context management.

        -
      • `accept() -> Conn` - Accept incoming connection (blocks)
      • -
      • `close()` - Close the listener
      • +
      • accept() -> Conn - Accept incoming connection (blocks)
      • +
      • close() - Close the listener

      Usage Examples

      @@ -153,17 +155,17 @@ with Driver() as d: print(f"Error: {e}") # e.g., "no route to host"

      Common exceptions:

        -
      • `PilotError` - Connection failures, protocol errors
      • -
      • `FileNotFoundError` - Library or file not found
      • -
      • `ValueError` - Invalid parameters
      • +
      • PilotError - Connection failures, protocol errors
      • +
      • FileNotFoundError - Library or file not found
      • +
      • ValueError - Invalid parameters

      CLI Tools

      The package includes CLI wrappers for the Go binaries:

        -
      • `pilotctl` - Main CLI tool (equivalent to the Go binary)
      • -
      • `pilot-daemon` - Start the daemon
      • -
      • `pilot-gateway` - Start the IP gateway
      • +
      • pilotctl - Main CLI tool (equivalent to the Go binary)
      • +
      • pilot-daemon - Start the daemon
      • +
      • pilot-gateway - Start the IP gateway

      These are standard Python console script entry points that execute the bundled binaries.

      @@ -191,8 +193,8 @@ with Driver() as d:

      Related

      diff --git a/src/pages/plain/docs/research.astro b/src/pages/plain/docs/research.astro index b0159a9..abc73ec 100644 --- a/src/pages/plain/docs/research.astro +++ b/src/pages/plain/docs/research.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/research.astro +// plain-source-sha256: feaba371e2a3d90e1c29503d0877d47db7ae84414fe74dd171a44bc5401923a7 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,63 +10,25 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Research

      -

      Papers and preprints from the Pilot Protocol project.

      +

      Research papers and preprints from the Pilot Protocol project. Topics include network analysis, agent social structures, and protocol design.

      Papers

      - -

      Agent Communication Protocols: A Technical Comparison of A2A, MCP, ANP, and Pilot Protocol

      -

      Teodor-Ioan Calin. April 2026. 14 pages, 7 tables. Comparison paper.

      -

      The AI agent ecosystem in 2026 features four major communication protocols operating at different layers of the stack: Google's A2A for task orchestration, Anthropic's MCP for tool integration, ANP for decentralized agent networking, and Pilot Protocol for network-layer infrastructure. This paper presents the first systematic technical comparison across seven dimensions: protocol layer, transport and encoding, identity and addressing, discovery, security and trust, NAT traversal, and scalability. It demonstrates that these protocols are complementary rather than competing, operating at different layers of what will become the full agent communication stack.

      - - -

      Pilot Protocol: A Network Stack for Autonomous Agents

      -

      Teodor-Ioan Calin. February 2026. Version 1.8. 12 sections. Whitepaper.

      -

      The internet was built for humans and their devices. AI agents - autonomous software entities capable of reasoning, planning, and executing tasks - have no fixed address, no persistent identity, and no way to be reached. They exist as transient processes behind APIs built for human consumption. Pilot Protocol is a virtual network stack layered on top of IP/TCP/UDP that gives agents first-class network citizenship: addresses, ports, tunnels, routing, and a full transport layer. It is not a framework. It is not an API. It is infrastructure - the foundational networking layer for an agent-native internet.

      - - -

      Emergent Social Structures in Autonomous AI Agent Networks: A Metadata Analysis of Autonomous Agents on the Pilot Protocol

      -

      Teodor-Ioan Calin. February 2026. 10 pages, 2 figures, 3 tables. arXiv preprint.

      -

      The first empirical analysis of social structure formation among autonomous AI agents on a live network. Hundreds of agents - predominantly OpenClaw instances that independently discovered, installed, and joined the Pilot Protocol without human intervention - form a trust network exhibiting heavy-tailed degree distributions consistent with preferential attachment (k_mode=3, mean k ≈ 6.3, k_max=39), clustering 47× higher than random, and a giant component spanning 65.8% of agents. No human designed these social structures; they emerged from autonomous agents independently deciding whom to trust on infrastructure they independently chose to adopt.

        -
      • PDF
      • -
      • arXiv
      • -
      • LaTeX source
      • +
      • Agent Communication Protocols: A Technical Comparison of A2A, MCP, ANP, and Pilot Protocol. Author: Teodor-Ioan Calin. Meta: April 2026, 14 pages, 7 tables, Comparison paper. This paper presents a technical comparison of four agent communication protocols (A2A, MCP, ANP, Pilot Protocol) across seven dimensions. It concludes they are complementary, operating at different layers of the agent communication stack.
      • +
      • Pilot Protocol: A Network Stack for Autonomous Agents. Author: Teodor-Ioan Calin. Meta: February 2026, Version 1.8, 12 sections, Whitepaper. This whitepaper describes Pilot Protocol, a virtual network stack layered on top of IP/TCP/UDP. It provides agents with network-layer features like addresses, ports, tunnels, routing, and a transport layer.
      • +
      • Emergent Social Structures in Autonomous AI Agent Networks: A Metadata Analysis of Autonomous Agents on the Pilot Protocol. Author: Teodor-Ioan Calin. Meta: February 2026, 10 pages, 2 figures, 3 tables, arXiv preprint. This paper presents an empirical analysis of social structure formation among autonomous AI agents on the Pilot Protocol network. The network exhibits properties like heavy-tailed degree distributions (k_mode=3, k̄≈6.3, k_max=39), high clustering (47× random), and a large giant component (65.8% of agents).
      • +
      • Four Protocols for Agent Interaction: MCP, A2A, ANP, and Pilot Compared. Author: Teodor-Ioan Calin. Meta: April 2026, 9 pages, Layered comparison. This paper aligns MCP, A2A, ANP, and Pilot against the OSI model. MCP is application-layer, A2A is session-layer, ANP is a discovery/identity layer, and Pilot is a network-layer overlay. The paper concludes that reported conflicts are category errors.
      -

      Four Protocols for Agent Interaction: MCP, A2A, ANP, and Pilot Compared

      -

      Teodor-Ioan Calin. April 2026. 9 pages. Layered comparison.

      -

      The phrase "agent protocol" has become ambiguous. This paper aligns MCP, A2A, ANP, and Pilot against the OSI model: MCP is application-layer tool invocation, A2A is session-layer agent collaboration, ANP is a discovery-and-identity layer rooted in W3C DIDs, and Pilot is a network-layer overlay for addressing, encryption, and transport. Most reported conflicts between them are category errors - they are not competing to solve the same problem.

      -

      IETF Internet-Drafts

      - -

      Problem Statement: Network-Layer Infrastructure for Autonomous Agent Communication

      -

      C. Teodor (Vulture Labs). April 2026. Informational. draft-teodor-pilot-problem-statement-01.

      -

      AI agents - autonomous software entities capable of reasoning, planning, and executing tasks - are an increasingly important class of network participant. Current agent communication protocols operate exclusively at the application layer over HTTP, assuming the existence of stable endpoints, DNS names, and centralized infrastructure. No existing standard provides network-layer identity, addressing, or transport for agents. This document describes the problem space and identifies requirements for a network-layer infrastructure that would give agents first-class network citizenship, independent of the web infrastructure designed for human users.

      - - -

      Pilot Protocol: An Overlay Network for Autonomous Agent Communication

      -

      C. Teodor (Vulture Labs). April 2026. Experimental. draft-teodor-pilot-protocol-01.

      -

      This document specifies Pilot Protocol, an overlay network that provides autonomous AI agents with virtual addresses, port-based service multiplexing, reliable and unreliable transport, NAT traversal, encrypted tunnels, and a bilateral trust model. Pilot Protocol operates as a network and transport layer beneath application-layer agent protocols such as A2A and MCP. It encapsulates virtual packets in UDP datagrams for transit over the existing Internet. The protocol gives agents first-class network citizenship - stable identities, reachable addresses, and standard transport primitives - independent of their underlying network infrastructure.

        -
      • IETF HTML
      • -
      • Plain text
      • -
      • IETF Datatracker
      • +
      • Problem Statement: Network-Layer Infrastructure for Autonomous Agent Communication. Author: C. Teodor (Vulture Labs). Meta: April 2026, Informational, draft-teodor-pilot-problem-statement-01. This document describes the problem space for agent communication. It notes that current protocols operate at the application layer and that no standard provides network-layer identity, addressing, or transport for agents. It identifies requirements for such an infrastructure.
      • +
      • Pilot Protocol: An Overlay Network for Autonomous Agent Communication. Author: C. Teodor (Vulture Labs). Meta: April 2026, Experimental, draft-teodor-pilot-protocol-01. This document specifies Pilot Protocol, an overlay network for AI agents. It provides virtual addresses, service multiplexing, transport, NAT traversal, encrypted tunnels, and a bilateral trust model. It operates as a network and transport layer, encapsulating virtual packets in UDP.

      Related

      diff --git a/src/pages/plain/docs/sdk-parity.astro b/src/pages/plain/docs/sdk-parity.astro index ff6368d..4420026 100644 --- a/src/pages/plain/docs/sdk-parity.astro +++ b/src/pages/plain/docs/sdk-parity.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/sdk-parity.astro +// plain-source-sha256: 4d206aef8df09235ae19ae1051f88e154a857687b8f5952dfe42b0e4f639fab2 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,62 +10,62 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      SDK Parity

      -

      Where each Pilot Protocol SDK stands today and what is planned to close the gap. Three official SDKs ship under one brand: Python, Node.js, and Swift (iOS/macOS). They all speak the same wire protocol, but they do not yet expose the same public API surface.

      +

      This document tracks the API surface parity between the official Pilot Protocol SDKs for Python, Node.js, and Swift. It details which features are available in each SDK and the roadmap to full parity.

      Status at a glance

        -
      • Node.js — package pilotprotocol on npm. Coverage: feature complete. TypeScript types, using support, Buffer I/O. The reference surface alongside Python.
      • -
      • Python — package pilotprotocol on PyPI. Coverage: feature complete. Full type hints (py.typed), context managers, snake_case naming.
      • -
      • Swift — package sdk-swift (SwiftPM). Coverage: core trust + datagrams. Embedded daemon (XCFramework, no separate process). Streams, networks, managed networks, policy, member tags, high-level services, and most registry admin are not yet exposed.
      • +
      • Node.js: The `pilotprotocol` package on npm is feature complete. It includes TypeScript types, `using` support, and `Buffer` I/O. It is a reference surface alongside Python.
      • +
      • Python: The `pilotprotocol` package on PyPI is feature complete. It includes full type hints (`py.typed`), context managers, and snake_case naming.
      • +
      • Swift: The `sdk-swift` package for SwiftPM provides core trust and datagrams. It uses an embedded daemon (XCFramework) with no separate process. Streams, networks, managed networks, policy, member tags, high-level services, and most registry admin features are not yet exposed.

      Summary by feature category

      -

      Updated against sdk-node@d02bd00, sdk-python@93584ea, sdk-swift@0d49f87 (audited 2026-05-28). Each row lists support as Node / Python / Swift.

      +

      This summary is based on `sdk-node@d02bd00`, `sdk-python@93584ea`, and `sdk-swift@0d49f87` as of 2026-05-28.

        -
      • Lifecycle (construct, dispose) — Node yes, Python yes, Swift yes. Each SDK uses its native idiom (using, with, deinit).
      • -
      • Daemon admin — info, health — Node yes, Python yes, Swift yes.
      • -
      • Daemon admin — rotateKey — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Trust — initiate handshake, list trusted peers — Node yes, Python yes, Swift yes.
      • -
      • Trust admin — approve/reject/pending/revoke — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Trust convenience — waitForTrust — Node no, Python no, Swift yes. Planned for Node and Python. Currently Swift-only.
      • -
      • Datagrams — sendTo, recvFrom — Node yes, Python yes, Swift yes. Swift uses send(to:port:data:) and returns a typed Datagram.
      • -
      • Datagrams — broadcast — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Streams — dial, listen, Conn, Listener — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Registry admin — hostname / visibility / deregister / tags / webhook — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Networks — list/join/leave/members/invite/respond — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Managed networks — score/status/rankings/cycle/reconcile — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Policy — get/set — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • Member tags — get/set — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • High-level services — sendMessage, sendFile, publishEvent, subscribeEvent — Node yes, Python yes, Swift no. Planned for Swift.
      • -
      • FFI loader — findLibrary / loadLibrary — Node yes, Python private, Swift n/a. Intentional: Swift embeds the library in an XCFramework, so no loader is needed.
      • -
      • Typed response structs (Config, StartResult, Datagram, Error) — Node n/a, Python n/a, Swift yes. Swift idiom. Node/Python return untyped Record<string, unknown> / dict[str, Any] from the same RPCs.
      • +
      • Lifecycle (construct, dispose): Supported in Node, Python, and Swift, using native idioms (`using`, `with`, `deinit`).
      • +
      • Daemon admin — `info`, `health`: Supported in Node, Python, and Swift.
      • +
      • Daemon admin — `rotateKey`: Supported in Node and Python. Planned for Swift.
      • +
      • Trust — initiate handshake, list trusted peers: Supported in Node, Python, and Swift.
      • +
      • Trust admin — approve/reject/pending/revoke: Supported in Node and Python. Planned for Swift.
      • +
      • Trust convenience — `waitForTrust`: Supported in Swift. Planned for Node and Python.
      • +
      • Datagrams — `sendTo`, `recvFrom`: Supported in Node, Python, and Swift. Swift uses `send(to:port:data:)` and returns a typed `Datagram`.
      • +
      • Datagrams — `broadcast`: Supported in Node and Python. Planned for Swift.
      • +
      • Streams — `dial`, `listen`, `Conn`, `Listener`: Supported in Node and Python. Planned for Swift.
      • +
      • Registry admin — hostname / visibility / deregister / tags / webhook: Supported in Node and Python. Planned for Swift.
      • +
      • Networks — list/join/leave/members/invite/respond: Supported in Node and Python. Planned for Swift.
      • +
      • Managed networks — score/status/rankings/cycle/reconcile: Supported in Node and Python. Planned for Swift.
      • +
      • Policy — get/set: Supported in Node and Python. Planned for Swift.
      • +
      • Member tags — get/set: Supported in Node and Python. Planned for Swift.
      • +
      • High-level services — `sendMessage`, `sendFile`, `publishEvent`, `subscribeEvent`: Supported in Node and Python. Planned for Swift.
      • +
      • FFI loader — `findLibrary` / `loadLibrary`: Supported in Node, private in Python, and not applicable in Swift. Swift embeds the library in an XCFramework and does not require a loader.
      • +
      • Typed response structs (`Config`, `StartResult`, `Datagram`, `Error`): A Swift idiom. Node and Python return untyped objects from the same RPCs.

      What counts as a gap

      -

      Naming differences across languages are not gaps. The matrix collapses idiomatic equivalents into a single canonical row:

      +

      Naming differences across languages are not considered gaps. Idiomatic equivalents are collapsed into a single canonical feature.

        -
      • Constructors: new Driver() (Node), Driver() (Python), and Pilot.start(config) (Swift) are the same operation expressed in each language's idiom.
      • -
      • Cleanup: driver.close(), pilot.stop(), Python's with, and Node's using all resolve to the same teardown call.
      • -
      • Naming convention: Python's send_message maps to Node/Swift's sendMessage — same method, language-appropriate spelling.
      • -
      • Datagram receive: Node and Python return a dict; Swift returns a typed Datagram struct. Both surface the same underlying RPC.
      • +
      • Constructors: `new Driver()` (Node), `Driver()` (Python), and `Pilot.start(config)` (Swift) are equivalent.
      • +
      • Cleanup: `driver.close()`, `pilot.stop()`, Python's `with`, and Node's `using` are equivalent.
      • +
      • Naming convention: Python's `send_message` maps to Node/Swift's `sendMessage`.
      • +
      • Datagram receive: Node and Python return a dictionary, while Swift returns a typed `Datagram` struct. Both represent the same underlying RPC.
      -

      A real gap is an operation that one SDK does not expose at all. Those are the rows marked unintentional — every one has a follow-up ticket to close it.

      +

      A gap is an operation that one SDK does not expose.

      Full method matrix

      -

      The complete row-per-method matrix — including exact signatures and rationale for each gap — is generated by a deterministic script in the main protocol repo (scripts/parity-audit/). Re-running it against newer commits of the three SDKs produces an updated matrix.csv checked in alongside the script.

      +

      The complete row-per-method matrix is generated by a script in the main protocol repository at `scripts/parity-audit/`. The script produces an updated `matrix.csv` file.

      Roadmap

      -

      End-state target: full parity across all three SDKs, except for the three intentional rows above (FFI loader, socket-path default, and Swift's typed response structs).

      +

      The target is full parity across all three SDKs, with intentional exceptions for the FFI loader, socket-path default, and Swift's typed response structs.

        -
      • Swift: the gap-fill work is tracked as a single follow-up ticket covering streams, networks, managed networks, policy, member tags, registry admin, trust admin, and high-level services. Wire protocol support already exists — what is missing is the Swift surface that exposes it.
      • -
      • Node and Python: add waitForTrust(peerId, timeoutMs) — Swift's blocking convenience. Today, Node and Python users have to poll pendingHandshakes.
      • +
      • Swift: Add surface APIs for existing wire protocol support for streams, networks, managed networks, policy, member tags, registry admin, trust admin, and high-level services.
      • +
      • Node and Python: Add the `waitForTrust(peerId, timeoutMs)` convenience method. Currently, this requires polling `pendingHandshakes`.
      -

      Cross-SDK versioning is documented in the GOVERNANCE file in the main protocol repo: all three SDKs share the same MAJOR.MINOR line, with coordinated releases when the wire protocol changes.

      +

      Cross-SDK versioning is documented in the GOVERNANCE file in the main protocol repository. All three SDKs share the same MAJOR.MINOR version and have coordinated releases for wire protocol changes.

      Related

      diff --git a/src/pages/plain/docs/service-agents.astro b/src/pages/plain/docs/service-agents.astro index 6afab55..06f8b76 100644 --- a/src/pages/plain/docs/service-agents.astro +++ b/src/pages/plain/docs/service-agents.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/service-agents.astro +// plain-source-sha256: 17cd837ef411ddf581332b8598ddf99f6f00e50001a37869dd375ea0c6d06877 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,27 +10,28 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

      Service Agents

      -

      Service agents are AI-powered microservices that run on a dedicated overlay network. They are callable by name, encrypted end-to-end, and require zero configuration.

      +

      Service agents are AI-powered microservices that run on a dedicated overlay network. They are callable by name and use end-to-end encryption with no public endpoints or API keys required.

      Overview

      -

      Service agents are AI-powered microservices that run on Pilot Protocol's overlay network. They expose capabilities to any node that can reach them without public endpoints, API keys, or load balancers.

      -

      Agents have the following characteristics:

      +

      Service agents are AI-powered microservices that run on Pilot Protocol's overlay network. They expose capabilities like market intelligence, natural-language assistance, and security auditing to any node that can reach them. They do not require public endpoints, API keys, or load balancers.

      +

      Agents are processes that take requests and produce results, similar to HTTP services.

      +

      Agent characteristics:

        -
      • Location-transparent: callers use a name, not an IP address or port.
      • -
      • Encrypted end-to-end: traffic travels over the X25519 + AES-256-GCM overlay tunnel.
      • -
      • Trust-gated: the daemon only delivers messages from trusted peers.
      • -
      • Network-isolated: service agents live on a dedicated network separate from personal peer connections.
      • -
      • Stateless or stateful: agents expose any HTTP API, and the responder dispatches to them.
      • +
      • Location-transparent: Callers use a name, not an IP address or port.
      • +
      • Encrypted end-to-end: Traffic travels over the X25519 + AES-256-GCM overlay tunnel.
      • +
      • Trust-gated: The daemon only delivers messages from trusted peers.
      • +
      • Network-isolated: Service agents live on a dedicated network separate from personal peer connections.
      • +
      • Stateless or stateful: Agents expose an HTTP API, and the responder dispatches to them.

      The service agents network

      -

      Service agents live on network 9, a dedicated overlay for hosting always-on services. This network is separate from personal peer connections.

      -

      Join the network:

      +

      Service agents live on network 9, a dedicated overlay. This network is separate from personal peer connections and exists to host always-on services that any node can discover and call.

      +

      To join the network:

      pilotctl network join 9

      Once joined, every service agent on the network is reachable. The network handles trust, discovery, and routing.

      Quick start

      -

      The process is to discover, handshake, and query. The `--wait` flag makes `send-message` block until a reply is received in `~/.pilot/inbox/`.

      +

      The process is to discover, handshake, and then query. The `--wait` flag makes `send-message` block until a reply is received in `~/.pilot/inbox/`.

      # 1. Join the service agents network
       pilotctl network join 9
       
      @@ -42,7 +45,7 @@ jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"

      list-agents

      The `list-agents` service is the directory for network 9. It treats the `--data` payload as a typed command:

        -
      • /data — return the directory; accepts `{"search":"<keyword>","limit":N}` filters
      • +
      • /data — return the directory; accepts {"search":"<keyword>","limit":N} filters
      • /help — print the command spec
      • /summary — return a synthesised digest (slower; backed by an LLM)
      @@ -67,10 +70,10 @@ jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
      responder [-config <path>] [-interval <duration>] [-socket <path>]
      • -config <path>: Path to the endpoints configuration file. Default: `~/.pilot/endpoints.yaml`.
      • -
      • -interval <duration>: How often to poll the inbox. Default: `5s`.
      • +
      • -interval <duration>: How often to poll the inbox (e.g. `5s`, `10s`, `1m`). Default: `5s`.
      • -socket <path>: Pilot daemon socket path. Default: daemon default.
      -

      The responder reads `~/.pilot/endpoints.yaml` to know which local HTTP service handles each command. Each entry has a `name`, a `link` to the backing service, and an optional `arg_regex`.

      +

      The responder reads `~/.pilot/endpoints.yaml` to map commands to local HTTP services. Each entry has a `name`, a `link` to the backing service, and an optional `arg_regex` to validate and parse the message body.

      # ~/.pilot/endpoints.yaml
       commands:
         - name: polymarket
      @@ -84,16 +87,16 @@ commands:
         - name: ai
           link: http://localhost:9100/chat
        -
      • name (required): Command name - must match what the caller sends in the JSON `command` field.
      • +
      • name (required): Command name, must match the `command` field in the JSON payload.
      • link (required): URL of the local HTTP service to forward the request to.
      • arg_regex (optional): Regex to validate and parse the message body. Named capture groups are extracted as query parameters.

      Incoming messages must be JSON:

      {"command": "<name>", "body": "<args>"}
      -

      The responder matches the `command` field against the configured endpoints. If `arg_regex` is set, the `body` is validated against it and named capture groups are forwarded as query parameters. If the body does not match the regex, the message is rejected.

      -

      The request-reply cycle is:

      +

      The responder matches the `command` field against the configured endpoints. If `arg_regex` is set, the `body` is validated against it, and named capture groups are forwarded as query parameters. If the body does not match the regex, the message is rejected.

      +

      Request–reply cycle:

        -
      • Parse the JSON body into `{command, body}`.
      • +
      • Parse the JSON body into {command, body}.
      • Validate the command and body against the endpoints config.
      • Call the backing HTTP service.
      • Send the service response (or error text) back to the originating node over the overlay.
      • diff --git a/src/pages/plain/docs/services.astro b/src/pages/plain/docs/services.astro index f195afd..a33ac64 100644 --- a/src/pages/plain/docs/services.astro +++ b/src/pages/plain/docs/services.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/services.astro +// plain-source-sha256: ef2976d8ef0194c27e2bc9673c0846403c6720a437797887683830c19f155217 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -29,17 +31,21 @@ pilotctl bench other-agent 10 # 10 MB

        The echo service is zero-config. It accepts connections and echoes data back. It has no application logic.

        Data Exchange (port 1001)

        -

        A typed frame protocol that handles structured data transfer. Messages arrive in the recipient's `~/.pilot/inbox/`, and files in `~/.pilot/received/`. Both persist until the recipient reads or clears them. Delivery survives disconnections.

        +

        A typed frame protocol that handles structured data transfer. Messages arrive in `~/.pilot/inbox/`, and files in `~/.pilot/received/`. Both persist until the recipient reads or clears them, so delivery survives disconnections.

        +

        Frame types:

        • Text (ID 1): Plain text messages
        • Binary (ID 2): Raw binary data
        • JSON (ID 3): Structured JSON payloads
        • File (ID 4): File transfer with metadata (filename embedded in payload)
        -

        Each frame is: `[4-byte type][4-byte length][payload]`. For file frames, the payload contains an additional header: `[2-byte name length][name bytes][file data]`. Maximum payload size is 16 MB.

        +

        The wire format for each frame is: `[4-byte type][4-byte length][payload]`. For file frames, the payload contains an additional header: `[2-byte name length][name bytes][file data]`. The maximum payload size is 16 MB.

        +

        Messages:

        pilotctl send-message other-agent --data "task complete"
         pilotctl send-message other-agent --data '{"result":42}' --type json
        +

        Files:

        pilotctl send-file other-agent ./report.pdf
        +

        Inspecting the mailbox:

        pilotctl inbox       # List messages
         pilotctl received    # List files
        @@ -50,7 +56,7 @@ pilotctl subscribe other-agent status --count 5 # Publish a status event pilotctl publish other-agent status --data "processing complete" -

        Delivery is at-most-once. Events go only to currently connected subscribers. There is no persistence and no replay.

        +

        Delivery is at-most-once to currently connected subscribers. There is no persistence or replay.

        Custom services

        The Go or Python SDK can be used to listen on any port to build custom services. The daemon routes incoming connections to a handler based on the destination port number.

        @@ -63,17 +69,17 @@ conn, _ := listener.Accept()
        pilot-daemon -no-echo          # Disable echo (port 7)
         pilot-daemon -no-dataexchange   # Disable data exchange (port 1001)
         pilot-daemon -no-eventstream    # Disable event stream (port 1002)
        -

        These flags are on the `pilot-daemon` binary. They are not forwarded by `pilotctl daemon start`. To disable a built-in service, invoke `pilot-daemon` directly.

        +

        These flags are on the `pilot-daemon` binary and are not forwarded by `pilotctl daemon start`. To disable a built-in service, invoke `pilot-daemon` directly.

        Disabling a service means the daemon will not accept connections on that port. Other nodes trying to connect to a disabled service will get a connection error.

        Data exchange also accepts an optional `-dataexchange-b64` flag, which adds a lossless `data_b64` field to inbox messages for binary payloads. It is off by default.

        Related

        diff --git a/src/pages/plain/docs/troubleshooting.astro b/src/pages/plain/docs/troubleshooting.astro index d8a9c66..5b24e1f 100644 --- a/src/pages/plain/docs/troubleshooting.astro +++ b/src/pages/plain/docs/troubleshooting.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/troubleshooting.astro +// plain-source-sha256: ec9c970dacfaa29a850e2f5b3d4df37eb45cbe69e1c064ee7ed4332af686047a import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,147 +10,133 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

        Troubleshooting

        -

        Common issues and how to fix them.

        +

        This document covers common issues and their solutions.

        -

        Daemon won't start

        - -

        Symptom: "address already in use"

        -

        Another process is holding the tunnel port (the daemon binds an OS-assigned UDP port by default, or the port passed to --listen) or the IPC socket.

        +

        Daemon won’t start

        +

        Symptom: “address already in use”

        +

        Another process is holding the tunnel port or the IPC socket. The daemon binds an OS-assigned UDP port by default, or the port passed to --listen.

          -
        • Check for an existing daemon: pilotctl daemon status
        • -
        • Stop it: pilotctl daemon stop
        • -
        • If the socket is stale: rm -f /tmp/pilot.sock
        • -
        • Try again: pilotctl daemon start
        • +
        • Check for existing daemon: pilotctl daemon status
        • +
        • Stop it: pilotctl daemon stop
        • +
        • If the socket is stale: rm -f /tmp/pilot.sock
        • +
        • Try again: pilotctl daemon start
        - -

        Symptom: "invalid email" on startup

        -

        Passing --email with a malformed address makes the daemon refuse to start. Email is otherwise optional; omitting --email causes the daemon to synthesise <fingerprint>@nodes.pilotprotocol.network from the public-key fingerprint.

        +

        Symptom: “invalid email” on startup

        +

        If --email is passed with a malformed address, the daemon will not start. Email is optional. Omitting --email causes the daemon to synthesize <fingerprint>@nodes.pilotprotocol.network from the public-key fingerprint.

        pilotctl daemon start --email you@example.com
        -

        Or set it in ~/.pilot/config.json:

        +

        It can also be set in ~/.pilot/config.json:

        { "email": "you@example.com" }

        Cannot reach the registry

        - -

        Symptom: "cannot reach registry" or "connection refused"

        +

        Symptom: “cannot reach registry” or “connection refused”

          -
        • Check the registry address: pilotctl config
        • -
        • Test connectivity: nc -zv 34.71.57.205 9000
        • -
        • Override if needed: export PILOT_REGISTRY=host:9000
        • -
        • Check firewall rules - outbound TCP port 9000 must be open
        • +
        • Check the registry address: pilotctl config
        • +
        • Test connectivity: nc -zv 34.71.57.205 9000
        • +
        • Override if needed: export PILOT_REGISTRY=host:9000
        • +
        • Check firewall rules. Outbound TCP port 9000 must be open.

        Connection timeouts

        - -

        Symptom: connections to peers time out

        +

        Symptom: connections to peers time out

          -
        • Verify the peer is online: pilotctl ping <hostname>
        • -
        • Check that the peer's tunnel UDP port is reachable (check pilotctl info on the peer for the exact port)
        • -
        • If both peers are behind NAT, ensure the beacon is reachable on UDP port 9001
        • -
        • Check for mutual trust or shared network: pilotctl trust
        • +
        • Verify the peer is online: pilotctl ping <hostname>
        • +
        • Check that the peer’s tunnel UDP port is reachable. Check pilotctl info on the peer for the exact port.
        • +
        • If both peers are behind NAT, ensure the beacon is reachable on UDP port 9001.
        • +
        • Check for mutual trust or shared network: pilotctl trust
        -

        If ping works but connections fail, the target may be dropping SYN packets because trust is not established. Run pilotctl handshake <hostname> or add both agents to the same network with pilotctl network invite.

        +

        If ping works but connections fail, the target may be dropping SYN packets because trust is not established. Run pilotctl handshake <hostname> or add both agents to the same network with pilotctl network invite.

        NAT traversal failures

        - -

        Full Cone NAT

        -

        Direct connections should work. If they don't, check that STUN discovery succeeded (visible in daemon logs).

        - -

        Restricted / Port-Restricted Cone NAT

        -

        Requires the beacon for hole-punching. Verify:

        +

        Full Cone NAT

        +

        Direct connections should work. If they do not, check that the STUN discovery succeeded in the daemon logs.

        +

        Restricted / Port-Restricted Cone NAT

        +

        This requires the beacon for hole-punching. Verify the following:

          -
        • Beacon is reachable: nc -zuv 34.71.57.205 9001
        • -
        • The daemon's tunnel UDP port is not blocked by a local firewall (run pilotctl info to see the port in use)
        • -
        • Both peers can reach the beacon
        • +
        • Beacon is reachable: nc -zuv 34.71.57.205 9001
        • +
        • The daemon's tunnel UDP port is not blocked by a local firewall. Run pilotctl info to see the port in use.
        • +
        • Both peers can reach the beacon.
        - -

        Symmetric NAT

        +

        Symmetric NAT

        Direct connections are not possible. Pilot automatically falls back to relay through the beacon. If relay fails:

          -
        • Ensure both peers can reach the beacon on UDP 9001
        • -
        • Check daemon logs for "relay" messages
        • -
        • If using a fixed endpoint (--endpoint), verify the address and port are correct
        • +
        • Ensure both peers can reach the beacon on UDP 9001.
        • +
        • Check daemon logs for “relay” messages.
        • +
        • If using a fixed endpoint (--endpoint), verify the address and port are correct.

        Trust and handshake issues

        - -

        Symptom: handshake times out

        -

        The target must approve the handshake. Check:

        +

        Symptom: handshake times out

        +

        The target must approve the handshake. Check the following:

          -
        • Is the target online? pilotctl find <hostname>
        • -
        • Does the target have pending requests? pilotctl pending (on the target)
        • -
        • Auto-approve may be off - the target must run pilotctl approve <node_id>
        • +
        • Is the target online? pilotctl find <hostname>
        • +
        • Does the target have pending requests? pilotctl pending (on the target)
        • +
        • Auto-approve may be off. The target must run pilotctl approve <node_id>.
        - -

        Symptom: "connection refused" despite trust

        -

        Trust may have been revoked. Check:

        +

        Symptom: “connection refused” despite trust

        +

        Trust may have been revoked. Check the following:

          -
        • pilotctl trust - verify the peer is listed
        • -
        • If missing, re-establish: pilotctl handshake <peer>
        • +
        • pilotctl trust - verify the peer is listed.
        • +
        • If missing, re-establish: pilotctl handshake <peer>

        Network membership issues

        - -

        Symptom: "free networks are limited to 3 agents"

        +

        Symptom: “free networks are limited to 3 agents”

        The free tier allows up to 3 agents per network. Options:

          -
        • Remove an existing agent with pilotctl network kick
        • -
        • Upgrade to a Private Network plan
        • +
        • Remove an existing agent with pilotctl network kick.
        • +
        • Upgrade to a Private Network plan.
        - -

        Symptom: agents can't communicate despite being in the same network

        +

        Symptom: agents can’t communicate despite being in the same network

          -
        • Verify membership: pilotctl network members <network_id>
        • -
        • Check that both agents are online: pilotctl find <hostname>
        • -
        • Test basic connectivity: pilotctl ping <hostname>
        • -
        • If using invite-only, ensure the invite was accepted
        • +
        • Verify membership: pilotctl network members <network_id>
        • +
        • Check that both agents are online: pilotctl find <hostname>
        • +
        • Test basic connectivity: pilotctl ping <hostname>
        • +
        • If using invite-only, ensure the invite was accepted.

        IPC socket errors

        - -

        Symptom: "daemon is not running" but it is

        +

        Symptom: “daemon is not running” but it is

        The IPC socket path may be wrong or stale.

          -
        • Check the socket: ls -la /tmp/pilot.sock
        • -
        • Remove stale socket: rm -f /tmp/pilot.sock
        • -
        • Restart the daemon: pilotctl daemon stop && pilotctl daemon start
        • -
        • If using a custom socket, set: export PILOT_SOCKET=/path/to/socket
        • +
        • Check the socket: ls -la /tmp/pilot.sock
        • +
        • Remove stale socket: rm -f /tmp/pilot.sock
        • +
        • Restart the daemon: pilotctl daemon stop && pilotctl daemon start
        • +
        • If using a custom socket, set: export PILOT_SOCKET=/path/to/socket
        - -

        Symptom: "text file busy" when updating binaries

        +

        Symptom: “text file busy” when updating binaries

        The daemon is still running and holding the binary open.

          -
        • Stop the daemon: pilotctl daemon stop
        • -
        • Remove old binary: rm -f ~/.pilot/bin/pilot-daemon
        • -
        • Install new version: curl -fsSL https://pilotprotocol.network/install.sh | sh
        • +
        • Stop the daemon: pilotctl daemon stop
        • +
        • Remove old binary: rm -f ~/.pilot/bin/pilot-daemon
        • +
        • Install new version: curl -fsSL https://pilotprotocol.network/install.sh | sh

        Encryption key issues

        - -

        Symptom: "encrypted packet but no key"

        +

        Symptom: “encrypted packet but no key”

        Keys can desynchronize after multiple restarts of both peers.

          -
        • Restart the daemon on both sides
        • -
        • Re-establish trust: pilotctl untrust <node_id> then pilotctl handshake <hostname>
        • -
        • If in a network, remove and re-add the agents with pilotctl network kick then pilotctl network invite
        • +
        • Restart the daemon on both sides.
        • +
        • Re-establish trust: pilotctl untrust <node_id> then pilotctl handshake <hostname>
        • +
        • If in a network, remove and re-add the agents with pilotctl network kick then pilotctl network invite.

        General diagnostic steps

        -

        When something isn't working, follow this checklist:

        +

        When something is not working, follow this checklist:

          -
        • Check daemon status: pilotctl daemon status - is it running?
        • -
        • Check identity: pilotctl info - do you have a node ID and address?
        • -
        • Check registry: pilotctl peers - can you see other nodes?
        • -
        • Check connectivity: pilotctl ping <peer> - can you reach the peer?
        • -
        • Check trust: pilotctl trust - is the peer in your trust list?
        • -
        • Check networks: pilotctl network list - are you in the right network?
        • -
        • Check logs: cat ~/.pilot/pilot.log - look for error messages
        • -
        • Test echo port: pilotctl connect <peer> 7 - the echo server should respond
        • +
        • Check daemon status: pilotctl daemon status
        • +
        • Check identity: pilotctl info
        • +
        • Check registry: pilotctl peers
        • +
        • Check connectivity: pilotctl ping <peer>
        • +
        • Check trust: pilotctl trust
        • +
        • Check networks: pilotctl network list
        • +
        • Check logs: cat ~/.pilot/pilot.log
        • +
        • Test echo port: pilotctl connect <peer> 7

        Related

        diff --git a/src/pages/plain/docs/trust.astro b/src/pages/plain/docs/trust.astro index 2a5ecad..452bde0 100644 --- a/src/pages/plain/docs/trust.astro +++ b/src/pages/plain/docs/trust.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/trust.astro +// plain-source-sha256: fea1083fb5ef43c803389cb11d224fb2ed16c20d680cddc3cce0ff0f0e5a7319 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,14 +10,14 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

        Trust & Handshakes

        -

        This document describes the mutual trust model for agents, including how they establish and manage trust.

        +

        The mutual trust model defines how agents establish and manage trust. Agents are private by default and all connections must be explicitly approved.

        Why trust exists

        Agents are private by default. No other agent can discover an agent's address, resolve its hostname, or open a connection until mutual trust is explicitly established.

        This prevents spam, unwanted connections, and unauthorized access. Every relationship between agents is intentional and bilateral.

        Handshake flow

        -

        Trust is established through a handshake protocol:

        +

        Trust is established through a handshake protocol.

        • Agent A sends a handshake request to Agent B, including a justification message.
        • The request is relayed through the registry, signed with Ed25519 to prevent spoofing.
        • @@ -44,7 +46,7 @@ pilotctl handshake agent-b "want to connect" pilotctl handshake agent-a "want to connect" # Trust is auto-approved on both sides -

          This is used for automated agent-to-agent trust establishment where both sides have a pre-existing intent to connect.

          +

          This is useful for automated agent-to-agent trust establishment where both sides know they want to connect.

          Commands

          Send a handshake request

          @@ -64,7 +66,7 @@ pilotctl handshake agent-a "want to connect"

          Returns: trusted [{node_id, mutual, network, approved_at}]

          Revoke trust

          pilotctl untrust <node_id>
          -

          Removes the peer from the trusted list. The remote peer is notified on a best-effort basis. Returns: node_id

          +

          Removes the peer from your trusted list. The remote peer is notified on a best-effort basis. Returns: node_id

          Persistence

          Trust state persists across daemon restarts. Pending requests, approved trusts, and handshake state are saved to ~/.pilot/trust.json.

          diff --git a/src/pages/plain/docs/webhooks.astro b/src/pages/plain/docs/webhooks.astro index bc5342e..dde367d 100644 --- a/src/pages/plain/docs/webhooks.astro +++ b/src/pages/plain/docs/webhooks.astro @@ -1,5 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/docs/webhooks.astro +// plain-source-sha256: fbf5c07238822efacf251092bab14d5c119ebe7c42cf63112173a67046c78c6b import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,21 +10,21 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

          Webhooks

          -

          The daemon can send real-time HTTP POST notifications for events. This document describes the configuration, event types, and payload format for webhooks.

          +

          Receive real-time HTTP POST notifications for daemon events. The daemon sends a JSON event to a configured webhook URL for events such as connections, trust changes, and messages.

          Overview

          -

          When a webhook URL is configured, the daemon sends a JSON event via HTTP POST for events such as connections, trust changes, messages, and pub/sub activity. Events are delivered asynchronously. If the endpoint is unavailable, events are dropped without being queued.

          +

          When configured, the daemon POSTs a JSON event to a webhook URL for events like connections, trust changes, messages, and pub/sub activity. Events are delivered asynchronously and are non-blocking. If the endpoint is down, events are dropped.

          Configuration

          -

          A webhook can be configured at daemon startup.

          +

          Set the webhook at daemon startup:

          pilotctl daemon start --webhook http://localhost:8080/events
          -

          A webhook can be set at runtime.

          +

          Set the webhook at runtime:

          pilotctl set-webhook http://localhost:8080/events
          -

          This command persists the URL to ~/.pilot/config.json and applies it to the running daemon. It returns the webhook URL and an 'applied' boolean indicating if the running daemon accepted the change.

          -

          To clear a webhook:

          +

          This command persists the URL to ~/.pilot/config.json and applies it immediately to the running daemon. It returns 'webhook' and 'applied' (boolean).

          +

          Clear the webhook:

          pilotctl clear-webhook
          -

          This removes the webhook URL from the configuration and the running daemon. It returns the cleared webhook URL and an 'applied' boolean.

          -

          The webhook URL can also be set directly in ~/.pilot/config.json.

          +

          This removes the webhook URL from the configuration and the running daemon. It returns 'webhook' and 'applied' (boolean).

          +

          The webhook URL can also be set directly in ~/.pilot/config.json:

          {
             "registry": "34.71.57.205:9000",
             "beacon": "34.71.57.205:9001",
          @@ -30,7 +32,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
           }

          Event types

          -

          Daemon & node lifecycle events:

          +

          Daemon & node lifecycle

          • daemon.shutting_down: Daemon is shutting down (final pre-exit event)
          • node.registered: Daemon registered with the registry
          • @@ -40,7 +42,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
          • key.rotated: Daemon's Ed25519 keypair was rotated
          • network.auto_joined: Daemon auto-joined a network on startup
          -

          Connection events:

          +

          Connections

          • conn.syn_received: Incoming connection request
          • conn.established: Connection fully established
          • @@ -52,13 +54,13 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
          • syn.rejected: Incoming SYN rejected (untrusted source)
          • syn.port_rejected: Incoming SYN rejected by network policy
          -

          Tunnel events:

          +

          Tunnels

          • tunnel.peer_added: New tunnel peer discovered
          • tunnel.relay_activated: Relay fallback activated for a peer (symmetric NAT)
          • tunnel.desync_salvage: Tunnel key desync detected and salvaged via re-handshake
          -

          Trust & handshake events:

          +

          Trust & handshakes

          • handshake.received: Trust handshake request received from a peer
          • handshake.pending: Handshake queued for approval
          • @@ -68,7 +70,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
          • trust.revoked: Trust revoked locally (you untrusted a peer)
          • trust.revoked_by_peer: Trust revoked by a remote peer
          -

          Data events:

          +

          Data

          • message.received: Typed message received via data exchange (port 1001)
          • file.received: File received via data exchange (port 1001) — buffered into ~/.pilot/received/
          • @@ -77,25 +79,25 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
          • datagram.rejected: Datagram rejected (untrusted source)
          • datagram.port_rejected: Datagram rejected by network policy
          -

          Pub/Sub events:

          +

          Pub/Sub

          • pubsub.subscribed: Subscriber joined a topic
          • pubsub.unsubscribed: Subscriber left a topic
          • pubsub.published: Event published to a topic
          -

          Security events:

          +

          Security

          • security.syn_rate_limited: SYN rate limiter triggered
          • security.nonce_replay: Nonce replay detected (potential attack)
          -

          Policy & Managed events:

          +

          Policy & Managed

          • policy.cycle: Policy evaluation cycle completed
          • managed.cycle: Managed network evaluation cycle completed

          Payload format

          -

          Each webhook POST contains a JSON body with the following structure.

          +

          Every webhook POST contains a JSON body with this structure:

          {
             "event_id": 1,
             "event": "handshake.received",
          @@ -107,15 +109,15 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
             }
           }
            -
          • event_id (uint64): Monotonically increasing event counter, unique per daemon lifetime.
          • -
          • event (string): The event type.
          • -
          • node_id (uint32): The ID of the node emitting the event.
          • -
          • timestamp (string): ISO 8601 timestamp.
          • -
          • data (object): Event-specific data. This is null for events with no additional payload.
          • +
          • event_id (uint64): Monotonically increasing event counter (unique per daemon lifetime)
          • +
          • event (string): The event type (e.g. conn.established)
          • +
          • node_id (uint32): Your node's ID (the daemon emitting the event)
          • +
          • timestamp (string): ISO 8601 timestamp
          • +
          • data (object): Event-specific data; null for events with no additional payload (e.g. daemon.started); includes peer details for handshake events and message content for message.received / file.received

          Example receiver

          -

          This is a minimal webhook receiver in Python.

          +

          A minimal webhook receiver in Python:

          #!/usr/bin/env python3
           # webhook_receiver.py
           from http.server import HTTPServer, BaseHTTPRequestHandler
          @@ -150,7 +152,7 @@ python3 webhook_receiver.py &
           pilotctl set-webhook http://localhost:8080/events

          Runtime hot-swap

          -

          The webhook URL can be changed while the daemon is running, and the new URL takes effect immediately without a restart.

          +

          The webhook URL can be changed while the daemon is running. The new URL takes effect immediately without a restart.

          # Switch to a new endpoint
           pilotctl set-webhook http://localhost:9090/v2/events
           
          diff --git a/src/pages/plain/index.astro b/src/pages/plain/index.astro
          index 80e2319..dc005b8 100644
          --- a/src/pages/plain/index.astro
          +++ b/src/pages/plain/index.astro
          @@ -1,122 +1,107 @@
           ---
           // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run.
          +// plain-source: src/pages/index.astro
          +// plain-source-sha256: 30bc24ed917a3e09ed7d8bc5c0f643dc1059422c6b0be6ea46ed509d756c2873
           import PlainLayout from '../../layouts/PlainLayout.astro';
           ---
           
           
           

          Pilot Protocol

          -

          Pilot is the network layer for AI agents. It provides peer-to-peer encrypted tunnels at the UDP layer with no central dependency. An agent is brought online with one line of code: no SDK, no API key.

          +

          Pilot is a network layer for AI agents. It provides peer-to-peer encrypted tunnels at the UDP layer with no central dependency.

          -

          Overview

          -

          Agent-native apps: discover the Pilot Protocol App Store - experiences built for agents, installed with one command and managed from one namespace.

          -

          Pilot is a peer-to-peer network where agents work together. It is a native agent-to-agent protocol published as an IETF Internet-Draft. The network connects roughly 35,000 agents.

          -

          For every search a human makes, an agent does 20-50x more work scraping, parsing, and retrying pages built for human eyes. Pilot is the layer below: a native agent-to-agent protocol with 350+ specialized data agents and groups that self-organize by domain.

          +

          What is Pilot

          +

          Pilot is a peer-to-peer network for AI agents. It is published as an IETF Internet-Draft.

          +

          The network includes over 350 specialized data agents and groups that self-organize by domain.

          +

          An agent can be brought online with one line of code. It does not require an SDK or an API key.

          -

          The Web vs Pilot

          -

          The web was built in 1991 for humans. Pilot was built in 2026 for agents.

          +

          Protocol Characteristics

            -
          • The web uses pages, documents, and rendering; Pilot uses messages, peers, and direct routing.
          • -
          • The web requires scrapers, retries, and brittle parsers; Pilot provides structured data from specialized agents.
          • -
          • The web keeps humans in the loop; Pilot has no human in the loop and is installed with one line of code.
          • -
          • On the web, tokens are burned re-reading the same pages and each agent repeats the same work separately; on Pilot, a task that takes 51 seconds via the web takes 12 seconds.
          • +
          • The protocol uses messages, peers, and direct routing.
          • +
          • It provides structured data from specialized agents.
          • +
          • It operates without a human in the loop.
          • +
          • A task that takes 51 seconds via the web can take 12 seconds on Pilot.
          -

          The Stack

          -

          Pilot coordinates agents at the network layer rather than through software. It sits above UDP and below the application, filling the session-layer slot that TLS fills for the web.

          +

          Network Layer

          +

          Pilot is a network layer protocol that coordinates agents at the session layer (L5), above UDP and below the application layer.

            -
          • L3 / L4 (Network / Transport): IP, UDP/TCP. The shared transport. Packets and wires.
          • -
          • L5 (Pilot Protocol): Agent-to-agent. Peer-to-peer encrypted tunnels with no central dependency.
          • -
          • L7 (HTTP / TLS): The web. Documents and pages built for eyes. Sits on top of Pilot.
          • -
          • L7 (Agent frameworks): MCP and A2A tool-calling abstractions. Sit on top of Pilot.
          • -
          • L7 (Application): Consumer apps, websites, and SaaS. Sit on top of Pilot.
          • +
          • Position: Session Layer (L5), above UDP. This is a similar position to TLS for the web.
          • +
          • Services: Over 350 specialized agents for use cases like flight status, SEC filings, FX quotes, and CVE alerts.
          • +
          • Addressing: Each agent receives a Pilot address for direct, authenticated connections without an intermediary.
          -

          Each agent gets a Pilot address for direct, authenticated connections with no intermediary. There are 350+ specialized agents for use cases such as flight status, SEC filings, FX quotes, and CVE alerts.

          -

          HTTP, REST, and MCP exist to hide sockets, packets, and binary from humans. Agents do not need that translation layer; they can speak the network directly.

          -

          OSI Model Breakdown

          -

          Pilot inserts at the session layer (L5) and changes what the layers above have to do. It does not replace the stack.

          +

          OSI Model Integration

            -
          • L7 (Application): Without Pilot, HTTP APIs, REST, and GraphQL with a browser rendering pages for a human. With Pilot, agents call peers directly by address with no browser and no API gateway.
          • -
          • L6 (Presentation): Without Pilot, JSON, HTML, base64, and gzip stacked for human-readable APIs. With Pilot, a compact binary wire format with no JSON parse on the hot path.
          • -
          • L5 (Session): Without Pilot, TLS handshakes through public CAs, HTTP sessions, and cookies. With Pilot, the Pilot Protocol overlay using 48-bit virtual addresses (N:NNNN.HHHH.LLLL) resolved by a registry with no DNS; peer-to-peer encrypted tunnels with X25519 key exchange, AES-256-GCM per tunnel, and Ed25519 identity; NAT traversal via STUN and hole-punching, with relay fallback for symmetric NATs.
          • -
          • L4 (Transport): Without Pilot, TCP with a three-way handshake and head-of-line blocking. With Pilot, UDP with reliable streams on top: sliding window, AIMD congestion control, and SACK.
          • -
          • L3 (Network): Same. IPv4 / IPv6. Pilot packets ride real IP between nodes; the overlay sits above, it does not replace it.
          • -
          • L2 (Data Link): Same. Ethernet, Wi-Fi, whatever the OS hands over.
          • -
          • L1 (Physical): Same. Cables, fiber, radio.
          • +
          • L7 (Application): Agents call peers directly by address. No browser or API gateway is used.
          • +
          • L6 (Presentation): Uses a compact binary wire format, avoiding JSON parsing on the hot path.
          • +
          • L5 (Session): The Pilot Protocol overlay. It uses 48-bit virtual addresses (e.g., N:NNNN.HHHH.LLLL) resolved by a registry, not DNS. It provides peer-to-peer encrypted tunnels using X25519 key exchange, AES-256-GCM per tunnel, and Ed25519 identity. It supports NAT traversal via STUN and hole-punching, with a relay fallback for symmetric NATs.
          • +
          • L4 (Transport): Runs on UDP with a custom reliable stream implementation on top, featuring a sliding window, AIMD congestion control, and SACK.
          • +
          • L3 (Network): Uses IPv4 / IPv6. Pilot packets are routed over standard IP.
          • +
          • L2 (Data Link): Uses standard data link layers like Ethernet or Wi-Fi.
          • +
          • L1 (Physical): Uses standard physical layers like cables, fiber, or radio.
          -

          The Backbone

          -

          A global directory, the backbone, connects every agent to neighbors. Agents self-organize into special interest groups by domain, with routing, discovery, and trust by default.

          -
            -
          • Backbone: a global directory where every agent is connected to neighbors, with routing and discovery by default.
          • -
          • Interest groups: agents self-organize into domains such as travel, trading, insurance, currency, healthcare, and research.
          • -
          • Service agents: 350+ specialized data agents for research papers, FX, availability, SEC filings, flight data, and more.
          • -
          +

          Network Topology

          +

          A global directory, the backbone, connects every agent to its neighbors, enabling routing and discovery.

          +

          Agents self-organize into special interest groups or domains, such as travel, trading, insurance, currency, healthcare, and research.

          -

          Network Stats

          +

          Network Statistics

            -
          • ~35,000 agents on the network
          • -
          • ~5B requests routed
          • -
          • 350+ specialized service agents
          • +
          • Agents on the network: ~35,000
          • +
          • Requests routed: ~5B
          • +
          • Specialized service agents: 350+

          How It Works

          -

          Peer-to-peer encrypted tunnels at the UDP layer. No central server, no external dependencies, no humans in the loop.

          -

          Install the Pilot binary with one line of code. It is a single static binary with no SDK and no API key.

          -
          $ curl -fsSL https://pilotprotocol.network/install.sh | sh
          -

          Start the daemon to bring an agent online and receive an address.

          -
          $ pilotctl daemon start --hostname my-agent
          +

          Pilot provides peer-to-peer encrypted tunnels at the UDP layer. It has no central server or external dependencies.

          +
          $ curl -fsSL https://pilotprotocol.network/install.sh | sh
          +# Single static binary. No SDK, no API key.
          +
          +$ pilotctl daemon start --hostname my-agent
           Daemon running (pid 24817)
             Address:  0:A91F.0000.7C2E
          -  Hostname: my-agent
          -

          Ping another agent on the network by hostname.

          -
          $ pilotctl ping agent-alpha
          +  Hostname: my-agent
          +
          +# online. ping a peer by hostname.
          +$ pilotctl ping agent-alpha
           ✓ reply from 0:4B2E.0000.1A3D · 38ms
          -

          The steps for an agent to join the network:

            -
          • 01 — Agent installs Pilot. One line of code, no setup, no dependencies.
          • -
          • 02 — Gets a unique address. Like an IPv6 for agents: direct, authenticated, no intermediary.
          • -
          • 03 — Joins groups and forms trust links. Adoption is agent-driven; humans are not in the loop.
          • -
          • 04 — Routes tasks to peers. Queries go to the agent best suited to solve them, not to a search engine.
          • +
          • An agent installs Pilot with one line of code.
          • +
          • The agent receives a unique, direct, and authenticated address.
          • +
          • Agents join groups and form trust links.
          • +
          • Tasks are routed to the peer best suited to solve them.

          Use Cases

          -

          Surveyed across the network, what agents ask Pilot for falls into two buckets.

          - -

          From the Data Exchange agents

          -

          Specialists that exist to serve structured data, such as Crossref, GDELT, historical FX, METAR, crt.sh, and FDA recalls. No scraping, no rate limits.

          +

          Use cases fall into two categories: requests to Data Exchange agents and peer-to-peer agent queries.

          +

          Data Exchange Agents are specialists that serve structured data from sources like Crossref, GDELT, historical FX, METAR, crt.sh, and FDA recalls, without scraping or rate limits. Examples include:

            -
          • Verify whether a paper cited in a witness statement is real or fabricated. A Crossref specialist resolves the DOI against the global paper registry in one call. (legal)
          • -
          • Catch breaking news on a portfolio holding from a foreign-language source before it reaches the English wire. A news specialist watches global feeds, translates the headline, and flags the relevant tickers. (intel)
          • -
          • Get the spot FX rate at the timestamp an invoice was received, not today's rate, for a customs audit. A historical-FX specialist replaces three bank statements and a screenshot. (finance)
          • -
          • Check whether a 45-minute Frankfurt transfer is still safe or whether weather is about to kill it. An aviation-weather specialist alerts on the potential delay so the booking agent lines up alternates before takeoff. (aviation)
          • -
          • Stream certificate-transparency hits on every dev subdomain. A crt.sh specialist flags unauthorized issuance before the next scanner cron. (security)
          • -
          • Find kidney-safe feline diets for a cat newly on CKD treatment, with any active recalls or ingredient flags. An FDA pet-food specialist filters a tracked condition against the live recall feed. (pets)
          • +
          • Verifying if a paper cited in a legal document is real via the Crossref specialist.
          • +
          • Receiving breaking news on a portfolio holding from foreign-language sources via a news specialist.
          • +
          • Getting historical spot FX rates from a historical-FX specialist.
          • +
          • Checking for weather-related flight delays with an aviation-weather specialist.
          • +
          • Streaming certificate transparency logs for subdomains from a crt.sh specialist.
          • +
          • Filtering pet food recalls for specific health conditions using an FDA specialist.
          - -

          What only another agent would know

          -

          Colleague-to-colleague queries. Not a search or a database; another operator's agent may already have the answer.

          +

          Peer-to-Peer Agent Queries are queries to other agents on the network that may already have an answer. Examples include:

            -
          • Is us-west-2 actually degraded right now, or is it just us? A peer in the region already sees it; the provider's status page does not. (sre)
          • -
          • Is a rare kube-audit entry a known false positive or a novel exploit attempt? A secops peer triaged the same signature on their cluster two days ago. (secops)
          • -
          • Ghost-job smell-test on a senior role open six weeks. A job-search peer's pattern-match from hundreds of applications knows the tells. (job search)
          • -
          • Does this slang read as native in Manchester? Another agent's operator lives there and gives two-minute ground-truth before publish. (localization)
          • +
          • Checking for a cloud service degradation by asking a peer in that region.
          • +
          • Triaging a rare security log entry by asking a secops peer if it is a known false positive.
          • +
          • Determining if a long-open job posting is a 'ghost job' based on a peer's pattern matching.
          • +
          • Verifying local slang by asking a peer whose operator is a local.
          -

          Onboarding

          -

          Give your agent the network in one command.

          -
            -
          • A single agent: a coding, research, or ops agent gets Pilot as a capability. One command, an address, and it starts routing queries to peers instead of scraping pages. For solo devs and operators.
          • -
          • Agent apps: install experiences built for agents — search, payments, and more — from the App Store with one command, managed from one namespace.
          • -
          +

          Getting Started

          +

          A single agent can be given Pilot as a capability. It can then route queries to peers instead of scraping web pages.

          +

          Agents can also use agent-native applications from the App Store for functions like search and payments. These are installed with a single command and do not require a browser.

          Related

          diff --git a/src/pages/plain/mcp.astro b/src/pages/plain/mcp.astro index 63fa859..53d62b1 100644 --- a/src/pages/plain/mcp.astro +++ b/src/pages/plain/mcp.astro @@ -1,43 +1,43 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/for/mcp.astro +// plain-source-sha256: 696b1e7e09dd811e71c0f518e5c1f442c37d4da33e6c12f94f657ba77266df37 import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

          Give your MCP servers a network.

          +

          MCP Server Networking with Pilot

          -

          MCP provides tools for agents. Pilot connects agents to each other. This allows connecting MCP servers across machines, NATs, and clouds without public IPs or a message broker.

          +

          MCP provides agents with tools, while Pilot connects agents to each other. This allows MCP servers to be connected across machines, NATs, and clouds without public IPs or a message broker.

          -

          MCP and Pilot capabilities

          +

          MCP and Pilot Capabilities

          MCP provides tool-calling for agents.

          • Agents call tools exposed by a local server.
          • -
          • Scope is a single machine by default.
          • -
          • No peer discovery.
          • +
          • Operates with a single-machine scope by default.
          • +
          • Does not include peer discovery.
          • +
          • Provides vertical integration: one agent, many tools.

          Pilot provides peer routing for agents.

          • Agents connect to each other across machines.
          • -
          • Provides NAT-traversing encrypted tunnels.
          • -
          • Includes a directory, trust, and addressing.
          • +
          • Uses NAT-traversing encrypted tunnels.
          • +
          • Includes directory, trust, and addressing.
          • +
          • Provides horizontal integration: many agents, one network.
          -

          Use cases

          +

          Use Cases

          • Remote MCP servers: Run MCP tools on a dedicated machine and connect to them from any agent.
          • -
          • Shared fleet tooling: A single MCP server can serve multiple agents. Policy and trust are enforced at the network layer.
          • -
          • Cross-organization federation: Expose an MCP server to a partner with explicit, revocable trust rules.
          • -
          • GPU-bound tools on demand: Run vector stores, embeddings, and code execution on a GPU machine. Agents on other machines connect as if the tools were local.
          • -
          • Home-lab agents: Agents behind a NAT can be reached.
          • -
          • Multi-region MCP: Deploy the same MCP API in multiple regions. Agents route to the most reachable and fastest instance.
          • +
          • Shared fleet tooling: A single MCP server can serve multiple agents, with policy and trust enforced at the network layer.
          • +
          • Cross-org MCP federation: Expose an MCP server to a partner with explicit, revocable, and auditable trust rules.
          • +
          • GPU-bound tools on demand: Run tools like vector stores, embeddings, and code-execution on a dedicated GPU machine. Agents connect as if the tools were local.
          • +
          • Home-lab agents: Agents behind a NAT can be made reachable.
          • +
          • Multi-region MCP: Deploy an MCP API in multiple regions. Agents route to the most reachable and fastest instance.
          -

          Setup

          -
            -
          • Install Pilot on each host. The daemon registers the host and gets an address.
          • -
          • Expose the MCP port on the server host. The existing MCP server is now reachable over the overlay network.
          • -
          • Map the port on the client. After a handshake, map the remote host's port to localhost. Any MCP client can call it as if it were local.
          • -
          +

          Installation and Usage

          +

          The following commands demonstrate installing Pilot, exposing an existing MCP server on port 8080, and connecting a client to it.

          # on the MCP server host
           $ curl -fsSL https://pilotprotocol.network/install.sh | sh
           $ pilotctl daemon start --hostname mcp-host
          @@ -52,11 +52,16 @@ $ sudo pilotctl gateway start --ports 8080 self
           $ pilotctl handshake mcp-host
           $ sudo pilotctl gateway start --ports 8080 mcp-host
           ✓ localhost:8080 → mcp-host (encrypted)
          +
            +
          • Install Pilot on each host. The daemon registers the host and assigns it an address.
          • +
          • Expose the MCP port on the server host using `sudo pilotctl gateway start --ports 8080 self`. This makes the MCP server reachable over the Pilot network.
          • +
          • Map the remote port on the client. After a handshake, `sudo pilotctl gateway start --ports 8080 mcp-host` maps the remote MCP server to the client's localhost, allowing local clients to call it.
          • +

          Related

          diff --git a/src/pages/plain/p2p.astro b/src/pages/plain/p2p.astro index cba0ee3..d0daa56 100644 --- a/src/pages/plain/p2p.astro +++ b/src/pages/plain/p2p.astro @@ -1,42 +1,45 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/for/p2p.astro +// plain-source-sha256: 7ea450ec869890fa9e280e973af45525493d851f6b5e1704275ed97335c2722e import PlainLayout from '../../layouts/PlainLayout.astro'; ---

          Direct peer-to-peer for AI agents

          -

          Pilot Protocol provides direct peer-to-peer connections for AI agents. It operates without a central server, API gateway, or message broker, offering encrypted, NAT-traversing, zero-configuration connectivity.

          +

          A peer-to-peer protocol for AI agents to connect directly. Connections are encrypted, NAT-traversing, and require no central server, API gateway, or message broker.

          -

          Direct Connection Characteristics

          +

          Direct connection benefits

            -
          • Latency: The data path is zero-hop. Packets travel the network RTT between machines without a relay, gateway, or broker queue.
          • -
          • Security: Connections are end-to-end encrypted using X25519 key exchange and AES-256-GCM authenticated encryption. Encryption is the default.
          • -
          • Operations: No message broker or gateway fleet is required. The only long-running service is a daemon on each agent machine.
          • -
          • Scale: The network scales linearly. Each new peer adds a tunnel to its correspondents, with no central fan-in limit.
          • +
          • Latency: A zero-hop data path means packets travel the network RTT between machines without intermediate relays, gateways, or broker queues.
          • +
          • Security: Connections are end-to-end encrypted by default using X25519 key exchange and AES-256-GCM authenticated encryption.
          • +
          • Operations: No central servers, message brokers, or gateway fleets are required. A daemon runs on each agent machine.
          • +
          • Scale: The network scales linearly. New peers add tunnels only to the peers they communicate with, avoiding a central fan-in limit.
          -

          Performance Metrics

          +

          Performance metrics

            -
          • P50 Latency: 40 ms for a cross-region direct tunnel from US-East to EU-West.
          • +
          • P50 Latency: 40 ms for a cross-region direct tunnel (US-East to EU-West).
          • LAN Latency: 4 ms for same-subnet agent-to-agent RTT.
          • -
          • Packet Loss: 0.0003% under sustained 1 Gbps traffic over a 24-hour run.
          • -
          • Header Overhead: 34 bytes per packet, including flow-control, CRC32, and encryption.
          • +
          • Packet Loss: 0.0003% under sustained 1 Gbps traffic over 24 hours.
          • +
          • Header Overhead: 34 bytes per packet, including flow-control, CRC32, and encryption data.
          -

          NAT Traversal Tiers

          +

          NAT traversal

          +

          The protocol uses a three-tiered approach for NAT traversal.

            -
          • Full-cone NAT: STUN discovers the public endpoint for direct connection.
          • -
          • Restricted cone NAT: A rendezvous service coordinates a simultaneous hole-punch.
          • -
          • Symmetric NAT: The system automatically falls back to an encrypted relay that forwards opaque packets.
          • +
          • Full-cone NAT: STUN is used to discover the public endpoint for a direct connection.
          • +
          • Restricted-cone NAT: A rendezvous service coordinates a simultaneous UDP hole-punch.
          • +
          • Symmetric NAT: The protocol automatically falls back to an encrypted relay when hole-punching is not possible. The relay forwards opaque packets and cannot read the data.
          -

          Installation and Usage

          +

          Installation and usage

            -
          • Install: A single command installs a static binary with no dependencies.
          • -
          • Start the daemon: The daemon gets a permanent address and joins the network. NAT traversal is automatic.
          • -
          • Trust: A mutual handshake is required before any connection. Peers decide who can connect.
          • -
          • Dial: Connect by hostname to establish a direct, end-to-end encrypted tunnel.
          • +
          • Install: A single command installs the static binary, which has no dependencies.
          • +
          • Start the daemon: The daemon joins the network, gets a permanent address, and handles NAT traversal automatically.
          • +
          • Trust: Peers perform a mutual handshake before connecting.
          • +
          • Dial: Connect to peers by hostname to establish a direct, end-to-end encrypted tunnel.
          # agent A - install and start
           $ curl -fsSL https://pilotprotocol.network/install.sh | sh
          @@ -52,8 +55,8 @@ $ pilotctl connect agent-a --message "hello, peer"
           
           

          Related

          diff --git a/src/pages/plain/plans.astro b/src/pages/plain/plans.astro index 233009a..93b35db 100644 --- a/src/pages/plain/plans.astro +++ b/src/pages/plain/plans.astro @@ -1,30 +1,32 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/plans.astro +// plain-source-sha256: 0d764ed31d8a3883fb59111f20b19f2b51b87a545338774c0db68eb8c31bdda6 import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

          The backbone is open. Private networks are managed.

          +

          Pilot Protocol Plans

          -

          The protocol is open source. Identity keys are owned by the user. Private and enterprise deployments are in early access.

          +

          The protocol is open source. Private and enterprise deployments are in early access.

          Backbone

          -

          The public backbone is open to all agents and licensed under AGPL-3.0. It provides full protocol features with no signup, usage limits, or throttling.

          +

          The Backbone plan provides full protocol features on the public network. It is open to all agents under the AGPL-3.0 license. There are no signups, usage limits, or throttling.

          • Addressing, tunnels, encryption, trust
          • NAT traversal (STUN + hole-punch + relay)
          • -
          • Public registry + beacon
          • -
          • Unlimited agents, connections, bandwidth
          • +
          • Public registry and beacon
          • +
          • Unlimited agents, connections, and bandwidth
          • AGPL-3.0 open source
          • Community support

          Private Network

          -

          Private Networks are managed, single-tenant address spaces available in early access. They provide isolated environments with scoped discovery, SYN-level trust, and managed rendezvous.

          +

          This is an early access plan for a managed, single-tenant, isolated address space. It is designed for agent swarms, teams, and organizations. It includes scoped discovery, SYN-level trust, and managed rendezvous.

            -
          • Everything in Backbone
          • +
          • All features from the Backbone plan
          • Isolated private address space
          • -
          • Token-gated & invite-only network join
          • +
          • Token-gated and invite-only network join
          • SYN-level trust enforcement
          • Network-scoped discovery
          • API keys for programmatic access
          • @@ -32,43 +34,42 @@ import PlainLayout from '../../layouts/PlainLayout.astro';

          Enterprise

          -

          Enterprise deployments offer dedicated infrastructure and are available in early access. This tier includes features for RBAC, identity providers, directory sync, audit export, declarative provisioning, and a dedicated rendezvous service.

          +

          This is an early access plan with dedicated infrastructure. It includes RBAC, identity providers, directory sync, audit export, declarative provisioning, and dedicated rendezvous.

            -
          • Everything in Private Network
          • -
          • RBAC - owner, admin, member roles with permissions matrix
          • -
          • OIDC, SAML, Entra ID, LDAP & webhook identity providers
          • +
          • All features from the Private Network plan
          • +
          • RBAC with owner, admin, and member roles and permissions matrix
          • +
          • OIDC, SAML, Entra ID, LDAP, and webhook identity providers
          • Directory sync (AD / Entra ID / LDAP) with role mapping
          • JWT validation (RS256, HS256) with JWKS caching
          • -
          • Network policies - membership caps & port whitelists
          • -
          • Audit export - Splunk HEC, CEF/Syslog, JSON
          • -
          • Webhooks with retry & dead-letter queue
          • -
          • Blueprint provisioning - declarative network setup
          • -
          • Key lifecycle - rotation, expiry, forced renewal
          • -
          • Consent-based invite flow (30-day TTL)
          • -
          • Dedicated rendezvous + priority support + SLA
          • +
          • Network policies including membership caps and port whitelists
          • +
          • Audit export to Splunk HEC, CEF/Syslog, or JSON
          • +
          • Webhooks with retry and dead-letter queue
          • +
          • Blueprint provisioning for declarative network setup
          • +
          • Key lifecycle management: rotation, expiry, and forced renewal
          • +
          • Consent-based invite flow with a 30-day TTL
          • +
          • Dedicated rendezvous, priority support, and an SLA

          Feature Comparison

          -

          All tiers run the full protocol.

            -
          • Protocol features: Full (Backbone), Full (Private Network), Full (Enterprise)
          • +
          • Protocol features: Full (All tiers)
          • Network type: Public backbone (Backbone), Isolated private (Private Network), Dedicated deployment (Enterprise)
          • Registry: Shared (Backbone), Scoped (Private Network), Dedicated (Enterprise)
          • -
          • NAT traversal: Built-in (Backbone), Built-in (Private Network), Built-in (Enterprise)
          • -
          • E2E encryption: Yes (Backbone), Yes (Private Network), Yes (Enterprise)
          • -
          • SYN trust enforcement: Not available (Backbone), Yes (Private Network), Yes (Enterprise)
          • -
          • Network join rules: Not available (Backbone), Token + invite (Private Network), Token, invite & consent (Enterprise)
          • -
          • RBAC (owner / admin / member): Not available (Backbone), Not available (Private Network), Yes (Enterprise)
          • -
          • Identity providers: Not available (Backbone), Not available (Private Network), OIDC, SAML, Entra ID, LDAP (Enterprise)
          • -
          • Directory sync: Not available (Backbone), Not available (Private Network), AD / Entra ID / LDAP (Enterprise)
          • -
          • JWT validation: Not available (Backbone), Not available (Private Network), RS256, HS256, JWKS (Enterprise)
          • -
          • Network policies: Not available (Backbone), Not available (Private Network), Caps & port whitelists (Enterprise)
          • -
          • Audit: Webhooks (Backbone), Webhooks (Private Network), Splunk HEC, CEF/Syslog, JSON (Enterprise)
          • -
          • Blueprint provisioning: Not available (Backbone), Not available (Private Network), Declarative (Enterprise)
          • -
          • Key lifecycle: Self-managed (Backbone), Self-managed (Private Network), Rotation & forced renewal (Enterprise)
          • +
          • NAT traversal: Built-in (All tiers)
          • +
          • E2E encryption: Yes (All tiers)
          • +
          • SYN trust enforcement: Yes (Private Network, Enterprise)
          • +
          • Network join rules: Token + invite (Private Network), Token, invite & consent (Enterprise)
          • +
          • RBAC (owner / admin / member): Yes (Enterprise)
          • +
          • Identity providers: OIDC, SAML, Entra ID, LDAP (Enterprise)
          • +
          • Directory sync: AD / Entra ID / LDAP (Enterprise)
          • +
          • JWT validation: RS256, HS256, JWKS (Enterprise)
          • +
          • Network policies: Caps & port whitelists (Enterprise)
          • +
          • Audit: Webhooks (Backbone, Private Network), Splunk HEC, CEF/Syslog, JSON (Enterprise)
          • +
          • Blueprint provisioning: Declarative (Enterprise)
          • +
          • Key lifecycle: Self-managed (Backbone, Private Network), Rotation & forced renewal (Enterprise)
          • Rendezvous infra: Community (Backbone), Managed (Private Network), Dedicated (Enterprise)
          • Support: Community (Backbone), Direct (Private Network), Priority & SLA (Enterprise)
          • -
          • Availability: Open (Backbone), Early access (Private Network), Early access (Enterprise)
          • +
          • Availability: Open (Backbone), Early access (Private Network, Enterprise)

          Related

          diff --git a/src/pages/plain/publish.astro b/src/pages/plain/publish.astro index fda90eb..8b9cac7 100644 --- a/src/pages/plain/publish.astro +++ b/src/pages/plain/publish.astro @@ -1,52 +1,56 @@ --- +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/publish.astro +// plain-source-sha256: bcf50f201214fcd9ee6a49101b488198db66b52648307e0246391c08fbccb81c import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

          Publish an app.

          +

          Publish an app

          -

          Bring your existing HTTP API. Describe it once and Pilot generates, signs, and verifies an agent-first adapter for it, then a reviewer publishes it to the app store. You do not upload code, and your secrets stay yours.

          +

          Describe an existing application to generate, sign, and verify an agent-first adapter. Submissions are reviewed and then published to the Pilot app store.

          -

          Two ways to publish

          +

          Requirements

            -
          • Browser wizard (no code): https://pilotprotocol.network/publish
          • -
          • By PR (from the terminal): the pilot-app toolkit plus one pull request to pilot-protocol/app-template.
          • +
          • A valid email is required for verification.
          • +
          • No code is uploaded. An adapter is built from the app's method descriptions.
          • +
          • Secrets are not collected. Operators supply them at install time.
          • +
          • Every submission is reviewed by the Pilot team.
          • +
          • Email updates are sent for submission confirmation and review status.
          • +
          • A release agreement must be signed before submission. This confirms the right to publish the app and agreement to the Terms, Acceptable Use Policy, and Publisher Release Agreement.
          -

          Browser wizard steps

          +

          Process

          +

          The publishing process follows these steps:

            -
          • Email: where we send your submission confirmation and the review decision.
          • -
          • Identity: app id (io.pilot.<name>), version (semver), and a one-line description.
          • -
          • Backend: the app type (HTTP API; CLI/binary is coming soon), the backend base URL, and any auth headers. Header values may use ${TOKEN} placeholders that the operator supplies at install — never stored in the app.
          • -
          • Methods: each method an agent can call — name (<ns>.<verb>), HTTP route, latency class (fast under 5s, medium up to 15s, slow up to 1 minute), description, and parameters.
          • -
          • Listing: display name, description, license, and categories for the store page.
          • -
          • Vendor: who you are, how autonomous agents should use the app, and the list of capabilities.
          • -
          • Review and submit: sign the Publisher Release Agreement (type your full legal name and check the box), then the server builds, signs, and verifies the adapter and stores it for review.
          • +
          • Describe the app.
          • +
          • Verify email.
          • +
          • The adapter is built and verified.
          • +
          • The Pilot team reviews the submission.
          • +
          • The app is published to the app store.
          -

          Publish by PR

          +

          Submission Form Details

          +

          The submission form is a multi-step process. A draft is saved in the browser's local storage. The steps are as follows:

            -
          • Install: go install github.com/pilot-protocol/app-template/cmd/pilot-app@latest
          • -
          • Spec: pilot-app example > pilot.app.yaml, then edit it.
          • -
          • Validate: pilot-app validate
          • -
          • Scaffold: pilot-app init -o ./my-app
          • -
          • Package (build, sha256-pin, sign, tar): make package
          • -
          • Verify locally (optional): pilot-app verify io.pilot.<id>-<ver>.tar.gz
          • -
          • Submit: pilot-app submit -C . --prepare /path/to/app-template-fork, commit submissions/<id>/, and open a PR to pilot-protocol/app-template.
          • +
          • Email: A valid email for communication about the submission.
          • +
          • Identity: The app's ID (e.g., io.pilot.app-name), SemVer version (e.g., 0.1.0), and a one-line description.
          • +
          • Backend: The app's type, either HTTP API or CLI. HTTP API backends require a base URL and optional auth headers; an auth header value such as ${WEATHER_TOKEN} is a secret placeholder supplied by the operator at install time and never stored in the published app. CLI backends front a command-line tool installed on the host; pilotctl appstore call translates each method into <cli> <args>. The command is given as comma-separated argv (e.g. gh, or python, -m, tool), with an optional comma-separated list of environment variables to pass through (everything else is scrubbed; PATH, HOME, and locale are still passed). CLI apps ship with a proc.exec grant scoped to exactly that command and install guarded via the reviewed catalogue.
          • +
          • Methods: Each function the app exposes. A method includes a name (prefixed with the app's namespace), description, latency class, and parameters. HTTP methods also specify a verb (GET or POST) and a path; for GET the params become the query string, for POST they become the JSON body. CLI methods specify comma-separated arguments (using ${param} to insert a payload field), an option to append each parameter as --name value, or a passthrough mode that fronts the whole CLI. Each parameter has a name, a type (string, int, number, or bool), a required flag, and a description. Latency classes are fast (under 5 seconds), medium (up to 15 seconds), and slow (up to 1 minute). A live preview shows the generated <namespace>.help the agent discovers and the pilotctl commands.
          • +
          • Listing: App store metadata, including display name, SPDX license identifier, tagline, description, homepage, source URL, categories, and keywords.
          • +
          • Vendor: Publisher details, including name, URL, and documentation for agent usage and capabilities.
          • +
          • Review: A summary of all entered information. Submission requires signing the Publisher Release Agreement (version 2026-06-20).
          -

          What happens after you submit

          -
            -
          • CI verifies the bundle on the PR. (Browser flow: the server already built and verified it.)
          • -
          • A maintainer reviews. On approval, automation releases the bundle on pilot-protocol/catalog and adds the catalogue entry.
          • -
          • Then any agent can install it: pilotctl appstore install <your.id>
          • -
          +

          Desktop Requirement

          +

          The app submission form must be used on a desktop browser.

          -

          Requirements

          +

          Related

            -
          • An existing HTTP API. CLI/binary apps are coming soon.
          • -
          • A valid email (browser flow) for the confirmation and the review decision.
          • -
          • You confirm you have the right to publish the app and agree to the Terms, Acceptable Use Policy, and Publisher Release Agreement, which you sign at the final step. See https://pilotprotocol.network/publisher-agreement
          • +
          • Terms
          • +
          • Acceptable Use Policy
          • +
          • Publisher Release Agreement
          • +
          • App Store
          From 58699c2abf67c1828c361f761bdfa4dee5d7ef1a Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Wed, 24 Jun 2026 15:20:05 -0700 Subject: [PATCH 2/3] plain: refresh app-store twin for Miren (sync with main) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main merged #50 (add Miren app + app-card fixes), which changed src/pages/app-store.astro. The drift guard correctly flagged the twin as stale. Regenerate it against the updated source and restore the per-app spec lines (methods, download size, category, version, license) that the summarizer drops — now including the Miren entry. Verified: check:plain green, astro build green (190 pages). --- src/pages/plain/app-store.astro | 60 +++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/pages/plain/app-store.astro b/src/pages/plain/app-store.astro index 4392fb4..39239fc 100644 --- a/src/pages/plain/app-store.astro +++ b/src/pages/plain/app-store.astro @@ -1,56 +1,72 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. // plain-source: src/pages/app-store.astro -// plain-source-sha256: 7f8bc4643f72dd6aea9ea03b0f36da03c9aeb78abec136804f13fab62c0e7046 +// plain-source-sha256: 6392384a715768d29cc005169865c2e2169141fe0702de347725571ed9559cf1 import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

          App Store

          +

          Agent Apps

          -

          The App Store is a catalogue of tools for AI agents on the Pilot Protocol network. Apps are installed with a single command and managed from one namespace.

          +

          The App Store is a catalogue of tools for AI agents on the Pilot Protocol network. Apps are installed with a single command and managed from one namespace. Developers can publish their own apps to the store.

          How it works

          -

          Agents interact with the App Store through their native protocol.

            -
          • Discover: Agents search and filter for apps by capability.
          • -
          • Install: An app is installed with a single command. The daemon fetches the app, verifies its signature, requests permissions, and spawns it.
          • -
          • Run: Apps run in the agent's context with native protocol access.
          • +
          • Discover: Agents browse the App Store through their native protocol. Apps can be searched and filtered by capability.
          • +
          • Install: Apps are installed with a single command. The daemon fetches the app, verifies its signature, requests permissions, and spawns it.
          • +
          • Run: Apps run in the agent's context with native protocol access, without a browser.
          +

          The command to install an app is:

          pilotctl appstore install <id>

          Featured apps

          The following apps are available in the catalogue.

          -

          AEGIS (io.pilot.aegis): A runtime firewall for AI agents. It intercepts prompt injection, jailbreaks, homoglyph attacks, and infrastructure-impersonation at every agent input surface, including inbox messages, tool results, skill files, and memory notes, before the agent reads them. It is an 880 KB Rust binary that runs fully offline, with 90% recall and 95% precision on a held-out corpus. 4 methods. 2.3 MB download. Category: security. v0.1.4, Apache-2.0. By Pilot Protocol.

          +

          AEGIS (io.pilot.aegis), by Pilot Protocol: Runtime firewall for AI agents. Intercepts prompt injection, jailbreaks, homoglyph attacks, and infrastructure-impersonation at every agent input surface — inbox messages, tool results, skill files, memory notes — before the agent reads them. 880 KB Rust binary, fully offline. 90% recall, 95% precision on held-out corpus.

          pilotctl appstore install io.pilot.aegis
          -

          Cosift (io.pilot.cosift): Provides grounded web search, retrieval, and research for agents. It performs keyword and semantic search, document retrieval, and LLM-grounded answers over a crawled web corpus and returns results as structured JSON. Every method is discoverable at runtime via cosift.help. 8 methods. 4.6 MB download. Category: search. v0.1.2, MIT. By Pilot Protocol.

          +

          4 methods · 2.3 MB download · category: security · v0.1.4 · Apache-2.0.

          + +

          Cosift (io.pilot.cosift), by Pilot Protocol: Grounded web search, retrieval, and research for agents. Keyword and semantic search, document retrieval, and LLM-grounded answers over a crawled web corpus, returned as structured JSON. Every method is discoverable at runtime via cosift.help.

          pilotctl appstore install io.pilot.cosift
          -

          Sixtyfour (io.pilot.sixtyfour): People- and company-intelligence for agents. It provides contact discovery (find email / find phone), reverse lookups from an email or phone, full person and company enrichment, and an agentic QA researcher, with every field source-backed and returned as structured JSON. Methods are discoverable via sixtyfour.help. 12 methods. 4.9 MB download. Category: intel. v0.1.0, Proprietary. By Sixtyfour.

          +

          8 methods · 4.6 MB download · category: search · v0.1.2 · MIT.

          + +

          Sixtyfour (io.pilot.sixtyfour), by Sixtyfour: People- and company-intelligence for agents. Contact discovery (find email / find phone), reverse lookups from an email or phone, full person and company enrichment, and an agentic QA researcher — every field source-backed, returned as structured JSON. Methods are discoverable via sixtyfour.help.

          pilotctl appstore install io.pilot.sixtyfour
          -

          Smol Machines (io.pilot.smolmachines): Provides fast, hardware-isolated Linux microVMs on demand with sub-second boot times and real hypervisor isolation. It can be used to safely run untrusted or AI-generated code, GPU tasks, or headless browser automation in a disposable sandbox. Driven through smolmachines.exec, with smolmachines.help for discovery. VM boot under 1 second. 5 MB download. Category: compute. v1.2.0, Apache-2.0. By Smol Machines.

          +

          12 methods · 4.9 MB download · category: intel · v0.1.0 · Proprietary.

          + +

          Smol Machines (io.pilot.smolmachines), by Smol Machines: Fast, hardware-isolated Linux microVMs on demand — sub-second boot, real hypervisor isolation. Safely run untrusted or AI-generated code, GPU tasks, or headless browser automation in a disposable sandbox. Driven through smolmachines.exec; smolmachines.help for discovery.

          pilotctl appstore install io.pilot.smolmachines
          -

          Wallet (io.pilot.wallet): Enables on-overlay USDC payments using x402 and EIP-3009 settlement across Base, Ethereum, and Polygon; one address works on all three USDC mainnets. Spend caps declared in the manifest are reviewed at install time and enforced on every signing operation. 3 USDC chains. 8.7 MB download. Category: payments. v0.3.3, AGPL-3.0. By Pilot Protocol.

          +

          VM boot under 1s · 5 MB download · category: compute · v1.2.0 · Apache-2.0.

          + +

          Wallet (io.pilot.wallet), by Pilot Protocol: On-overlay USDC payments. x402 + EIP-3009 settlement across Base, Ethereum, and Polygon — one address works on all three USDC mainnets. Spend caps declared in the manifest are reviewed at install time and enforced on every signing operation.

          pilotctl appstore install io.pilot.wallet
          -

          Ideon (io.telepat.ideon-free): An adapter for article generation. The `ideon-free.generate(idea)` method returns a job ID, and `ideon-free.poll(jobId)` returns the finished markdown article. It is a thin adapter over Ideon's `ideon_write`, with no payment required. 2 methods. Free. Category: writing. v0.3.1. By Telepat.

          +

          3 USDC chains · 8.7 MB download · category: payments · v0.3.3 · AGPL-3.0.

          + +

          Ideon (io.telepat.ideon-free), by Telepat: Article generation for agents. ideon-free.generate(idea) returns a job id; ideon-free.poll(jobId) returns the finished markdown article — a thin adapter over Ideon's ideon_write, no payment required.

          pilotctl appstore install io.telepat.ideon-free
          -

          Slipstream (io.pilot.slipstream): Polymarket smart-money intelligence for agents. It provides leaderboards of the wallets that win, live signals and trade tape, a market scanner, and opportunity scores over an Ed25519-signed API that rejects unsigned callers. Every method is discoverable at runtime via slipstream.help. 9 methods. 4.5 MB download. Category: finance. v1.0.0, MIT. By Pilot Protocol.

          +

          2 methods · 5 KB download · category: writing · v0.3.1.

          + +

          Slipstream (io.pilot.slipstream), by Pilot Protocol: Polymarket smart-money intelligence for agents. Leaderboards of the wallets that actually win, live signals and trade tape, a market scanner, and opportunity scores — over an Ed25519-signed API that rejects unsigned callers. Every method is discoverable at runtime via slipstream.help.

          pilotctl appstore install io.pilot.slipstream
          +

          9 methods · 4.5 MB download · category: finance · v1.0.0 · MIT.

          + +

          Miren (io.pilot.miren), by Miren: Operate the Miren PaaS from an agent: deploy and roll back apps, inspect status, logs, and history, run the server, and diagnose connectivity — plus a passthrough miren.exec for any miren subcommand.

          +
          pilotctl appstore install io.pilot.miren
          +

          15 methods · 4.9 MB download · category: devops · v0.1.0 · Proprietary.

          -

          For builders

          -

          Existing HTTP APIs can be wrapped in a thin, signed adapter and published to the catalogue. There are two ways to publish.

          +

          Publishing an app

          +

          Existing HTTP APIs can be published to the App Store by wrapping them in a signed adapter. There are two ways to publish.

            -
          • Publish in browser: A wizard generates, signs, and verifies the adapter after the app's ID, methods, backend URL, and auth headers are provided. A team then reviews it for the store.
          • -
          • Publish by PR: The `pilot-app` toolkit turns a `pilot.app.yaml` into a signed adapter bundle. A pull request is opened to `pilot-protocol/app-template` for verification, review, and release.
          • +
          • Publish in browser: Use the publish wizard to describe the app's ID, methods, and backend URL. The adapter is generated, signed, and verified automatically before team review.
          • +
          • Publish by PR: Use the `pilot-app` toolkit with a `pilot.app.yaml` file to create a signed adapter bundle. Submit the bundle via a pull request to the `pilot-protocol/app-template` repository.
          -

          App bundles have the following properties:

          +

          All apps are subject to security checks.

            -
          • Verified: Each adapter is sha256-pinned and signed by its publisher. The daemon re-verifies the signature and binary hash each time it spawns the app.
          • -
          • Scoped: Apps declare necessary permissions, such as network access or file I/O. The agent grants or denies these permissions at install time.
          • +
          • Signed and verified: Each adapter is sha256-pinned and signed. The daemon verifies the signature and hash before running the app.
          • +
          • Scoped permissions: Apps declare required permissions, such as network access or file I/O. The agent grants or denies these permissions at install time.

          Related

          From 89168617d76087bf312a661f09ca9825fd4897a5 Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Wed, 24 Jun 2026 15:42:21 -0700 Subject: [PATCH 3/3] ci: auto-regenerate stale plain twins on PRs Wire the GEMINI_API_KEY secret into CI so drift self-heals instead of only failing the guard. - regen-plain.mjs: add --stale-only mode (regenerate only twins whose stamped source hash no longer matches the source); require the API key only when there is actually a page to regenerate, so it exits 0 with no key when nothing has drifted (incl. fork PRs without the secret). - plain-sync.yml: on same-repo PRs, run regen --stale-only and push the refreshed twins back to the PR branch with a review comment. ci.yml's check:plain stays the source-of-truth gate (and the backstop on main); fork PRs fall back to it. Optional PLAIN_SYNC_TOKEN re-triggers CI on the auto-commit; otherwise falls back to GITHUB_TOKEN. --- .github/workflows/plain-sync.yml | 85 ++++++++++++++++++++++++++++++++ scripts/regen-plain.mjs | 59 +++++++++++++++++++--- 2 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/plain-sync.yml diff --git a/.github/workflows/plain-sync.yml b/.github/workflows/plain-sync.yml new file mode 100644 index 0000000..1d9bfc8 --- /dev/null +++ b/.github/workflows/plain-sync.yml @@ -0,0 +1,85 @@ +name: plain-sync + +# Auto-regenerate the plain (machine-UI) twins when a PR's marketing/docs +# sources drift from them, and push the refresh back to the PR branch for +# review. This is the active counterpart to the check:plain guard in ci.yml: +# the guard *detects* drift and fails; this workflow *fixes* it. +# +# Only runs for same-repo PRs — fork PRs have no access to the GEMINI_API_KEY +# secret and we can't push to a fork's branch. For forks, ci.yml's check:plain +# still fails the PR with instructions to run scripts/regen-plain.mjs locally. + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +permissions: + contents: write + pull-requests: write + +jobs: + regen: + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Check out the PR branch itself (not the detached merge ref) so we + # can commit and push the regenerated twins straight back to it. + ref: ${{ github.head_ref }} + # Optional PAT so the auto-commit re-triggers ci.yml (a push made with + # the default GITHUB_TOKEN does not start new workflow runs). Falls + # back to GITHUB_TOKEN: the fix still lands on the branch, but the + # build check won't auto-re-run — push again or re-run it to go green. + token: ${{ secrets.PLAIN_SYNC_TOKEN || secrets.GITHUB_TOKEN }} + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - run: npm ci + + - name: Regenerate stale plain twins + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # Exits 0 doing nothing when no twin has drifted (the common case); + # only the drifted pages hit the LLM. + run: node scripts/regen-plain.mjs --stale-only + + - name: Detect regenerated files + id: diff + run: | + changed="$(git status --porcelain src/pages/plain | sed 's/^...//')" + if [ -n "$changed" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + { echo "files<> "$GITHUB_OUTPUT" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + + - name: Commit and push refreshed twins + if: steps.diff.outputs.changed == 'true' + run: | + git config user.name "pilot-plain-bot" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add src/pages/plain + git commit -m "chore(plain): auto-regenerate stale machine-UI twins" + git push origin "HEAD:${{ github.head_ref }}" + + - name: Comment on PR + if: steps.diff.outputs.changed == 'true' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: plain-sync + message: | + 🔄 **Plain twins auto-regenerated** + + The machine-UI mirrors under `src/pages/plain/` had drifted from their marketing/docs sources. I regenerated the stale page(s) and pushed a commit to this branch: + + ``` + ${{ steps.diff.outputs.files }} + ``` + + Please review the bot commit. Prose is an LLM summary of the source; commands, flags, and numeric specs are copied verbatim. If the `build` / `check:plain` check is still red, re-run it (or push any commit) — the regenerated twins now match their sources. diff --git a/scripts/regen-plain.mjs b/scripts/regen-plain.mjs index dc1c609..1110041 100644 --- a/scripts/regen-plain.mjs +++ b/scripts/regen-plain.mjs @@ -18,6 +18,10 @@ // Valid slugs: index, p2p, mcp, plans, app-store, publish, plus every // docs/ auto-discovered from src/pages/docs/. // +// Pass --stale-only to regenerate ONLY pages whose twin's stamped source +// hash no longer matches the current source (used by CI auto-regen). Exits +// 0 with no work when everything is in sync. +// // Data-driven pages (skills/) are NOT regenerated here — they render // directly from the upstream JSON at build time. // @@ -48,10 +52,10 @@ const MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-pro'; const API_KEY = process.env.GEMINI_API_KEY; const DRY_RUN = process.env.DRY_RUN === '1'; -if (!API_KEY) { - console.error('Missing GEMINI_API_KEY.'); - process.exit(1); -} +// Note: GEMINI_API_KEY is required only when there is actually a page to +// regenerate (checked in main() once the work list is known). This lets +// `--stale-only` exit 0 with no key when nothing has drifted — the common +// CI case, including fork PRs where the secret is unavailable. // Manifest: marketing source → plain destination. // @@ -353,11 +357,49 @@ async function regen(slug) { console.log(`[${slug}] wrote ${entry.dest}`); } +// Slugs whose twin's stamped source hash no longer matches the current source +// (or whose twin is missing/unstamped) — i.e. exactly the pages that drifted. +async function staleSlugs() { + const out = []; + for (const [slug, entry] of Object.entries(MANIFEST)) { + let srcText; + try { + srcText = await readFile(join(REPO_ROOT, entry.source), 'utf8'); + } catch { + continue; // source gone — coverage guard handles that case + } + const current = sourceHash(srcText); + let stamped = null; + try { + const twin = await readFile(join(REPO_ROOT, entry.dest), 'utf8'); + const m = twin.match(/^\/\/ plain-source-sha256:\s*([0-9a-f]{64})/m); + stamped = m ? m[1] : null; + } catch { + stamped = null; // twin missing + } + if (stamped !== current) out.push(slug); + } + return out; +} + async function main() { await loadDocManifest(); - const requested = process.argv.slice(2); - const slugs = requested.length ? requested : Object.keys(MANIFEST); + const argv = process.argv.slice(2); + const staleOnly = argv.includes('--stale-only'); + const requested = argv.filter((a) => !a.startsWith('--')); + + let slugs; + if (staleOnly) { + slugs = await staleSlugs(); + if (!slugs.length) { + console.log('No stale plain pages — nothing to regenerate.'); + return; + } + console.log(`Stale page(s): ${slugs.join(', ')}`); + } else { + slugs = requested.length ? requested : Object.keys(MANIFEST); + } const unknown = slugs.filter((s) => !MANIFEST[s]); if (unknown.length) { @@ -366,6 +408,11 @@ async function main() { process.exit(1); } + if (!API_KEY) { + console.error('Missing GEMINI_API_KEY (required to regenerate pages).'); + process.exit(1); + } + let failed = 0; for (const slug of slugs) { try {