07. Plugin Marketplace
1. Positioning
The plugin marketplace is a distribution layer, not a runtime core.
Principle:
Get the local plugin system working first, then integrate the marketplace.
The marketplace is responsible for:
- Discovery
- Search
- Displaying metadata
- Download
- Update checking
The host is responsible for:
- Validation
- Authorization
- Install
- Run
- Security isolation
2. Phase strategy
Phase A ✅
- Protocol and data model
- Local official market provider (
plugins/market/catalog.json)
Phase B ✅
- Browse/search + download install are implemented against the official provider
- Official provider is the dedicated GitHub repo
vastsa/pi-desktop-plugins - Default catalog URL:
https://raw.githubusercontent.com/vastsa/pi-desktop-plugins/main/catalog.json - Package URLs may be absolute
https:///http:///file://, or relative paths resolved against the catalog URL - HTTPS fetch uses
curlin host-core
Client-visible catalog policy
Development-only sample entries whose stable ID starts with demo. remain available as offline fixtures and direct install targets for plugin development, but the desktop client filters them before updating its marketplace state. They do not appear as cards, categories, or search results. An already-installed sample remains visible in Installed so it can still be disabled or uninstalled.
Phase C (partial ✅)
- Auto-update policy + permission-diff gating implemented
- Ratings / mandatory signing still planned
3. Market Provider abstraction
interface MarketProvider {
id: string
list(query: MarketQuery): Promise<MarketSearchResult>
get(pluginId: string): Promise<MarketPluginDetail>
getDownloadInfo(pluginId: string, version?: string): Promise<MarketDownloadInfo>
checkUpdates(installed: InstalledPluginRef[]): Promise<MarketUpdateInfo[]>
}Supports multiple providers:
officialcustom(enterprise private source)local-mock(development)
4. Data model
MarketPluginSummary
type MarketPluginSummary = {
id: string
name: string
description: string
author: string
iconUrl?: string
latestVersion: string
downloads?: number
updatedAt: string
categories?: string[]
permissionSummary: string[]
verified?: boolean
}MarketPluginDetail
type MarketPluginDetail = MarketPluginSummary & {
readmeMarkdown?: string
versions: Array<{
version: string
publishedAt: string
changelog?: string
minPiDesktop?: string
}>
screenshots?: string[]
homepage?: string
repository?: string
permissions: string[]
safetyNotes?: string
}MarketDownloadInfo
type MarketDownloadInfo = {
pluginId: string
version: string
url: string
sizeBytes: number
shasum: string // sha256
signature?: string // mandatory later
signatureAlg?: "ed25519"
publishedAt: string
}5. Install path (marketplace)
browse/search
→ detail
→ install
→ download to cache
→ verify shasum/(signature)
→ hand to local packaging installer
→ permission review
→ enable?On any validation failure: abort and optionally clean up the cache.
.piplug packages are now producible locally: pnpm pi-plugin pack <dir> (equally, the PluginPack agent tool) writes dist/<id>-<version>.piplug and prints its sha256, and the plugins page installs that file through the same validation and permission review as a marketplace download. Distribution through the marketplace is therefore optional — a plugin written for personal use never has to leave the machine. See Plugin developer experience.
A catalog entry whose plugin declares contributes.skills must also declare agent.prompt.inject in permissions; without it the skills are inert and the permission review will not mention them. pi-plugin check warns on that combination.
6. Update path
checkUpdatesafter startup or on a schedule- Compare installed version with latest
- UI shows the list of available updates
- Download and upgrade after user confirmation
Strategy:
- First version after MVP: manual update
- Later: optional auto-update (low-risk plugins or official plugins only)
7. Marketplace UI information architecture
Extensions
├─ Installed
│ ├─ Search + result count
│ └─ Groups: Needs attention · Updates available · Active · Turned off
├─ MCP
├─ Skills
├─ Marketplace
│ ├─ Search
│ ├─ Categories
│ └─ Card grid
├─ Detail sheet (shared by both tabs)
└─ Permission dialog (install / upgrade)All five surfaces live under one segmented control that carries relevant per-tab counts. There is no separate numeric overview band; the update alert, tab counts, and installed group counts retain the actionable state without duplicating it in a static card row (D196). The header keeps a single contextual primary action (Browse marketplace / Refresh marketplace) and moves Check for updates, Apply automatic updates, Install package, and Load local plugin into an overflow menu (D169).
Installed rows intentionally default to a quiet two-line summary containing the plugin name, optional local-source marker, id, and version. The state group heading carries Active / Turned off / Updates available / Needs attention, and load errors remain inline. Capabilities, resident service status, and risk-tinted permission chips are rendered inside a collapsed native Details disclosure; expanding it exposes the existing full readout without making every row tall. Installed rows use one current-state scope trigger; opening it shows the three scope choices with their explanations, and choosing This project opens the existing project picker. Row icon actions remain visible at rest and show their labels on hover and keyboard focus. This is a renderer-only presentation choice; plugin permissions and activation contracts are unchanged.
The Detail sheet must show:
- Permissions, grouped and labeled by risk tier
- Author
- Version (selectable version list)
- Update time
- Risk description (safety notes callout)
- Install button
The Extensions list page keeps its overview copy compact: the page header and tab labels identify the surface, while section headers and empty states use direct labels and actions without explanatory paragraphs. Explanations remain where they help a decision — permission review, detail sheets, editors, and error states.
Marketplace cards render a monogram glyph rather than fetching iconUrl; the renderer performs no remote image loads (D169).
8. Trust model
| Level | Meaning |
|---|---|
| verified | Official or certified publisher |
| community | Community plugin |
| unknown | Custom source / uncertified |
The UI must make the trust level visible. Community must not be disguised as verified.
9. Private sources (enterprise-facing)
Supports configuration:
{
"marketProviders": [
{
"id": "official",
"url": "https://market.example.com"
},
{
"id": "corp",
"url": "https://plugins.company.local",
"tokenEnv": "PI_DESKTOP_MARKET_TOKEN"
}
]
}10. Remote API draft (HTTP)
Draft (post-MVP). Not a final implementation binding; protocol draft only.
GET /v1/plugins?query=&category=&page=GET /v1/plugins/:idGET /v1/plugins/:id/versionsGET /v1/plugins/:id/download?version=POST /v1/updates/check
All download metadata must include shasum.
11. Explicitly not doing (marketplace v1)
- In-app paid checkout
- Remote plugin code hot patching
- Silent auto-install
- Unverified download-and-execute
- Comment/social system (can be deferred)
12. Acceptance (marketplace read-only + install)
- Can browse the plugin list
- Can view permissions and versions
- Can download and install
- Cannot install if validation fails
- Appears in Installed after install
12. Implementation status
Desktop Extensions page now includes a Marketplace tab that calls:
market.searchmarket.getDetailmarket.installmarket.checkUpdatesmarket.applyUpdates
Installs always pass through checksum verification and permission review before enable.
13. Official marketplace repository
Repository: vastsa/pi-desktop-plugins
catalog.json
packages/*.piplug
plugins/<id>/
scripts/pack_plugin.py
scripts/rebuild_catalog.pyMaintenance flow:
- Edit
plugins/<id> python3 scripts/pack_plugin.py plugins/<id>python3 scripts/rebuild_catalog.py- Commit + push to
main - PI-Desktop refreshes via
market.refresh/ marketplace UI
Override catalog URL with env:
PI_DESKTOP_PLUGIN_MARKET_URL=https://raw.githubusercontent.com/<owner>/<repo>/<ref>/catalog.json14. Marketplace detail UX
The Extensions destination opens details as a right-side sheet (scrim + Escape + outside click dismiss) that loads market.getDetail and shows:
- about text, author, and repository / homepage links (opened in the work panel browser, never the system browser)
- safety notes as a warning callout
- permissions grouped by risk tier, each with its plain-language explanation
- version list as selectable rows, with the picked version driving the sticky install / update action
- README markdown
Installing from either tab routes through the permission dialog, which groups requests into High / Medium / Low risk sections and tags entries that are new relative to the installed version, so an upgrade cannot silently widen access (D169).
Contribution docs live in the official warehouse:
- https://github.com/vastsa/pi-desktop-plugins/blob/main/CONTRIBUTING.md
- Practical template:
plugins/demo.workspace-summary