+
+
+
+
+
+ crafted with stubborn curiosity by Bongbetic
+
-Created by Bongbetic.
+
+
+
-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.
+
Fenris πΊ β Your SSD's Tell-All Diary
-## Requirements
+
+ 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 -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.
+
+---
+
+
+ Fenris πΊ β by Bongbetic Β· Be kind to your SSD and it'll be kind to you.
+
+
+
diff --git a/assets/bongbetic-brand/b_glyph.svg b/assets/bongbetic-brand/b_glyph.svg
new file mode 100755
index 0000000..9618252
--- /dev/null
+++ b/assets/bongbetic-brand/b_glyph.svg
@@ -0,0 +1,40 @@
+
+
+
diff --git a/assets/bongbetic-brand/favicon-32.png b/assets/bongbetic-brand/favicon-32.png
new file mode 100755
index 0000000..6ab61ee
Binary files /dev/null and b/assets/bongbetic-brand/favicon-32.png differ
diff --git a/assets/bongbetic-brand/icon-dark-512.png b/assets/bongbetic-brand/icon-dark-512.png
new file mode 100755
index 0000000..a68e9de
Binary files /dev/null and b/assets/bongbetic-brand/icon-dark-512.png differ
diff --git a/assets/bongbetic-brand/icon-light-512.png b/assets/bongbetic-brand/icon-light-512.png
new file mode 100755
index 0000000..336c688
Binary files /dev/null and b/assets/bongbetic-brand/icon-light-512.png differ
diff --git a/assets/bongbetic-brand/wordmark-dark.png b/assets/bongbetic-brand/wordmark-dark.png
new file mode 100755
index 0000000..607068e
Binary files /dev/null and b/assets/bongbetic-brand/wordmark-dark.png differ
diff --git a/assets/bongbetic-brand/wordmark-dark@2x.png b/assets/bongbetic-brand/wordmark-dark@2x.png
new file mode 100755
index 0000000..7654828
Binary files /dev/null and b/assets/bongbetic-brand/wordmark-dark@2x.png differ
diff --git a/assets/bongbetic-brand/wordmark-light.png b/assets/bongbetic-brand/wordmark-light.png
new file mode 100755
index 0000000..b4bc818
Binary files /dev/null and b/assets/bongbetic-brand/wordmark-light.png differ
diff --git a/plan-dash-changes.md b/plan-dash-changes.md
deleted file mode 100644
index 7a15b31..0000000
--- a/plan-dash-changes.md
+++ /dev/null
@@ -1,219 +0,0 @@
-# Plan: Fenris Live Dashboard β Dynamic Polling + Bongbetic Brand + shadcn/ui
-
-> Status: PLAN ONLY β no code touched. **Option A approved.** Data-management removed; cumulative write chart X = hours-in-day (0β24); dense layout; hourly diagnostics + real-time forecast model.
-
-## 1. Context & Goals
-
-**User request (consolidated):**
-- Dashboard live/dynamic, updating at each poll interval without refresh β **Option A (zero-build).**
-- Bongbetic branding + logo everywhere (`~/Documents/bongbetic/Logo`).
-- shadcn/ui styling (CSS parity, no React build).
-- **Remove data-management feature** (backup/restore/backups/repair/purge).
-- **Cumulative data-written graph X-axis = hours in a day (0β24).**
-- **Graphs smaller, side-by-side, flexible.** **Cards tighter/dense.**
-- **Estimated life remaining = real-time, based on live hourly GB usage.** After 24h of run, app must keep recording hourly, store diagnostics, log GB/hour, and forecast remaining days from current hourly rate.
-
-**Current state (verified 2026-08-19):**
-- `fenris.py` single `DASHBOARD_HTML` via `ThreadingHTTPServer`. Hand-rolled dark CSS, 2Γ `