The GUI frontends landed with README/CLI/operations docs but no packaging coverage. Add a PACKAGING.md section with the command/script/requirement table for both frontends, note why stdlib tkinter is still an optdepend rather than a hard dependency, and record that packaging must not grant the GUIs privileges beyond the operator's own account. Add the matching PKGBUILD optdepends (`tk`, `pyside6`), plus `python-pystray`/`python-pillow` for the tray applet, which were never listed when it shipped. Also add `gui/` to the CLAUDE.md architecture notes and state snapshot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JX86xeoBJVBb16qHkDf53K
276 lines
10 KiB
Markdown
276 lines
10 KiB
Markdown
# 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. A bounded asynchronous worker subsequently hashes
|
|
regular executables no larger than 8 MiB and records file metadata; a full queue
|
|
skips those optional fields without delaying an alert. Python remains
|
|
authoritative for package ownership, 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 the sidecar's isolated incident records and timeline without starting
|
|
the service or changing Python state:
|
|
|
|
```bash
|
|
sudo enodia-sentinel-go --incidents-list
|
|
sudo enodia-sentinel-go --incident-show <incident-id>
|
|
```
|
|
|
|
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.
|
|
|
|
## Desktop GUI Frontends (optional)
|
|
|
|
Two windowed dashboards ship alongside the tray applet. Both are optional
|
|
frontends over the same local state the TUI and web console read; neither is
|
|
required by the daemon, and core runtime dependencies stay empty.
|
|
|
|
| Frontend | Command | Console script | Requirement |
|
|
| --- | --- | --- | --- |
|
|
| tkinter | `enodia-sentinel gui` | `enodia-sentinel-gui` | stdlib `tkinter` (distro `tk` / `python-tk` / `python-tkinter`) |
|
|
| Qt6 | `enodia-sentinel gui-qt` | `enodia-sentinel-gui-qt` | `[qt]` extra (`PySide6`) |
|
|
|
|
Notes:
|
|
|
|
- `tkinter` is stdlib but is packaged separately on most distros, so it is an
|
|
`optdepends` (Arch `tk`), never a hard dependency.
|
|
- The `[qt]` extra (`pip install 'enodia-sentinel[qt]'`) pulls in `PySide6`
|
|
only for the Qt frontend; the Arch package lists `pyside6` as an optdepend.
|
|
- Both GUIs are read-only over detection state and display response plans for
|
|
review only — packaging must not grant them privileges beyond what the
|
|
operator's own account has. Daemon start/stop/restart goes through
|
|
`systemctl` and prompts for polkit authorization as usual.
|
|
- Only `gui/app.py` (tkinter) and `gui/qt_app.py` (Qt) import GUI libraries;
|
|
`gui/model.py` stays import-clean so headless builders can run the test
|
|
suite without a display server or `PySide6` installed.
|
|
|
|
## 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-<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:
|
|
|
|
```bash
|
|
RELEASE_SIGN=1 make release-artifacts
|
|
```
|
|
|
|
To select a specific GPG key:
|
|
|
|
```bash
|
|
RELEASE_SIGN=1 RELEASE_SIGNING_KEY=<key-id-or-fingerprint> make release-artifacts
|
|
```
|
|
|
|
Verification flow for operators:
|
|
|
|
```bash
|
|
gpg --verify SHA256SUMS.asc SHA256SUMS
|
|
sha256sum -c SHA256SUMS
|
|
```
|