/docs/plugins/<name> and /docs/plugins/by/<maintainer> are generated at build time by a small Docusaurus plugin (website/plugins/plugin-catalog-pages) from the same plugins.json the grid fetches, so a merged catalog PR is the only way a page appears or changes. Plugin page: full description with the Disclosure sentence pulled into a callout, pinned commit / version / platforms / requires facts, tools, hooks and env chips, Desktop install button + CLI command, repository and reviewed source links, optional screenshots gallery, optional README rendered from the PINNED commit (fetched at build, allowlisted HTML, raw HTML dropped, relative links and images resolved against the pinned tree), and a "More by this author" shelf. Author page: everything a maintainer has in the catalog with total stars and a profile link when all repos share one owner. Cards on the grid link through: the title is a link and a card click opens the page (the Desktop picker embed keeps expanding in place). The shared vocabulary (entry type, tier/category taxonomy, link builders) moved to src/components/PluginCatalog/catalog.ts so the three surfaces cannot drift. Docs: field table and catalog README describe screenshots:/readme: and the pages.
Hermes Plugin Catalog
Curated, Nous-approved Hermes plugins. Each YAML file in this directory
(except removed.yaml) is one catalog entry, discoverable via
hermes plugins catalog / hermes plugins search and installable with
hermes plugins install <name>.
Admission policy
Presence in this directory is the trust signal. The rules that keep it meaningful:
- Human-merged gate. Entries are added only via a PR to the
hermes-agentrepository, reviewed and merged by a maintainer. There is no self-serve registry, no automated ingestion. - Exact SHA pins are mandatory. Every entry pins a full 40-character commit SHA. Branches, tags, and short SHAs are rejected by the loader. Installs clone the repository and check out exactly that commit.
- No self-updating code. A listed plugin must not fetch and replace
its own files (in-app "check for updates", signed release downloaders,
remote
plugin.jsloaders). The exact SHA pin is the trust model; a self-updater lets an installed copy move to a commit nobody reviewed. Updates reach users only through a SHA-bump PR here plushermes plugins update <name>. Keep the updater in the standalone distribution if you want one; strip it from the catalog build. - SHA bumps are new PRs. Updating an entry's pin is a new PR whose diff (old SHA → new SHA) is re-reviewed like any other change — reviewers are expected to look at the upstream commit range being adopted.
- Owner-or-major-contributor submissions, or a maintainer-curated sweep. An entry may be submitted by the plugin repository's owner or a major contributor to it; drive-by submissions of third-party repos are declined. Hermes maintainers may also add entries in batches from a reviewed sweep of community plugins (every pin validated and scanned at the pinned commit, self-updater and credential-store checks run, English-first UI). Authors of swept-in entries keep control: a PR from the owner adjusting or removing their entry is accepted on request, and SHA bumps stay owner-or-maintainer PRs under rule 4.
- Declared capabilities must match reality. The
capabilities:block (tools, hooks, middleware, env vars) must match what the plugin actually registers at the pinned commit. Validation fails the entry otherwise — undeclared capability creep is treated as a security issue. - The install scanner runs at admission.
hermes plugins validateincludes thesecurity scancheck:dangerousfails the entry;cautionfindings appear as warnings in the CI log and the reviewer reads them before merging. In exchange, installs at the pinned SHA acceptcautionwithout a prompt (dangerousstill blocks). Review the warnings; do not merge past them. - Desktop plugins stay inside the SDK surface. A
desktop/plugin.jsruns in the Desktop renderer with the app's full authority (the loader isolates errors, not capabilities), so a listed one may only use the plugin SDK: no prototype patching (X.prototype.y =,Object.defineProperty(...prototype), noeval/new Function, noimport()of anything but@hermes/plugin-sdk/react(app bundle chunks, blob or http URLs included), no script-tag injection, no reaching into the app's internal stores.hermes plugins validaterefuses these at admission (desktop surfacecheck); a plugin that needs a capability the SDK lacks asks for an SDK hook instead of patching around it.
Entry schema
name: example-plugin # [a-z0-9_-]{1,64}, the catalog key
repo: https://github.com/owner/repo # https:// only
sha: <40-hex commit sha> # mandatory exact pin
subdir: "" # optional path within the repo
description: One-line description.
maintainer: OwnerName
tier: official # official | community (default community)
category: memory # desktop | memory | platform | web | tools | voice | automation | models | general
# (default desktop) — the shelf the entry sits on at /docs/plugins
requires_hermes: ">=0.19" # optional
docs_url: "" # optional
version: "1.4.0" # optional human label for the sha (quote it); shown as "1.4.0 @ abcd1234"
image: "" # optional https image on a GitHub host, 2:1 (e.g. 1200x600), e.g.
# https://raw.githubusercontent.com/owner/repo/<sha>/docs/banner.png
screenshots: [] # optional, up to 6 https images on a GitHub host; gallery on /docs/plugins/<name>
readme: false # optional; true renders the README FROM THE PINNED SHA on /docs/plugins/<name>
platforms: [] # optional, e.g. [linux, macos]; empty = all
capabilities:
provides_tools: []
provides_hooks: []
provides_middleware: []
requires_env: []
version, image, screenshots and readme are cosmetic: none is parsed or
used to pick what installs. The sha stays the release; bump version in the
same PR that bumps sha so the label on the card matches the code. Images must
live on raw.githubusercontent.com, github.com or *.githubusercontent.com
so the Desktop catalog and the docs site never fetch from third-party hosts;
pin the raw URL to the entry's commit and the picture is as immutable as the
code.
Every entry gets a page at https://hermes-agent.nousresearch.com/docs/plugins/<name>
and every maintainer a page at /docs/plugins/by/<maintainer>, both generated
from these files at docs build time. screenshots: fills the page's gallery;
readme: true makes the build fetch README.md (from subdir if set, else the
repo root) at the pinned sha — GitHub and GitLab repos — and render it
through an allowlist (raw HTML dropped, links and images resolved against the
pinned tree). The page therefore shows the README the reviewer read, and it
changes only when a reviewed re-pin lands.
removed.yaml — the blocklist
When an entry is pulled from the catalog for security or policy reasons, it
is recorded in removed.yaml with a reason and date. The installer refuses
to install anything matching a removed entry's name or repo URL, so a
malicious plugin cannot be re-installed from a stale identifier after
removal. Removals, like additions, land via reviewed PRs.
Delisting is different from removal: an entry that is merely unmaintained, superseded, or squatting a name it is not affiliated with is deleted from the catalog (plain file removal, users who already installed it are unaffected) and is welcome back under a distinct name.
Names
The catalog key and the manifest name: are what users search, install, and — for memory
providers — put in memory.provider. A memory / exclusive entry must not reuse the name of
another provider or of a well-known upstream project it is not affiliated with: two providers
registering the same register_memory_provider name make memory.provider ambiguous
(whichever loads last wins). Reviewers check the registered provider name, not just the file
name, and the affiliated project gets the bare key.