enodia-sentinal/docs/PACKAGING.md

8.6 KiB

Enodia Sentinel Packaging

Applies to package version: 0.7.0 Docs version: 0.7 Schema generation: v1

Sentinel is a stdlib-only Python package installed as a plain source directory plus launcher wrapper. The service and package are designed around Linux hosts with systemd.

Arch Linux

Arch is the primary packaged target.

Build from the repository root:

packaging/build-package.sh

Install the generated package:

sudo pacman -U enodia-sentinel-*.pkg.tar.zst
sudo systemctl daemon-reload
sudo systemctl enable --now enodia-sentinel.service

The Arch package installs:

Path Purpose
/usr/bin/enodia-sentinel Launcher wrapper for the Python package.
/usr/bin/sentinel-redteam Safe local red-team harness.
/usr/lib/enodia-sentinel/enodia_sentinel/ Stdlib-only Python package.
/etc/enodia-sentinel.toml Operator configuration, preserved as a pacman backup file.
/usr/lib/systemd/system/enodia-sentinel.service Hardened daemon unit.
/usr/lib/systemd/system/enodia-sentinel-web.service Read-only dashboard unit.
/usr/share/libalpm/hooks/enodia-sentinel-fim.hook Post-transaction FIM baseline refresh hook.
/usr/lib/tmpfiles.d/enodia-sentinel.conf Runtime log directory definition.
/usr/share/doc/enodia-sentinel/ Versioned operator docs and examples.
/var/log/enodia-sentinel/ Alert snapshots, events, incidents, response plans, and audit logs.

The pacman hook calls /usr/bin/enodia-sentinel fim-update after package transactions so legitimate package changes refresh the FIM baseline. It does not silence unexplained non-package drift.

Optional Arch packages:

  • python-bpfcc for eBPF exec/syscall telemetry.
  • bpftrace for short-lived exec capture inside forensic snapshots.
  • libnotify for local desktop notifications.
  • libarchive for signed-cache-package .MTREE verification helpers.

The default service is hardened for polling mode. To enable eBPF, copy the packaged drop-in example and reload systemd:

sudo install -Dm644 /usr/share/doc/enodia-sentinel/examples/enodia-sentinel-ebpf.conf \
  /etc/systemd/system/enodia-sentinel.service.d/ebpf.conf
sudo systemctl daemon-reload
sudo systemctl restart enodia-sentinel.service

Go Migration Sidecar (opt-in)

The Go agent has a separate validation service and is never installed by the default make install target or the Arch package. Build and install it explicitly from a checkout with Go 1.25 or newer:

make build-go
sudo make install-go PREFIX=/usr/local
sudo make enable-go

The static enodia-sentinel-go binary emits enodia.event.v1 JSONL to the system journal. Its service owns /var/lib/enodia-sentinel-go for baseline state and a bounded retained event stream. events.jsonl rotates to one events.jsonl.1 segment before crossing 64 MiB; both are private to root, and non-status records are synchronized before emission succeeds. The service requests the embedded native exec and syscall probes and fails open to polling when the kernel refuses a probe. It does not write the production Python daemon's /var/log/enodia-sentinel state or replace enodia-sentinel.service. Systemd readiness is emitted only after baseline initialization, event-log and snapshot-store preflight, and both requested probe load attempts complete.

Alert events are additionally retained as private alert-YYYYMMDD-HHMMSS.{json,log} pairs in /var/lib/enodia-sentinel-go. Same-second events are merged into one snapshot, required enodia.alert.snapshot.v1 fields are preserved, and the shared max_snapshots / max_snapshot_age_days settings bound retention. The current sidecar also writes a private atomic incidents.json with the required enodia.incident.v1 fields, process-lineage/time-window grouping, and additive correlation evidence. Hot-path Go enrichment is bounded and no-I/O: process and parent context, lineage, remote classification, and candidate paths come from already-captured records. Python remains authoritative for ownership/hashes, file and integrity metadata, assurance chaining, notifications, and management consumers.

