hub v0.172.0 RELEASED (13:08 UTC); felhom-tenantsync v1.4.0 INSTALLED on ep0 (operator yes); R-1292 live on Tester 1 (PASS; run 1 was an instrument failure, kept).
gates / gates (push) Successful in 13m17s

This commit is contained in:
admin committed 2026-10-26 14:17:07 +01:00
1 parent a90a9745a8
commit 69443d3c4e
6 files changed
+137 -3

No files matched your search

@@ -0,0 +1,17 @@
rc=0
[1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'QBT_ADMIN_PASSWORD']
[1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
[1] deployed, controller state=running, pinned={'qbittorrent': 'qbittorrentofficial/qbittorrent-nox:5.2.4-1'}
1. qBittorrent installed: True
before: {'error': 'unreadable'}
2. alternative speed ON, alternative limit 500 KiB/s (the household's): ok ok
now: {'error': 'unreadable'}
[3. first tick] rule lines: 2026/10/26 12:18:53 [INFO] [upwatch] qBittorrent upload limit set to 5892096 B/s (50 % of the tested line, 94.3 Mbit/s) | record: (none)
after the first tick: {'error': 'unreadable'}
4. the household switches back to normal speed: ok {'error': 'unreadable'}
after the next tick: {'error': 'unreadable'} | record: | lines: (none at INFO)
VERDICT: FAIL
[7] stop 200, remove with data -> 200 {'ok': True, 'data': {'removed': 'qbittorrent', 'volumes_removed': ['qbittorrent_qbittorrent_config'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'userdata_kept': ['/mnt/felhom-driv
5. leftovers: {'containers': '', 'volumes': '', 'appyaml': '', 'appdata': '', 'kept': '', 'subdomain': '404'}
[exited with code 0]
@@ -0,0 +1,14 @@
[1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'QBT_ADMIN_PASSWORD']
[1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
[1] deployed, controller state=running, pinned={'qbittorrent': 'qbittorrentofficial/qbittorrent-nox:5.2.4-1'}
1. qBittorrent installed: True
before: {'up_limit': 0, 'alt_up_limit': 10240, 'mode_alt': False}
2. alternative speed ON, alternative limit 500 KiB/s (the household's): ok ok
now: {'up_limit': 0, 'alt_up_limit': 512000, 'mode_alt': True}
[3. first tick] rule lines: 2026/10/26 12:53:06 [INFO] [upwatch] qBittorrent upload limit set to 5892096 B/s (50 % of the tested line, 94.3 Mbit/s) | record: {'box_set_bytes': 5892096, 'line_mbps': 94.28828951955792, 'set_at': '2026-10-26T12:53:06Z'}
after the first tick: {'up_limit': 5892096, 'alt_up_limit': 512000, 'mode_alt': True}
4. the household switches back to normal speed: ok {'up_limit': 5892096, 'alt_up_limit': 512000, 'mode_alt': False}
after the next tick: {'up_limit': 5892096, 'alt_up_limit': 512000, 'mode_alt': False} | record: {'box_set_bytes': 5892096, 'line_mbps': 94.28828951955792, 'set_at': '2026-10-26T12:53:06Z'} | lines: (none at INFO)
VERDICT: PASS
[7] stop 200, remove with data -> 200 {'ok': True, 'data': {'removed': 'qbittorrent', 'volumes_removed': ['qbittorrent_qbittorrent_config'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'userdata_kept': ['/mnt/felhom-drives/adatlem
5. leftovers: {'containers': '', 'volumes': '', 'appyaml': '', 'appdata': '', 'kept': '', 'subdomain': '404'}
@@ -0,0 +1,11 @@
2026-10-26T13:09:21Z
syntax-ok
-rwxr-xr-x 1 root root 17964 Oct 26 13:09 /usr/local/bin/felhom-tenantsync
-rwxr-xr-x 1 root root 8253 Jul 10 18:57 /usr/local/bin/felhom-tenantsync.bak-1.0.0
-rwxr-xr-x 1 root root 10480 Jul 17 11:12 /usr/local/bin/felhom-tenantsync.bak-1.1.0
-rwxr-xr-x 1 root root 12004 Jul 17 19:15 /usr/local/bin/felhom-tenantsync.bak-1.2.0
-rwxr-xr-x 1 root root 15542 Oct 19 14:07 /usr/local/bin/felhom-tenantsync.v1.3.0
751b3c26f3e4c872
# felhom-tenantsync v1.4.0 — the offsi
{"status":"ok","fp":"c6:07:28:3f"}
{"status":"ok","unpruned_checked":true,"unpruned":[]}
@@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""r1292_live.py — R-1292 live on a box with a TESTED line (Tester 1; 9202 has none, so the rule never acts there):
a THROWAWAY qBittorrent; the household's „alternative speed" switched ON with an alternative limit of 500 KiB/s; then
the controller's 50 % rule (its 15-minute tick) must set the NORMAL limit (`up_limit`) and leave the alternative alone;
then the household switches back to normal speed, and the next tick must NOT read that as „the household changed it".
qBittorrent's own API from inside its container (the admin password rides an env var, never printed). Removed through
the product at the end (data and backups).
usage: SC=<dir with .ctlpw> SWEEP_EV=<evidence dir> TARGET=tester-1 DRIVE_ROOT=<drive> python3 r1292_live.py"""
import json, os, sys, time
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../catalog-sweep-2026-10-24/tools"))
import sweep as sw # noqa: E402
w = sw.w
APP = "qbittorrent"
def qb(path, data=None):
"""One qBittorrent API call inside its container, through /root/qbtool.sh in the guest (the password via env QP)."""
pw = (w.GENERATED.get(APP) or {}).get("QBT_ADMIN_PASSWORD", "")
arg = " '" + data + "'" if data else ""
return sw.g(f"QP='{pw}' sh /root/qbtool.sh {path}{arg}").strip()
def record():
"""The 50 % rule's record (settings.json upwatch.qb_limit), parsed — never a grep on a JSON file."""
raw = sw.g("docker exec felhom-controller cat /opt/docker/felhom-controller/data/settings.json")
try:
return (json.loads(raw).get("upwatch") or {}).get("qb_limit")
except ValueError:
return "unreadable"
def prefs():
try:
p = json.loads(qb("app/preferences"))
return {"up_limit": p.get("up_limit"), "alt_up_limit": p.get("alt_up_limit"), "mode_alt": qb("transfer/speedLimitsMode") == "1"}
except ValueError:
return {"error": "unreadable"}
def rule_lines(since):
return sw.g(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'qBittorrent (upload )?limit' | cut -c1-200").strip()
def wait_tick(since, say, label, cap=1100):
for _ in range(cap // 20):
time.sleep(20)
out = sw.g(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'upwatch' | grep -vE 'DEBUG' | tail -1")
# the 15-minute tick also logs the hold seeder; any upwatch line after `since` with a qBittorrent verdict counts
ln = rule_lines(since)
if ln:
break
st = record()
say(f" [{label}] rule lines: {rule_lines(since) or '(none at INFO)'} | record: {st or '(none)'}")
return st
def main():
say = print
w.say = say
w.login()
t0 = int(time.time())
try:
ok = w.deploy(APP, "torrent") and w.wait_app("torrent", "/", tries=90, delay=5)
say("1. qBittorrent installed:", ok)
before = prefs()
say(" before:", before)
if "error" in before:
raise SystemExit("INSTRUMENT: qBittorrent's preferences are unreadable — no verdict (fix the probe first)")
say("2. alternative speed ON, alternative limit 500 KiB/s (the household's):",
qb("app/setPreferences", "json=" + json.dumps({"alt_up_limit": 512000})) or "ok",
qb("transfer/toggleSpeedLimitsMode", "x=1") or "ok")
say(" now:", prefs())
rec1 = wait_tick(t0, say, "3. first tick")
p1 = prefs()
say(" after the first tick:", p1)
say("4. the household switches back to normal speed:", qb("transfer/toggleSpeedLimitsMode", "x=1") or "ok", prefs())
t1 = int(time.time())
time.sleep(960) # one full 15-minute tick; a "kept" decision logs nothing, a "household" one logs at INFO
rec2 = record()
say(" after the next tick:", prefs(), "| record:", rec2, "| lines:", rule_lines(t1) or "(none at INFO)")
verdict = ("PASS" if p1.get("up_limit", 0) and abs(p1["up_limit"] - 94.3 * 0.5 * 1e6 / 8) < 2048 and p1.get("alt_up_limit") == 512000
and isinstance(rec2, dict) and not rec2.get("household") else "FAIL")
say("VERDICT:", verdict)
finally:
sw.remove(APP, keep=False, say=say)
say("5. leftovers:", sw.leftovers(APP, "torrent"))
if __name__ == "__main__":
main()
+1 -1
View File
@@ -1,4 +1,4 @@
## v0.172.0 (unreleased, 2026-10-26) — a fresh install's whole-guest off-site set-up never fails silently (R-1295)
## v0.172.0 (2026-10-26) — a fresh install's whole-guest off-site set-up never fails silently (R-1295) — RELEASED 2026-10-26 13:08 UTC on the operator's yes (image sha256:7c8d129a…, from `232f42d2`, CI 2366; rc.1 ran the live proof)
- **Cause (measured, `documentation/audits/r1295-pbsdr-selfcheck-2026-10-26/`):** PBS's proxy caches permissions for up to
5 s and an ACL-only change does not clear the cache; under concurrent requests ep0's self-check of a new token is
+2 -2
View File
@@ -1,10 +1,10 @@
## 2026-10-26 — felhom-tenantsync v1.4.0: the self-check waits out the PBS permission cache (R-1295) — committed, NOT installed on ep0
## 2026-10-26 — felhom-tenantsync v1.4.0: the self-check waits out the PBS permission cache (R-1295) — INSTALLED on ep0 2026-10-26 13:09Z on the operator's yes (sha256 `751b3c26…` = the repository's; v1.3.0 kept as `.v1.3.0`; `fingerprint` and `usage` ok)
- **`felhom-tenantsync.sh` v1.4.0:** the new token is re-checked every 0.5 s for up to 15 s (a log line per try) before
the old regenerate-once-and-rollback; the transient admin token waits the same way before its first namespace list
(under load it met the same cache and a wrong `namespace create` died). Test `test_tenantsync_selfcheck.py` (19 checks;
v1.3.0 fails 11). Live on a throwaway PBS 4.2.5 under load, the hub's exact order, 30 pairs: v1.3.0 failed 11, v1.4.0
failed 0 (`documentation/audits/r1295-pbsdr-selfcheck-2026-10-26/`). **Installing it on ep0 is the operator's yes.**
failed 0 (`documentation/audits/r1295-pbsdr-selfcheck-2026-10-26/`, `ep0-install-v1.4.0.txt`).
## 2026-10-25 — installer 1.36.1: the preflight words a refusal again — PUBLISHED 2026-10-25 evening on the operator's yes (`installer-v1.36.1` → `79ee3e33`, served sha256 `e18cdd17…`; the three refusal sentences seen live)