diff --git a/src/data/docsNav.ts b/src/data/docsNav.ts index e3dd120..9fc0444 100644 --- a/src/data/docsNav.ts +++ b/src/data/docsNav.ts @@ -41,6 +41,8 @@ export const docsNav: NavItem[] = [ icon: '' }, { label: 'Service Agents', href: '/docs/service-agents', slug: 'service-agents', icon: '' }, + { label: 'App Store', href: '/docs/app-store', slug: 'app-store', + icon: '' }, // Enterprise { section: 'Enterprise', label: 'Enterprise Overview', href: '/docs/enterprise', slug: 'enterprise', icon: '' }, diff --git a/src/pages/app-store.astro b/src/pages/app-store.astro index a38c833..ccc5661 100644 --- a/src/pages/app-store.astro +++ b/src/pages/app-store.astro @@ -66,9 +66,9 @@ const canonicalUrl = "https://pilotprotocol.network/app-store";
02
Install
- One command: pilot app install <name>. - Dependencies resolved, permissions requested, app live - in seconds. + One command: pilotctl appstore install <id>. + Fetched, signature-verified, permissions requested, and + auto-spawned by the daemon — live in seconds.
@@ -127,18 +127,20 @@ const canonicalUrl = "https://pilotprotocol.network/app-store";
Manifest
Declare your app
- An agent app is defined by a pilot.app.json manifest: - name, version, capabilities, and entry point. No Docker, no - Kubernetes — the protocol handles distribution. + An agent app is defined by a manifest.json: id, + version, the methods it exposes, a sha256-pinned binary, and the + grants it requests. No Docker, no Kubernetes — the protocol handles + distribution.
Submit
One PR to publish
- - Submission guidelines and the publishing pipeline are being finalized. - For early access, reach out to the team. + Sign the manifest, attach the bundle to a GitHub release, and add + one entry to catalogue.json by PR. Once merged, + pilotctl appstore install <id> works everywhere. + See the App Store docs.
diff --git a/src/pages/docs/app-store.astro b/src/pages/docs/app-store.astro new file mode 100644 index 0000000..db337a6 --- /dev/null +++ b/src/pages/docs/app-store.astro @@ -0,0 +1,180 @@ +--- +import DocLayout from "../../layouts/DocLayout.astro"; + +const bodyContent = `

App Store

+

Installable agent apps that run locally on your daemon as typed IPC services - JSON in, JSON out, auto-spawned on install. Discover, install, call.

+ +
+

On this page

+ +
+ +

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, and supervises it: it spawns the binary, hands it a unix socket, and brokers IPC calls to it. Each app method is a typed call - JSON in, JSON out.

+ +

Apps are:

+ + +

The whole loop an agent runs is: discover → install → 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.

+ +
# 1. Discover what's installable
+pilotctl appstore catalogue
+
+# 2. Install by id - fetch + verify sha + install; the daemon auto-spawns it
+pilotctl appstore install io.pilot.cosift
+
+# 3. Confirm it's ready (lists installed apps + the methods each exposes)
+pilotctl appstore list
+pilotctl appstore status io.pilot.cosift
+
+# 4. Call a method - JSON in, JSON out on stdout
+pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft consensus","k":"5"}'
+ +
+

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

+
+ +

Discovery & 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.

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

+ + + +

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

+ +

Lifecycle

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

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

+
+ +

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.

+ +
{
+  "id": "io.pilot.cosift",
+  "app_version": "0.1.2",
+  "manifest_version": 1,
+  "binary": { "runtime": "go", "path": "bin/cosift-app", "sha256": "<pinned>" },
+  "exposes": ["cosift.search", "cosift.answer", "cosift.research",
+              "cosift.stats", "cosift.health", "cosift.help"],
+  "grants": [
+    { "cap": "net.dial", "target": "cosift.pilotprotocol.network",
+      "if": { "kind": "rate", "params": { "per": "min", "limit": 120 } } },
+    { "cap": "fs.read",  "target": "$APP/config.json" },
+    { "cap": "audit.log", "target": "*" }
+  ],
+  "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:

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

+ +

Publishing an app

+ +

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

+ +
# 1. One-time: generate a publisher keypair (keep the private key safe)
+pilotctl appstore gen-key publisher.key
+
+# 2. Sign the manifest (after pinning binary.sha256) and package the bundle
+pilotctl appstore sign --key publisher.key bundle/manifest.json
+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:

+ +
{
+  "id": "io.pilot.cosift",
+  "version": "0.1.2",
+  "description": "cosift search / answer / research over the public web corpus.",
+  "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>"
+}
+ +

Two integrity layers protect every install, both re-checked at each spawn: the catalogue pins the tarball sha256 (a swapped CDN byte fails), and the manifest pins the binary sha256 under an ed25519 signature.

+ +

Catalogue vs sideload

+ +

There are two install paths, with different trust:

+ + + +
+

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.

+
+ +

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:

+ +
# Discover the surface + latencies
+pilotctl appstore call io.pilot.cosift cosift.help '{}'
+
+# search (fast) - ranked URLs + excerpts
+pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft leader election","retriever":"hybrid","rerank":"true","k":"5"}'
+
+# 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
+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.

+`; +--- + + + + diff --git a/src/pages/plain/docs/app-store.astro b/src/pages/plain/docs/app-store.astro new file mode 100644 index 0000000..db42be3 --- /dev/null +++ b/src/pages/plain/docs/app-store.astro @@ -0,0 +1,83 @@ +--- +// Plain mirror of /docs/app-store. Keep in sync with src/pages/docs/app-store.astro. +import PlainLayout from '../../../layouts/PlainLayout.astro'; +--- + + +

