9.6 KiB
REPORT — weekend night 2026-10-23: the hub's last plain tokens locked; the upload and CPU watch (spike, design, build)
Safest readings taken (unattended), stated first:
- A test image is not a release. The upload watch was proven on scratch 9202 with
felhom-controller:0.342.0-rc.1, set by hand in 9202's/etc/felhom-controller-image(the shape of today's security-lane rc.1). An-rctag is never offered to a household's update button. No floor was touched; only 9202 ran it, and 9202 is back on 0.341.0. - 9202's line was seeded by hand (one 40 Mbit/s record): its off-site backup is off by design, so it can never learn a line itself. The learning path is pinned by unit tests and by tonight's one-second read of the real pushes on the three test boxes (read only).
- The hub 0.168.0 image was built (the brief asked for it) and not deployed. The four new hub event types are in hub 0.169.0, committed only.
- Per-app trigger instead of a whole-box trigger. The brief said „the box's upload above X % of the line". Inside its container the controller cannot tell LAN from internet bytes, nor see the box's total; it can see each app's exact bytes, and the per-app view is what the explanation compares. So the trigger is per app (§6.2 of the spike).
- The first day: apps the box can ask are judged at once on the 5 Mbit/s floor (a mismatch is evidence whatever the line); apps it cannot ask are judged only after ~20 h of history. The brief's „first day: no alarm, only learning" holds for those apps; for the others a mismatch on day one is still an alarm.
Five lines
- Tokens locked: the Cloudflare tunnel token, the Cloudflare API token and the git token are sealed in hub 0.168.0 — built, CI green, not deployed (you deploy it on Monday).
- Backup copies: two options — encrypt the hub's Longhorn disk, or take it out of the Longhorn backup because the hub's own copy to ep0 is already encrypted at rest and in transit (measured). My pick: take it out (D40).
- The line: measured from the box's own nightly off-site upload (one-second samples, best 10 seconds) — a lower bound; most nights the upload is too small to say anything, so a real test is your decision (R-1247).
- Apps that explain their traffic: qBittorrent and Transmission (their own byte counters, within 1–7 %), Plex (its viewers and transcodes). Jellyfin needs a key the household makes; Nextcloud and Immich give no number.
- The fake hacked app was caught: yes — a flood from inside qBittorrent, hidden behind a real seed: one alarm after 15 minutes, one clear 15 minutes after it stopped. A CPU burner in Plex: the same. A real seed: no alarm.
The spike's numbers
documentation/audits/SPIKE-upload-watch-2026-10-23.md (evidence beside it). Short form:
- Normal levels (three boxes, 19–31 days of one-minute samples): no app's own upload p95 reached 6 Mbit/s; the longest run at ≥ 5 Mbit/s was 3 minutes, at ≥ 1 Mbit/s 9 minutes. CPU: the longest run at ≥ 150 % of a core was 7 minutes, at ≥ 80 % 13 minutes. DooPlex's hacked Plex: ~44 Mbit/s for hours, ~10 cores.
- Line speed: at a one-minute sample the push exceeded 2 Mbit/s on 3 of 81 box-nights (the push moves a few MB); the box keeps no byte count of it. Tonight's one-second read: §Night data below. No active test was run (ep0 changes forbidden; a public test's terms not read).
- Apps: qBittorrent counted 131.1 Mbit/s while its container sent 134.6–140.5 (×1.03–1.07); Transmission ×1.001–1.02; the limit API works and persists (KiB/s rounding); Plex answers its session lists (with the token it already has when claimed); Jellyfin 401 without a household-made key.
- A hard cap (
tcfrom a helper in the app's network namespace) works in an unprivileged guest: a flood held at 20 Mbit/s, the app kept answering. Not built (R-1248).
The household's line (final text)
- HU: „A qBittorrent feltöltése legfeljebb N Mbit/s, hogy a családi internet gyors maradjon. A qBittorrentben átállíthatod."
- EN: "qBittorrent uploads at most N Mbit/s, so the family internet stays fast. You can change this in qBittorrent."
- The system settings page: „Feltöltési sebesség: legalább N Mbit/s (az éjszakai távoli mentésből mérve)" / "Upload speed: at least N Mbit/s (measured from the nightly off-site backup)"; „még mérjük …" until known.
Morning list (Monday)
- Deploy hub 0.168.0 (the last plain tokens locked). Then read its start line
config tokens sealed at rest (8 …). - D40: the hub's plain Longhorn copies — stop that backup (my pick) or encrypt the disk; and whether to delete the two plain copies already there.
- D41: qBittorrent's default limit — 50 % of the measured line (my pick). D42: who is told — the family only about the limit, the hacked-app alarms only you (my pick).
- Release the upload watch when you say yes (hub 0.169.0 build + deploy, then controller 0.342.0 through the normal path).
- For the homelab session (list only, no HM numbers): an upload-saturation alert on DooPlex's
eno1(the line is ~100 Mbit/s; the flood peaked at 905 Mbit/s).
Commits, images, CI (by commit)
| Repo | Commit | What | CI |
|---|---|---|---|
| felhom.eu | 5277d859 |
hub 0.168.0: the tokens sealed; R-1243; STATUS D40 | run 2269 success |
| felhom.eu | af5c005a |
hub 0.169.0: the four events (allowed, operator-only, mail line) | run 2271 success |
| felhom.eu | a91d0b24 |
spike, design (08 §9), live proofs, rows R-1244..R-1248, STATUS |
run 2272 success |
| felhom-controller | a9c1997f |
v0.342.0 (unreleased): the upload and CPU watch | run 2270 success |
| felhom-controller | 60e487d |
CONTEXT + REPORT | run 2273 success |
Images: felhom-hub:0.168.0 sha256:3bfdecaae527d4762059600cdfaad83daf4e4b5b20661fc47acf128180d87cb3 (from 5277d859,
not deployed); felhom-controller:0.342.0-rc.1 (from a9c1997f, scratch 9202 only, removed from 9202 after).
Teardown, three layers: machine — 9202: the four throwaway apps removed through the product (containers and volumes
gone), my test files, probes and the alpine:3.20 helper image removed, back on 0.341.0, the seeded record removed;
host — demo-hp: the receiver LXC 9471 destroyed with its disk; DooPlex: my private copy of the hub snapshot shredded,
the 9202 password copy shredded; hub — none. ep0: read only (the hub-copy snapshot list). No household box touched.
Rows
Before 188, after 194; opened 6 (R-1243 … R-1248), closed 0. R-975 stays open (WAITING-ON-OPERATOR: deploy 0.168.0).
Observations
- The hub's Cloudflare/tunnel/git tokens unsealed in
config_json— FIXED in hub 0.168.0 (R-975, not yet deployed). - The hub's Longhorn copies are plain and sit in the open folder — FILED: R-1243.
- The upload watch is not released — FILED: R-1244; its limit size — FILED: R-1245; who is told — FILED: R-1246.
- The line is only a lower bound — FILED: R-1247. An app's API can lie — FILED: R-1248.
appliance_registrations.pairing_codeis plain — NOT-A-FINDING: it is shown on the box's screen and is one of two factors; the other is hashed.- R-879's sealing (0.138.0) ran no VACUUM, so old plaintext of those columns could sit in freed blocks of the live
hub.db— NOT-A-FINDING: 0.168.0's one-time VACUUM rewrites the file on its first start. - The hub CHANGELOG's 0.167.0 header still said „NOT built" though it is deployed — fixed without a row (small).
- The shared memory index went over its size ceiling twice in one day (another session's lines) and blocked the push gates — fixed without a row (shortened, no link lost); it sits 14 bytes under the ceiling now.
- The watch's very first minute after a start cannot reach apps by name (network joined seconds later) — NOT-A-FINDING: that minute only sets baselines.
- The media wiring's generated override file stays after an app's removal — NOT-A-FINDING: rewritten before every start.
docker statstext in the stored metrics has three significant digits — NOT-A-FINDING for the watch (it reads the Engine API); the charts are display only.felhom-sambaruns on the host network and has no counter — NOT-A-FINDING: infrastructure, not judged.- Transmission's rate under its limit was not observed (no peer contact) — NOT-A-FINDING: the limit API was read back; qBittorrent's was observed.
- A dated check came due on the push (R-1214, Tester 1's night copy): read read-only — the guest's memory „full" stall was 444 µs in total over 11 h, 0 OOM kills; the copy was small (0.24 GB) and the capped container is not shown — the row stays WATCHING, the due line removed.
- The night's line floor is low (10 Mbit/s on demo-hp, one second at 70) — FILED in R-1245's text (D41), no new row.
Instruction files, poster
No instruction file edited. System poster: no fact on it changed (nothing deployed) — none of the three applied.
Night data
Read 02:08–02:30Z, once a second, on the three test boxes (read only; spike §2.1):
- demo-hp: counts. Best 10 seconds 12.6 MB = 10.0 Mbit/s. One second reached 70 Mbit/s.
- demo-felhom: too small (best 10 seconds 3.2 MB = 2.5 Mbit/s).
- Tester 1: too small (3.5 MB = 2.7 Mbit/s).
What it means: the nightly push is a burst of a few seconds. So the line it gives is a low floor. On demo-hp, 50 % of that floor is about 5 Mbit/s for qBittorrent — probably much less than the real line. The floor rises only when a bigger night comes (the box keeps the best of 14 nights). Added to the D41 decision row (R-1245): if you want a looser limit before a real line test exists (R-1247), pick a higher share or „none".