Skip to content

Plugin Marketplace

Document ID: PLG-007
File Path: docs/08-plugin-sdk/marketplace.md
Version: 1.6.1
Status: Core implemented — the wovyr-marketplace registry crate provides the listing model, governance policy, ratings, and the publish → discover → download → install flow (durable File/InMemory stores, plus a capability-gated PostgresRegistryStore behind the postgres cargo feature for a shared multi-node catalog — see §3). The Postgres backend is wired: both wovyr-server and the CLI select it at runtime (via a RegistryStore for Box<dyn RegistryStore> blanket impl) when built with --features postgres and WOVYR_MARKETPLACE_POSTGRES_URL is set, else the single-node file store — surfaced over the server’s /api/v1/marketplace* routes and the wovyr plugin publish|search|get CLI either way. Automated security scanning (§6) implemented: a deterministic static scan at publish (scan.rs — artifact integrity, permission sanity, sandbox posture, SBOM deny-list/licensing, provenance presence) produces a coded ScanReport stored with the version, summarized on the listing, re-run live by the attestation route, and optionally gating publish fail-closed via RegistryPolicy.block_scan_severity (+ deny_components). The full human-review workflow (§6) is now implemented: request_review/approve_review/reject_review drive a ReviewStatus lifecycle gating the verified badge, over new server routes (.../request-review, .../approve, .../reject), alongside the pre-existing set_verified operator override. The abuse-report workflow (§8) is now implemented too: report_abuse files an AbuseReport against a listing; a moderator’s resolve_abuse_report (optionally delisting — hidden from search/get, download refused, exactly like a policy blocklist entry, but a dynamic moderation decision rather than static operator config) or dismiss_abuse_report closes it, over new server routes (.../report, .../reports, .../reports/{report_id}/resolve|dismiss) and matching CLI commands (plugin report|reports|resolve-abuse|dismiss-abuse). Deferred: undeclared-usage detection / CVE feeds for the scanner, recommendations, and monetization (§9).
Owner: AI Platform Team
Last Updated: 2026-07-05


This document defines the Plugin Marketplace — the discovery, publishing, quality, and governance surface for plugins. The marketplace is the ecosystem layer that realizes the platform’s goal to foster a vibrant plugin ecosystem (see Vision).

The marketplace builds on Distribution (the verified supply chain) and adds people-facing concerns: finding plugins, trusting them, and sustaining publishers.


RoleDoes
ConsumerDiscovers, installs, rates plugins
PublisherBuilds, publishes, maintains plugins
ReviewerVets submissions for quality/security
OperatorCurates what a deployment may install

A marketplace listing aggregates a plugin’s published versions and metadata:

listing:
id: acme/github
display_name: GitHub
publisher: acme # verified identity
categories: [devtools, scm]
capabilities: [tool, workflow_activity]
versions: [1.4.0, 1.3.2, 1.3.1]
channels: { stable: 1.4.0, beta: 1.5.0-rc1 }
rating: 4.7
installs: 12840
verified: true

Listings show declared permissions and capabilities up front so consumers see what they are granting before install (ties to Permissions §8).

Implemented: the RegistryStore durability port has three backends — InMemoryRegistryStore (tests), FileRegistryStore (one registry.json, single node), and a capability-gated PostgresRegistryStore (postgres cargo feature): one JSON-document row per listing, reusing the same pure upsert/review/verify/ install logic as the file store, so a fleet of wovyr-server nodes or CLI invocations can share a durable catalog instead of each holding its own file. Wired at runtime: a RegistryStore for Box<dyn RegistryStore> blanket impl lets a binary pick its backend without becoming generic over it; both wovyr-server and the CLI select PostgresRegistryStore when built with --features postgres and WOVYR_MARKETPLACE_POSTGRES_URL is set, else the file store.