Each successful sweep atomically refreshes /var/lib/enodia-sentinel-go/heartbeat as a Unix timestamp with mode 0600; the retained value becomes stale when the sidecar stops or wedges. enodia-sentinel-go --health reports that state as JSON and exits nonzero when it is unavailable or older than the configured heartbeat_max_age.

Inspect the retained event stream without depending on journald:

sudo tail -n 50 /var/lib/enodia-sentinel-go/events.jsonl
sudo enodia-sentinel-go --events-tail 50

The native reader includes events.jsonl.1, preserves chronological order, and fails nonzero if either retained segment contains malformed JSON.

Inspect or remove the validation service with:

sudo make status-go
sudo make disable-go
sudo make uninstall-go

Uninstalling preserves the shared configuration and sidecar state for forensic review or a later reinstall.

Debian and Ubuntu

There is no native .deb package yet. Use the source install path:

sudo apt install python3 iproute2 procps
sudo make install PREFIX=/usr/local
sudo systemctl daemon-reload
sudo systemctl enable --now enodia-sentinel.service

Notes:

  • The installed launcher does not require pip or a virtualenv.
  • fim-check --packages uses the available package-manager backend; Debian package verification support should be validated for the target host before relying on it operationally.
  • The source install path includes the pacman hook file for parity with Arch source installs. On non-Arch systems it is inert unless pacman/libalpm is installed; Debian installs should manage FIM baseline refresh through operator procedure or a future dpkg trigger package.

RPM-family Linux

There is no native RPM package yet. Use the same source install path:

sudo dnf install python3 iproute procps-ng
sudo make install PREFIX=/usr/local
sudo systemctl daemon-reload
sudo systemctl enable --now enodia-sentinel.service

Notes:

  • Native RPM packaging should eventually install systemd units under the distro unit path, preserve /etc/enodia-sentinel.toml, and create a package-manager trigger equivalent to the Arch FIM refresh hook.
  • Validate package verification behavior on the target distro before enabling signed-package mismatch alerting.

Desktop Tray Applet (optional)

The [tray] optional extra (pip install 'enodia-sentinel[tray]') pulls in pystray and Pillow and provides the enodia-sentinel-tray console script. The extra is never required by the daemon; core runtime dependencies stay empty.

packaging/enodia-sentinel-tray.desktop is an opt-in autostart entry. It is not installed to a system autostart path by default — desktop users copy it to ~/.config/autostart/ themselves.

Terminal TUI and Completion

The terminal dashboard is stdlib-only and available through enodia-sentinel tui in source/wrapper installs. Python packaging metadata also declares enodia-sentinel-tui = enodia_sentinel.tui:main for pip-style script generation.

Shell completion has no runtime dependency: operators generate scripts with enodia-sentinel completion bash or enodia-sentinel completion zsh and install them in their normal shell completion path.

Package Hardening Checklist

  • Package version matches pyproject.toml and enodia_sentinel.__version__.
  • Config is preserved across upgrades.
  • Log directory is created with mode 0750.
  • Service units remain hardened and read-mostly by default.
  • eBPF privilege relaxation is shipped only as an opt-in drop-in example.
  • Package-manager hooks use absolute paths and do not depend on ambient PATH.
  • Versioned operator docs are installed with the package.

Release Artifacts

Release artifacts are built with:

make release-artifacts

The release target writes to dist/ by default:

Artifact Purpose
enodia-sentinel-<version>.tar.gz Versioned source release.
SHA256SUMS SHA-256 checksums for the source tarball and release manifest.
RELEASE_MANIFEST.json Project/version/git metadata for the generated release.
SHA256SUMS.asc Optional detached GPG signature over SHA256SUMS.

The script refuses a dirty git worktree by default so a release maps to a known commit. For local packaging smoke tests only, set RELEASE_ALLOW_DIRTY=1.

To sign checksum artifacts:

RELEASE_SIGN=1 make release-artifacts

To select a specific GPG key:

RELEASE_SIGN=1 RELEASE_SIGNING_KEY=<key-id-or-fingerprint> make release-artifacts

Verification flow for operators:

gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum -c SHA256SUMS