2026-09-16 08:53:38 +05:30
2026-09-16 08:05:28 +05:30
2026-09-16 08:05:28 +05:30
2026-09-16 08:05:28 +05:30

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

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:

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:

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:

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:

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:

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:

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
sudo xbps-install -Syu                        # Void Linux

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. 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 <local-repository> -f fenris-<version>. 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.

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
sudo xbps-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 3 minutes (OnUnitInactiveSec=3min 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.

On Void, Fenris uses its native runit service instead: its initial collection is delayed by two minutes and later collections run three minutes after the previous run finishes. Inspect its state and diagnostics with:

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

Reading the dashboard

Chalktone dashboard with a dotted activity plot

Preview uses synthetic observations, not measurements from a real drive.

The Chalktone dashboard opens with a large Live activity plot. Day shows hourly evidence for a selected date, labelled UTC, and History shows daily evidence. Local-day totals keep their recorded timezone. Dotted traces show measured read/write volumes, not transfer speed; missing evidence breaks the trace. ? marks a gap, ~ a partial total, and u unallocated daily volume. Use the selected-point readout for exact values and evidence state.

  • v cycles Live / Day / History; the tabs are also clickable.
  • ← / → inspect points; w switches read/write volume in every view.
  • [ / ] browse dates, g enters a date, and t returns to today/live.
  • Tab / Shift+Tab move focus; z expands the focused panel, and z or Esc restores it. Monitoring status and controls remain visible.
  • s cycles Chalktone, Amber, Nord, and High Contrast; saved theme preferences survive upgrades. m toggles reduced motion.

On smaller terminals, textual summaries and scrollable panels keep evidence accessible. Pause, resume, collect, disclosures, help, and quit remain available in the fixed control row.

Run fenris as your normal user to open the TUI dashboard. The dashboard does not need sudo. Use sudo for package installation and system configuration; pause, resume, and collect-now actions normally authenticate through polkit.

If the observation store is inaccessible, add your login user to the fenris group with sudo usermod -aG fenris "$USER", then log out and back in.

If polkit authentication is unavailable, quit the dashboard and run only the required administrative action in your terminal:

sudo fenris monitor resume   # Enable monitoring now and across reboots
sudo fenris monitor pause    # Confirm a deliberate monitoring pause
sudo fenris sample           # Request one collection run

Reopen the dashboard with fenris afterward. Press ? for these instructions and keyboard controls at any time; use the arrow keys to scroll and Esc to close.

The CLI and TUI share the same authenticated action path. Authentication and waiting for collection have no separate dashboard deadline; the native collector enforces its 90-second runtime limit. An interrupted or failed action is not automatically retried—check fenris status before retrying.

  • 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 — the launch notice explains normal-user startup and polkit authentication, then clears on the first refresh. The ? help screen remains available.

Per-release notes live on the releases page: 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

S
Description
An app to measure your ssd wear and tear realtime
Readme MIT
2.2 MiB
v0.6.0
Latest
2026-09-28 22:50:20 +00:00
Languages
Python 95.5%
Shell 2.9%
Makefile 1.6%