Status: pre-alpha. Architecture is settled, implementation is starting. Nothing here is stable yet.
░░
▒██▒
▓████▓
▓ ▓██████▓ ▓
▓█▓░ ▒██████▒ ░▓█▓
██████▓▒▓██▓▒▓██████
▒███████▓▒▒▓███████▒
░▒▓█████░░█████▓▒░
▒████▒
██
▒▒
█▀▀ █▄ █ █▀█ █▀▄ █ ▄▀█
██▄ █ ▀█ █▄█ █▄▀ █ █▀█ asks your deployed services what version they are running, then checks
those versions against vendor lifecycle calendars and tells you what is
already dead, what is dying, and where your fleet has drifted apart.
You describe your services once. Enodia handles the rest.
services:
- name: Jira Main
product: jira
url: https://jira.example.com
- name: TeamCity Build
product: teamcity
url: https://tc.example.com
auth: { kind: bearer, value: "${TEAMCITY_TOKEN}" }$ enodia check
SERVICE PRODUCT LOCAL CYCLE PATCH LIFECYCLE NEWER
Jira Main jira 10.3.2 10.3 current active 11.0
TeamCity Build teamcity 2025.03.1 2025.03 behind active —| Tool | Tracks upstream releases | Knows what you deployed | Lifecycle dates |
|---|---|---|---|
eol CLI |
— | — | yes |
| nvchecker | yes | — | — |
| Renovate / Dependabot | yes (dependencies) | — | — |
| what's-up-docker | yes (containers only) | containers only | — |
| Uptime Kuma | — | up/down only | — |
| Enodia | yes | yes | yes |
The gap Enodia fills is the join: inventory of the live fleet, matched against lifecycle data.
Two phases, deliberately separable. Collection talks to your services; evaluation talks to the internet. They almost never have network access to the same places.
collect → inventory.jsonl → evaluate → assessment → render
For air-gapped environments this is the whole point:
# inside the closed network — no internet needed
enodia collect -c config.yaml -o inventory.jsonl
# anywhere else — no access to your services needed
enodia check --from inventory.jsonlThree orthogonal axes, not one verdict. A branch can be perfectly healthy while a newer major exists — Confluence 10 LTS is alive and supported even though 11 shipped. Collapsing that into a single status throws away the information you actually wanted.
| Axis | Values |
|---|---|
| Patch | current · behind · ahead · unknown |
| Lifecycle | active · security · eol · unknown |
| Newer branch | latest · newer · newer_lts · unknown |
Facts and judgement are separate. The inventory records what was observed. Severity is computed on top, from policy you control. Export the facts and apply your own rules if ours do not fit.
Time is a parameter. Every evaluation takes an asOf date, so
--as-of 2027-01-01 answers "what dies before next budget year" — and tests
stay deterministic instead of rotting.
Probes are compiled in. One product, one file, one entry in an explicit
registry. Adding support means a new release, not a plugin ABI. For anything
in-house, product: generic takes a parser spec straight from your config.
Not published yet. When it is:
docker run --rm \
-v /etc/enodia:/config:ro \
ghcr.io/epicmorg/enodia:1 check --config /config/config.yamlenodia export --format html writes a single self-contained file. Point nginx
at it and refresh it from cron or a systemd timer.
There is no built-in web server and no refresh button, by design: a button that polls your entire fleet on every click is a self-inflicted denial of service. Collection runs on a schedule; the page shows the latest snapshot and states plainly when it was taken.
Enodia holds credentials to your infrastructure. Consequences, all deliberate:
- Credentials never appear in the inventory, in exported reports, or in logs.
- HTTPS is tried before HTTP. Credentials are never sent over plain HTTP unless you explicitly opt in per service.
- TLS verification is on by default. Custom CA and certificate pinning are
supported so that
insecure: truestays a last resort — and services checked without verification are flagged in the report. - Secrets live in a separate
credentials.yamlor environment variables, so your service inventory can be committed to git and your secrets cannot.
Found a hole? See SECURITY.md.
Adding a product is one file plus one line in the registry, and a recorded
vendor response in testdata/ so it stays honest. See
CONTRIBUTING.md.
Contributions require signing the CLA — the bot handles it on your first pull request. This exists so the project can be offered under commercial terms alongside the AGPL; you keep the copyright to your work.
Enodia is licensed under AGPL-3.0-or-later. See LICENSE.
If the AGPL does not fit your situation, a commercial licence is available — contact <developer@epicm.org>.
Enodia, "she of the wayside", is an epithet of Hecate: torchbearer, keeper of crossroads. Fitting for something that lights up what is decaying and stands where you choose which way to upgrade.
