- Introduced the `@open-design/registry-protocol` package, enabling improved interactions with plugin registries. - Updated the `typecheck` script in the daemon's `package.json` to include the new registry protocol. - Enhanced the CLI with new flags and commands for better plugin management, including `yank` and additional marketplace functionalities. - Implemented a plugin lockfile system to manage installed plugins and their versions, improving reliability during upgrades. - Added new marketplace doctor functionality to validate plugin entries and ensure compliance with registry standards. This update significantly enhances the plugin ecosystem by providing robust registry interactions and improved management capabilities.
45 KiB
Open Design Plugin Registry Strategy Plan
Date: 2026-05-13 Related: docs/plugins-spec.zh-CN.md, docs/plans/plugins-implementation.md, specs/current/plugin-authoring-flow-plan.md, specs/current/plugin-driven-flow-plan.md
Purpose
Turn Open Design plugins from "installed local workflows plus a light marketplace index" into a registry-shaped ecosystem:
- Open Design hosts the official catalog on the main site and ships first-party plugins there.
- Community authors publish by opening GitHub PRs into the Open Design registry repository.
odremains the canonical headless API for CRUD, search, install, update, trust, pack, doctor, and publish.- Third parties can self-host the same
open-design-marketplace.jsonshape as their own plugin source. - The initial backend is GitHub repository state driven through
gh; the daemon and CLI speak through a registry interface that can later be backed by a database. - Enterprise deployments can run the same registry contract on a real database backend for private catalogs, policy, audit, approvals, SSO, and commercial entitlements.
The intended product posture is closer to "ClawHub plus npm registry discipline" than a static plugin gallery: discoverable, versioned, auditable, automatable, and portable across official, community, and private sources.
External Reference Takeaways
ClawHub establishes a useful split:
- Runtime installs stay native to the host product: OpenClaw exposes skill/plugin search, install, and update through
openclawcommands. - Registry-authenticated flows live in a separate CLI surface:
clawhub login, publish, delete/undelete, sync, inspect, and package publish. - Public detail pages show enough state before install: semver versions, tags, changelogs, files, downloads, stars, and security scan summaries.
- Install/update persists source metadata so later updates keep resolving through the same registry source.
- Package installs verify compatibility metadata and archive digest when an uploaded package artifact exists.
skills.sh contributes the low-friction public directory pattern:
- One-command install is the core CTA.
- GitHub repo identity is the common authoring/distribution primitive.
- The public site is primarily a searchable directory and leaderboard, not a heavy authoring UI.
npm contributes the package-management discipline we should selectively borrow:
name@versionandname@tagresolution, withlatestas the default tag.- lockfile entries with resolved source and integrity for reproducible installs.
doctor,view/info,outdated,update,publish --tag, and policy-aware install/update flows.
References checked:
Current State
The repo already has the right substrate:
docs/plugins-spec.zh-CN.mdnames CLI as the canonical API, supports headless OD, and reserves federatedopen-design-marketplace.json.packages/contracts/src/plugins/marketplace.tshas a permissive catalog schema that can carry community-specific fields.apps/daemon/src/plugins/marketplaces.tssupports add/list/info/refresh/remove/trust and bare-name resolution through configured marketplaces.apps/daemon/src/cli.tsalready exposesod marketplace add/list/info/search/refresh/remove/trust.apps/web/src/components/MarketplaceView.tsxandPluginDetailView.tsxexist for/marketplaceand/marketplace/:id.apps/landing-pagenow has a static public/plugins/registry renderer and per-plugin detail routes generated fromplugins/registry/*/open-design-marketplace.jsonplus bundled official manifests.apps/web/src/components/PluginsView.tsxnow has the firstInstalled / Available / Sources / TeamUI slice: source management is enabled and Available entries are built from cached marketplace manifests.apps/daemon/src/plugins/pack.tscan produce.tgzplugin archives.apps/daemon/src/plugins/publish.tsnow builds submission links for external catalogs and the Open Design registry target. Full GitHub fork/branch/PR mutation is still future backend work.
Main gaps:
- Marketplace provenance was the first closure gap and is now plumbed through install, upgrade, installed records, and applied snapshots. Remaining work is exact version/tag resolution and lockfiles.
- Trust vocabulary has been unified to
official | trusted | restricted; legacyuntrustedmarketplace rows normalize torestricted. - Marketplace catalog entries now carry registry-grade optional fields:
versions,dist,integrity,manifestDigest,publisher,homepage,license,capabilitiesSummary,distTags, deprecated state, and yanking metadata. - UI discovery has a first slice but still needs backend closure. The Plugins page can show Available entries from cached manifests and manage Sources, but large-catalog browsing, provenance-aware
--from, and richer detail pages are still pending. - Private marketplace support is public-HTTPS-only. There is no
gh-backed private GitHub/GitHub Enterprise source flow, refresh policy, allowlist, TLS/private-network guidance, or offline cache mode. - npm-grade update semantics are not implemented. There is no version range resolver, dist-tag support, lockfile/update policy, publisher verification, or archive checksum enforcement.
Product Model
There are three product surfaces, all backed by the same registry contract:
-
Official web registry
- Hosted on the main Open Design site.
- Renders official and community-approved plugins from the Open Design registry repo.
- Provides SEO pages, preview screenshots, install commands,
od://deep links, provenance, trust/risk metadata, and publish/contribute guidance.
-
In-app marketplace
- Runs against the local daemon.
- Shows installed plugins and available catalog entries from configured sources.
- Lets users add, refresh, trust, restrict, remove, search, inspect, install, upgrade, and use plugins without leaving OD.
-
Headless CLI registry
- The canonical API for humans, agents, CI, and self-hosted deployments.
- Must be feature-complete before or at the same time as UI surfaces.
- Uses
ghas the v1 backend driver and auth provider, without inventing an OD account system.
The registry is deliberately source-index-first, not binary-store-first. A catalog entry points at github:owner/repo[@ref][/subpath], an HTTPS archive, or another transport source. The registry records metadata, trust, version selection, and integrity. It does not need to own all bytes in v1.
Surface Relationships And Product Mental Model
Use one mental model across product copy, CLI help, docs, and website:
Plugin source repo
Author-owned GitHub repo or enterprise source repo.
open-design.json must include plugin.repo.
|
| od plugin validate / pack / publish
v
Plugin artifact
GitHub ref, GitHub Release .tgz, HTTPS archive, or local folder.
|
| registry entry generated by publish/CI
v
Registry backend
v1: GitHub repo + generated open-design-marketplace.json.
Later: DatabaseRegistryBackend with the same contract.
|
| od marketplace search/install/upgrade
v
Installed plugin
Local runnable record with trust, provenance, integrity, and resolved ref.
The UI layers are not additional backends; they are different views over this same lifecycle:
- Home / Official starters is a usage shelf, not a registry. It should show a curated subset of already-installed bundled/official workflows so a user can immediately click
Use. Bundled official plugins are the preinstalled cache of theofficialregistry source, not a separate distribution model. Product copy should sayOfficial startersorOfficial installed, not imply this is the registry itself. - Plugins / Installed is the complete local inventory: bundled official plugins, user-created plugins, local imports, GitHub/URL installs, and marketplace-installed plugins.
- Plugins / Available is the discovery layer: registry entries from configured Sources that are not installed yet or have a newer version available.
- Plugins / Sources is the registry management layer: official, community, self-hosted, and enterprise catalog sources; trust tier; refresh; removal; auth/cache status later.
- Plugins / Team is the future enterprise governance layer: private catalogs, organization policy, allowlists, review, audit, and refresh policy.
- open-design.ai/plugins is the public presentation of the official and community registry sources. It is equivalent to a polished static renderer over repo-owned catalog data, not a separate source of truth.
open-design.ai/marketplacecan remain an alias later if needed. odCLI remains the canonical client. Every UI action must map to a CLI operation or daemon API that the CLI can also drive.- Open Design GitHub registry repo is the v1 storage backend. It can later be swapped for a database backend without changing user-facing nouns.
The agent consumption boundary is explicit:
User adds registry source
-> Sources stores URL/trust and refreshes catalog
-> Available shows installable or upgradeable entries
-> Install writes local installed record
-> Installed becomes part of agent context/runtime consumption
Open Design packaged runtime
-> official registry entries are bundled as a preinstall cache
-> startup records them as Installed/bundled with sourceMarketplaceId=official
-> Home / Official starters exposes a curated quick-use shelf
-> agent can consume them immediately
Default community registry
-> community source is configured by default
-> Available shows restricted community entries
-> user explicitly installs one
-> plugin is copied to ~/.open-design/plugins/<plugin-id>
-> Installed becomes part of agent context/runtime consumption
Available entries are supply candidates, not runnable capabilities. The agent should consume the installed set: bundled official plugins, user-created plugins, direct GitHub/URL/local installs, and marketplace-installed plugins. A future "Use from Available" shortcut can auto-install first, but it must still produce an installed record before the agent runs it.
User-created and user-installed plugins live in the user-state plugin root,
~/.open-design/plugins/<plugin-id> by default. The daemon reloads those
installed records and folders on later boots. Runtime-bundled official plugins
stay inside the app/repo image and are re-registered on boot as official-source
preinstalls; updating them can later happen by refreshing/installing from the
official registry source instead of waiting for an app release.
The production-side loop is the mirror image:
Create plugin
-> agent-assisted authoring flow
-> od plugin scaffold / validate / local install / pack
-> od plugin login/whoami through gh
-> od plugin publish opens GitHub registry PR
-> merge regenerates open-design-marketplace.json
-> users refresh Sources, see Available, install into Installed
The Create plugin product entry should therefore start an agent workflow, not a bare JSON form. The agent can gather intent, generate SKILL.md, open-design.json, examples, previews, and capability metadata, run local validation, install a local test copy, pack it, and drive the gh-backed publish flow.
High-level architecture relationship:
open-design.ai/plugins
public registry pages and docs
|
v
+--------------------------------------------------+
| Open Design GitHub registry repo |
| |
| plugins/registry/official/open-design-marketplace.json |
| plugins/registry/community/open-design-marketplace.json |
| plugins/community/<vendor>/<plugin-name>/open-design.json |
| generated open-design-marketplace.json |
+-----------------------------+--------------------+
|
| GitHub PR, gh auth, CI validation
v
+--------------------------------------------------+
| od CLI |
| |
| plugin login/whoami -> gh |
| plugin publish -> PR to registry repo |
| marketplace search/add/refresh/remove/trust |
| plugin install/upgrade/doctor/lock |
+-----------------------------+--------------------+
|
v
+--------------------------------------------------+
| Local daemon |
| |
| RegistryBackend resolver |
| source cache and trust policy |
| provenance/integrity/lock persistence |
| installed plugin runtime |
+-----------------------------+--------------------+
|
v
+--------------------------------------------------+
| Product UI |
| |
| Home: Official starters |
| Plugins: Installed / Available / Sources / Team |
| Plugin detail: provenance, risk, commands |
+--------------------------------------------------+
First-phase registry scope:
- In v1, "registry" can be understood as a GitHub repo containing source entries plus a generated marketplace index JSON.
- The generated
open-design-marketplace.jsonis the machine-readable artifact fetched by daemon/CLI/UI. - The per-plugin
community/**/open-design.jsonorentry.jsonfiles are the human-reviewable source data used for GitHub PR governance. - The file can initially live in the main Open Design repo if that lowers setup cost, but the product abstraction should still be
RegistryBackend, not "read this monorepo path". Moving toopen-design/plugin-registrylater should be a data relocation, not a product rewrite.
Registry Repository Shape
Recommended official repository shape:
open-design-plugin-registry/
├── open-design-marketplace.json
├── community/
│ └── official/
│ ├── open-design-marketplace.json
│ └── plugins/
│ └── <vendor>/<plugin-name>/entry.json
├── plugins/
│ └── <vendor>/<plugin-name>/
│ ├── entry.json
│ ├── README.md
│ ├── screenshots/
│ └── examples/
├── publishers/
│ └── <publisher>.json
├── policies/
│ ├── accepted-capabilities.json
│ └── blocked-sources.json
└── tools/
└── doctor.ts
open-design-marketplace.json remains the portable file that third-party hosts can copy. The per-plugin entry.json files make PRs reviewable, reduce merge conflicts, and let the build step generate the final marketplace index deterministically.
Current repo-friendly data placement:
- First-party runtime plugins that ship inside OD still live under
plugins/_official/**and are installed asbundled, but they carry official marketplace provenance so product/audit treat them as preinstalled official registry entries. - Registry presentation data can start as static catalog data under
plugins/registry/officialandplugins/registry/community, or mirror that shape in the registry repo until it exists. - The main site should render official plugins from generated catalog artifacts, not by importing daemon internals or walking
plugins/_officialdirectly. - Community submissions can land as plugin source folders under
plugins/community/<vendor-or-plugin>and be referenced byplugins/registry/community/open-design-marketplace.json; trust tier stays encoded per source/entry.
Namespace and source policy:
- Published registry package ids are always
vendor/plugin-name. vendoris the publisher namespace: GitHub org/user for GitHub-backed publish, or enterprise/org namespace for database-backed registries.plugin-nameis a stable lowercase slug. Once a package is published, the full id is immutable.- Existing flat local/bundled ids remain readable for compatibility, but registry publish must require the namespaced form.
- A rename creates a new package id plus an alias/deprecation entry on the old id; it must not rewrite historical installs or lockfiles.
- The source-of-truth repo is declared in
open-design.jsonasplugin.repo. Published plugins can be "anything that packs": no special repo layout is required beyond passingod plugin validateandod plugin pack, shipping a runnable skill anchor, and declaringplugin.repo. plugin.repopoints at the canonical source repository or subdirectory, for examplehttps://github.com/open-design/plugins/tree/main/make-a-deck. Registry entries may mirror it assourceRepositoryfor search and review without replacing the manifest field.
Minimum entry shape:
{
"name": "open-design/make-a-deck",
"title": "Make a Deck",
"version": "1.2.0",
"distTags": {
"latest": "1.2.0",
"beta": "1.3.0-beta.1"
},
"source": "github:open-design/plugins@3f4c2d1/make-a-deck",
"ref": "3f4c2d1",
"dist": {
"type": "github",
"archive": "https://github.com/open-design/plugins/archive/3f4c2d1.tar.gz",
"integrity": "sha512-...",
"manifestDigest": "sha256-..."
},
"publisher": {
"id": "open-design",
"github": "open-design"
},
"sourceRepository": "https://github.com/open-design/plugins/tree/main/make-a-deck",
"homepage": "https://open-design.ai/plugins/open-design/make-a-deck/",
"license": "MIT",
"capabilitiesSummary": ["prompt:inject", "fs:read"],
"tags": ["deck", "presentation", "investor"],
"trust": "official",
"yanked": false
}
plugins[] in the generated marketplace file can stay permissive, but the official registry repo should validate this stricter shape before publishing.
Required open-design.json fields for registry publish:
{
"specVersion": "1.0.0",
"name": "open-design/make-a-deck",
"version": "1.2.0",
"plugin": {
"repo": "https://github.com/open-design/plugins/tree/main/make-a-deck"
}
}
The current manifest parser is passthrough, so plugin.repo can land before a strict schema bump. The formal schema update should still happen before the public publish flow is considered stable.
Backend Architecture
Introduce a small registry backend interface so "GitHub today, database later" is not just a slogan:
interface PluginRegistryBackend {
listSources(): Promise<RegistrySource[]>;
refreshSource(sourceId: string): Promise<RegistryRefreshResult>;
search(query: RegistrySearchQuery): Promise<RegistrySearchResult[]>;
resolve(spec: PluginSpec, options?: ResolveOptions): Promise<ResolvedPlugin>;
publish(request: PublishRequest): Promise<PublishPlan | PublishResult>;
doctor(sourceId?: string): Promise<RegistryDoctorReport>;
}
Initial implementations:
LocalMarketplaceJsonBackend: reads cachedopen-design-marketplace.jsonrows from SQLite.GitHubRegistryBackend: shells through a narrowGhClientadapter forgh auth,gh api,gh repo, andgh properations. It creates PRs against the official registry repo, reads entries from GitHub contents/raw URLs, and never persists a GitHub token itself.StaticHttpsRegistryBackend: supports public self-hosted JSON over HTTPS. Private authenticated catalogs in v1 should be GitHub/GitHub Enterprise sources reached throughgh; bearer/header/basic auth profiles are future work.
Future implementation:
DatabaseRegistryBackend: same interface backed by a database, object storage, and first-party auth.
Implementation boundary:
- Contracts define request/response DTOs.
- Daemon owns cache, integrity verification, install side effects, and
ghinvocation. GitHub credentials remain owned bygh. - CLI is the stable external surface.
- Web calls daemon APIs only, never GitHub directly.
Enterprise And Commercial Backend Path
The registry contract must support a future enterprise deployment where the storage backend is a real database instead of GitHub/JSON. This is a product requirement for toB, not an optional refactor.
The same PluginRegistryBackend interface should map cleanly to a database-backed service:
organizations: tenant, plan, billing/entitlement status, SSO config.registry_sources: official, community, team, private, mirrored GitHub, external HTTPS.plugin_packages: stable package identity, publisher, owner org, visibility.plugin_versions: semver, dist-tags, changelog, yanked/deprecated state, compatibility.release_artifacts: archive URL/object key, manifest digest, archive integrity, size, scan status.publishers: GitHub owner, verified domain, org membership, signing/verifier state.reviews: approval workflow, policy exceptions, reviewer notes, security scan result.installs: org/project/user install records, resolved version, lock provenance.policies: allowlists, blocked capabilities, required approval, source restrictions.audit_events: publish, approve, yank, install, upgrade, trust, policy decision.
Enterprise self-hosting model:
- A company can deploy OD plus
DatabaseRegistryBackendinside its own VPC. - The enterprise registry exposes the same daemon-facing and CLI-facing contract as the GitHub/static backend.
od marketplace add <enterprise-url>and in-app Sources continue to work; only the source backend changes.- GitHub Enterprise can remain the identity/source-of-code layer through
gh, while the database stores registry state, approvals, scans, and policy decisions. - Fully managed commercial OD can use the same database backend with multi-tenant org boundaries, billing entitlements, SSO/SAML/OIDC, SCIM later, audit exports, and private object storage.
Commercial invariant:
- v1 data can be static files in
plugins/registry/official, but contracts must not assume "registry equals GitHub repo". - UI must ask the daemon for registry/search/resolve/publish data; it must never assume the catalog is a local directory.
- CLI commands must not expose GitHub-specific nouns except where the source explicitly is GitHub.
od plugin publish --to open-designmay useghinternally, but the command contract should survive a later database backend. - Trust, provenance, versioning, integrity, and audit fields are mandatory because those become enterprise policy inputs later.
GitHub CLI Dependency And Auth
gh is a first-class runtime dependency for registry-backed publishing and private GitHub sources.
- Installing the
odCLI should ensureghis available. If the host has nogh, the installer bootstraps it using the platform package path available to that distribution channel, or fails with a precise remediation when auto-install is impossible. od plugin loginwrapsgh auth loginwith Open Design copy and required scopes/host guidance.od plugin whoamiwrapsgh auth statusplusgh api userand prints the active account, host, scopes, and whether it can publish to the configured registry repo.od plugin logoutcan wrapgh auth logoutonly after an explicit confirmation, because it affects the user's global GitHub CLI session.- Daemon code must never read or store GitHub tokens directly. When it needs GitHub data, it calls a
GhClientabstraction that shells out toghor consumesgh auth tokenonly in-memory for one request. - GitHub Enterprise is modeled as a
ghhost, not a separate auth backend.
CLI Plan
Keep existing od marketplace and od plugin naming. Avoid adding a second registry noun until there is a real non-marketplace backend exposed to users.
Marketplace Source Commands
od marketplace add <url> [--trust official|trusted|restricted] [--github-host <host>] [--refresh daily|manual|startup]od marketplace list [--json]od marketplace info <id> [--json]od marketplace plugins <id> [--query <q>] [--tag <tag>] [--json]od marketplace search "<query>" [--tag <tag>] [--json]od marketplace refresh <id>od marketplace remove <id>od marketplace trust <id> --trust official|trusted|restrictedod marketplace login <id>(delegates togh auth loginfor the marketplace host)od marketplace whoami <id>(delegates togh auth status/gh api user)od marketplace doctor [id] [--strict] [--json]
Plugin Consumer Commands
od plugin install <source-or-name>od plugin install <name>@<version-or-tag> [--from <marketplace-id>] [--save-lock]od plugin upgrade [id] [--policy latest|pinned|range] [--dry-run]od plugin outdated [--json]od plugin list/info/search/apply/run/trust/uninstall/doctorod plugin view <name> [--from <marketplace-id>] [--versions] [--json]od plugin lock write|verify|diff
Plugin Author Commands
od plugin login [--host <github-host>]od plugin whoami [--host <github-host>] [--json]od plugin scaffoldod plugin validateod plugin packod plugin publish --to open-design [--dry-run]od plugin publish --to marketplace-json --catalog <path-or-url> [--branch <name>]od plugin publish --to github --repo <owner/repo> [--public|--private]od plugin deprecate <name>@<version> --reason <text>od plugin yank <name>@<version> --reason <text>
Publish v1 should use gh:
od plugin loginwrapsgh auth login;od plugin whoamiwrapsgh auth statusandgh api user.od plugin publish --to open-designvalidates, packs if needed, renders the registry entry, creates a branch, opens a PR, and prints the PR URL.od plugin publishusesgh api,gh repo fork, andgh pr createthrough a testableGhClient; it does not implement a parallel GitHub OAuth flow.- The CLI must have
--dry-run --jsonfor CI and agent workflows.
Web And Presentation Plan
Public Site
Main navigation:
- Plugins
- Official
- Community
- Sources
- Publish
- Docs
Listing page:
- Search by name, description, tags, task kind, capabilities, publisher, and trust.
- Filters: Official, Trusted community, Restricted community, Works headless, Has preview, Has examples, Requires connectors.
- Cards show title, publisher, version/tag, task kind, trust tier, capability badges, install count or stars if available, and preview thumbnail.
- Primary CTA: copy install command.
- Secondary CTA: open in desktop through
od://plugins/<name>?source=<source>.
Detail page:
- Hero should be the plugin output or preview, not generic marketing art.
- Show install command,
od plugin view,od plugin runexample, version selector, dist-tags, changelog, source repo, publisher, license, integrity, capabilities, connector requirements, examples, and security notes. - Show provenance in plain language: "Indexed from GitHub repo X at commit Y via marketplace Z".
- Show review state: official, trusted, restricted, yanked, deprecated, scan status.
Publish page:
- "Contribute to Open Design registry" flow with required files, checklist, CLI command, and GitHub PR path.
- "Self-host your own source" flow with static JSON instructions and
od marketplace add.
In-App UI
Replace the current Plugins tab model:
Installed: installed plugins, upgrade/outdated state, use/uninstall/trust.Available: entries from configured marketplaces, install/use, filters, source badges.Sources: add URL, refresh, remove, trust tier, GitHub host/auth status, cache status.Team: private catalogs, org allowlist, default trust, audit and refresh policy.
Use the current Installed / Available / Sources / Team model as the product baseline. Available is discovery, Sources is registry source management, and Team is future enterprise governance.
Available plugin card states:
- Not installed:
Install - Installed same version:
Use - Installed older version:
Upgrade - Restricted capabilities:
Review - Yanked/deprecated: disabled install with reason
Detail page additions:
- Marketplace provenance: source marketplace id/name, resolved source, marketplace trust, plugin entry name/version.
- Publisher block: GitHub owner, verified status, homepage, license.
- Version block: selected version/tag, resolved ref, manifest digest, archive digest.
- Risk block: capabilities, connector requirements, install source, trust tier, yanked/deprecated state.
- Commands block: install, run, view, trust, lock.
Trust And Provenance Rules
Use one vocabulary everywhere:
- Marketplace trust:
official | trusted | restricted - Installed plugin trust:
bundled | trusted | restricted
Mapping:
officialmarketplace installs astrustedby default, with provenance indicating official source.trustedmarketplace installs astrustedby default.restrictedmarketplace installs asrestrictedby default.- direct GitHub, URL, and local installs remain
restrictedunless the user explicitly grants trust/capabilities. - bundled first-party plugins remain
bundled.
Installed record semantics:
- Keep
sourceKindas transport:github | url | local | marketplace | bundled | user | project. - Add/keep
sourceMarketplaceIdfor discovery provenance. - Add
sourceMarketplaceEntryName,sourceMarketplaceEntryVersion,marketplaceTrust,resolvedSource,resolvedRef,manifestDigest, andarchiveIntegrity. - UI grouping should use provenance first, transport second.
Install flow for bare marketplace names:
- Resolve
name[@version-or-tag]across configured marketplaces. - Select marketplace by priority or explicit
--from. - Resolve dist-tag/range to an exact version.
- Resolve transport source and ref.
- Fetch bytes.
- Verify manifest digest and archive integrity when present.
- Persist installed record with marketplace provenance.
- Write/update lock entry when requested.
Artifact hosting and yanking:
- Primary tarball hosting for GitHub-backed v1 is GitHub Releases on the source repo.
- Tarball hosting fallback is allowed: any HTTPS archive URL can be used when GitHub Releases are unavailable, including enterprise mirrors and object storage, but
integrityis mandatory. - Database-backed enterprise registries can store tarballs in private object storage and expose signed/download URLs through the same
dist.archivefield. - Versions are never hard-deleted from the registry. Yanking marks a version with
yanked: true,yankedAt, andyankReason. - New installs and upgrades refuse yanked versions unless the user explicitly installs an already-locked exact version with a warning.
- Existing lockfiles remain reproducible as long as the archive is still retrievable and integrity matches.
- Package-level deprecation is separate from version yanking: deprecation warns and can point to a replacement package id; yanking blocks new resolution for that version.
Private And Enterprise Sources
Private marketplace support in v1 should reuse GitHub auth instead of introducing token profiles:
- Auth is
ghauth for GitHub.com or GitHub Enterprise hosts. - Credentials live in
gh, never insideopen-design-marketplace.jsonand never inside the daemon database. od marketplace addstores URL, trust tier, GitHub host, refresh policy, and cache policy.- Enterprise policy can restrict install sources to an allowlist of marketplace ids, GitHub orgs, or publisher ids.
- Offline cache should keep the last valid marketplace JSON, entry digests, and downloaded archives when enabled.
- Audit events should record add/remove/refresh/trust/install/upgrade/yank/deprecate decisions.
- Non-GitHub auth forms such as bearer/header/basic/mTLS are reserved for the future database or generic HTTPS backend, not the first GitHub-backed registry pass.
Phased Plan
P0: Close The Contract Loop
Goal: make the current federated marketplace implementation trustworthy and auditable.
- Change
MarketplaceTrustSchematoofficial | trusted | restricted. - Migrate/accept old
untrustedrows asrestrictedduring read or migration. - Extend install options with marketplace provenance fields.
- When
/api/plugins/installresolves a bare marketplace name, pass the fullResolvedPluginEntryintoinstallPlugin(). - Persist
sourceMarketplaceId, entry name/version, marketplace trust, and resolved source/ref oninstalled_plugins. - Map marketplace trust into installed plugin default trust.
- Extend
AppliedPluginSnapshotwith marketplace entry name/version and resolved source metadata. - Add tests: marketplace add -> install by name -> installed record contains source marketplace id and inherited trust.
- Add tests: restricted marketplace install stays restricted even when transport is GitHub.
P1: Registry Entry And Version Semantics
Goal: move from "catalog index" to "registry entry".
- Update plugin manifest schema to allow published namespaced ids
vendor/plugin-namewhile keeping flat ids readable for legacy local/bundled plugins. - Add formal
plugin.reposchema field toopen-design.jsonand require it for registry publish. - Extend marketplace entry contract and JSON schema with
versions,dist,integrity,manifestDigest,publisher,homepage,license,capabilitiesSummary,distTags,deprecated, andyanked. - Keep
.passthrough()for community extensions. - Add
od marketplace plugins <id>with pagination/search/filter. - Add
od plugin install <name>@<version-or-tag>. - Add resolver support for exact version, dist-tag, and conservative
^/~ranges. - Add initial
.od/od-plugin-lock.jsonshape with name, version, source, marketplace id, resolved ref, manifest digest, archive integrity. - Add
od plugin lock verify. - Add
od plugin outdated. - Add yanking metadata and resolver behavior: yanked versions are visible for audit and refused for new resolution. Exact locked replay warning remains a route-level follow-up once lock verify lands.
P2: GitHub-Backed Publish Flow
Goal: make Open Design contributions feel like npm publish, while actually opening a GitHub PR.
- Define official registry repo layout and generated index build step.
- Make
ghan explicitodCLI dependency and installer prerequisite/bootstrap step. - Add
od plugin loginandod plugin whoamias wrappers overgh auth login/statusandgh api user. - Add
od plugin publish --to open-design --dry-run --json. - Use
gh auth status,gh api,gh repo fork, andgh pr createthrough a narrowGhClientadapter. - Generate a registry entry from local plugin metadata,
plugin.repo, current ref, digest, publisher, license, and capability summary. - Enforce "anything that packs": publish requires successful
validateandpack, not a special source repository layout. - Upload the
.tgzto GitHub Releases when available, or accept a fallback HTTPS tarball URL with mandatory integrity. - Run
od plugin validate,pack,doctor, and integrity calculation before PR creation. - Add PR template with source, version, capability risk, preview, screenshots, and validation output.
- Add CI in registry repo: schema validate, source fetch, plugin manifest parse, checksum verify, preview smoke, blocked source scan.
- Add
od plugin publish --to marketplace-json --catalog <path>for self-hosted static catalogs.
P3: Product UI And Public Site
Goal: upgrade from "installed plugin gallery" to "multi-source plugin registry".
- Replace the Plugins page tabs with
Installed / Available / Sources / Team. - Enable source management in-app: add URL, refresh, remove, and trust tier using the existing
/api/marketplacesendpoints. - Add an
Availableview from configured marketplace manifests. Current implementation reads cached manifests returned by/api/marketplaces; a follow-up should move this to a typed paginated/api/marketplaces/:id/pluginsresponse for large catalogs. - Add install/use/upgrade card states for available entries. Current install uses the existing bare-name
od plugin install <name>path and now preserves provenance; explicit--from <marketplace-id>remains a P1 follow-up. - Rename the Home page official shelf copy to
Official startersorOfficial installed, and add a lightweightBrowse registrypath to/pluginsso Home stays a fast-use surface while/pluginsremains the registry console. - Make
Create pluginlaunch an agent-assisted authoring flow backed byod plugin scaffold/validate/pack/publish, including local install/run validation before publish andghlogin/whoami checks before opening a registry PR. Current slice updates the agent prompt and CLI wrapper; full GitHub PR mutation remains in P2. - Add public
/plugins/route onapps/landing-pagefor open-design.ai: searchable official/community registry listing, static plugin detail pages, canonical/OG/Twitter metadata, JSON-LD item/detail data, and homepage/header entry points. - Add source filters: Official, Community, My plugins, Team, specific source.
- Add detail provenance, publisher, version, integrity, command, and risk sections to the public website detail route; in-app drawer polish remains tracked separately.
- Add GitHub host/auth status, cached status, and refresh policy to the source manager.
- Add public plugin detail
od://deep links and static search JSON. README rendering and preview galleries remain content-quality follow-ups. - Decide whether
/marketplaceshould redirect to/plugins/or remain an alias for compatibility.
P4: Private, Enterprise, And Offline
Goal: make third-party/self-hosted registries first-class while staying compatible with a future database backend.
- Add private GitHub/GitHub Enterprise marketplace auth entry through
od marketplace login <id|url> --host <host>, delegated togh. - Keep source management behind
RegistryBackend, not GitHub-specific API calls. - Add enterprise allowlist policy: source ids, publishers, GitHub orgs, capabilities.
- Add refresh policy and last-known-good cache.
- Add offline install from cache when enabled.
- Add audit log/events for source and install decisions.
- Add Team page for private catalog status, trust defaults, org policy, and audit.
- Document static hosting options: GitHub Pages, private GitHub repos, GitHub Enterprise, S3/R2 public HTTPS, internal HTTPS, and private network caveats.
P5: Database Backend And Commercial ToB
Goal: make the registry deployable as an enterprise service with real database state.
- Define database-backed registry schema for orgs, sources, packages, versions, artifacts, publishers, reviews, policies, installs, and audit events.
- Add
DatabaseRegistryBackendbehind the same resolve/search/publish/doctor interface. - Add object-storage abstraction for plugin archives and preview assets.
- Add org-scoped auth/identity boundary; hosted can use first-party auth, self-host can use enterprise IdP integration later.
- Add policy engine hooks: source allowlist, capability denylist, required review, yanked/deprecated enforcement, approval exceptions.
- Add admin APIs and UI for Team/Enterprise registry governance.
- Add migration/import from static
open-design-marketplace.jsonand GitHub registry repo into database rows. - Add export back to
open-design-marketplace.jsonso enterprises can mirror or air-gap catalogs. - Add backend parity tests for static/GitHub/database list/search/resolve/publish. Full CLI/UI parity against DB remains an enterprise API follow-up.
P6: npm-Grade Hardening
Goal: make updates reproducible and safe enough for CI/enterprise use.
- Add range resolution only after exact/tag resolution is solid.
- Add update policies:
pinned,patch,minor,latest. - Add yanked/deprecated handling in resolver; UI surfacing remains part of detail drawer polish.
- Add publisher verification hooks against GitHub org/user metadata.
- Add signed provenance schema hooks, without blocking v1 on PKI.
- Add
od marketplace doctorchecks: entry naming/source/yank/capability/license/integrity basics, with strict mode for warnings. - Add daemon smoke coverage: add marketplace -> resolve/search -> install by name -> installed row and lockfile preserve provenance.
Suggested First PR Split
-
Provenance fix
- Files:
packages/contracts/src/plugins/*,apps/daemon/src/plugins/marketplaces.ts,apps/daemon/src/plugins/installer.ts,apps/daemon/src/plugins/registry.ts,apps/daemon/src/server.ts, daemon tests. - Outcome: marketplace installs are auditable.
- Files:
-
Trust vocabulary cleanup
- Files: contracts, daemon marketplace persistence/read path, CLI help, web types.
- Outcome: no more
untrustedvsrestricteddrift.
-
Available marketplace API
- Files: daemon
/api/marketplaces/:id/plugins, CLIod marketplace plugins, contracts response shape. - Outcome: UI can browse not-yet-installed entries without scraping cached manifests client-side.
- Files: daemon
-
Plugins UI source management
- Files:
PluginsView, marketplace state helpers, CSS, web tests. - Outcome: add/refresh/remove/trust source operations in-app.
- Files:
-
GitHub publish dry run
- Files:
publish.ts, CLI publish route, tests, docs. - Outcome: authors can see the exact registry PR body/entry before mutation.
- Files:
-
GitHub CLI auth wrapper
- Files: CLI install/bootstrap docs,
apps/daemon/src/cli.ts, newGhClientadapter, tests. - Outcome:
od plugin login/whoamiprovide the product auth surface while reusing the user'sghsession.
- Files: CLI install/bootstrap docs,
Validation Bar
Minimum validation for regular registry work:
pnpm guard
pnpm typecheck
pnpm --filter @open-design/daemon test
pnpm --filter @open-design/web test
Additional validation by area:
- Contract/schema changes: add parser/schema fixtures in
packages/plugin-runtime/testsand contracts tests where appropriate. - CLI changes: add daemon CLI tests or command-level tests matching the existing harness.
- UI changes: add web component/state tests; use Browser/Playwright screenshots for larger visual route changes.
- Publish/GitHub changes: tests must run without real network by injecting a fake
GhClientor dry-run backend. - Enterprise/private source changes: tests must assert GitHub credentials stay in
ghand are never serialized into marketplace manifests or daemon SQLite. - Registry product evaluation cases are tracked in
docs/testing/plugin-registry-eval-cases.md; keep it updated whenever Sources, Available, Installed, GitHub publish, or enterprise backend behavior changes.
Open Questions
- Should the official registry live in this monorepo or a separate
open-design/plugin-registryrepo? Separate is cleaner for community PR review and static-site generation. - Should
officialmarketplace trust be allowed for user-added URLs, or only for built-in source ids shipped by Open Design? Recommendation: only built-in sources can beofficial; user-added sources can betrustedorrestricted. - Should lockfile be project-local (
.od/plugins-lock.json) or user-global? Recommendation: project-local for reproducible runs, with user-global cache as an implementation detail. - Should
od plugin publish --to marketplace-jsonmutate a local catalog file directly or create a branch/PR when the catalog URL maps to GitHub? Recommendation: support both, but default to PR when a GitHub remote is detectable. - How much popularity/ranking data should the official site show before telemetry policy is settled? Recommendation: show stars/downloads only when sourced from public GitHub or explicit registry events; keep install telemetry opt-in.
Resolved Decisions
- Namespace policy: registry package ids are
vendor/plugin-name. - Source repo shape: accept anything that passes
od plugin validateandod plugin pack. - Source pointer: registry-published
open-design.jsonmust includeplugin.repo. - Tarball hosting fallback: yes, with mandatory integrity.
- Yanking: yes; mark versions yanked instead of deleting them.
- Plugin ID stability: yes; ids are immutable after publish, and renames create new ids plus aliases/deprecations.
Product Judgment
The architecture is already pointed in the right direction: Open Design can become a "multi-source plugin registry" without giving up local-first/headless operation. The next move should not be a bigger gallery. It should be provenance, trust vocabulary, and exact resolution first; then the UI can safely graduate from "official plugins plus import" to "installed and available plugins across sources."