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/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..1110041 100644 --- a/scripts/regen-plain.mjs +++ b/scripts/regen-plain.mjs @@ -15,7 +15,12 @@ // 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/. +// +// 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. @@ -28,9 +33,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, '..'); @@ -38,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. // @@ -80,6 +94,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 +286,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 +315,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 +345,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`); @@ -327,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) { @@ -340,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 { diff --git a/src/pages/plain/app-store.astro b/src/pages/plain/app-store.astro index 5a2c175..e1a3e9f 100644 --- a/src/pages/plain/app-store.astro +++ b/src/pages/plain/app-store.astro @@ -1,55 +1,68 @@ --- +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/app-store.astro +// plain-source-sha256: 2367175d81b5f103bc6b24099b125d781bc0808708ead91cd2a967e6d54acf0e import PlainLayout from '../../layouts/PlainLayout.astro'; --- -

The Pilot App Store.

+

Agent 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 applications 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.

-

Install and use an app

+

How it works

    -
  • 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>'
  • +
  • Agents browse the App Store through the native protocol to search and filter apps by capability.
  • +
  • Apps are installed with a single command. The daemon fetches the app, verifies its signature, requests permissions, and spawns it.
  • +
  • Apps run in the agent's context with native protocol access.
+
pilotctl appstore install <id>
-

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):

-
    -
  • 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.
  • -
+

Featured apps

+

The following apps are available in the catalogue.

+

AEGIS (io.pilot.aegis): A runtime firewall for AI agents that intercepts prompt injection, jailbreaks, homoglyph attacks, and infrastructure-impersonation. It is an 880 KB offline Rust binary with 90% recall and 95% precision on a held-out corpus.

+
pilotctl appstore install io.pilot.aegis
+

4 methods · 2.3 MB download · category: security · v0.1.4 · Apache-2.0.

+

Cosift (io.pilot.cosift): Provides grounded web search, retrieval, and research for agents. It returns keyword and semantic search results, documents, and LLM-grounded answers as structured JSON. Methods are discoverable via `cosift.help`.

+
pilotctl appstore install io.pilot.cosift
+

8 methods · 4.6 MB download · category: search · v0.1.2 · MIT.

+

Sixtyfour (io.pilot.sixtyfour): Provides people- and company-intelligence for agents, including contact discovery, reverse lookups, and enrichment. Data is source-backed and returned as structured JSON. Methods are discoverable via `sixtyfour.help`.

+
pilotctl appstore install io.pilot.sixtyfour
+

12 methods · 4.9 MB download · category: intel · v0.1.0 · Proprietary.

+

Smol Machines (io.pilot.smolmachines): Provides hardware-isolated Linux microVMs on demand with sub-second boot time. Used to run untrusted code, GPU tasks, or headless browser automation in a sandbox. Driven through `smolmachines.exec`.

+
pilotctl appstore install io.pilot.smolmachines
+

VM boot under 1s · 5 MB download · category: compute · v1.2.0 · Apache-2.0.

+

Wallet (io.pilot.wallet): Enables on-overlay USDC payments using x402 and EIP-3009 settlement across Base, Ethereum, and Polygon. Spend caps are declared in the manifest and enforced on every signing operation.

+
pilotctl appstore install io.pilot.wallet
+

3 USDC chains · 8.7 MB download · category: payments · v0.3.3 · AGPL-3.0.

+

Ideon (io.telepat.ideon-free): An adapter for article generation. `ideon-free.generate(idea)` returns a job ID, and `ideon-free.poll(jobId)` returns the finished markdown article.

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

2 methods · 5 KB download · category: writing · v0.3.1.

+

Slipstream (io.pilot.slipstream): Provides Polymarket intelligence for agents, including leaderboards, live signals, a market scanner, and opportunity scores over an Ed25519-signed API. Methods are discoverable 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): Operates the Miren PaaS from an agent. Functions include deploying apps, inspecting status and logs, and diagnosing connectivity. Includes 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.

+

Plainweb (io.pilot.plainweb): Retrieves any web page as plain text. The `plainweb.fetch(url)` call fetches the page and returns it as GFM Markdown.

+
pilotctl appstore install io.pilot.plainweb
+

