Files
Fenris/README.md

8.5 KiB

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.10 (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 (pinned in the lockfile).

Debian / Ubuntu (apt)

The Gitea instance Debian registry signs metadata with its own key. Verify the instance key fingerprint (TOFU hardening):

Fingerprint: <print after first release — paste beside the curl one-liner>

Add the instance key and repository:

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):

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:

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).

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:

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. Units are present but disabled; nothing runs. The only opt-in is the sanctioned toggle:

fenris monitor resume    # enable timer + open first monitoring period
fenris monitor pause     # close the period, disable timer

Development install (make install)

For contributors building from source:

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.

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

sudo apt update && sudo apt upgrade fenris    # Debian/Ubuntu
sudo dnf upgrade fenris                       # Fedora

Development upgrade

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.

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.

Uninstall and purge

Package removal

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

Development removal

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:

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 <json> CLI-side validation, then polkit-guarded persistence.
fenris baseline clear Remove the endurance baseline.
fenris import <path> 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 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