- open_store_readonly(): stat() PermissionError (non-group user on the 2750 store dir) now maps to StoreFault so status/TUI degrade instead of crashing with a traceback. - init_store(): chmod db + -wal/-shm group rw after WAL setup — SQLite WAL readers need write access to sidecars even for mode=ro opens. - Store dir 2750 → 2770 (tmpfiles + make install) and UMask=002 on the collect unit so root-created files stay group-accessible. - rpm %post upgrade path re-runs systemd-tmpfiles --create to correct placement modes on existing machines. Bump to 0.3.3.
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).
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):
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:
- Snapshots
observations.dbto a one-generation backup (.bak). - Replaces the locked runtime packages under
/opt/fenris/vendor. - Syncs units and polkit against the manifest; runs
daemon-reload. - 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.
- 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.