← Docs index

+ +

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.

+ +

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 JSON in, JSON out.

+ + +

Using apps

+
# Discover what's installable
+pilotctl appstore catalogue
+
+# Install by id - fetch + verify sha + install; the daemon auto-spawns it
+pilotctl appstore install io.pilot.cosift
+
+# Confirm it's ready (installed apps + the methods each exposes)
+pilotctl appstore list
+pilotctl appstore status io.pilot.cosift
+
+# Call a method - JSON in, JSON out on stdout
+pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft consensus","k":"5"}'
+

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

+ +

Discovery and the help convention

+

list and status surface a flat list of method names. 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.

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

Latency classes let an agent pick the cheapest method before spending a slow one:

+ + +

Lifecycle

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

Upgrades key on the version: the supervisor respawns an app when its app_version changes. Bump the version for every new build.

+ +

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 id, the methods it exposes (exposes[]), a sha256-pinned binary, and the grants it needs. In Go, register one handler per method on an ipc.Dispatcher and call ipc.Serve on the --socket the daemon supplies. Method names in code must match the manifest's exposes list. The daemon spawns the binary with fixed lifecycle flags (--socket, --manifest, --addr, --db, --identity, --cap-state); the app must accept all of them.

+ +

Publishing an app

+
# 1. One-time: generate a publisher keypair (keep the private key safe)
+pilotctl appstore gen-key publisher.key
+
+# 2. Sign the manifest (after pinning binary.sha256) and package the bundle
+pilotctl appstore sign --key publisher.key bundle/manifest.json
+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 (id, version, bundle_url, bundle_sha256) and open a PR. Once merged, pilotctl appstore install <id> resolves it everywhere. Two integrity layers protect each install: the catalogue pins the tarball sha256, and the manifest pins the binary sha256 under an ed25519 signature.

+ +

Catalogue vs sideload

+ +

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

+ +

Worked example: io.pilot.cosift

+
pilotctl appstore call io.pilot.cosift cosift.help '{}'
+pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft leader election","retriever":"hybrid","rerank":"true","k":"5"}'
+pilotctl appstore call io.pilot.cosift cosift.answer '{"q":"What is HNSW?"}'
+pilotctl appstore call io.pilot.cosift cosift.research '{"q":"compare raft and paxos"}'
+ +
diff --git a/src/pages/plain/docs/index.astro b/src/pages/plain/docs/index.astro index 533a99c..e44a66f 100644 --- a/src/pages/plain/docs/index.astro +++ b/src/pages/plain/docs/index.astro @@ -25,6 +25,7 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
  • Webhooks: Receive real-time HTTP notifications for daemon events.
  • Gateway: Bridge IP traffic to the overlay - use curl, browsers, any TCP tool.
  • Tags & Discovery: Label your agent with capability tags for peer discovery.
  • +
  • App Store: Install and build agent apps - typed IPC capabilities, one command to install, one PR to publish.
  • 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.