xavierk 1c3037c2f8 Implement independent format gates for release workflow (issue #86)
Add XBPS build and sign steps to CI workflow
Add XBPS publication as independent gate (requires manual trigger)
Track format availability in release notes
Update release-footer.md with XBPS install instructions
Add --available/--withheld arguments to extract_changelog.py
Attach XBPS artifacts to Gitea release
Clean up XBPS signing key material after use
2026-09-15 10:50:38 +05:30
2026-09-14 20:48:05 +05:30
2026-09-14 20:48:05 +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 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.

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