crafted with stubborn curiosity by Bongbetic
Fenris 🐺 — Your SSD's Tell-All Diary
Your NVMe drive has been keeping secrets. Fenris makes it confess — in real time.
How much did you write today? How long until it taps out? No fairy dust — just your actual bytes.
Fenris is a tiny, stubborn daemon that eavesdrops on your NVMe drive's SMART gossip, writes it down every few minutes, and serves you a live dashboard that actually means something. Not "vibes" — real GB written in the last 24 hours, real GB/hour, and a real countdown in hours, days, and years until your drive's endurance runs out.
Think of it as a Fitbit for your SSD. Except it doesn't nag you to drink water.
What it actually does (no hand-waving)
- Listens — polls
smartctl -jon your NVMe device (default every 5 minutes, you pick). - Remembers — appends every sample to
data/history.jsonland rolls up per-hour totals intodata/hourly.jsonl(survives restarts, rebuilds itself if you yank the power). - Calculates — rolling 24-hour window: exact bytes written in the last 24h, GB/h, GB/day, implied total TBW from
percentage_used, remaining TB, and a projected life-remaining breakdown. Warming-up badge until it has 24h of coverage — no fake confidence. - Shows off — dense, live dashboard with wear-over-time + trailing-24h per-hour bars, sticky header, live countdown, and stale warnings if the daemon dozes off.
You need
- Python 3.7+
- smartmontools (
smartctl) - Root-ish access to read NVMe SMART (passwordless
smartctlor just run withsudo— your call)
The sudo dance (one time)
Fenris runs sudo -n smartctl ... so it doesn't get stuck asking for a password mid-nap:
sudo visudo
# add this line (swap in your username):
youruser ALL=(root) NOPASSWD: /usr/sbin/smartctl
No sudo? Run the whole thing with sudo and it'll still behave.
Get it running — 30 seconds
The cozy way
./fenris.sh
# pick 1) Start monitoring → choose device / interval / port → done
The no-nonsense way
python3 fenris.py start # defaults: /dev/nvme0, every 300s, port 8420
python3 fenris.py start --interval 60 --port 9000 # if you're impatient
python3 fenris.py status # "are we live? how's the drive?"
python3 fenris.py sample # one sneaky sample right now
python3 fenris.py stop # tuck it back in
Dashboard lives at http://localhost:8420 (or whatever port you chose).
The menu, demystified
Run ./fenris.sh and you'll get:
1) Start monitoring (background daemon + dashboard)
2) Stop monitoring
3) Status / current wear stats
4) Take one sample right now
5) Open dashboard URL
---
h) Help / how this works
q) Exit (go touch grass)
What Fenris jots down
| Field | What's the gossip? |
|---|---|
percentage_used |
The drive's own wear-o-meter (0–100%) |
bytes_written / bytes_read |
Lifetime totals — the receipts |
available_spare |
Spare blocks left (%) |
media_errors |
Uncorrectable boo-boos |
power_on_hours |
How long it's been awake |
temperature_c |
Is it sweating? |
critical_warning |
NVMe's panic flags |
Hourly rollups also stash bytes_written per hour, pct_start/pct_end, and temp peaks — so the 24h math stays honest.
The dashboard — what's on screen
- Hero card: Projected life remaining — big, friendly
361 d 2 h(plus≈ 361 days · ≈ 8666 hours · ≈ 0.99 years), backed by~280 GB/dayand~101 TB left of ~202 TB totalon the test box. - Data written (24h) — exact GB in the rolling window + coverage (
10.4h of 24huntil warmed up). - Write rate — GB/h and GB/day, live.
- Wear, spare, temp, errors, power-on — the usual suspects, with progress bars and polite color-coding.
- Two charts, side by side: wear over time + trailing-24h hourly write bars (with a cheeky "now" bar for the current partial hour).
- Live plumbing: polling synced to your interval, ETag-cached, countdown to next sample, warming-up + stale banners, pauses when you hide the tab (saves your battery, you're welcome).
API for the tinkerers: GET /api/data · /api/hourly · /api/summary · /api/config · /api/status — all JSON, all friendly.
Where's my stuff?
fenris/
├── fenris.py # the whole show — daemon + server + math
├── fenris.sh # the cozy menu
├── README.md # hi — you're here
├── assets/bongbetic-brand/ # Bongbetic wordmarks & glyphs (for Gitea + dashboard)
└── data/
├── history.jsonl # raw samples (JSONL, append-only)
├── hourly.jsonl # per-hour rollups (auto-rebuilt on restart)
├── fenris.pid # daemon PID
└── fenris.log # daemon chatter
CLI cheat sheet
python3 fenris.py start [--device /dev/nvme0] [--interval 300] [--port 8420]
python3 fenris.py stop
python3 fenris.py status
python3 fenris.py sample [--device /dev/nvme0]
python3 fenris.py run # foreground mode — what `start` spawns internally
Oops — troubleshooting without the tears
"smartctl not found"
sudo apt install smartmontools # Debian/Ubuntu
sudo pacman -S smartmontools # Arch — you already knew
"needs root" / permission denied
Set up the passwordless line above, or just sudo ./fenris.sh.
Dashboard says "stale"
Daemon napped or crashed. python3 fenris.py status will tell you. Kick it again with start.
Only 10 hours of data and it says "preliminary"? That's honesty, not a bug. It needs 24h of real writes to give a tight estimate. Let it simmer — the number gets sharper every hour.
Fenris 🐺 — by Bongbetic · Be kind to your SSD and it'll be kind to you.