Skip to content

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:

plugins-catalog.json (shape)
{
  "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 notify and change-source types are accepted; an entry's name and the url's filename component are restricted to ASCII letters/digits/-/_/. so a crafted value can't corrupt the whitespace-delimited .conf format or an install path.
  • Rollback protection. catalog_version is a monotonic high-water mark (stored in a root-owned .catalog-version file); 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 with ETMINAN_PLUGIN_ALLOW_REDIRECTS) to close an SSRF-via-302 path.
  • Post-write re-verification. After writing the file and its .conf line, the verifier re-verifies the file actually on disk through the same runtime gate; if it fails, the .conf entry 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
Email 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