Files
felhom.eu/scripts/hostinstall_gates.py
T
admin d319ae573e hub: delete the host-install version label (R-94) + invert hostinstall gate 1
The Setup tab said 'host-install 1.19.0' while the served script was 1.22.0, and had
been wrong since 2026-07-14. Deriving the number honestly is not possible: the Option-1
command downloads felhom-host-install.sh from the website at RUN TIME and the website
git-syncs main every 30s (R-110), so no build-time value in the hub can be true. R-94(a)
offered derive-or-delete; deleted, which removes the drift class instead of automating it.

- configs.go: hostInstallVersion const, pageData.ScriptVersion field and its assignment
  all removed; a NOTE in their place records why there is no constant here.
- customer_unified.html: the sentence now says the command always fetches the current
  installer, and renders no version.
- hostinstall_gates.py gate 1: the third assertion INVERTS — it used to require the hub
  const to equal SCRIPT_VERSION, it now asserts the hub carries no host-install version
  literal at all, matched in six code shapes across every .go/.html under hub/ (comments
  are deliberately not stripped: a // inside a URL literal would blind the scan).
- render_test.go: the assertion 'html contains hostInstallVersion' compared the constant
  to itself and passed at ANY value — demonstrated green with the const at 9.9.9 while the
  script was 1.22.0. Deleted, not replaced: there is no longer a version to assert.
- felhom-host-install.sh: COMMENT ONLY (SCRIPT_VERSION untouched) — it claimed the gate
  keeps the hub copy equal, an invariant that no longer exists.

Red-proofs: restoring the const fails the rewritten gate 1 (3 shapes hit); the old
render_test assertion passes at 9.9.9.
2026-08-02 15:16:01 +02:00

154 lines
8.3 KiB
Python

# -*- coding: utf-8 -*-
"""DR-tier-by-default installer gates — mechanical grep-assertions against
felhom-host-install.sh's known failure mode: a Day-0 that silently regresses one of the
drill-swept findings (DRILL-day0-vm-2026-07-12 F-1/F-7/F-9/F-10 + the ACL-narrowing 403).
Run from the repo root: python scripts/hostinstall_gates.py
Gates (all must pass; non-zero exit on any failure):
1. version — exactly ONE version source: SCRIPT_VERSION exists, the header line carries no
version literal, and **the hub carries no host-install version literal at all**.
The third assertion inverted on 2026-08-02 (R-94): it used to require the hub's
`hostInstallVersion` const to EQUAL SCRIPT_VERSION, which is unachievable
honestly — the Option-1 install command downloads felhom-host-install.sh from
the website at RUN TIME and the website git-syncs `main` every 30 seconds
(R-110), so the hub cannot know which version a given box will run. A
build-time literal there is a guess with a version number's authority, and the
real one drifted to 1.19.0-vs-1.22.0 and stayed wrong for 19 days. The label was
deleted rather than derived; this gate now pins its absence (F-1 structural fix,
second form).
2. age — the `age` package is installed by the agent-install step (F-10)
3. pbs-apply — configs/felhom-pbs-apply is fetched + installed to
/usr/local/sbin/felhom-pbs-apply (F-7), and the uninstall removes it
4. wg — the rendered agent.json defaults wg_tunnel enabled=true (F-9 / decision 5),
and the byo assert no longer forbids it
5. acl — the default PVE_STORAGES set still contains felhom-pbs (narrowing it is the
drill's apply-bridge 403)
"""
import io, os, re, sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SCRIPT = os.path.join(ROOT, "scripts", "felhom-host-install.sh")
HUB_CONFIGS = os.path.join(ROOT, "hub", "internal", "web", "configs.go")
fails = []
def fail(msg):
fails.append(msg)
print("FAIL:", msg)
def ok(msg):
print(" ok:", msg)
with io.open(SCRIPT, "r", encoding="utf-8") as f:
src = f.read()
lines = src.splitlines()
# ── 1. version single-source (F-1) ──────────────────────────────────────────────
m = re.search(r'^SCRIPT_VERSION="(\d+\.\d+\.\d+)"', src, re.M)
if not m:
fail("SCRIPT_VERSION=\"x.y.z\" not found — the single version source is gone")
script_ver = None
else:
script_ver = m.group(1)
ok("SCRIPT_VERSION=%s" % script_ver)
# header (first 10 lines) must NOT carry its own version literal — that is the F-1 drift.
header = "\n".join(lines[:10])
if re.search(r'felhom-host-install\.sh\s+v\d+\.\d+\.\d+', header):
fail("header line carries a hardcoded version — SCRIPT_VERSION is the only source (F-1)")
else:
ok("header has no version literal")
# The hub must carry NO host-install version literal at all (R-94, 2026-08-02). It cannot know
# which version a box will run — the Option-1 command fetches the script from the website at run
# time and the website git-syncs `main` every 30s. The const this replaced said 1.19.0 while the
# served script was 1.22.0, and had been wrong since 2026-07-14.
#
# Matched in CODE SHAPES, never as bare prose: the deleted declarations, the struct field, the
# assignment and the template action, plus a rename-proof generic form of each. Comments are
# deliberately NOT stripped (a `//` inside a URL string literal would truncate the scan and turn
# this gate blind); a comment that merely NAMES the identifier is allowed, and configs.go carries
# exactly such a note explaining the absence.
BANNED = [
(r'\bconst\s+hostInstallVersion\b', "const hostInstallVersion"),
(r'\bhostInstallVersion\s*=', "hostInstallVersion assignment"),
(r'(?i)\bconst\s+\w*hostinstall\w*version\b', "a renamed host-install version const"),
(r'\bScriptVersion\s+string\b', "ScriptVersion struct field"),
(r'\bScriptVersion\s*:', "ScriptVersion struct assignment"),
(r'\{\{\s*\.ScriptVersion\s*\}\}', "{{.ScriptVersion}} template action"),
]
HUB_DIR = os.path.join(ROOT, "hub")
if not os.path.isdir(HUB_DIR):
fail("hub/ not found at %s — cannot assert the absence of a host-install version literal" % HUB_DIR)
else:
scanned, hits = 0, 0
for dirpath, dirs, files in os.walk(HUB_DIR):
dirs[:] = [d for d in dirs if d not in (".git", "vendor", "node_modules")]
for fn in files:
if not (fn.endswith(".go") or fn.endswith(".html")):
continue
fp = os.path.join(dirpath, fn)
scanned += 1
with io.open(fp, "r", encoding="utf-8") as f:
for lineno, line in enumerate(f, 1):
for pat, what in BANNED:
if re.search(pat, line):
hits += 1
fail("%s:%d carries %s — the hub must render NO host-install version "
"(R-94: the served script is fetched at run time, so no build-time "
"value can be true). Single source: scripts/felhom-host-install.sh "
"SCRIPT_VERSION. Line: %s"
% (os.path.relpath(fp, ROOT), lineno, what, line.strip()[:120]))
if not hits:
ok("hub carries no host-install version literal (%d .go/.html files scanned, %d shapes checked)"
% (scanned, len(BANNED)))
# ── 2. age package (F-10) ───────────────────────────────────────────────────────
# must match the REAL install invocation, not the log_dry echo (red-proof-hardened twice:
# a prefix regex matched "agekit", then a loose one matched the dry-run print line).
if re.search(r'DEBIAN_FRONTEND=noninteractive apt-get install -y -q age\b', src):
ok("age is in the installed package set")
else:
fail("`age` install not found (F-10 — the fresh-box escrow ceremony dies without it)")
# ── 3. pbs-apply wrapper shipped + removed (F-7) ────────────────────────────────
if 'fetch_raw "configs/felhom-pbs-apply"' in src:
ok("felhom-pbs-apply is fetched from the agent repo")
else:
fail("configs/felhom-pbs-apply fetch not found (F-7 — pbsdr capabilities born DEGRADED)")
if re.search(r'install -m 0755 -o root -g root "\$patmp" /usr/local/sbin/felhom-pbs-apply', src):
ok("felhom-pbs-apply installed 0755 to /usr/local/sbin")
else:
fail("felhom-pbs-apply install line not found (F-7)")
if re.search(r'rm -f /usr/local/sbin/felhom-pbs-apply', src):
ok("uninstall removes felhom-pbs-apply")
else:
fail("uninstall does not remove /usr/local/sbin/felhom-pbs-apply")
# ── 4. wg_tunnel default-on (F-9 / decision 5) ──────────────────────────────────
if re.search(r"base\.setdefault\('wg_tunnel',\s*\{\"enabled\":\s*True\}\)", src):
ok("rendered agent.json defaults wg_tunnel.enabled=true")
else:
fail("wg_tunnel enabled-by-default missing from the agent.json render (F-9)")
# the byo assert must NOT forbid wg_tunnel any more (decision 5: WG is base infrastructure).
byo_assert = re.search(r"byo-forbidden config keys.*?sys\.exit\(1\)", src, re.S)
if byo_assert and "wg_tunnel" in byo_assert.group(0):
fail("the byo config assert still forbids wg_tunnel.enabled (decision 5 retired that)")
else:
ok("byo assert no longer forbids wg_tunnel")
# ── 5. default ACL keeps felhom-pbs (the drill 403) ─────────────────────────────
if re.search(r'^PVE_STORAGES=\([^)]*felhom-pbs[^)]*\)', src, re.M):
ok("PVE_STORAGES default contains felhom-pbs")
else:
fail("felhom-pbs missing from the default PVE_STORAGES — narrowing it 403s the PBS-DR apply-bridge")
print()
if fails:
print("hostinstall gates: %d FAILURE(S)" % len(fails))
sys.exit(1)
print("hostinstall gates: ALL PASS")