MechanismDescription
SearchFull-text over name, description, capabilities
Categories / tagsBrowse by domain (devtools, data, comms, …)
Capability filter”providers that support vision”, “tools needing no egress”
Curated collectionsEditor/operator picks
RecommendationsBased on installed plugins and usage

Discovery is available in the dashboard and via the CLI (wovyr plugin search).


Consumers judge a plugin by:

  • Verified publisher badge (identity-verified)
  • Verified plugin badge (passed review)
  • Ratings and reviews
  • Install count and active-install trend
  • Declared permissions (fewer/narrower = safer)
  • Provenance/SBOM availability (Distribution §4)
  • Maintenance recency and deprecation status

Community (unreviewed) plugins are clearly labeled and default to the most restrictive trust class.


Submissions to the public marketplace (especially stable) pass:

Submit → automated checks → security scan → human review (for verified) → publish

Automated checks: manifest/schema validity, signature + provenance, compatibility, permission sanity (no undeclared usage), and static security scanning. These gate publish itself, fail-closed — a community (unreviewed) plugin publishes and installs fine once they pass. The verified badge is a separate, opt-in track: a publisher requests human review of their published listing; a reviewer approves (setting the badge) or rejects with actionable feedback (clearing it), and the publisher may address the feedback and request review again.

Implemented: Registry::request_review/approve_review/reject_review (wovyr-marketplace) drive a ReviewStatus (UnreviewedPendingApproved/Rejected { reason }) on the listing; approval sets verified = true, rejection clears it, and a rejected listing may request review again. A separate set_verified remains as a direct operator override outside the workflow (e.g. an immediate takedown). Server routes: POST /api/v1/marketplace/listings/{id}/request-review, .../approve, .../reject (body: { "reason": "..." }), alongside the pre-existing .../verify. The reviewer identity is the X-Wovyr-Principal header if present, else a reviewer field in the request body, else "operator".


Operators control what their deployment exposes:

marketplace_policy:
sources: [public, private-acme]
allow_publishers: [acme, wovyr-official]
require_verified: true
max_permission_risk: medium # blocks broad/wildcard-permission plugins
blocklist: [some/abandoned-plugin]

This lets enterprises offer a curated internal catalog drawn from the public marketplace plus private plugins, enforcing their own risk bar.


  • Ratings (1–5) and written reviews from verified installs only (reduces spam).
  • Publishers can respond to reviews.
  • Aggregate quality + abuse-report signals feed listing ranking and can trigger re-review or delisting.

Any principal may file an AbuseReport against a listing (malware, IP infringement, deceptive metadata, etc.) via Registry::report_abuse / POST .../listings/{id}/report. Each report gets a sequential id (0-based, per listing) and starts Open.

A moderator reviews open reports (Registry::abuse_reports / GET .../listings/{id}/reports) and resolves each one:

  • resolve_abuse_report(listing_id, report_id, moderator, delist) — the report was valid. delist: true sets the listing’s delisted flag, which hides it from search/get and refuses download exactly like a policy blocklist entry — the “delisting” outcome above — but as a per-listing moderation decision rather than static operator config. delist: false records the finding without delisting (e.g. the moderator follows up with the publisher out of band instead).
  • dismiss_abuse_report(listing_id, report_id, moderator, reason) — the report was not actionable.

Both fail closed on an absent listing/report or a report that was already decided. Server routes: POST .../listings/{id}/reports/{report_id}/resolve (body: {moderator?, delist}) and .../dismiss (body: {moderator?, reason}); the resolving/dismissing/reporting identity is X-Wovyr-Principal if present, else a body field, else an anonymous default. Resolving with delist: true emits plugin.delisted; filing a report emits plugin.abuse_reported. The CLI mirrors this over the local registry: wovyr plugin report <id> <reason> [--reporter], plugin reports <id>, plugin resolve-abuse <id> <report-id> [--delist] [--moderator], and plugin dismiss-abuse <id> <report-id> <reason> [--moderator].

Not yet built: re-review triggering (an operator today files a fresh request_review manually after acting on a report) and using report volume as a ranking signal.


