docs: quirky README with Bongbetic branding, drop internal plan from remote
- Rewrite README in a playful, plain-English voice and add Bongbetic wordmarks/glyph/icons so Gitea renders the brand nicely (light/dark). - Vendor assets/bongbetic-brand/* from /mnt/Toto/Documents/bongbetic/Logo. - Remove plan-dash-changes.md from tracking (internal only) and gitignore it.
This commit is contained in:
@@ -1,89 +1,79 @@
|
||||
# Fenris — NVMe Wear Monitor & Live Dashboard
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source srcset="assets/bongbetic-brand/wordmark-light.png" media="(prefers-color-scheme: dark)">
|
||||
<img src="assets/bongbetic-brand/wordmark-dark.png" alt="Bongbetic" width="260">
|
||||
</picture>
|
||||
<br>
|
||||
<sub>crafted with stubborn curiosity by <a href="https://bongbetic.com">Bongbetic</a></sub>
|
||||
</p>
|
||||
|
||||
Created by Bongbetic.
|
||||
<p align="center">
|
||||
<img src="assets/bongbetic-brand/b_glyph.svg" width="48" alt="Fenris glyph">
|
||||
</p>
|
||||
|
||||
Periodically reads your NVMe drive's SMART health data, logs it over time, and serves a self-contained HTML dashboard estimating SSD lifespan from your actual daily usage trend.
|
||||
<h1 align="center">Fenris 🐺 — Your SSD's Tell-All Diary</h1>
|
||||
|
||||
## Requirements
|
||||
<p align="center">
|
||||
<em>Your NVMe drive has been keeping secrets. Fenris makes it confess — in real time.</em>
|
||||
<br>
|
||||
<em>How much did you write today? How long until it taps out? No fairy dust — just your actual bytes.</em>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
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 -j` on your NVMe device (default every 5 minutes, you pick).
|
||||
- **Remembers** — appends every sample to `data/history.jsonl` and rolls up per-hour totals into `data/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`) installed
|
||||
- Root access to read NVMe SMART logs
|
||||
- **smartmontools** (`smartctl`)
|
||||
- Root-ish access to read NVMe SMART (passwordless `smartctl` or just run with `sudo` — your call)
|
||||
|
||||
### Setting up passwordless smartctl
|
||||
### The sudo dance (one time)
|
||||
|
||||
Fenris runs `sudo -n smartctl ...` (no-prompt sudo). Either run with sudo or allow passwordless access:
|
||||
Fenris runs `sudo -n smartctl ...` so it doesn't get stuck asking for a password mid-nap:
|
||||
|
||||
```bash
|
||||
sudo visudo
|
||||
# Add this line (replace youruser with your username):
|
||||
# add this line (swap in your username):
|
||||
youruser ALL=(root) NOPASSWD: /usr/sbin/smartctl
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
No sudo? Run the whole thing with `sudo` and it'll still behave.
|
||||
|
||||
### Interactive Menu
|
||||
## Get it running — 30 seconds
|
||||
|
||||
### The cozy way
|
||||
|
||||
```bash
|
||||
./fenris.sh
|
||||
# pick 1) Start monitoring → choose device / interval / port → done
|
||||
```
|
||||
|
||||
### CLI
|
||||
### The no-nonsense way
|
||||
|
||||
```bash
|
||||
# Start daemon + dashboard in background
|
||||
python3 fenris.py start
|
||||
|
||||
# Check status and latest wear stats
|
||||
python3 fenris.py status
|
||||
|
||||
# Take one sample now
|
||||
python3 fenris.py sample
|
||||
|
||||
# Stop the daemon
|
||||
python3 fenris.py stop
|
||||
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 available at: `http://localhost:8420`
|
||||
Dashboard lives at **http://localhost:8420** (or whatever port you chose).
|
||||
|
||||
## CLI Reference
|
||||
## The menu, demystified
|
||||
|
||||
### fenris.py start
|
||||
|
||||
Start monitoring in background (daemon + dashboard).
|
||||
|
||||
```bash
|
||||
python3 fenris.py start [OPTIONS]
|
||||
|
||||
Options:
|
||||
--device PATH NVMe device (default: auto-detect, e.g. /dev/nvme0)
|
||||
--interval SEC Seconds between samples (default: 300)
|
||||
--port PORT Dashboard HTTP port (default: 8420)
|
||||
```
|
||||
|
||||
### fenris.py stop
|
||||
|
||||
Stop background monitoring and clean up PID file.
|
||||
|
||||
### fenris.py status
|
||||
|
||||
Show daemon status and latest wear statistics.
|
||||
|
||||
### fenris.py sample
|
||||
|
||||
Take one sample immediately and print it.
|
||||
|
||||
```bash
|
||||
python3 fenris.py sample [--device /dev/nvme0]
|
||||
```
|
||||
|
||||
### fenris.py run
|
||||
|
||||
Run in foreground (used internally by `start`). Not intended for direct use.
|
||||
|
||||
## Interactive Menu
|
||||
|
||||
Run `./fenris.sh` for a guided interface:
|
||||
Run `./fenris.sh` and you'll get:
|
||||
|
||||
```
|
||||
1) Start monitoring (background daemon + dashboard)
|
||||
@@ -93,53 +83,80 @@ Run `./fenris.sh` for a guided interface:
|
||||
5) Open dashboard URL
|
||||
---
|
||||
h) Help / how this works
|
||||
q) Exit
|
||||
q) Exit (go touch grass)
|
||||
```
|
||||
|
||||
## Data Collected
|
||||
## What Fenris jots down
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `percentage_used` | SSD's own wear indicator (0-100%) |
|
||||
| `bytes_written` / `bytes_read` | Total data written/read |
|
||||
| `available_spare` | Remaining spare capacity (%) |
|
||||
| `media_errors` | Number of uncorrectable errors |
|
||||
| `power_on_hours` | Total power-on hours |
|
||||
| `temperature_c` | Current temperature |
|
||||
| `critical_warning` | NVMe critical warning flags |
|
||||
| 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 |
|
||||
|
||||
## Dashboard Features
|
||||
Hourly rollups also stash `bytes_written` per hour, `pct_start`/`pct_end`, and temp peaks — so the 24h math stays honest.
|
||||
|
||||
- Real-time wear level + projected life remaining in **hours / days / years** from the actual **rolling-24h write rate** and implied TBW endurance
|
||||
- Exact **GB written in the last 24 hours** + GB/h and GB/day rate (updates every poll interval)
|
||||
- Wear-over-time chart + **trailing-24h per-hour write bars**
|
||||
- Dense layout with sticky header, ETag-cached polling synced to the daemon interval, countdown and live badge, stale/preliminary banners
|
||||
- API: `GET /api/data`, `/api/hourly`, `/api/summary`, `/api/config`, `/api/status`
|
||||
## The dashboard — what's on screen
|
||||
|
||||
## File Structure
|
||||
- **Hero card: Projected life remaining** — big, friendly `361 d 2 h` (plus `≈ 361 days · ≈ 8666 hours · ≈ 0.99 years`), backed by `~280 GB/day` and `~101 TB left of ~202 TB total` on the test box.
|
||||
- **Data written (24h)** — exact GB in the rolling window + coverage (`10.4h of 24h` until 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 # Main Python script
|
||||
├── fenris.sh # Interactive menu wrapper
|
||||
├── README.md
|
||||
├── 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 sample log (JSONL)
|
||||
├── hourly.jsonl # Per-hour aggregates (rebuilt from history on restart)
|
||||
├── fenris.pid # Daemon PID file
|
||||
└── fenris.log # Daemon log output
|
||||
├── history.jsonl # raw samples (JSONL, append-only)
|
||||
├── hourly.jsonl # per-hour rollups (auto-rebuilt on restart)
|
||||
├── fenris.pid # daemon PID
|
||||
└── fenris.log # daemon chatter
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
## CLI cheat sheet
|
||||
|
||||
```bash
|
||||
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"**
|
||||
```bash
|
||||
sudo apt install smartmontools # Debian/Ubuntu
|
||||
sudo pacman -S smartmontools # Arch
|
||||
sudo pacman -S smartmontools # Arch — you already knew
|
||||
```
|
||||
|
||||
**"needs root" / permission denied**
|
||||
Set up passwordless sudo (see Requirements) or run with sudo.
|
||||
Set up the passwordless line above, or just `sudo ./fenris.sh`.
|
||||
|
||||
**Dashboard shows "stale"**
|
||||
Daemon not running. Check with `python3 fenris.py status` and restart if needed.
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user