Plugin directory¶
Every change-correlation and notification-channel plugin Etminan ships today — with an honest status on each, and where a contributed one would fit.
This is a running list, not a fixed one. Every ticket system and paging tool Etminan talks to is a separately-vetted, allowlisted, SHA-256-pinned external script under the shared Plugin API — no in-process HTTP clients, no hardcoded integrations. As more plugins get written, by us or by you, they get added here.
A directory, not a marketplace — yet
etminan-verifier plugins list/install/update ships in the verifier binary
today: a certified-install path that verifies a catalog manifest's Ed25519
signature against a compiled-in trust anchor before trusting anything it
lists, then downloads, hashes, and allowlists a plugin in one command. What
doesn't exist yet is a published, signed catalog for it to fetch — so
every plugin below is still a copy-and-verify starting point, installed by
the manual six-step recipe in Writing a new plugin.
Status legend¶
Each plugin carries an honest status rather than a blanket "supported" claim:
| Status | Meaning |
|---|---|
| Built-in | Ships in the binary itself, not a plugin — always available, no allowlist entry needed. |
| Requires testing | Real code, built directly against the target system's documented (or read-from-source) API — not yet verified against a live instance. Test against your own before production use. |
| Certified | Not yet available. Reserved for a plugin published in a real, signed catalog release. Nothing carries this tag today because the catalog-publishing pipeline is still pending. |
How the catalog works¶
The catalog is a single signed JSON manifest. When you run a catalog command, the verifier fetches that manifest and its detached signature, verifies the signature against a dedicated, single-purpose Ed25519 key compiled into the binary — before parsing a single byte as JSON — and only then trusts the entries it lists.
sequenceDiagram
participant Op as Operator
participant V as etminan-verifier
participant C as catalog host (curl)
Op->>V: plugins install pagerduty
V->>C: fetch plugins-catalog.json + .sig
C-->>V: manifest bytes + detached signature
V->>V: Ed25519 verify vs compiled-in anchor
Note over V: refuse everything if it fails —<br/>nothing parsed until verified
V->>V: rollback check (catalog_version ≥ floor)
V->>C: download plugin file (url)
C-->>V: plugin bytes
V->>V: SHA-256 == manifest's pinned hash?
V->>V: write root-owned, append .conf,<br/>re-verify the file on disk
V-->>Op: installed + "add to ETMINAN_*_CHANNELS, restart"
Each manifest entry is a small, fixed record:
{
"catalog_version": 1,
"plugins": [
{
"name": "pagerduty",
"type": "notify",
"version": "1.2.0",
"sha256": "aa…",
"url": "https://etminan.dev/downloads/plugins/notify/pagerduty.sh",
"description": "PagerDuty Events API v2"
}
]
}
Beyond the signature, the catalog client hardens every step:
- Only
notifyandchange-sourcetypes are accepted; an entry'snameand theurl's filename component are restricted to ASCII letters/digits/-/_/.so a crafted value can't corrupt the whitespace-delimited.confformat or an install path. - Rollback protection.
catalog_versionis a monotonic high-water mark (stored in a root-owned.catalog-versionfile); a replayed older-but-validly- signed manifest is refused rather than silently downgrading a plugin. - Bounded, redirect-averse fetch. Fetches shell out to
curl(no in-process HTTP client, on purpose), capped at 64 MiB,http(s)-only, with redirects off by default (opt in withETMINAN_PLUGIN_ALLOW_REDIRECTS) to close an SSRF-via-302 path. - Post-write re-verification. After writing the file and its
.confline, the verifier re-verifies the file actually on disk through the same runtime gate; if it fails, the.confentry is rolled back so a failed install leaves no trace.
Browsing and installing from the catalog¶
# list every catalog entry with its installed status
etminan-verifier plugins list
# install one (must run as root — the written file has to be root-owned)
sudo etminan-verifier plugins install pagerduty
# later, re-pin an installed plugin to the catalog's current version
sudo etminan-verifier plugins update pagerduty
plugins list prints each entry's name, type, version, install status, and
description. Status is one of not installed, installed, up to date, or
installed, update available (the pinned hash differs from the catalog's).
No live catalog yet
The mechanism above is shipped and tested, but nothing publishes a real,
signed plugins-catalog.json at its default URL
(https://etminan.dev/downloads/plugins-catalog.json) yet, so plugins
list/install/update error on the fetch until that pipeline exists. Until
then, install every plugin below by the manual recipe in the
Plugin API chapter. request-tracker and
freeitsm — already listed as change-source entries in
deploy/plugins-catalog.json.example — are the first candidates to pick up
a Certified tag once a real catalog release exists.
Change-source correlation plugins¶
These answer one question during baseline review: was this change actually planned? Each queries whichever change/ticket system is configured for a pending baseline change near the time it was first seen — purely informational, never a gate. Full contract and config in Change-source correlation.
| Plugin | Status | What it does | Needs |
|---|---|---|---|
| Request Tracker | Requires testing | Searches RT via TicketSQL for a ticket mentioning the host, in a window around the finding's first-seen time. Built against RT's REST2 API and documented TicketSQL syntax. | curl, jq, GNU date(1) |
| FreeITSM | Requires testing | The same correlation model against FreeITSM's REST API v1 — built from its real endpoint and response shape, not a guessed generic-ITSM one. Two limitations disclosed in the plugin itself: no upper-bound time filter, title-only search. | curl, jq, GNU date(1) |
Notification-channel plugins¶
Every finding can fan out to any combination of these, each independently filterable by severity. Full contract and config in Notification channels.
| Channel | Status | What it does | Needs |
|---|---|---|---|
| Built-in | A local sendmail call, not a plugin — always available, no allowlist entry needed. Every channel below is an external plugin under the same Plugin API. |
(system sendmail) |
|
| PagerDuty | Requires testing | One Events API v2 incident per finding, deduplicated per host so a repeat finding correlates into one incident instead of paging fresh every cycle. | curl, jq |
| Slack | Requires testing | One digest message per notification cycle via an incoming webhook — a noisy cycle with many findings is still a single message, not a flood. | curl, jq |
| Generic webhook | Requires testing | The escape hatch, and the template to copy for a fully custom channel — posts the findings array to any HTTPS endpoint, optionally reshaped via a jq filter first. |
curl (jq only if reshaping) |
Contribute a plugin¶
Your ticket system or paging tool is a script and a hash away. Writing a plugin for a system not listed here is the same recipe as every reference implementation above — see Writing a new plugin. No Rust code, no verifier rebuild: a script satisfying one category's input/output contract, allowlisted by name, path, and pinned SHA-256.
A well-tested plugin against a real instance of its target system is exactly the kind of thing this directory exists to grow — and exactly what the certified catalog (mechanism shipped, publishing pipeline still pending) would let others install with one command instead of a manual allowlist-by-hand copy.
See also¶
- Plugin API — the full contract, security model, and step-by-step authoring guide
- Notification channels — configuring PagerDuty, Slack, and webhook channels
- Change-source correlation — configuring Request Tracker and FreeITSM