# 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 a systemd timer. 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.9** (verified at install time) - **smartmontools** (`smartctl` โ€” verified at install time) - **systemd** with a polkit agent (the collector runs as root oneshot; elevation is exclusively polkit) No other OS packages or Python dependencies beyond [Textual](https://textual.textualize.io/) (pinned in the lockfile). ## Install ```bash sudo make install ``` What it does: 1. Builds a wheel from the checkout and installs it โ€” with pinned dependencies โ€” into the dedicated venv at `/opt/fenris`. 2. Places the `fenris` wrapper in `/usr/local/bin`, helpers in `/usr/libexec/fenris`, systemd units in `/etc/systemd/system`, and the polkit policy in `/usr/share/polkit-1/actions/`. 3. Creates `/var/lib/fenris` (root-written, group-readable) โ€” the observation store is created lazily by the first collection run. 4. Records every placed file in a manifest consumed by upgrade and uninstall. 5. Detects `./data/history.jsonl` beside the source checkout and runs the idempotent legacy import if present. **A fresh install is fully dormant.** Units are present but disabled; nothing runs. The only opt-in is the sanctioned toggle: ```bash fenris monitor resume # enable timer + open first monitoring period fenris monitor pause # close the period, disable timer ``` ## Upgrade ```bash sudo make upgrade ``` What it does: 1. Snapshots `observations.db` to a one-generation backup (`.bak`). 2. Installs the new wheel into the same venv with pinned dependencies. 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`. ## Uninstall and purge ```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 venv, 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. ## 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 the timer and closes the monitoring period. | | `fenris monitor resume` | Sanctioned enable โ€” enables the timer 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. | ## 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 | Location | |---|---| | Wrapper | `/usr/local/bin/fenris` | | Helpers | `/usr/libexec/fenris/fenris-collect`, `fenris-monitor` | | Units | `/etc/systemd/system/fenris-collect.{timer,service}` | | Polkit policy | `/usr/share/polkit-1/actions/com.bongbetic.fenris.monitor.policy` | | Configuration | `/etc/fenris/fenris.conf` | | Observation store | `/var/lib/fenris/observations.db` | | Venv | `/opt/fenris` | | Manifest | `/var/lib/fenris/manifest.txt` | | Legacy history | `./data/history.jsonl` (auto-imported on install if present) | ---

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