# 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: ```bash packaging/build-package.sh ``` Install the generated package: ```bash 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: ```bash 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: ```bash 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 richer integrity metadata, notifications, and management consumers. Each snapshot text/JSON revision is SHA-256 hashed into a synchronized private `hash-chain.jsonl` using the stable `enodia.hash_chain.v1` record schema. 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: ```bash 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: ```bash 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: ```bash 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: ```bash 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: ```bash make release-artifacts ``` The release target writes to `dist/` by default: | Artifact | Purpose | |---|---| | `enodia-sentinel-.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: ```bash RELEASE_SIGN=1 make release-artifacts ``` To select a specific GPG key: ```bash RELEASE_SIGN=1 RELEASE_SIGNING_KEY= make release-artifacts ``` Verification flow for operators: ```bash gpg --verify SHA256SUMS.asc SHA256SUMS sha256sum -c SHA256SUMS ```