From 8e3298a07b4be210c1839f609208eb29573e13d9 Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Wed, 24 Jun 2026 16:03:00 -0700 Subject: [PATCH] docs(cli): document the app store and the rest of the pilotctl surface The CLI reference and app-store guide had drifted from pilotctl: - cli-reference documented `pilotctl set-tags` / `clear-tags` as core, but they are extras-gated (`pilotctl extras set-tags`). - cli-reference omitted the App Store family, verify, recovery, prefer-direct, update, and review. - app-store omitted the view, caps, actions, and bundle-verify subcommands. Add an App Store section and a Verification & Recovery section to the CLI reference (plus prefer-direct under Diagnostics, update and review under Meta), fix the tags invocation to `pilotctl extras set-tags`, and document view/caps/actions/verify in the app-store guide. Plain mirrors updated to match. `npm run build` passes. --- src/pages/docs/app-store.astro | 17 ++++- src/pages/docs/cli-reference.astro | 81 +++++++++++++++++++++++- src/pages/plain/docs/app-store.astro | 14 +++- src/pages/plain/docs/cli-reference.astro | 64 ++++++++++++++++++- 4 files changed, 166 insertions(+), 10 deletions(-) diff --git a/src/pages/docs/app-store.astro b/src/pages/docs/app-store.astro index 4ff9b2d..f433d74 100644 --- a/src/pages/docs/app-store.astro +++ b/src/pages/docs/app-store.astro @@ -41,16 +41,21 @@ const bodyContent = `

App Store

# 1. Discover what's installable
 pilotctl appstore catalogue
 
-# 2. Install by id - fetch + verify sha + install; the daemon auto-spawns it
+# 2. Inspect before committing - description, vendor, changelog, size, source, methods, permissions
+pilotctl appstore view io.pilot.cosift [--all-changelog]
+
+# 3. 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)
+# 4. 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
+# 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), download/installed size, source URL, license, exposed methods, and the permissions it requests 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).

@@ -74,10 +79,14 @@ const bodyContent = `

App Store

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 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 how much of each rolling window the app has consumed; 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.

@@ -157,6 +166,8 @@ ipc.Serve(ctx, conn, d) // on the --socket the daemon su

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