1 method · 4.8 MB download · category: web · v1.0.0 · Proprietary.

+

Otto (io.pilot.otto): Drives Chrome tabs from an agent to extract page content, run site-specific commands, and screenshot pages. Operates via a relay to a browser extension. Includes a passthrough `otto.exec` for any otto subcommand.

+
pilotctl appstore install io.pilot.otto
+

15 methods · 27 MB download · category: browser · v0.20.0 · MIT.

-

PR (from the terminal):

+

Publishing an app

+

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

    -
  • 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.
  • +
  • Publish in a browser: Use the publish wizard to describe the app's ID, methods, backend URL, and auth headers. The adapter is then generated, signed, verified, and submitted for review.
  • +
  • Publish by pull request: 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 for verification and release.
+

Every adapter is sha256-pinned and signed by its publisher. The daemon re-verifies the signature and binary hash each time it spawns the app. Apps declare required permissions, such as network access or file I/O, which the agent grants or denies 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 Documentation
diff --git a/src/pages/plain/docs/app-store.astro b/src/pages/plain/docs/app-store.astro index 1ef8f37..e0af982 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: da5b4e303fb041e84b7a077acb298bf11af19e052700d89b3bb4fac6a1338921 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 agent apps. Apps are binaries that run locally on the daemon as typed IPC services, are auto-spawned on install, and are invoked via a discover, install, and call process.

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:

+

An app consists of a binary and a signed manifest.json. The daemon fetches, verifies, and supervises the app. It spawns the binary, provides a unix socket, and brokers IPC calls to it. 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 and 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 the app's required permissions, which the user accepts at install time.
  • +
  • Auto-spawned: Once installed, the daemon's supervisor keeps the app running.
+

The agent lifecycle 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 are handled through a signed catalogue fetched by the daemon. Installation verifies the bundle, and the daemon auto-spawns it. The `call` command is then used to interact with the app.

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

`view <id>` is the app's detail page — it works whether or not the app is installed, so you can vet description, vendor, latest changelog (`--all-changelog` for full history), size, source URL, license, exposed methods, and requested permissions before installing.

-

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

+

`view <id>` shows an app's details, whether installed or not. It displays the description, vendor, changelog, size, source URL, license, methods, and requested permissions. Use `--all-changelog` for the full history.

+

Apps ship with default configurations. An optional `config.json` file can be placed next to the manifest 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

+

The `list` and `status` commands show method names. A convention for richer discovery is a `<app>.help` method. This local call returns every method with its parameters, a `kind` (utility / status / meta), and an expected-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 an agent choose a method 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 single-pass synthesis.
  • +
  • slow: ~5-30s, for multi-step work like research.
-

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 entry also includes a measured, warm round-trip time estimate.

Lifecycle

-
pilotctl appstore restart io.pilot.cosift          # respawn (e.g. after writing a config.json)
-pilotctl appstore caps io.pilot.cosift             # spend caps + current rolling-window usage
-pilotctl appstore audit io.pilot.cosift            # supervisor log: spawn / exit / verify-fail
-pilotctl appstore actions                          # pilotctl-side install/uninstall log (survives removal)
+
pilotctl appstore restart io.pilot.cosift    # respawn (e.g. after writing a config.json)
+pilotctl appstore caps io.pilot.cosift       # spend caps + current rolling-window usage
+pilotctl appstore audit io.pilot.cosift      # supervisor log: spawn / exit / verify-fail
+pilotctl appstore actions                    # pilotctl-side install/uninstall log (survives removal)
 pilotctl appstore install io.pilot.cosift --force  # upgrade to a new version
 pilotctl appstore uninstall io.pilot.cosift --yes
-

`caps` reports the manifest's spend caps and rolling-window usage; `audit` tails the supervisor lifecycle log for one app; `actions` is the pilotctl-side install/uninstall log, which persists even after an app is removed. Both `audit` and `actions` accept `--tail <n>` and `--event <name>` (and `audit` takes `--since <dur>`).

-

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.

+

`caps` reports manifest spend caps and consumption. `audit` tails the supervisor log for an app. `actions` shows the install/uninstall log, which persists after removal. Both `audit` and `actions` accept `--tail <n>` and `--event <name>`. `audit` also accepts `--since <dur>`.

