xavierkandCommandCodeBot b2243a85f7 fix(packaging): add RPM ownership assertions, ghost group fix, and conffile check for #46
- Fix nfpm.yaml ghost directory to include `group: fenris` so RPM metadata
  matches the tmpfiles.d-created ownership (root:fenris 2750)
- Add RPM-native ownership assertions: store dir reported as package-owned
  via `rpm -qf`, store contents verified as never owned by the package
- Add RPM conffile assertion: `rpm -qc` verifies fenris.conf is listed
- Unify store dir stat assertion across both formats (deb and rpm both
  assert mode 2750 root:fenris)
- Remove unused `distro` parameter from `_find_package()`

All 16 packaging tests pass across the full matrix (3 deb + 1 rpm × 4 scenarios).
All 289 non-packaging tests pass.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-03 10:18:04 +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.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 (pinned in the lockfile).

Install

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:

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

Upgrade

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

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:

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

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%