Skip to content

Getting started

The easiest path — one command, picks the right binary for your OS/arch:

Terminal window
curl -fsSL https://get.enodia.sh/unix | sh # Linux/macOS/Android (Termux)
Terminal window
irm https://get.enodia.sh/windows | iex # Windows

enodia is runnable immediately afterward in that same PowerShell window — the installer patches the current session’s PATH directly, not just the persisted registry value a fresh terminal would pick up.

On Windows, winget and Chocolatey work too, if you’d rather your package manager track updates:

Terminal window
winget install EpicMorg.Enodia # winget
choco install enodia # Chocolatey

Or a package, if you’d rather your package manager track updates:

Terminal window
sudo dpkg -i enodia_linux_amd64.deb # Debian/Ubuntu
sudo rpm -i enodia_linux_amd64.rpm # Fedora/RHEL
apk add --allow-untrusted enodia_linux_amd64.apk # Alpine
sudo pacman -U enodia_linux_amd64.pkg.tar.zst # Arch

Every package installs the binary at /usr/bin/enodia, man pages under /usr/share/man/man1/, and creates a dedicated, unprivileged enodia system user — nothing here needs root to run. Grab the right one from the latest release.

Or a container:

Terminal window
docker run --rm \
-v /etc/enodia:/config:ro \
ghcr.io/epicmorg/enodia:latest check --config /config/config.yaml

Since 1.1.0, this image is built and published by a companion repo, EpicMorg/docker, on its own schedule — not by this project’s own release pipeline anymore, though the published address and tags stay the same. Also published to docker.io/epicmorg/enodia and Quay, same tags — latest, a bare major (2), and the exact version with no build suffix (e.g. 2.2.1 — confirmed live on all three registries; an earlier pipeline’s own tags looked like 1.0.0-1 instead, still pullable, just not how new releases tag from here on). Two real changes worth knowing about: the image is linux/amd64 only now (arm64 was dropped when publishing moved), and it runs as root rather than a dedicated user, on the project’s own debian:trixie-light house base instead of scratch. It declares /var/lib/enodia/cve as a volume for the CVE databases — mount your copies there, or let enodia cve update fill it.

Requires Go — check go.mod for the exact version enodia currently targets.

Terminal window
git clone https://github.com/EpicMorg/enodia.git
cd enodia
go build -o enodia ./cmd/enodia
./enodia version
OS Arch Minimum version
Linux amd64, arm64 Kernel 3.2 or later — Debian 8+, Ubuntu 14.04+, RHEL/CentOS 7+ all comfortably qualify
Windows amd64, arm64, 386 Windows 10 / Windows Server 2016 or later
macOS amd64, arm64 macOS 12 Monterey or later
Android (Termux) arm64 only Android 7 or later — Termux’s own floor, stricter than the Android 5.0 Lollipop PIE-support minimum that actually drove the separate build (see the Termux note above). Rooted devices may need su — see the caution above

These are the Go toolchain’s own floor, not something enodia adds on top. Building from source with a newer Go raises the macOS floor further — that’s a toolchain decision, not a project one.

Create enodia.yaml next to the binary (or in any of the locations listed in Configuration):

enodia.yaml
schemaVersion: 1
targets:
- id: gitlab-main
product: gitlab
address: https://gitlab.example.com

Then run:

Terminal window
enodia check
Terminal window
ID PRODUCT PATCH LIFECYCLE BRANCH SEVERITY REASON CVES
gitlab-main gitlab ...

check with no --from collects and evaluates in one process — enodia reaches your target, then reaches the internet to check its lifecycle data. If your target only has network access to your infrastructure (a closed environment) and not the internet, split the two phases instead:

Terminal window
# inside the closed network - no internet needed
enodia collect --config enodia.yaml -o inventory.jsonl
# anywhere else - no access to your services needed
enodia check --from inventory.jsonl

A target with a private API needs a named credential, resolved from enodia.yaml’s own credentials: map (or a separate credentials.yaml — see Configuration):

enodia.yaml
schemaVersion: 1
targets:
- id: gitlab-main
product: gitlab
address: https://gitlab.example.com
credentials: gitlab-token
credentials:
gitlab-token:
kind: token-header
header: PRIVATE-TOKEN
value: "${GITLAB_TOKEN}"

${GITLAB_TOKEN} is interpolated from the environment at load time — see Configuration. Secrets never need to live in the same file as your service inventory.

  • Concepts for the design decisions behind all of this.
  • CLI reference for every command and flag.
  • Views for lifecycle, drift, and fleet — not just the default table.