Night 2026-10-25 Parts 1–3: every named artifact still downloads; installer 1.36.1 published (operator yes) and its three refusal sentences seen live; D50 = A recorded (09 §3 262, CONTEXT, build skill); R-1284 and R-1290 closed; R-1261 rig evidence so far
gates / gates (push) Successful in 12m56s

This commit is contained in:
admin committed 2026-10-25 20:52:56 +01:00
1 parent b841192670
commit 68ee667a83
19 files changed
+378 -8

No files matched your search

@@ -1099,6 +1099,16 @@ its length, and both fixes cost something the household would notice — operato
`plex: security fix CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken` and ended `done` in 30.1 s
after the update's second-drive copy and undo copy. Released as controller 0.341.0, global floor 11:24 UTC.
### 2026-10-25 (evening, attended) — D50: which agent packages the registry keeps
262. **The registry keeps the newest 5 released agent versions, plus the hub's MinAgent floor, plus the version the hub's
day-0 manifest names; CI asserts exactly these (D50 = A).** *Operator ruling 2026-10-25 evening.* Options: A) this
— cost: a box on a version outside the set cannot fetch its own binary again (none is, on the day); B) the newest 10,
never pruned by hand — cost: today's four are gone, so CI stays red until ten new versions exist. Why A: it is what
the operator prunes to, and the two pinned versions are the only old ones anything still asks for. Built: agent
`scripts/retention-policy.json` (`generic_versions_kept` 5, `pinned`) + `check-published-versions.py`; the pins are
copies of hub state, updated by the session that records a vouch or a new MinAgent (R-1290).
### 2026-10-25 (attended release run) — D49: a restore that does not start keeps both copies; D44–D46: the review picks
258. **A restore whose app does not start keeps the restored data AND the old copy, tells the operator, and never puts the
@@ -0,0 +1,14 @@
# Part 1 — what a box or the installer can still ask for (read only), 2026-10-25 ~19:00Z
# The operator pruned old controller, agent and golden packages by hand on 2026-10-25 (about the last 5; 10 for the controller).
hub manifest (hub.db + WAL copy, read only, deleted after): agent 0.165.0 e9d04a47…, bundle 8dea1f34…, golden 0.331.0 3881de62…, MinAgent 0.158.1, global floor 0.343.0
registry now: agent 0.158.1, 0.161.0–0.165.0, 0.163.0-step1 | golden 0.311.0, 0.312.0, 0.313.0, 0.318.0, 0.329.0, 0.331.0 | controller 0.331.0, 0.341.0, 0.342.0, 0.343.0 (+ rc) | hub 0.165.0–0.170.0 | samba 1.0.0–1.2.0
anonymous downloads (HTTP, bytes, sha256 prefix):
felhom-agent/0.165.0/felhom-agent 200 16286780 e9d04a47ee85222c = manifest
felhom-agent/0.165.0/felhom-config-bundle.json 200 370108 8dea1f34d77ba40a = manifest
felhom-agent/0.158.1/felhom-agent 200 14919180 20264df64ef646a8 (the MinAgent floor)
felhom-agent/0.158.1/felhom-config-bundle.json 200 345462 c23c3d355fe70a6f
felhom-golden/0.331.0/golden.tar.zst 200 637197198 3881de62b0986ccb = manifest
agent tags v0.158.1 … v0.165.0 (10): raw/tag/<t>/configs/felhom-os-apply → 200 each
controller images (docker manifest inspect from guest 9202, no login): 0.343.0 OK, 0.342.0 OK (rollback), 0.331.0 OK (the golden's), felhom-samba:1.2.0 OK (the pinned one); control 0.340.0 MISSING (pruned) — the check can fail
ISO iso.felhom.eu felhom-installer-1.30.0-pve9.2-1.iso 200 (1705324544 B); its manifest names only host-install-url https://felhom.eu/scripts/felhom-host-install.sh (fetched at run time — the ISO embeds no installer) → 200
VERDICT: nothing that is still named is missing.
@@ -0,0 +1,11 @@
SCRIPT_VERSION="1.36.1" # the SINGLE version source (F-1): -h and the run banners follow it.
e18cdd17823911e7d87da12fcb8d48b6827f1706a84b3168114fe19fe30aa1f1
== a customer id that does not exist
[ERROR] customer 'no-such-customer-cc' not found in hub (404) — create it in the hub first
[ERROR] PRE-FLIGHT FAIL (exit 1) — fix the finding above and re-run
== Tester4 with a wrong passphrase
[ERROR] passphrase REJECTED (401) for customer 'Tester4'
[ERROR] PRE-FLIGHT FAIL (exit 1) — fix the finding above and re-run
[ERROR] the hub answered 409 on the config preflight (GET /config): this customer already has a live box, which uses its own key.
HU: a hub 409-cel válaszolt (the config preflight (GET /config)): ennek az ügyfélnek már van élő gépe, amely a saját kulcsát használja.
[ERROR] PRE-FLIGHT FAIL (exit 1) — fix the finding above and re-run
@@ -0,0 +1,16 @@
0. fresh:
mode=0 uploadLimit=0 prefs={'up_limit': 0, 'alt_up_limit': 10240, 'scheduler_enabled': False}
1. setUploadLimit 2500000 (normal mode):
mode=0 uploadLimit=2499584 prefs={'up_limit': 2499584, 'alt_up_limit': 10240, 'scheduler_enabled': False}
2. setPreferences alt_up_limit 3072000:
mode=0 uploadLimit=2499584 prefs={'up_limit': 2499584, 'alt_up_limit': 3072000, 'scheduler_enabled': False}
3. toggleSpeedLimitsMode (to alt):
mode=1 uploadLimit=3072000 prefs={'up_limit': 2499584, 'alt_up_limit': 3072000, 'scheduler_enabled': False}
4. setUploadLimit 1024 in ALT mode:
mode=1 uploadLimit=1024 prefs={'up_limit': 2499584, 'alt_up_limit': 1024, 'scheduler_enabled': False}
5. setPreferences up_limit 5120000 in ALT mode:
mode=1 uploadLimit=1024 prefs={'up_limit': 5120000, 'alt_up_limit': 1024, 'scheduler_enabled': False}
6. toggle back to normal:
mode=0 uploadLimit=5120000 prefs={'up_limit': 5120000, 'alt_up_limit': 1024, 'scheduler_enabled': False}
7. setPreferences up_limit 0:
mode=0 uploadLimit=0 prefs={'up_limit': 0, 'alt_up_limit': 1024, 'scheduler_enabled': False}
@@ -0,0 +1,4 @@
# D50 decoys against the OLD gate (check-published-versions.py at 6b6ba0c), 2026-10-25
FAIL: published: GENUINE (D50): a pinned version older than the window is asserted and downloads: rc=0 expected 0; missing ['verifying: 0.8.0']
FAIL: published: FACT (D50): a pinned version older than the window whose package 404s: rc=0 expected 1 - LIVE HOLE; missing ['FAIL v0.8.0', 'binary NOT downloadable']
FAIL: published: FACT (D50): a pin that names a version with no tag: rc=0 expected 1 - LIVE HOLE; missing ['FAIL pinned day0_agent=0.150.9', 'no git tag']
@@ -0,0 +1,22 @@
# R-1261 red-proofs: each fix switched off in turn (controller working tree, 2026-10-25)
## 1. hold only the first running app
=== RUN TestR1261_EverySeederIsHeldAndEveryLimitComesBack
linehold_r1261_test.go:90: during the test qbittorrent={Bytes:1024 Alt:false Enabled:false KBps:0} transmission={Bytes:0 Alt:false Enabled:true KBps:500} — every seeder must be at the test value
--- FAIL: TestR1261_EverySeederIsHeldAndEveryLimitComesBack (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/upwatch 0.007s
## 2. no installed check
=== RUN TestR1261_ARecordOfAnAppRemovedWhileHeldIsDropped
linehold_r1261_test.go:121: the record of a removed app must be dropped, got &{App:qbittorrent Prev:{Bytes:2499584 Alt:false Enabled:false KBps:0} Held:{Bytes:1024 Alt:false Enabled:false KBps:0} At:}
--- FAIL: TestR1261_ARecordOfAnAppRemovedWhileHeldIsDropped (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/upwatch 0.007s
## 3. the release reads the current mode's slot
=== RUN TestR1261_ASpeedModeSwitchDuringTheTestNeverLeavesTheTestValue
=== RUN TestR1261_ASpeedModeSwitchDuringTheTestNeverLeavesTheTestValue/hold_in_alt_mode=false
linehold_r1261_test.go:168: after the test up_limit=1024 alt_up_limit=3072000, want 2499584 and 3072000 — never the 1 KiB/s test value
=== RUN TestR1261_ASpeedModeSwitchDuringTheTestNeverLeavesTheTestValue/hold_in_alt_mode=true
linehold_r1261_test.go:168: after the test up_limit=2499584 alt_up_limit=1024, want 2499584 and 3072000 — never the 1 KiB/s test value
--- FAIL: TestR1261_ASpeedModeSwitchDuringTheTestNeverLeavesTheTestValue (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/upwatch 0.012s
@@ -0,0 +1,8 @@
20:43:20 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'QBT_ADMIN_PASSWORD']
20:43:20 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
20:43:35 [1] deployed, controller state=running, pinned={'qbittorrent': 'qbittorrentofficial/qbittorrent-nox:5.2.4-1'}
qbittorrent deployed True running
20:43:35 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'TR_ADMIN_PASSWORD']
20:43:35 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
20:43:55 [1] deployed, controller state=running, pinned={'transmission': 'lscr.io/linuxserver/transmission:version-4.1.3-r0'}
transmission deployed True running
@@ -0,0 +1,2 @@
init 200 {'data': {'phase': 'formatting', 'started': True}, 'ok': True}
status {'data': {'device': '/dev/sdb', 'durable_id': '', 'error': '', 'opsign': '', 'phase': 'done', 'reason': '', 'started_at': '2026-10-25T19:42:47.597592227Z', 'updated_at': '2026-10-25T19:42:49.836540017Z', 'where': '/mnt/felhom-drives/adat'}, 'ok': True}
Whitespace-only changes.
@@ -0,0 +1,163 @@
[INFO] felhom-host-install v1.36.1 — mode=byo customer=Tester4 vmid=9201
[STEP] 1/8 pre-flight
[INFO] pve-manager/9.2.2/b9984c6d90a4bd80 (running kernel: 7.0.2-6-pve)
[INFO] node: pf-test (explicit)
[INFO] agent config: /etc/felhom-agent/agent.json
[INFO] agent: not installed yet — will be fetched + installed in step 5/8
[INFO] local-lvm free: ~10 GiB
[WARN] local-lvm free ~10 GiB is below the recommended 120 GiB — the install continues; proceed only if you sized the grows deliberately
[INFO] free RAM: ~4383 MiB
[INFO] existing guests on this host: 0 (pct+qm)
[INFO] archive storage 'local' present
[INFO] acl storages all present or pre-positioned: local local-lvm
[INFO] dnsmasq: not present before Felhom — recorded; uninstall will remove it again if we install it
[INFO] host DNS (:53): free
[INFO] hub reachable (https://hub.felhom.eu)
[OK] customer 'Tester4' exists + passphrase valid
[INFO] golden: none local — will fetch + verify from Gitea in step 7/8
pf-test
[STEP] BYO disclosure — installing Felhom on a host the operator does NOT own will create:
pveum: roles FelhomAgentBase/FelhomAgentGuest/FelhomAgentStore + user felhom-agent@pve + token felhom-agent@pve!agent
+ scoped ACL grants (Base@/, Guest@/pool/felhom, Store@[local local-lvm]) + the 'felhom' pool
agent: service user felhom-agent + /usr/local/bin/felhom-agent (+ .bak-* backups; A/B slots via the guarded self-update)
+ /etc/systemd/system/felhom-agent.service (+ felhom-agent-rollback.service + start-limit drop-in)
+ /etc/sudoers.d/felhom-agent + state dir /var/lib/felhom-agent + config /etc/felhom-agent/agent.json (0600)
host: /usr/local/sbin/felhom-mkfs-guarded + /usr/local/sbin/felhom-selfupdate-guarded
+ /usr/local/sbin/felhom-pbs-apply (PBS-DR apply wrapper — DR capability is baked
on every install; ACTIVATION stays a hub flag, off = zero effect on this host)
+ felhom-mgmt-watchdog service+timer+script + /etc/tmpfiles.d/felhom-privsep.conf
+ /usr/local/sbin/felhom-priv-apply + the guest-hook snippet under /var/lib/vz/snippets/
(both from the agent's config bundle)
+ the 'sudo' and 'age' packages if absent + install state dir /var/lib/felhom-install
wg: an OUTBOUND WireGuard tunnel to the Felhom hub (wg_tunnel.enabled=true — base
infrastructure like the cloudflared tunnel; hands-free peer registration; the
operator can disable it per-customer hub-side on request)
guest: the provisioned Felhom LXC (vmid 9201, capped 3 cores / 4096 MiB) + its volumes
+ the golden vzdump imported onto storage 'local'
reuse: a golden already on 'local' is used only if its sha256 or its controller version
matches the hub's vouched golden; otherwise the vouched one is fetched (a --golden you name is refused)
update: operator-signed self-update authority: felhom-op-1
NOT touched in byo mode: root@pam (no break-glass), host DNS (:53), OOB sshd.
Type this host's short hostname (pf-test) to acknowledge the byo install: [OK] byo install acknowledged for host pf-test
[OK] pre-flight passed
[STEP] 2/8 Proxmox API token
[OK] token minted (secret captured, not logged)
[OK] scoped ACL applied (Base@/, Guest@/pool/felhom + /vms/990000..990009, Store@[local local-lvm])
[SKIP] old broad role FelhomAgent already absent
[STEP] 3/8 compute volume grows
[INFO] auto-computed from a 10 GiB thin pool (16 §7: 50/100/130 GiB)
[INFO] grows: rootfs +0G (->32G), data +26G (->50G, ONE volume)
[STEP] 4/8 host enrollment (POST /host-enroll)
[OK] host MINTED (first enroll)
[INFO] host_id: Tester4-84bbe7 (api_key captured, not logged)
[SKIP] break-glass (step 4b) SKIPPED (byo: the host owner manages break-glass; root@pam untouched, nothing vaulted)
[STEP] 5/8 agent install (fetch + verify + install)
[SKIP] apt repo alignment skipped (byo — the owner manages repos)
[WARN] no git credential in controller.yaml — fetching artifacts ANONYMOUSLY (they are world-readable; sha256 verification unchanged)
[INFO] manifest: agent v0.165.0 (sha e9d04a47ee85222c…), golden v0.331.0
[INFO] fetching agent binary v0.165.0 from Gitea …
[OK] verified sha256 e9d04a47ee85222c… matches the hub manifest
[OK] installed /usr/local/bin/felhom-agent (felhom-agent 0.165.0)
[INFO] installing the 'sudo' package (required for the non-root agent model) …
[OK] sudo installed (Sudo version 1.9.16p2)
[INFO] installing the 'age' package (escrow ceremony identity-wrap dependency) …
[OK] age installed (1.2.1)
[OK] created service user felhom-agent
[OK] added felhom-agent to systemd-journal (unprivileged journal read for NAS verify)
[OK] verified sha256 8dea1f34d77ba40a… matches the hub manifest
os-apply: BUNDLE /etc/ssh/sshd_config.d/10-felhom-root-key-only.conf skipped: a BYO host keeps its own settings (D43)
os-apply: BUNDLE START agent=0.165.0 sha=8dea1f34d77ba40a authority=installer files=32 write=26 same=1 kept=0 skipped=5
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-mkfs-guarded (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-selfupdate-guarded (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-pbs-apply (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-backup-target-apply (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-vzdump-partial-clean (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-thin-check (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-internal-place (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-crash-guard (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-priv-apply (new)
os-apply: BUNDLE WROTE /var/lib/vz/snippets/felhom-guest-hook.sh (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-shared-parent.sh (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-shared-parent.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard-check.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard-check.timer (new)
os-apply: BUNDLE WROTE /etc/felhom/crash-guard.conf (new)
os-apply: BUNDLE WROTE /etc/grub.d/01_felhom_oneshot (new)
os-apply: BUNDLE WROTE /etc/grub.d/42_felhom_oneshot (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent-rollback.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent.service.d/felhom-agent-limits.conf (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-mgmt-watchdog (new)
os-apply: BUNDLE WROTE /etc/tmpfiles.d/felhom-privsep.conf (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-mgmt-watchdog.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-mgmt-watchdog.timer (new)
os-apply: BUNDLE WROTE /etc/sudoers.d/felhom-agent (new)
os-apply: BUNDLE DONE agent=0.165.0 written=26 same=1 self-check=ok signers-created=False
[OK] installed every root-owned file from config bundle v0.165.0 (sha 8dea1f34d77ba40a…; checked, previous copies kept)
[SKIP] OOB (felhom-sshd) off (byo, or appliance --no-oob) — the bundle skipped its files
[STEP] 5b/8 internal data place (Belső tárhely)
[SKIP] byo: the host owner's thin pool is not ours to divide — no internal place
[STEP] 6/8 agent config + service
[WARN] backup target: DEGRADED — no eligible second drive, so the whole-system backup stays on the SYSTEM drive.
[WARN] It protects against file corruption but NOT against a disk failure. Attach a second drive and assign it in the dashboard.
[INFO] node=pf-test local_api=192.168.0.150:8443 tls_fp=65:D5:C9:E7:A0:4C…
[OK] wrote /etc/felhom-agent/agent.json (0600 felhom-agent)
[OK] byo config asserts passed (lan_resolver.enable / oob.enabled off; deployment_mode=byo; wg_tunnel intentionally ON — base infrastructure)
[OK] agent --selftest (read-only) passed
[OK] felhom-agent service active (non-root felhom-agent reads the config OK)
[OK] wrote /etc/felhom/os-trust.json + operator-signers (felhom-op-1) — the root-owned slow-lane anchors
[STEP] 7/8 golden archive
[WARN] no git credential in controller.yaml — fetching artifacts ANONYMOUSLY (they are world-readable; sha256 verification unchanged)
[INFO] fetching golden v0.331.0 from Gitea → /var/lib/vz/dump/vzdump-lxc-9100-2026_10_25-20_25_22.tar.zst
[OK] verified sha256 3881de62b0986ccb… matches the hub manifest
[OK] golden imported + verified: local:backup/vzdump-lxc-9100-2026_10_25-20_25_22.tar.zst
[STEP] 8/8 provision guest 9201
[SKIP] pool felhom already exists
=== felhom-agent 0.165.0 selftest=provision (vmid=9201 customer=Tester4 hostname=tester4) ===
--- front half: bring-up (provision) local:backup/vzdump-lxc-9100-2026_10_25-20_25_22.tar.zst → vmid 9201 ---
time=2026-10-25T20:26:42.227+01:00 level=INFO msg="bring-up: pool membership re-asserted" vmid=9201 pool=felhom
[OK] front half: vmid 9201 up (boot+running) in 1m9s; MAC=BC:24:11:09:4B:78
--- back half: mint per-guest token + populate bootstrap config mount ---
time=2026-10-25T20:26:46.965+01:00 level=INFO msg="provision: back-half complete" vmid=9201 mount=mp9 guest_path=/etc/felhom-bootstrap endpoint=192.168.0.150:8443
[OK] back half: bootstrap mount mp9 → /etc/felhom-bootstrap on vmid 9201 (host dir /var/lib/felhom-agent/guests/9201/bootstrap)
local-api endpoint 192.168.0.150:8443 · leaf fp 2096006172416bcd1bfe974a512763dcd920be6f0c4127f9e0de83ebe83810a9 · token: minted (not printed)
=== selftest=provision OK — guest 9201 provisioned + bootstrap-mounted (KEPT) ===
next: reboot the guest → the golden's baked controller-bootstrap unit deploys the controller,
which PULLS its controller.yaml from the hub (retrieval passphrase) and merges in this local_api.
[OK] provision completed
[INFO] rebooting guest 9201 so the baked controller-bootstrap unit picks up the mount
[STEP] verify
[OK] pct status: running
[OK] onboot: 1
mp0: local-lvm:vm-9201-disk-1,mp=/var/lib/felhom,backup=1,size=50G
mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives
rootfs: local-lvm:vm-9201-disk-0,size=32G
[OK] pool: guest 9201 is a member of felhom
[OK] acl: FelhomAgentBase@/ present (user+token)
[OK] acl: FelhomAgentGuest@/pool/felhom present (user+token)
[OK] acl: FelhomAgentStore@/storage/local present (user+token)
[OK] acl: FelhomAgentStore@/storage/local-lvm present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990000 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990001 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990002 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990003 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990004 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990005 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990006 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990007 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990008 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990009 present (user+token)
[OK] authz signers: 2 (operator-signed self-update armed)
[OK] controller: Up 6 seconds (healthy) (after ~0s)
[INFO] controller image: gitea.dooplex.hu/admin/felhom-controller:0.343.0
[WARN] cloudflared not visible yet
[INFO] (confirm in the hub UI that host Tester4-84bbe7 reports guest 9201)
[OK] Day-0 provision SUCCESS — vmid=9201 host_id=Tester4-84bbe7 customer=Tester4 golden=local:backup/vzdump-lxc-9100-2026_10_25-20_25_22.tar.zst
INSTALL_RC=0
Connection to 192.168.0.150 closed.
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""mktorrent.py <file> <announce-url> <out.torrent> — a single-file, PRIVATE (no DHT/PEX) v1 torrent, 4 MiB pieces.
The night's R-1261 rig only; stdlib only."""
import hashlib, os, sys
def benc(x):
if isinstance(x, int):
return b"i%de" % x
if isinstance(x, bytes):
return b"%d:%s" % (len(x), x)
if isinstance(x, str):
return benc(x.encode())
if isinstance(x, list):
return b"l" + b"".join(benc(i) for i in x) + b"e"
if isinstance(x, dict):
return b"d" + b"".join(benc(k) + benc(v) for k, v in sorted(x.items())) + b"e"
raise TypeError(type(x))
path, announce, out = sys.argv[1:4]
plen = 4 << 20
pieces = []
with open(path, "rb") as f:
while True:
b = f.read(plen)
if not b:
break
pieces.append(hashlib.sha1(b).digest())
info = {"name": os.path.basename(path), "length": os.path.getsize(path), "piece length": plen,
"pieces": b"".join(pieces), "private": 1}
open(out, "wb").write(benc({"announce": announce, "info": info, "created by": "felhom CC rig 2026-10-25"}))
print(out, hashlib.sha1(benc(info)).hexdigest())
@@ -0,0 +1,32 @@
"""rigw.py — the box walk (catalog-sweep-2026-10-24/tools/box_walk.py) pointed at the night's rig: nested VM 9372 on
demo-hp (Proxmox host 192.168.0.150, BYO install, customer Tester4), guest 9201 at 192.168.0.138, domain tester4.test.
Import this instead of box_walk; it sets the target before box_walk reads it, then points the ssh at the rig host."""
import os, sys
os.environ.setdefault("TARGET", "sweep")
os.environ.setdefault("BASE", "https://192.168.0.138")
os.environ.setdefault("DOMAIN", "tester4.test")
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../catalog-sweep-2026-10-24/tools"))
import box_walk as w # noqa: E402
w._T["ssh"] = "root@192.168.0.150"
w._T["hp"] = "rig-9372"
def _login():
"""box_walk.login, but the token is read from /settings: on this fresh box „/" redirects to /launcher, which
carries no csrf meta (measured 2026-10-25)."""
import re
pw = open(f"{w.SC}/.ctlpw").read().strip()
w.sh(["curl", "-sk", "-D", f"{w.SC}/hdr{os.getpid()}.txt", "-o", "/dev/null", "-H", w.HOSTHDR,
"-X", "POST", "--data-urlencode", f"password={pw}", f"{w.BASE}/login"])
m = re.search(r"felhom_session=[A-Za-z0-9._-]+", open(f"{w.SC}/hdr{os.getpid()}.txt").read(), re.I)
if not m:
sys.exit("login failed: no session cookie")
open(f"{w.SC}/sess{os.getpid()}.txt", "w").write(m.group(0))
r = w.sh(["curl", "-sk", "-H", w.HOSTHDR, "-H", f"Cookie: {m.group(0)}", f"{w.BASE}/settings"])
c = re.search(r'<meta name="csrf-token" content="([^"]+)"', r.stdout or "")
if not c:
sys.exit("login failed: no csrf token on /settings")
open(f"{w.SC}/csrf{os.getpid()}.txt", "w").write(c.group(1))
w.login = _login
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""tracker.py [port] — a minimal HTTP BitTorrent tracker for the night's R-1261 rig (stdlib only). It records every
peer that announces (by source address and the announced port) and answers the others in compact form."""
import http.server, socket, struct, sys, time, urllib.parse
PEERS = {} # info_hash -> {(ip, port): last_seen}
def benc(d):
out = b"d"
for k in sorted(d):
v = d[k]
out += b"%d:%s" % (len(k), k.encode())
out += (b"i%de" % v) if isinstance(v, int) else (b"%d:%s" % (len(v), v))
return out + b"e"
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
u = urllib.parse.urlsplit(self.path)
q = urllib.parse.parse_qs(u.query, keep_blank_values=True, encoding="latin-1")
ih = q.get("info_hash", [""])[0].encode("latin-1")
port = int(q.get("port", ["0"])[0] or 0)
ip = self.client_address[0]
now = time.time()
peers = PEERS.setdefault(ih, {})
if q.get("event", [""])[0] == "stopped":
peers.pop((ip, port), None)
else:
peers[(ip, port)] = now
others = [p for p, t in peers.items() if p != (ip, port) and now - t < 600]
compact = b"".join(socket.inet_aton(a) + struct.pack(">H", pt) for a, pt in others)
body = benc({"interval": 30, "peers": compact})
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
sys.stdout.write("%s announce %s:%d -> %d peer(s)\n" % (time.strftime("%H:%M:%S"), ip, port, len(others)))
sys.stdout.flush()
def log_message(self, *a):
pass
http.server.ThreadingHTTPServer(("0.0.0.0", int(sys.argv[1]) if len(sys.argv) > 1 else 6969), H).serve_forever()
+7
View File
@@ -26,6 +26,13 @@
---
## 2026-10-25 (evening) — installer 1.36.1 published; D50 (full text of the rows: `git show b8411926:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1284** | **The installer's preflight never words a refusal: `http_code` printed the status twice („401401"), so the 401 („passphrase REJECTED"), 404 („customer not found") and 409 (R-1167's live-box sentence) branches never matched — every refusal read „unexpected hub status 401401".** (P3) | CLOSED 2026-10-25 evening — **installer 1.36.1 published on the operator's yes:** tag `installer-v1.36.1` → `79ee3e33` (CI 2336 ✓), both git-sync refs in `manifests/webpage.yaml` (`b8411926`), ArgoCD sync of `Deployment/felhom-webpage` alone; `https://felhom.eu/scripts/felhom-host-install.sh` serves `SCRIPT_VERSION="1.36.1"`, sha256 `e18cdd17…` = the tag's file. Seen live on a throwaway Proxmox VM (demo-hp VM 9372): a wrong customer id → „customer … not found in hub (404)", a wrong passphrase → „passphrase REJECTED (401)", the right passphrase on a customer with a live box → „the hub answered 409 … already has a live box" — **R-1167's sentence, which could not show before 1.36.1, now shows.** | `audits/night-2026-10-25/` |
| **R-1290** | **The agent's CI is red since 2026-10-25 ~18:00Z: the registry no longer serves agent 0.159.0, 0.159.1, 0.159.2 and 0.160.0, while the `published` gate asserts the newest 10 released versions are downloadable (`felhom-agent/scripts/retention-policy.json`, „what the registry keeps").** (P3) | CLOSED 2026-10-25 evening — **D50 = A** (operator; `09` §3 decision 262): agent `feaaa0e` (CI 2339 ✓) — `retention-policy.json` keeps the newest 5 + `pinned` (MinAgent 0.158.1, day-0 0.165.0); `check-published-versions.py` asserts exactly those (a pin with no tag fails); three decoys, two of them LIVE HOLES on the old gate. Every artifact still named downloads (`audits/night-2026-10-25/part1-artifacts.txt`). | `audits/night-2026-10-25/` |
## 2026-10-25 — the attended release run (full text of the rows: R-1265, R-1266 `git show 9647f1d1:documentation/backlog/OPEN-ITEMS.md`; the release closures `git show 36ef8dcd:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
+2 -4
View File
@@ -113,7 +113,7 @@ match what the reader sees is how an instrument stops being believed (R-421). It
**nothing was proven on the day this line was drawn** and a stopping line that moves a status is a
stopping line that lies.
## Install & onboarding — 8 rows (P3 6, P4 2)
## Install & onboarding — 7 rows (P3 5, P4 2)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -123,7 +123,6 @@ stopping line that lies.
| **R-1273** | Install & onboarding | P3 | **An install that fails on the image registry's rate limit tells the household only „exit code 1”.** Catalog sweep 2026-10-24, code-server 16:01:37: `lscr.io … toomanyrequests` → the product cleaned up right (R-649) but the event and the card say „Alkalmazás telepítése nem sikerült: Code-Server — exit code 1”; nothing says the download server is busy and a later try works. The same install went through later (`audits/catalog-sweep-2026-10-24/code-server/`). | OPEN | — | Recognise `toomanyrequests` (and a pull timeout) in the deploy error and say „the app's download server is busy — try again in an hour”; a test with the stderr line | CC |
| **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC |
| **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator |
| **R-1284** | Install & onboarding | P3 | **The installer's preflight never words a refusal: `http_code` printed the status twice („401401"), so the 401 („passphrase REJECTED"), 404 („customer not found") and 409 (R-1167's live-box sentence) branches never matched — every refusal read „unexpected hub status 401401".** Seen live 2026-10-25 on the BYO test VM (attended release run) with installer 1.36.0: a wrong id read `404404`, a wrong passphrase `401401`. R-1167 was CLOSED 2026-10-20 on a publish whose 409 sentence could not show. With `-f`, curl prints `-w` and exits 22 on a 4xx, and the fallback curl printed the code again (line 1833 already worked around it by keeping the last 3 characters). | OPEN — **fixed on main, NOT published:** installer 1.36.1 (`http_code`: one curl without `-f`, never failing); `test_http_code_prints_one_status_on_a_refusal`, seen failing on the old helper (`audits/release-2026-10-26/redproof/installer-http-code-doubled.txt`). | the publish is a DooPlex change (tag + webpage git-sync pins + the webpage sync) — the operator's | publish installer 1.36.1 (cut `installer-v1.36.1`, bump both `--ref` in `manifests/webpage.yaml`, sync the webpage alone); then a preflight with a wrong passphrase reads „passphrase REJECTED (401)"; then close | operator |
## Apps & catalog — 16 rows (P3 6, P4 10)
@@ -353,7 +352,7 @@ stopping line that lies.
| **R-990** | Business & legal | P4 | **Operator: read `documentation/audits/forum-research-2026-10-12/FELHOM.md` §5 and rule on each recommendation.** READ-ONLY research 2026-10-12: three years (#7943–#11092, 2,908 posts, 195 people, 82 beginner threads) of the public PROHARDVER! topic „Home server / házi szerver építése”, tagged and counted by person (`FINDINGS.md`, method and spot-check in `METHOD.md`; no username or post text in git). Ten ranked recommendations, none acted on; the top three: a „which machine works” page (8 of 33 beginners who name their hardware own only a Synology, a router disk or a Raspberry Pi — the installer cannot run there; the same gap is R-989's first recommendation from a different source, and the shipped-page half is R-991), „you do not choose an operating system — we do” (the biggest beginner question, 20 of 82 threads), and „bring your old office PC” as the cheap entry. Catalog gap (≥ 3 people): a DLNA server (7), an ad/DNS blocker (5), a camera recorder (5). | **WAITING-ON-OPERATOR** — filed 2026-10-12 | the operator's reading | Operator: rule on each of the ten (yes / no / later). If nothing is done: nothing changes; the research ages | operator |
| **R-1035** | Business & legal | P4 | **Did the „felhő tárhely” page earn anything? Two dated checks.** **Baseline (reported by the operator from Search Console, 2026-10-14, last 3 months):** 1 click, 48 impressions, CTR 2.1 %, average position **73.6**; „felhő tárhely” 19 impressions, „felhőtárhely” 5, „felhő alapú tárhely” 3. CC measured separately that the phrase „felhő tárhely” appeared **0 times** on the home, FAQ and backup pages — Google was showing the home page for those queries by accident. `/sajat-felho-tarhely` + `/en/own-cloud-storage` published **2026-10-14**; full baseline, price sources and the power provenance in `audits/seo-2026-10-14/BASELINE.md`. **What to measure, both times:** impressions and average position for „felhő tárhely” and „saját felhő tárhely”, plus the new page's own impressions, clicks and position. **A null result at +4 weeks means nothing on its own** — a new page on a site at position 73.6 takes weeks to months to move, and „felhő tárhely” alone is held by Microsoft, Google and the big list sites; the page aims at the two longer phrases. That is why there are two checks and not one, and why neither should be read as a verdict on the copy. **There is no Search Console connector in this project, so CC cannot read these numbers** — the operator reads them, CC records and compares. **Blocked until the operator asks for indexing** (Search Console → URL-ellenőrzés → the URL → „Indexelés kérése”): until that happens, or Google finds the page itself, both checks read zero for a reason that is not the page. **This is a slow channel and does not replace talking to people.** | **WATCHING** | operator: request indexing, then supply the Search Console read | 2026-11-11: first read (+4 weeks); 2026-12-09: second (+8 weeks), compared with the first | operator + CC |
## Process & tooling — 33 rows (P3 8, P4 25)
## Process & tooling — 32 rows (P3 7, P4 25)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -389,7 +388,6 @@ stopping line that lies.
| **R-1236** | Process & tooling | P3 | **Gitea (the code and the registry) is OOM-killed at its 1 GiB limit — 4 restarts in 17 days, the last at 06:41:10 UTC on 2026-10-23, six seconds after the global floor moved three boxes to a new controller image.** Read 2026-10-23 (`kubectl get pod`: `lastState.terminated.reason OOMKilled`, exit 137; kernel: „git invoked oom-killer”; limit `memory: 1Gi`, request 256Mi; 620 Mi in use at rest). During a restart a box update, an agent package fetch, a CI clone or a push fails. Not changed: DooPlex's configuration is the operator's. | OPEN | the operator's yes (a DooPlex change) | Operator: raise the limit (e.g. 2 GiB; DooPlex has the RAM) — CC's pick; or keep it and accept a restart in release hours | operator |
| **R-1264** | Process & tooling | P4 | **Skills tooling (review round 2, DOC-B U7/S22):** the grandfathered skill has no size cap — felhom-build-deploy is 240 lines while R-394 recorded 179 (`scripts/check_skills.py:45-47,98-101`); a NEW skill folder (`skills/felhom-web-design`) is not live until `install_skills.py` runs again (not run: no DooPlex change in this unattended run). | OPEN | — | cap the grandfathered entry at its current size; run install_skills.py in an attended session | CC |
| **R-1277** | Process & tooling | P4 | **The catalog's box fixtures drifted from the apps they test** — fixed only in the sweep's copy (`audits/catalog-sweep-2026-10-24/tools/upgrade_fixtures_box.py`, marked SWEEP COPY): OpenGist 1.15 signs up at `/-/register`; Calibre-Web's admin is the generated ADMIN_USER; Mealie's after_install replaces the factory password (the seed raced it); Papra 26.6 needs an Origin header; Rallly 4.15 closed password sign-up (no fixture possible by password); Vaultwarden 1.37.4 answers 404 on `POST /identity/accounts/register` (the fixture was written on 1.36; the admin invite works, the invited sign-up has no route — the newer clients' register/finish flow);the read-back through a family gate needs a fresh cookie after the setup gate opens (Grimmory stayed inconclusive). New fixtures for Homebox, Immich, Komga, Jellyfin, Ghost, Radarr, Sonarr in `tools/sweep_fixtures.py`; `box_walk.py` gained a `sweep` target. | OPEN | — | Port them into `app-catalog-felhom.eu/scripts/` with the bench tests | CC |
| **R-1290** | Process & tooling | P3 | **The agent's CI is red since 2026-10-25 ~18:00Z: the registry no longer serves agent 0.159.0, 0.159.1, 0.159.2 and 0.160.0, while the `published` gate asserts the newest 10 released versions are downloadable (`felhom-agent/scripts/retention-policy.json`, „what the registry keeps").** Measured 2026-10-25 (attended release run): CI job 2326 (`4697f8a`, after the 0.165.0 upload) green; job 2334 (`6b6ba0c`, docs only) red on those four (HTTP 404); the registry lists 7 versions — 0.158.1, 0.161.0–0.165.0 and a test build `0.163.0-step1`. Our scripts delete only the version they re-upload (`publish-agent.sh:77`), so the prune is outside the repos (a Gitea package clean-up rule or a hand prune on DooPlex — not CC's to change). No box needs those versions (all run 0.165.0; MinAgent 0.158.1 is served), but every agent push now mails a CI failure and hides a real one. | OPEN | the operator (DooPlex's Gitea) | the operator: name what pruned on 2026-10-25 and its keep rule; then CC sets `generic_versions_kept` to that rule (or bounds the gate on the hub's MinAgent, R-287's follow-up) in one commit, and CI is green again by hash | operator |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.