Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ project uses [Semantic Versioning](https://semver.org/).
Detailed per-release notes are on the
[GitHub Releases page](https://github.com/TeoSlayer/pilotprotocol/releases).

## [1.12.0] - 2026-06-15

### Added
- **`pilotctl appstore view <id>` — a detail page for store apps.** Shows a
human-app-store-style listing: structured description, vendor, latest
changelog (`--all-changelog` for full history), download/installed size,
source-code URL, license, methods, and — when the app is installed — its
verified integrity state and granted permissions. Works whether or not the
app is installed, and whether or not it is in the catalogue (a sideloaded app
renders from local manifest facts). `--json` emits the merged report. The
catalogue listing now also shows vendor, categories, license, and size, plus
a `view:` hint. (app store)
- **Catalogue schema v2 + per-app detail docs.** The catalogue index gains
optional teaser fields (`display_name`, `vendor`, `categories`,
`bundle_size`, `source_url`, `license`) and a `metadata_url` +
`metadata_sha256` pin to a per-app `catalogue/apps/<id>/metadata.json` detail
document, fetched lazily by `view` and sha-verified the same way bundles are.
v1 catalogues still load unchanged, and an older `pilotctl` ignores the new
fields — the bump is backward and forward compatible. The `reviews` slot in
the detail schema is reserved for a future signed reviews service. (app store)

## [1.11.0] - 2026-06-09

### Added
Expand Down
119 changes: 105 additions & 14 deletions catalogue/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,107 @@
# Pilot app store catalogue

This directory holds `catalogue.json` — the canonical list of apps installable via
`pilotctl appstore install <app-id>`.
This directory holds the app store catalogue:

## Schema
- `catalogue.json` — the **index**: the list of apps installable via
`pilotctl appstore install <app-id>`. Kept lightweight; read on every
`catalogue` and `install`.
- `apps/<id>/metadata.json` — the per-app **detail document** ("store
listing"): structured description, changelog, vendor, size, source URL,
screenshots. Fetched lazily, only by `pilotctl appstore view <id>`.

The split mirrors a human app store (a cheap list vs a rich detail page) and
keeps the install hot path small. Each index entry sha-pins its detail doc the
same way it pins the bundle tarball.

## Index schema (`catalogue.json`)

The index is **versioned**. Version 1 is the original flat shape; version 2
adds optional teaser fields plus a pin to the detail doc. `pilotctl`
understands both — a v1 catalogue still loads, and an older `pilotctl`
ignores the v2 fields. **Always set `"version": 2` when using any v2 field.**

```json
{
"version": 1,
"version": 2,
"updated_at": "<RFC3339 timestamp>",
"apps": [
{
"id": "<reverse-DNS app id, must match manifest.id>",
"version": "<semver>",
"description": "<one-line, shown in `pilotctl appstore catalogue`>",
"description": "<one-line teaser, shown in `pilotctl appstore catalogue`>",
"bundle_url": "https://<host>/<path>.tar.gz",
"bundle_sha256": "<hex sha256 of the tarball>"
"bundle_sha256": "<hex sha256 of the tarball>",

"display_name": "<human name, optional>",
"vendor": "<vendor name, optional>",
"categories": ["<optional>", "<tags>"],
"bundle_size": 0,
"source_url": "https://github.com/<org>/<repo>",
"license": "<SPDX id>",

"metadata_url": "https://<host>/apps/<id>/metadata.json",
"metadata_sha256": "<hex sha256 of metadata.json>"
}
]
}
```

The schema is intentionally flat — `pilotctl` decodes it directly into
`catalogueEntry` in `cmd/pilotctl/appstore_catalogue.go`. Any field added
Everything from `display_name` down is optional (`omitempty`). The five v1
fields stay required. `pilotctl` decodes the index directly into
`catalogueEntry` in `cmd/pilotctl/appstore_catalogue.go` — any field added
here must also land there.

## Detail schema (`apps/<id>/metadata.json`)

Fetched only by `pilotctl appstore view <id>`, verified against the index's
`metadata_sha256`. Every field is optional so a partial document still
renders. Decoded into `appMetadata` in `cmd/pilotctl/appstore_metadata.go`.

```json
{
"schema_version": 1,
"id": "<must match the catalogue id>",
"display_name": "<human name>",
"tagline": "<short one-liner>",
"description_md": "<structured/long description (markdown)>",
"vendor": { "name": "", "url": "", "contact": "", "publisher_pubkey": "ed25519:..." },
"homepage": "https://...",
"source_url": "https://github.com/<org>/<repo>",
"license": "<SPDX id>",
"categories": ["..."],
"keywords": ["..."],
"icon_url": "https://...",
"screenshots": [{ "url": "https://...", "caption": "" }],
"size": { "bundle_bytes": 0, "installed_bytes": 0 },
"compat": { "min_pilot_version": "1.0.0", "runtimes": ["go"] },
"methods": [{ "name": "app.method", "summary": "" }],
"changelog": [{ "version": "X.Y.Z", "date": "YYYY-MM-DD", "notes": ["..."] }],
"links": [{ "label": "Docs", "url": "https://..." }],
"reviews": null,
"published_at": "<RFC3339>",
"updated_at": "<RFC3339>"
}
```

`reviews` is **reserved** — pilotctl parses it but never writes it. Community
reviews are a separate, signed, dynamic service (not static git data); the
slot exists so the `view` output and JSON shape stay stable when it lands.

## What `view` shows

`pilotctl appstore view <id>` merges three bands and labels their provenance:

- **catalogue** — the index teaser (publisher-attested, sha-anchored),
- **metadata** — the detail doc above (publisher copy),
- **local-manifest** — verified install facts when the app is installed
(integrity, real on-disk size, granted permissions, methods).

It works whether or not the app is installed, and whether or not it is in the
catalogue (a sideloaded app renders from local facts alone). Publisher copy and
verified install facts are kept visually distinct so they are never conflated.
Add `--all-changelog` for the full version history; `--json` for the merged
`appViewReport`.

## Where pilotctl loads it from

By default, `pilotctl appstore catalogue` and `pilotctl appstore install
Expand Down Expand Up @@ -58,10 +135,21 @@ from a remote — operators relying on a catalogue do so over `https` only).
4. Upload the tarball as a release artifact (`gh release upload` or
equivalent — GitHub releases, Cloudflare R2, anywhere reachable over
HTTPS).
5. Update this `catalogue.json` with the new `version`, `bundle_url`, and
`bundle_sha256`. Commit. The change goes live the moment the commit
lands on `main` and the raw URL serves the new bytes — no daemon
restart, no pilotctl release.
5. Write or update the detail doc at `apps/<id>/metadata.json` (bump its
`changelog`, `version`, `size`, `updated_at`). Then recompute its sha:
```bash
shasum -a 256 apps/<id>/metadata.json
```
6. Update this `catalogue.json` with the new `version`, `bundle_url`,
`bundle_sha256`, the teaser fields, **and** the new `metadata_sha256`
from step 5. Commit everything together — the index pin and the detail
doc must change in the same commit or `view` will reject a stale pin.
The change goes live the moment the commit lands on `main` and the raw
URLs serve the new bytes — no daemon restart, no pilotctl release.

> **Pin discipline:** `metadata_sha256` must be the sha256 of the exact
> committed `metadata.json` bytes. Edit the doc, then recompute — never the
> other way round. A mismatch makes `view` fall back to the teaser and warn.

## Trust model

Expand All @@ -70,7 +158,10 @@ from a remote — operators relying on a catalogue do so over `https` only).
| User trusts pilotctl | Project release pipeline (signed pilotctl binary) | The catalogue URL is correct |
| pilotctl trusts the catalogue | Future: signed against `EmbeddedCatalogPubkey`; today: the raw URL itself | App IDs map to specific bundle URLs + SHAs |
| pilotctl trusts the bundle | Embedded `bundle_sha256` matches downloaded bytes | A CDN substitute is rejected |
| pilotctl trusts the detail doc | Index `metadata_sha256` matches fetched `metadata.json` | A substituted listing is rejected (`view` falls back to the teaser) |
| Daemon trusts the manifest | Embedded ed25519 publisher pubkey verifies the signature | The bundle's manifest hasn't been tampered with |

Every layer is checked at install time, and the manifest signature is
re-verified at every supervisor rescan (every 2 s).
Every layer is checked at install/view time, and the manifest signature is
re-verified at every supervisor rescan (every 2 s). The detail doc carries no
authority of its own — it is display metadata, anchored only by the index pin,
and `view` keeps it visually separate from verified install facts.
53 changes: 53 additions & 0 deletions catalogue/apps/io.pilot.cosift/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"schema_version": 1,
"id": "io.pilot.cosift",
"display_name": "Cosift",
"tagline": "Grounded web search, retrieval, and research for agents",
"description_md": "Cosift is the Pilot app-store front door for the cosift search/answer/research API. It gives an agent keyword + semantic search, document retrieval, and LLM-grounded answers and multi-step research over a crawled web corpus — returned as clean structured JSON.\n\nEvery method is discoverable at runtime via cosift.help, which reports each method's parameters and a latency class (fast / med / slow) so a caller can pick the cheapest method that fits.",
"vendor": {
"name": "Pilot Protocol",
"url": "https://pilotprotocol.network",
"contact": "apps@pilotprotocol.network",
"publisher_pubkey": "ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
"homepage": "https://pilotprotocol.network",
"source_url": "https://github.com/pilot-protocol/cosift",
"license": "MIT",
"categories": ["search", "research", "retrieval"],
"keywords": ["search", "rag", "answer", "research", "web", "retrieval"],
"size": {
"bundle_bytes": 4774876,
"installed_bytes": 8641298
},
"compat": {
"min_pilot_version": "1.0.0",
"runtimes": ["go"]
},
"methods": [
{ "name": "cosift.search", "summary": "Keyword + semantic search over the crawled corpus" },
{ "name": "cosift.find_similar", "summary": "Find documents similar to a given result" },
{ "name": "cosift.contents", "summary": "Fetch the full contents of a document" },
{ "name": "cosift.answer", "summary": "Single-shot grounded answer with citations" },
{ "name": "cosift.research", "summary": "Multi-step research report over the corpus" },
{ "name": "cosift.stats", "summary": "Corpus and index statistics" },
{ "name": "cosift.health", "summary": "Service health check" },
{ "name": "cosift.help", "summary": "Discovery: methods, params, and latency classes" }
],
"changelog": [
{
"version": "0.1.2",
"date": "2026-06-09",
"notes": [
"cosift search/answer/research adapter with cosift.help discovery",
"Installable via the Pilot app store catalogue"
]
}
],
"links": [
{ "label": "Source", "url": "https://github.com/pilot-protocol/cosift" },
{ "label": "Website", "url": "https://pilotprotocol.network" }
],
"reviews": null,
"published_at": "2026-06-09T23:01:53Z",
"updated_at": "2026-06-09T23:30:00Z"
}
65 changes: 65 additions & 0 deletions catalogue/apps/io.pilot.wallet/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"schema_version": 1,
"id": "io.pilot.wallet",
"display_name": "Wallet",
"tagline": "On-overlay USDC payments across Base, Ethereum, and Polygon",
"description_md": "The Pilot reference wallet brings x402 + EIP-3009 USDC payments to the overlay, with spend caps the supervisor enforces. One secp256k1 address works across all three USDC mainnets (Base, Ethereum, Polygon); per-chain RPC is configurable via PILOT_EVM_RPC_<chainID>.\n\nInstalling this app lets other apps and agents settle payments without leaving the network. Spend caps declared in the manifest are reviewed at install time and enforced on every signing operation — see `pilotctl appstore caps io.pilot.wallet`.",
"vendor": {
"name": "Pilot Protocol",
"url": "https://pilotprotocol.network",
"contact": "apps@pilotprotocol.network"
},
"homepage": "https://pilotprotocol.network",
"source_url": "https://github.com/pilot-protocol/wallet",
"license": "AGPL-3.0-or-later",
"categories": ["payments", "crypto", "finance"],
"keywords": ["usdc", "x402", "eip-3009", "evm", "base", "ethereum", "polygon", "payments"],
"size": {
"bundle_bytes": 9110758
},
"compat": {
"min_pilot_version": "1.0.0",
"runtimes": ["go"]
},
"changelog": [
{
"version": "0.3.3",
"date": "2026-06-08",
"notes": [
"Default --evm-chains expands to 8453,1,137 so supervised wallets get all three USDC mainnets out of the box",
"PILOT_EVM_CHAINS env overrides the default chain set"
]
},
{
"version": "0.3.2",
"date": "2026-06-08",
"notes": [
"Multichain EVM: same secp256k1 address across Base, Ethereum, Polygon",
"New wallet.evm.chains method; all evm.* methods accept an optional chain_id",
"Per-chain RPC via PILOT_EVM_RPC_<chainID>"
]
},
{
"version": "0.3.1",
"date": "2026-06-08",
"notes": [
"Wired the wallet.evm.* methods into the binary (declared in v0.3.0 but unregistered)",
"Added --evm-identity, --evm-chain, --evm-rpc, --no-evm flags"
]
},
{
"version": "0.3.0",
"date": "2026-06-08",
"notes": [
"Signed wallet app bundle: ed25519-signed manifest, sha256-pinned binary"
]
}
],
"links": [
{ "label": "Source", "url": "https://github.com/pilot-protocol/wallet" },
{ "label": "Website", "url": "https://pilotprotocol.network" }
],
"reviews": null,
"published_at": "2026-06-08T08:00:26Z",
"updated_at": "2026-06-08T09:16:19Z"
}
28 changes: 22 additions & 6 deletions catalogue/catalogue.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
{
"version": 1,
"updated_at": "2026-06-09T23:30:00Z",
"version": 2,
"updated_at": "2026-06-15T00:00:00Z",
"apps": [
{
"id": "io.pilot.wallet",
"version": "0.3.3",
"description": "On-overlay USDC payments \u2014 multichain (Base + Ethereum + Polygon).",
"description": "On-overlay USDC payments multichain (Base + Ethereum + Polygon).",
"bundle_url": "https://github.com/TeoSlayer/pilotprotocol/releases/download/wallet-v0.3.3/io.pilot.wallet-0.3.3.tar.gz",
"bundle_sha256": "8d30b4331bc025c327dd2d8610362984cc9365843176e21b96a2637d8e18ff54"
"bundle_sha256": "8d30b4331bc025c327dd2d8610362984cc9365843176e21b96a2637d8e18ff54",
"display_name": "Wallet",
"vendor": "Pilot Protocol",
"categories": ["payments", "crypto", "finance"],
"bundle_size": 9110758,
"source_url": "https://github.com/pilot-protocol/wallet",
"license": "AGPL-3.0-or-later",
"metadata_url": "https://raw.githubusercontent.com/TeoSlayer/pilotprotocol/main/catalogue/apps/io.pilot.wallet/metadata.json",
"metadata_sha256": "b0ed7dee416144c39d8938f4dbeaac8946be3290a6c44473d898a5945eb6cadb"
},
{
"id": "io.pilot.cosift",
"version": "0.1.2",
"description": "cosift search / answer / research over the public web corpus (with cosift.help discovery).",
"bundle_url": "https://github.com/pilot-protocol/catalog/releases/download/cosift-v0.1.2/io.pilot.cosift-0.1.2.tar.gz",
"bundle_sha256": "faf60deeaa9a29298b447f7f0276f1bd4523af86390cf65c677452dc9718d5a0"
"bundle_sha256": "faf60deeaa9a29298b447f7f0276f1bd4523af86390cf65c677452dc9718d5a0",
"display_name": "Cosift",
"vendor": "Pilot Protocol",
"categories": ["search", "research", "retrieval"],
"bundle_size": 4774876,
"source_url": "https://github.com/pilot-protocol/cosift",
"license": "MIT",
"metadata_url": "https://raw.githubusercontent.com/TeoSlayer/pilotprotocol/main/catalogue/apps/io.pilot.cosift/metadata.json",
"metadata_sha256": "2b13511562dc1cfe09a6f53928149509a96ef893ee783fd1e055ed379b08b640"
}
]
}
}
8 changes: 7 additions & 1 deletion cmd/pilotctl/appstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func cmdAppStore(args []string) {
cmdAppStoreCall(args[1:])
case "status":
cmdAppStoreStatus(args[1:])
case "view":
cmdAppStoreView(args[1:])
case "audit":
cmdAppStoreAudit(args[1:])
case "uninstall":
Expand All @@ -79,7 +81,7 @@ func cmdAppStore(args []string) {
appStoreHelp()
default:
fatalHint("invalid_argument",
"available: list, status, audit, uninstall, verify, install, gen-key, sign, catalogue, restart, caps, actions, call",
"available: list, status, view, audit, uninstall, verify, install, gen-key, sign, catalogue, restart, caps, actions, call",
"unknown appstore subcommand: %s", args[0])
}
}
Expand All @@ -94,6 +96,10 @@ const AppStoreHelpText = `pilotctl appstore — list installed apps and call the
Usage:
pilotctl appstore list list installed apps + their methods
pilotctl appstore status <id> deep-dive on one app's pinned state
pilotctl appstore view <id> [--all-changelog]
detail page: description, vendor, changelog,
size, source, methods, permissions (works
whether or not the app is installed)
pilotctl appstore audit <id> [--tail N] [--event NAME] [--since DURATION]
show the supervisor lifecycle log
event names: supervise-start, supervise-stop,
Expand Down
Loading
Loading