# Fenris ๐Ÿบ *Observes an NVMe drive's real-world use and translates that history into an understandable endurance outlook.* Fenris is a persistent TUI monitor backed by a short-lived privileged collector on the host's native scheduler. It reads SMART data every few minutes, stores compact observation history in SQLite, and recomputes a usage-adjusted theoretical lifespan on every screen render โ€” no fairy dust, just your actual bytes. --- ## Requirements - **Python โ‰ฅ 3.10** (verified at install time) - **smartmontools** (`smartctl` โ€” verified at install time) - **systemd** or **runit**, with a polkit agent (the collector runs as root; elevation is exclusively polkit) No other OS packages or Python dependencies beyond [Textual](https://textual.textualize.io/) (pinned in the lockfile). ## Install from package (recommended) ### Debian / Ubuntu (apt) The Gitea instance Debian registry signs metadata with its own key. Verify the instance key fingerprint (TOFU hardening): ```text Fingerprint: ``` Add the instance key and repository: ```bash sudo mkdir -p /etc/apt/keyrings sudo curl -fsSL -o /etc/apt/keyrings/gitea-xavierk.asc \ https://git.bongbetic.com/api/packages/xavierk/debian/repository.key echo "deb [signed-by=/etc/apt/keyrings/gitea-xavierk.asc] \ https://git.bongbetic.com/api/packages/xavierk/debian bookworm main" \ | sudo tee /etc/apt/sources.list.d/fenris.list sudo apt update && sudo apt install fenris ``` Replace `bookworm` with your distribution codename (`bookworm`, `jammy`, or `noble`). ### Fedora / openSUSE Tumbleweed (RPM) Use the Fenris-owned repo file (not Gitea's auto-generated one): ```bash sudo dnf config-manager --add-repo \ https://git.bongbetic.com/xavierk/Fenris/raw/branch/main/packaging/fenris.repo sudo dnf install fenris ``` On openSUSE Tumbleweed, add the same standard RPM repository file and install with zypper: ```bash sudo zypper addrepo --refresh \ https://git.bongbetic.com/xavierk/Fenris/raw/branch/main/packaging/fenris.repo fenris sudo zypper install fenris ``` The repo file sets `gpgcheck=1` against the Fenris packaging key (downloaded from the raw URL in `gpgkey`) and `repo_gpgcheck=0` (metadata check left to TLS). ### Void Linux (XBPS) Void x86_64 with glibc and runit is the native target. Its signed XBPS channel is available from the permanent repository below. Add it, refresh its metadata, and install the released package: ```bash sudo install -d -m 0755 /etc/xbps.d echo 'repository=https://git.bongbetic.com/xavierk/Fenris-xbps/raw/branch/stable/x86_64' \ | sudo tee /etc/xbps.d/fenris.conf sudo xbps-install -M -S fenris ``` XBPS requires remote repositories to be signed. On the first refresh it displays the repository signing key embedded in the signed metadata; accept it only when its RSA SHA256 fingerprint is `SHA256:AvPMRlKMikPg75u0iKr8AUkxlfU/Ad4k/S4o2M9W4/w`. The public key is also available at `https://git.bongbetic.com/xavierk/Fenris-xbps/raw/branch/stable/keys/fenris-xbps-signing.pub`. For later updates, always refresh first so XBPS fetches the current index: ```bash sudo xbps-install -M -Syu ``` The `-M` flag bypasses XBPS's on-disk repodata cache. It is required when checking for a newly published package through Gitea's cached raw-file URL. The runit service remains dormant after installation. `fenris monitor resume` creates `/var/service/fenris-collect`; pause removes that link and records a deliberate disable in the observation history. Fenris keeps the observation store root-written and readable by the `fenris` group. Add each TUI user to that group, then start a new login session before running Fenris: ```bash sudo usermod -aG fenris "$USER" ``` ### Package signature verification The RPM payload is signed with the Fenris packaging key (RSA 3072). Verification happens automatically via dnf's `gpgcheck=1`. For manual verification of downloaded assets: ```bash rpm -Kv fenris-*.x86_64.rpm # RPM payload signature gpg --verify SHA256SUMS.asc SHA256SUMS # Clearsigned checksum manifest sha256sum -c SHA256SUMS # Checksum match ``` The packaging public key is published in-repo โ€” no keyservers. See `packaging/keys/fenris-packaging.asc` and `docs/install/signing-key-ceremony.md` for key lifecycle details. ### Dormant install A fresh package install is fully dormant. Its native scheduler is present but disabled; nothing runs. The only opt-in is the sanctioned toggle: ```bash fenris monitor resume # enable scheduling + open first monitoring period fenris monitor pause # close the period, disable scheduling ``` ## Development install (make install) For contributors building from source: ```bash sudo make install ``` This builds a wheel, installs its locked pure-Python runtime packages into `/opt/fenris/vendor`, and places helpers, units, and the polkit policy. Units are dormant by default. ```bash sudo make upgrade # re-sync wheel, units, schema make uninstall # removes artifacts, preserves config and store make purge # also removes /etc/fenris and /var/lib/fenris ``` ## Upgrade ### Package upgrade ```bash sudo apt update && sudo apt upgrade fenris # Debian/Ubuntu sudo dnf upgrade fenris # Fedora sudo xbps-install -Syu # Void Linux ``` ### Development upgrade ```bash sudo make upgrade ``` What it does: 1. Snapshots `observations.db` to a one-generation backup (`.bak`). 2. Replaces the locked runtime packages under `/opt/fenris/vendor`. 3. Syncs units and polkit against the manifest; runs `daemon-reload`. 4. Restarts the timer **only** if unit contents changed **and** it is active โ€” a running collection run finishes on its mapped interpreter; the next run uses the new code. 5. Applies forward-only schema migrations (the store directory is never rebuilt; automatic downgrade does not exist). Rollback: reinstall the previous version and restore `observations.db.bak`. On Void, pause monitoring first, copy the compatible snapshot back to `/var/lib/fenris/observations.db`, then force-install the matching older package version. If that version is no longer indexed, add its retained XBPS archive to a local repository with `xbps-rindex -a` and use `xbps-install -R -f fenris-`. Installing an older package over a newer observation store is unsupported. ## Migration from make install If Fenris was previously installed with `sudo make uninstall` first, then installed from the package, existing config, store, and group survive by path continuity. Over-installing the package over a `make install` is **forbidden** โ€” stale units shadow vendor placement. See [docs/install/migrate-from-makeinstall.md](docs/install/migrate-from-makeinstall.md). ## Uninstall and purge ### Package removal ```bash sudo apt remove fenris # preserves config and store sudo apt purge fenris # also removes config and store sudo dnf remove fenris # preserves config and store sudo xbps-remove fenris # preserves config and store ``` ### Development removal ```bash make uninstall # removes artifacts, preserves config and observation history make purge # also removes /etc/fenris and /var/lib/fenris ``` Uninstall performs the sanctioned disable first (`fenris-monitor disable --now`) โ€” an open period closes `user_disabled` โ€” then removes the runtime packages, helpers, units, polkit policy, and wrapper while keeping `/etc/fenris` and the observation store. Reinstalling resumes from the preserved store. ## Cadence drop-ins The default collection cadence is **5 minutes** (`OnUnitInactiveSec=5min` in the timer unit). To change it, place a systemd drop-in: ```bash sudo systemctl edit fenris-collect.timer # Add: # [Timer] # OnUnitInactiveSec=10min ``` No interval key exists in `/etc/fenris/fenris.conf`. Cadence is a systemd concern, not a Fenris configuration key. On Void, Fenris uses its native runit service instead: its initial collection is delayed by two minutes and later collections run five minutes after the previous run finishes. Inspect its state and diagnostics with: ```bash sv status fenris-collect sudo tail -n 50 /var/log/fenris-collect/current ``` `fenris status` also reports the separate boot-enabled, runtime-active, collection outcome, and observation-store freshness facts. A failed collection is retried at the next interval; it never fabricates missing observations. ## CLI reference | Command | Behavior | |---|---| | `fenris` | Opens the TUI (no arguments). | | `fenris status` | Projection facts, enabled/active state, last collect outcome, journal hint on failure or staleness. Never auto-samples. | | `fenris sample` | On-demand collection via the privileged helper. Blocks until the run completes. | | `fenris monitor pause` | Sanctioned disable โ€” asks for confirmation, then disables native scheduling and closes the monitoring period. | | `fenris monitor resume` | Sanctioned enable โ€” enables native scheduling and opens a monitoring period. No confirmation. | | `fenris baseline set ` | CLI-side validation, then polkit-guarded persistence. | | `fenris baseline clear` | Remove the endurance baseline. | | `fenris import ` | Idempotent single-transaction legacy import. | | `fenris start` / `stop` / `run` | Rejected with a one-line migration pointer โ€” never aliased. | | `fenris --device` | Rejected with a pointer to the configuration file. | ## Reading the dashboard `fenris` opens the TUI dashboard. - **Continuity** โ€” the service strip's continuity line (and `fenris status`) reports whether monitoring survives reboots: `monitoring: active in background ยท persists across reboots`, or `monitoring: does not start on next boot`. - **Paused vs. quit** โ€” a full-width `monitoring: paused โ€” deliberate disable` block means collection is stopped (`fenris monitor pause`); resume with `fenris monitor resume`. Pressing `q` only leaves the screen โ€” monitoring keeps running in the background. - **Auth banner** โ€” at launch, `privileged actions will prompt for authentication (polkit)` shows once and clears on the first refresh. Privileged actions elevate via polkit; Fenris never asks for sudo. Per-release notes live on the [releases page](https://git.bongbetic.com/xavierk/Fenris/releases): each entry is the version's `CHANGELOG.md` section โ€” what was added, changed, and fixed โ€” plus standing install and verification instructions. ## Retired menu options The legacy `fenris.sh` menu script and the `fenris.py` monolith have been removed. Here's where the old options went: | Legacy option | Successor | |---|---| | 1) Start monitoring | `fenris monitor resume` | | 2) Stop monitoring | `fenris monitor pause` | | 3) Status / current wear stats | `fenris status` | | 4) Take one sample right now | `fenris sample` | | 5) Open dashboard URL | Removed โ€” the HTML dashboard and HTTP server are gone; the TUI is the primary interface. | ## Configuration `/etc/fenris/fenris.conf` holds exactly one key โ€” the device selector: ``` device = /dev/disk/by-id/nvme-Samsung_SSD_980_PRO_2TB_S6BENS0Txxxxx ``` Use a stable `/dev/disk/by-id/` path. Raw `/dev/nvmeX` paths are warned against. The file is re-read every collection run. ## Where's my stuff? | Artifact | Package install | make install | |---|---|---| | Wrapper | `/usr/bin/fenris` | `/usr/local/bin/fenris` | | Helpers | `/usr/libexec/fenris/` | `/usr/libexec/fenris/` | | Units | `/usr/lib/systemd/system/` (vendor) | `/etc/systemd/system/` | | Polkit policy | `/usr/share/polkit-1/actions/` | `/usr/share/polkit-1/actions/` | | sysusers/tmpfiles | `/usr/lib/{sysusers,tmpfiles}.d/fenris.conf` | managed by Makefile | | Configuration | `/etc/fenris/fenris.conf` | `/etc/fenris/fenris.conf` | | Observation store | `/var/lib/fenris/observations.db` | `/var/lib/fenris/observations.db` | | Runtime packages | `/opt/fenris/vendor` | `/opt/fenris/vendor` | | Legacy history | โ€” | `./data/history.jsonl` (auto-imported) | ---

Fenris ๐Ÿบ โ€” by Bongbetic ยท Be kind to your SSD and it'll be kind to you.
Bongbetic icon