Skip to content

The virtual catalog

One URL for the whole governed estate. The gateway synthesizes a catalog repository — served by the ordinary facade at /git/catalog — containing the currently approved-and-served snapshot of every marketplace, so a consumer runs one claude plugin marketplace add instead of one per marketplace.

$ claude plugin marketplace add https://skills.corp.example/git/catalog
$ claude plugin install acme-tools-deploy   # <marketplace>-<plugin>

What's inside

Each served marketplace is vendored under a subdirectory named after it, and one merged manifest ties them together:

catalog/
  .claude-plugin/marketplace.json   # merged: names "acme-…", sources "./acme/…"
  acme/…      # acme's approved snapshot, byte-identical
  tools/…     # tools' approved snapshot

Plugin names are prefixed with their marketplace (acme-hello), and every source is a relative path into the vendored subtree — the catalog never points anywhere outside itself.

Strictly derived content

The catalog is rebuilt from what each marketplace's published repository is serving right now — the same ref the facade serves. Nothing held, rejected, or revoked can appear in it, and the approval gate is completely untouched: the catalog adds a view, never a way in.

Rebuilds happen on their own at the two moments the served estate changes:

  • an approval publishes a snapshot → the catalog gains or updates that marketplace;
  • a revocation unpublishes one → the marketplace leaves the catalog, with no operator action.

Each catalog revision is a parentless commit: history depth one, so a retracted constituent is unreachable from every advertised catalog ref the moment the next revision lands — a consumer cannot fetch yesterday's catalog to get around a retraction.

With nothing serving at all, the catalog serves a manifest with an empty plugin list rather than nothing: an empty estate and a broken gateway must look different.

Provenance and audit

GET /api/v1/catalog returns the served revision and its constituents — the (marketplace, SHA) pairs vendored into it, which are also recorded in the catalog commit itself. Fetches of /git/catalog land on the audit ledger under the name catalog like any marketplace, and a manual POST /api/v1/catalog/rebuild (the on-demand repair path) is ledger-recorded with the acting identity and requires admin (see role enforcement); the catalog read stays open to any session.

Configuration

The catalog name is reserved: registering a marketplace called catalog is refused, because the catalog occupies that facade path. See Configuration for the skills-gateway.catalog block.

Contested names

The <marketplace>-<plugin> join is not injective: marketplace a with plugin b-c and marketplace a-b with plugin c both claim a-b-c. A name is published only when exactly one served marketplace claims it. A contested name is withheld from every claimant, and the same rule withholds a plugin name one marketplace declares twice. Uncontested names are unaffected, and their order in the manifest is unchanged.

Withholding it from both is the point. Awarding a contested name to one claimant serves that marketplace's content under the name the other's consumers install — content substitution across a publisher boundary — and the award was re-decided on every rebuild, so a newly registered marketplace whose name sorted earlier could silently change what an existing install name means. Omitting a name is legitimate; substituting content is not.

Every withheld name is announced: a WARN log line, a ledger entry catalog-name-collision from the catalog-builder actor naming the claimants, an increment of the skills_gateway.catalog.collisions counter, and an entry in the collisions field of GET /api/v1/catalog. The counter is untagged — a contested name is unbounded cardinality, so the ledger and the catalog read are where you look for which. That field is persisted in the catalog commit beside the constituents, so the served revision reports its own collisions.

Resolve it upstream — rename one of the two plugins, or one of the two marketplaces — and the next rebuild publishes both names.