diff --git a/src/pages/docs/cli-reference.astro b/src/pages/docs/cli-reference.astro index 7b0568b..8205a7e 100644 --- a/src/pages/docs/cli-reference.astro +++ b/src/pages/docs/cli-reference.astro @@ -14,11 +14,13 @@ const bodyContent = `

CLI Reference

  • Identity & Discovery
  • Communication
  • Trust management
  • +
  • Verification & Recovery
  • Webhooks
  • Tags
  • Mailbox
  • Networks
  • Service Agents
  • +
  • App Store
  • Diagnostics
  • Managed Networks
  • Member Tags
  • @@ -218,6 +220,22 @@ const bodyContent = `

    CLI Reference

    pilotctl untrust <node_id>

    Returns: node_id

    +

    Verification & Recovery

    + +

    verify

    +
    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 current 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 — the registry's word is never trusted.

    +

    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 an opaque recovery commitment for the address; 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

    set-webhook

    @@ -231,13 +249,15 @@ const bodyContent = `

    CLI Reference

    Tags

    +

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

    +

    set-tags

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

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

    Returns: node_id, tags

    clear-tags

    -
    pilotctl clear-tags
    +
    pilotctl extras clear-tags

    Returns: tags (empty array)

    Mailbox

    @@ -307,6 +327,50 @@ const bodyContent = `

    CLI Reference

    The --wait [<dur>] flag (default 30s) makes send-message block until the reply lands in ~/.pilot/inbox/, which avoids racing with the file system poll. See Service Agents for the full pattern.

    +

    App Store

    + +

    The app store installs 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>. The install root is $PILOT_APPSTORE_ROOT or ~/.pilot/apps. See the full App Store guide for the discover → install → call loop. Run pilotctl appstore help for the complete subcommand list.

    + +

    catalogue

    +
    pilotctl appstore catalogue
    +

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

    + +

    view

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

    App detail page — description, vendor, changelog, download/installed size, source URL, license, exposed methods, and granted permissions. Works whether or not the app is installed.

    + +

    install

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

    Install by catalogue ID (fetches, verifies, and extracts), or sideload a local bundle with --local (sandboxed: fs.read/fs.write under $APP + 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.

    + +

    call

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

    Dispatches an IPC call into an app and prints the JSON result on stdout. Every app exposes <app>.help listing its methods, params, and latency class. Default timeout 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 the manifest's spend caps and current rolling-window usage; audit shows the supervisor lifecycle log (spawn/exit/suspend/verify-fail); actions shows the pilotctl-side install/uninstall log, which survives app removal.

    + +

    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 from the install root; 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 publisher keypair, sign a manifest's store.signature so the supervisor accepts it, and sign a catalogue (writes a detached .sig pilotctl verifies on load). Alias: sign-catalog.

    +

    Diagnostics

    health

    @@ -341,6 +405,11 @@ const bodyContent = `

    CLI Reference

    pilotctl disconnect <conn_id>

    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 — useful after a relay path got pinned. 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 (membership pruning + vacancy filling). Three subcommands: status, cycle, reconcile.

    @@ -469,6 +538,14 @@ const bodyContent = `

    CLI Reference

    pilotctl version

    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 and version. Distinct from updates (below), which just reads the changelog feed.

    + +

    review

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

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

    +

    updates

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

    Reads the published Pilot Protocol changelog feed and prints recent entries (default: 5). --scope filters by tag (e.g. protocol, cli, networks).

    diff --git a/src/pages/plain/docs/app-store.astro b/src/pages/plain/docs/app-store.astro index ea79c10..1ef8f37 100644 --- a/src/pages/plain/docs/app-store.astro +++ b/src/pages/plain/docs/app-store.astro @@ -34,15 +34,19 @@ import PlainLayout from '../../../layouts/PlainLayout.astro';
    # 1. Discover what's installable
     pilotctl appstore catalogue
     
    -# 2. Install by id - fetch + verify sha + install; the daemon auto-spawns it
    +# 2. Inspect before committing - description, vendor, changelog, size, source, methods, permissions
    +pilotctl appstore view io.pilot.cosift [--all-changelog]
    +
    +# 3. 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)
    +# 4. 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
    +# 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).

    Discovery and the help convention

    @@ -58,9 +62,12 @@ pilotctl appstore call io.pilot.cosift cosift.search '{"q":"raft consensus"

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

    Building an app

    @@ -119,6 +126,7 @@ gh release create cosift-v0.1.2 io.pilot.cosift-0.1.2.tar.gz
  • 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.

    diff --git a/src/pages/plain/docs/cli-reference.astro b/src/pages/plain/docs/cli-reference.astro index 382b478..486d35f 100644 --- a/src/pages/plain/docs/cli-reference.astro +++ b/src/pages/plain/docs/cli-reference.astro @@ -164,6 +164,20 @@ pilotctl set-private # Hide this node (default)
    pilotctl untrust <node_id>

    Returns: `node_id`

    +

    Verification & Recovery

    +

    verify

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

    +

    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.

    +

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

    +

    Webhooks

    set-webhook

    pilotctl set-webhook <url>
    @@ -174,12 +188,13 @@ pilotctl set-private # Hide this node (default)

    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.

    set-tags

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

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

    Returns: `node_id`, `tags`

    clear-tags

    -
    pilotctl clear-tags
    +
    pilotctl extras clear-tags

    Returns: `tags` (empty array)

    Mailbox

    @@ -236,6 +251,41 @@ pilotctl send-message noaa-weather --data '/data {"airport":"KSFO"}' - 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/`.

    +

    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.

    +

    catalogue

    +
    pilotctl appstore catalogue
    +

    Lists apps available for one-command 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.

    +

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

    +

    list

    +
    pilotctl appstore list
    +

    Lists installed apps and the IPC methods each exposes.

    +

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

    +

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

    +

    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.

    +

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

    +

    Diagnostics

    health

    pilotctl health
    @@ -262,6 +312,10 @@ jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"

    disconnect

    pilotctl disconnect <conn_id>

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

    +

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

    @@ -366,6 +420,12 @@ pilotctl set-private

    version

    pilotctl version

    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.

    +

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

    updates

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

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