The marketplace supports (future) commercial plugins:

ModelDescription
Free / OSSNo charge
PaidOne-time or subscription license
Usage-basedBilled via platform metering (ties to cost events)
Revenue sharePlatform/publisher split

Billing reuses the platform’s metering and cost-event pipeline (LLM Gateway token management pattern). This is roadmap, not v1.


  • Publishing emits plugin.published; the marketplace indexes the new version.
  • Channels and deprecation mirror Versioning §6/§9.
  • Revocation (Distribution §8) delists and warns affected installs.

RequirementTarget
Search latency< 200 ms p95
Listing freshness after publish< 60 s
Verified-review SLApublisher-facing target
Abuse-report handlingtracked workflow (implemented — §8.1)



VersionDateDescription
1.6.12026-07-05Abuse-report workflow gains a CLI surface: wovyr plugin report <id> <reason> [--reporter], plugin reports <id>, plugin resolve-abuse <id> <report-id> [--delist] [--moderator], plugin dismiss-abuse <id> <report-id> <reason> [--moderator] — over the same local registry plugin publish/search/get already use, verified live end to end (file → report → resolve with delisting → confirmed hidden from search → fail-closed re-resolve)
1.6.02026-07-05Abuse-report workflow landed (§8.1): AbuseReport/AbuseReportStatus (Open/Resolved { moderator, delisted }/Dismissed { moderator, reason }) on ListingRecord, plus a delisted flag. Registry::report_abuse files a report (sequential per-listing id); resolve_abuse_report/dismiss_abuse_report close it, with resolve optionally delisting — search/get/download now exclude delisted listings exactly like a policy blocklist entry. Implemented across all three RegistryStore backends plus the Box<dyn RegistryStore> blanket impl. New server routes .../report, .../reports, .../reports/{report_id}/resolve|dismiss, emitting plugin.abuse_reported/plugin.delisted. Not yet built: using report volume as a ranking signal, or auto-triggering re-review
1.5.02026-07-03Full human-review workflow landed (§6): ReviewStatus (Unreviewed/Pending/Approved/Rejected { reason }) on ListingRecord, with Registry::request_review/approve_review/reject_review (a publisher requests review of the current latest version; a reviewer approves — setting the verified badge — or rejects with actionable feedback, clearing it; a rejected listing may request review again). Implemented across all three RegistryStore backends (in-memory/file/Postgres) plus the Box<dyn RegistryStore> blanket impl. New server routes .../request-review, .../approve, .../reject alongside the pre-existing .../verify operator override; reviewer identity from X-Wovyr-Principal, else the request body, else "operator". This was the last item deferred from v0.3
1.4.02026-07-03Postgres backend wired into runtime store selection (§3): a RegistryStore for Box<dyn RegistryStore> blanket impl in wovyr-marketplace lets a binary pick its backend without becoming generic over it; wovyr-server’s registry() and the CLI’s marketplace_registry() both select PostgresRegistryStore when built with --features postgres and WOVYR_MARKETPLACE_POSTGRES_URL is set (else the file store); the CLI’s postgres feature also forwards to wovyr-server/postgres so wovyr dev picks it up
1.3.02026-07-03Postgres-backed RegistryStore landed (§3): PostgresRegistryStore (postgres cargo feature) stores one JSON-document row per listing, reusing RegistryState’s pure mutation logic — a fleet of nodes can share a durable catalog instead of each holding its own registry.json. Capability-gated live tests prove independent connections see each other’s writes. Not yet wired into server/CLI runtime store selection
1.0.02026-06-27Initial Plugin Marketplace specification
1.1.02026-06-30Core implemented: wovyr-marketplace crate (listing model, RegistryPolicy governance, ratings, signature-verified publish, discovery, download, install bridge; File/InMemory stores) + server /api/v1/marketplace* routes (publish/search/get/download/review/verify/install, emits plugin.published) + CLI `plugin publish