130 lines
9.6 KiB
Markdown
130 lines
9.6 KiB
Markdown
# 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 `-rc` tag 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
|
||
|
||
1. **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).
|
||
2. **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).
|
||
3. **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).
|
||
4. **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.
|
||
5. **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** (`tc` from 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)
|
||
|
||
1. Deploy hub 0.168.0 (the last plain tokens locked). Then read its start line `config tokens sealed at rest (8 …)`.
|
||
2. 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.
|
||
3. 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).
|
||
4. Release the upload watch when you say yes (hub 0.169.0 build + deploy, then controller 0.342.0 through the normal path).
|
||
5. 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_code` is 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 stats` text 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-samba` runs 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".
|