Implement the complete redesign per fenris-redesign spec: - Observation store: SQLite WAL mode, six entities, schema versioning - Collector: smartctl acquisition, sysfs identity, normalization - Projection: sustained regime rate, habit change, confidence states - Panes TUI: Textual keyboard-first layout with four normative regions - Status CLI: read-only composition with four service facts - Monitor helper: polkit-guarded toggle, collect, baseline ops - Legacy migration: idempotent single-transaction import - Hour classification, day aggregates, monitoring periods - Pruning, segmentation, drive health facts Cross-cutting acceptance sweep (CI-1 through CI-4): - 59 tests covering state matrix, TUI/CLI parity, prohibition set, required wording and six disclosures - Full suite: 289 tests, all green Issues #20, #32 closed.
132 lines
5.7 KiB
Markdown
132 lines
5.7 KiB
Markdown
# 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](https://textual.textualize.io/) (pinned in the lockfile).
|
|
|
|
## Install
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
fenris monitor resume # enable timer + open first monitoring period
|
|
fenris monitor pause # close the period, disable timer
|
|
```
|
|
|
|
## Upgrade
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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) |
|
|
|
|
---
|
|
|
|
<p align="center">
|
|
<sub>Fenris 🐺 — by <a href="https://bongbetic.com">Bongbetic</a> · Be kind to your SSD and it'll be kind to you.</sub>
|
|
<br>
|
|
<img src="assets/bongbetic-brand/icon-dark-512.png" width="64" alt="Bongbetic icon">
|
|
</p>
|