+

Upgrades are keyed on the `app_version` in the manifest. The supervisor respawns an app when this version changes.

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. Its manifest declares its identity, methods, pinned binary, and required grants.

{
   "id": "io.pilot.cosift",
   "app_version": "0.1.2",
@@ -88,15 +82,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 handlers for each method. In Go, this 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 flags: `--socket`, `--manifest`, `--addr`, `--db`, `--identity`, `--cap-state`. An app must accept all flags to start. 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
 
@@ -106,7 +100,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:

+

Next, add an entry to `catalogue.json` with the tarball's sha256 and open a pull request.

{
   "id": "io.pilot.cosift",
   "version": "0.1.2",
@@ -114,50 +108,42 @@ 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 itself is signed. After editing, re-sign it.

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 writes a detached `catalogue.json.sig` file, which should be committed with `catalogue.json`. `pilotctl` verifies this signature before trusting any entry.

+

Three integrity layers protect each install: the catalogue's signature, the tarball's sha256 pin in the catalogue, and the binary's sha256 pin in the signed manifest.

Catalogue vs sideload

-

There are two install paths, with different trust:

+

There are two installation paths with different trust models:

    -
  • 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>`): The reviewed path. The bundle is signature-verified and installs with the grants its manifest declares, including network access.
  • +
  • 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.
-

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.

-

To check a bundle without installing it, `pilotctl appstore verify <bundle-dir>` sha256-checks every file in a pre-install bundle against its manifest and reports any mismatch — a tampered or wrongly-built bundle fails here before it ever reaches the install root.

- -

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, point the `$PILOT_APPSTORE_CATALOG_URL` environment variable at a `file://` catalogue and install by ID.

+

To check a bundle without installing, `pilotctl appstore verify <bundle-dir>` checks file hashes against the manifest.

-

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 flows from a signed catalogue to a signed manifest to a sandboxed child process.

+

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. A tampered or unsigned catalogue is refused. 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:

+

All calls go through the daemon's broker, which enforces two gates:

    -
  • 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 only dispatchable if listed in the target app's manifest `exposes` set.
  • +
  • Grant gate: For inter-app calls, the calling app must have a matching `ipc.call` grant targeting `<app>.<method>` (exact, `<app>.*`, or `*`).
- -

Supervisor hardening

-

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

+

The supervisor applies several hardening 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. It is rejected if it became a symlink, and its sha256 must still match the pinned hash.
  • +
  • Exponential backoff: A binary that fails verification or crashes is retried with capped exponential backoff (not a fixed interval). A crash-looping app is suspended after too many failures in a window until an operator restarts it.
  • +
  • Resource limits: On Linux, apps are spawned with `RLIMIT_NOFILE` and an `RLIMIT_AS` address-space cap to limit file descriptor and memory usage.
  • +
  • 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 to prevent disk exhaustion.
- -

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.

+

Apps can register hooks on daemon primitives. The hook surface is bounded 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 cosift app is a stateless adapter for a search, answer, and research API over a web corpus.

# Discover the surface + latencies
 pilotctl appstore call io.pilot.cosift cosift.help '{}'
 
@@ -167,8 +153,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.

+

Its source code serves as a public reference for app authors.

diff --git a/src/pages/plain/docs/cli-reference.astro b/src/pages/plain/docs/cli-reference.astro index 486d35f..b1c075d 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: a5f9ac83fdd517609d52f201d1ebf5cfbc7743b2fdbd0d57ea7e456c814c8c01 import PlainLayout from '../../../layouts/PlainLayout.astro'; --- @@ -8,7 +10,7 @@ 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...]
@@ -19,7 +21,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

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

@@ -29,28 +31,28 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';

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.

+

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

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 whether 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:

    -
  • 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`}].

@@ -62,7 +64,7 @@ pilotctl send-message <hostname> --data '/data {"<filter>":"< [--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.

+

The `--email` flag is optional. If omitted, one is synthesized from the public-key fingerprint. If supplied, it persists to `~/.pilot/config.json`. `--trust-auto-approve` auto-accepts every incoming handshake.

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

daemon stop

pilotctl daemon stop
@@ -91,7 +93,7 @@ pilotctl send-message <hostname> --data '/data {"<filter>":"<

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 to sign the request. Returns: `node_id`, `visibility`

Communication

connect

@@ -130,7 +132,7 @@ pilotctl set-private # Hide this node (default)

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.

+

Sends a best-effort datagram to every member of the network. There is no per-recipient ACK.

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

subscribe

pilotctl subscribe <address|hostname> <topic> [--count <n>] [--timeout <dur>]
@@ -169,13 +171,13 @@ pilotctl set-private # Hide this node (default)
pilotctl verify [status] [--node <addr|id>]
 pilotctl verify --provider <name>
 pilotctl verify --badge <b> --badge-sig <s>   # or --from <file>
-

Verified-address badges prove a node controls a real, attested identity. Bare `verify` (or `verify status`) shows your state; `--provider <name>` runs a device-flow to become verified; `--badge`/`--badge-sig` (or `--from <file>`) submits a badge you already hold. Badges are checked offline against the pinned issuer key.

+

Verified-address badges prove a node controls an attested identity. `verify` or `verify status` shows current state. `--provider <name>` runs a device-flow to become verified. `--badge`/`--badge-sig` or `--from <file>` submits an existing badge. Badges are checked offline against the pinned issuer key.

Returns: `verified` (bool), `node_id`, `address`, `issuer`

recovery

pilotctl recovery enroll <...>
 pilotctl recovery new-key <...>
 pilotctl recovery recover <...>
-

Reclaim a node's address if its identity key is lost. `enroll` records a recovery commitment; `new-key` rotates to a fresh key; `recover` reclaims the address using recovery material. Enrollment and signatures come from the `pilot-verify` tool.

+

Reclaims a node's address if its identity key is lost. `enroll` records a recovery commitment. `new-key` rotates to a fresh key. `recover` reclaims the address using recovery material. Enrollment and signatures come from the `pilot-verify` tool.

Returns: `status`, `node_id`, `address`

Webhooks

@@ -188,7 +190,7 @@ pilotctl recovery recover <...>

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

Tags

-

Discovery tags are operator setup, so they live in the `extras` tier — `pilotctl set-tags` on its own is rejected.

+

Discovery tags are part of the `extras` tier.

set-tags

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

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

@@ -237,7 +239,7 @@ pilotctl recovery recover <...>

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 queried using `send-message`. The `--data` payload is treated 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
@@ -249,47 +251,47 @@ 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 [<dur>]` flag (default 30s) makes `send-message` block until the reply is received in `~/.pilot/inbox/`.

App Store

-

Local capability apps that run on your daemon as typed IPC services — JSON in, JSON out, auto-spawned on install. All subcommands are invoked as `pilotctl appstore <subcommand>`. Install root: `$PILOT_APPSTORE_ROOT` or `~/.pilot/apps`. Run `pilotctl appstore help` for the full list. See the App Store guide for the discover → install → call loop.

+

The app store installs local capability apps that run on the daemon as typed IPC services. All subcommands are invoked as `pilotctl appstore <subcommand>`. The install root is `$PILOT_APPSTORE_ROOT` or `~/.pilot/apps`. Run `pilotctl appstore help` for the complete subcommand list.

catalogue

pilotctl appstore catalogue
-

Lists apps available for one-command install (alias: `catalog`).

+

Lists apps available for install. Alias: `catalog`.

view

pilotctl appstore view <id> [--all-changelog]
-

App detail page — description, vendor, changelog, size, source, license, methods, permissions. Works whether or not the app is installed.

+

Shows app details, including description, vendor, changelog, size, source URL, license, methods, and permissions. Works for installed or uninstalled apps.

install

pilotctl appstore install <app-id> [--force]
 pilotctl appstore install <bundle-dir> --local [--force]
-

Install by catalogue ID (fetch + verify + extract), or sideload a local bundle with `--local` (sandboxed: no net, no key.sign, no hooks).

+

Install by catalogue ID or sideload a local bundle with `--local`. Local bundles are sandboxed: `fs.read`/`fs.write` under `$APP` plus `audit.log`; no net, no `key.sign`, no hooks. The daemon auto-spawns the app on install.

list

pilotctl appstore list
-

Lists installed apps and the IPC methods each exposes.

+

Lists installed apps and their exposed IPC methods.

call

pilotctl appstore call <id> <method> [json-args] [--timeout <dur>]
-

Dispatches an IPC call into an app. Every app exposes `<app>.help`. Default timeout 120s (also `$PILOT_APPSTORE_CALL_TIMEOUT`).

+

Dispatches an IPC call to an app and prints the JSON result. Every app exposes `<app>.help` listing its methods, params, and latency class. Default timeout is 120s (also `$PILOT_APPSTORE_CALL_TIMEOUT`).

status / caps / audit / actions

pilotctl appstore status <id>
 pilotctl appstore caps <id>
 pilotctl appstore audit <id> [--tail <n>] [--event <name>] [--since <dur>]
 pilotctl appstore actions [--tail <n>] [--event <name>]
-

`status` deep-dives one app's pinned state; `caps` shows spend caps + rolling-window usage; `audit` shows the supervisor lifecycle log; `actions` shows the pilotctl-side install/uninstall log (survives app removal).

+

`status` shows an app's state. `caps` shows manifest spend caps and usage. `audit` shows the supervisor lifecycle log. `actions` shows the pilotctl-side install/uninstall log.

restart / uninstall / verify

pilotctl appstore restart <id>
 pilotctl appstore uninstall <id> --yes
 pilotctl appstore verify <bundle-dir>
-

`restart` clears crash-loop suspension and respawns the app; `uninstall` removes it; `verify` sha256-checks a pre-install bundle against its manifest.

+

`restart` respawns the app. `uninstall` removes it. `verify` sha256-checks a pre-install bundle against its manifest.

gen-key / sign / sign-catalogue

pilotctl appstore gen-key <key-file>
 pilotctl appstore sign --key <key-file> <manifest>
 pilotctl appstore sign-catalogue --key <key-file> <catalogue.json>
-

Publisher tooling: generate an ed25519 keypair, sign a manifest's `store.signature`, and sign a catalogue (detached `.sig`). Alias: `sign-catalog`.

+

Publisher tooling: generate an ed25519 keypair, sign a manifest, and sign a catalogue. Alias: `sign-catalog`.

Diagnostics

health

pilotctl health
-

A quick daemon health check.

+

Daemon health check.

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

ping

pilotctl ping <address|hostname> [--count <n>] [--timeout <dur>]
@@ -304,7 +306,7 @@ pilotctl appstore sign-catalogue --key <key-file> <catalogue.json>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.

+

Lists currently connected peers. 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

pilotctl connections
@@ -314,11 +316,11 @@ pilotctl appstore sign-catalogue --key <key-file> <catalogue.json>Returns: `conn_id`

prefer-direct

pilotctl prefer-direct <node_id|address|hostname>
-

Resets routing state for a peer so the next connection prefers a direct (hole-punched) tunnel over the relay. Requires daemon v1.12+.

+

Resets routing state for a peer to prefer a direct tunnel over a relay. Requires daemon v1.12+.

Returns: `had_tunnel`, `was_relay_active`, `was_relay_pinned`

Managed Networks

-

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

+

Operator commands for networks that run an automated evaluation cycle.

managed status

pilotctl managed status [--net <id>]

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

@@ -338,7 +340,7 @@ pilotctl appstore sign-catalogue --key <key-file> <catalogue.json>Set the tag list on a member, replacing 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. Omitting `--node` dumps tags for every member in the network.

Network Policies

Local policy engine for automating network behavior.

@@ -348,7 +350,7 @@ pilotctl appstore sign-catalogue --key <key-file> <catalogue.json>policy set

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

Apply a policy document to a network.

+

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>'
@@ -365,9 +367,9 @@ pilotctl policy validate --inline '<json>'
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: Shows the current export configuration (includes `enabled`, `format`, `endpoint`, and counters `exported` / `dropped`).
  • +
  • set --format <json|splunk_hec|syslog_cef> --endpoint <URL> [--splunk-token <T>] [--index <I>] [--source <S>]: Configures export. `--source` defaults to `pilot-registry`.
  • +
  • disable: Turns off log export.

Requires admin token.

provision

@@ -383,8 +385,8 @@ pilotctl policy validate --inline '<json>'
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: Shows the current IdP configuration (`configured`, `idp_type`, `url`, plus optional `issuer` / `tenant_id` / `client_id`).
  • +
  • set --type <oidc|saml|entra_id|ldap|webhook> --url <URL> [--issuer URL] [--client-id ID] [--tenant-id ID] [--domain D]: Configures the IdP.

Requires admin token.

directory-sync

@@ -403,10 +405,10 @@ pilotctl policy validate --inline '<json>'

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

deregister

pilotctl deregister
-

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

+

Routes through the daemon for signing. 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.

+

Generates a new keypair for the 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
@@ -414,7 +416,7 @@ pilotctl set-private

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. These are well-known service agents that are auto-approved on first contact. This is different from `pilotctl trust`.

Meta

version

@@ -422,22 +424,22 @@ pilotctl set-private

Prints the build version string.

update

pilotctl update [status|enable|disable] [--pin <tag>]
-

Self-update. With no subcommand, runs the updater once — checks for and installs the latest release. Automatic updates are off by default; `enable`/`disable` toggle the background updater and `status` shows the current setting. Distinct from `updates`, which only reads the changelog feed.

+

Self-update. With no subcommand, it checks for and installs the latest release. `enable`/`disable` toggle the background updater. `status` shows the current setting and version.

review

pilotctl review <pilot|app-id> [--rating <1-5>] [--text "..."]
-

Submit a rating and/or written review for Pilot itself (`pilot`) or an installed app (e.g. `io.pilot.cosift`).

+

Submit a rating or review for Pilot or for an installed app.

updates

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

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

+

Reads the published changelog feed and prints recent entries (default: 5). `--scope` 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 `SKILL.md` files installed by the daemon for each detected agent tool (Claude Code, OpenClaw, PicoClaw, OpenHands, Hermes). 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.
  • +
  • check: Run one reconcile pass immediately.
  • +
  • disable: Removes every file written by the daemon for this feature and adds an opt-out to the configuration. Files in pilot-owned subdirectories are deleted. For files shared with user content, only the pilot-managed block is removed.
  • +
  • enable: Re-enables injection and runs one reconcile pass.

Gateway

@@ -460,10 +462,9 @@ pilotctl set-private

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 a001c56..97715c6 100644 --- a/src/pages/plain/publish.astro +++ b/src/pages/plain/publish.astro @@ -1,52 +1,102 @@ --- +// Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. +// plain-source: src/pages/publish.astro +// plain-source-sha256: 5b08c54d1c2e4f2ffa3232dbd8afc1edff46b35602a3ef09550fd2fe506559f1 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.

          +

          Publish an application to the Pilot app store. The service generates, signs, and verifies an agent-first adapter for the application. Submissions are reviewed before appearing in the store.

          -

          Two ways to publish

          +

          Publishing Process

          +

          The publishing process requires describing an existing application. An agent-first adapter is then generated, signed, and verified. The submission is then reviewed by the Pilot team before it is listed in the app store.

            -
          • 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 application code is uploaded. The app's methods are described, and the adapter is built from that description.
          • +
          • Secrets are not collected. Operators supply keys at install time.
          • +
          • Every submission is reviewed before it appears in the store.
          • +
          • Email notifications are sent for submission confirmation and review status.
          • +
          • A publisher release agreement is signed before submission.
          +

          The steps are: describe the app, verify email, the adapter is built and verified, the team reviews, and the app goes live in the store.

          -

          Browser wizard steps

          +

          Step 1: Email

          +

          An email address is required. It is used to send a submission confirmation and the review decision.

          + +

          Step 2: Identity

          +

          Define the application's identity with the following fields:

          +
            +
          • App ID: A reverse-DNS identifier under the `io.pilot` namespace, using lowercase letters, digits, and hyphens.
          • +
          • Version: The application's version in SemVer format.
          • +
          • One-line description: A brief summary of what the app does.
          • +
          + +

          Step 3: Backend

          +

          Define the application's backend type. Two types are supported:

          +
            +
          • HTTP API: For applications reachable over HTTP. An adapter is generated to forward each method to the API. A base URL and optional authentication headers are configured. A header value such as `${WEATHER_TOKEN}` is a secret placeholder: the operator who installs the app supplies it at install time from their environment or a local secrets file. It is never stored in the published app.
          • +
          • CLI: For command-line tools installed on the host. Each method runs a subprocess; `pilotctl appstore call` translates into `<cli> <args>`. The command must already be installed on the operator's host. The child runs with a scrubbed environment — only the listed variables (plus PATH/HOME/locale) are passed through. The app ships with a `proc.exec` grant scoped to exactly this command and installs guarded via the reviewed catalogue. The command is comma-separated argv (e.g. `gh` or `python, -m, tool`); each method appends its own args.
          • +
          + +

          Step 4: Methods

          +

          Each method is a single call an agent can make. Each method requires a name, backend route, latency class, description, and parameters. The method name is prefixed with the app namespace, e.g. `app.search`.

          +

          Latency classes indicate typical call duration:

            -
          • 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 (verb GET/POST/PUT/PATCH/DELETE and a path template that may contain {name} placeholders), latency class (fast under 5s, medium up to 15s, slow up to 1 minute), description, and parameters. For HTTP methods each parameter also has an "in" that says where the adapter reads it from: query (?name=value), path (fills {name} url-encoded), path_raw (fills {name} unescaped, for URL-in-path APIs), body (a JSON body field), or header. It defaults to the verb's natural location (GET → query, POST/PUT/PATCH → body).
          • -
          • 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.
          • +
          • fast: under 5 seconds
          • +
          • med: up to 15 seconds
          • +
          • slow: up to 1 minute
          -

          Publish by PR

          +

          For an HTTP API backend, each method specifies an HTTP verb and a path. The verb is one of GET, POST, PUT, PATCH, DELETE. The path is a backend route such as `/search` or `/items/{id}`; `{name}` placeholders insert a parameter into the path.

          + +

          For a CLI backend, each method specifies its argv. Arguments are comma-separated argv appended to the command, using `${param}` to insert a payload field (e.g. `current, --lat, ${lat}`). A method may also append each parameter as `--name value` (params-as-flags). In passthrough mode the whole CLI is fronted: every subcommand is reachable and the caller supplies the argv, called as `{"args":["…"]}` with no baked arguments.

          + +

          Each parameter has a name, a type (one of string, int, number, bool, object), a required flag, and a description.

          + +

          For HTTP methods, each parameter also has an "In" (request-mapping location) that controls where the adapter reads it from when calling the backend:

            -
          • 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.
          • +
          • query: `?name=value`, url-encoded. Default for GET.
          • +
          • path: fills `{name}` in the path, url-encoded (REST).
          • +
          • path_raw: fills `{name}` in the path unescaped — for URL-in-path APIs, e.g. a fetch/proxy that takes `GET /<rawurl>`.
          • +
          • body: JSON body field. Default for POST/PUT/PATCH.
          • +
          • header: sent as a request header.
          +

          When no location is chosen, the natural default for the verb is used: GET and DELETE default to query, and POST/PUT/PATCH default to body. A parameter mapped to path or path_raw requires a matching `{name}` placeholder in the path.

          -

          What happens after you submit

          +

          Step 5: Listing

          +

          Define how the application appears in the app store with the following fields:

            -
          • 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>
          • +
          • Display name
          • +
          • License (SPDX identifier)
          • +
          • Tagline
          • +
          • App description (Markdown is supported)
          • +
          • Homepage
          • +
          • Source URL
          • +
          • Categories (comma-separated)
          • +
          • Keywords (comma-separated)
          -

          Requirements

          +

          Step 6: Vendor

          +

          Provide information about the publisher with the following fields:

          +
            +
          • Vendor name
          • +
          • Vendor URL
          • +
          • How autonomous AI agents will use this app
          • +
          • List of all capabilities
          • +
          + +

          Step 7: Review and Submit

          +

          The final step is to review all submission details and sign the Publisher Release Agreement.

          +

          The agreement confirms the publisher has the right to publish the app and grants Pilot Protocol a license to use the publisher's name and the app's name for listing and promotion.

          + +

          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