doorstep: console is Felhom's (ISO 1.27.0 source), passphrase hand-over copy (hub 0.113.0 source), rulings
gates / gates (push) Successful in 19s

Phase 0: the public ISO never auto-installs by construction (no answer.toml,
G1); the operator re-affirmed the interactive installer 2026-09-14.
- felhom-bootstrap.sh: mask pvebanner.service, write a Hungarian /etc/issue
  (no :8006 admin URL); pairing banner names the Tulajdonosi jelmondat and
  paints through the CONSOLE_DEV seam (R-496). Harness: 8 checks, red first;
  fake hub now sends a pairing code (the banner was never tested, R-502).
- hub: created flash + Credentials block tell the operator to hand the phrase
  over; the self-bind mail names the operator (R-497). Tests red first.
- iso-release-gate G14-G16; domain ruling in 01-topology + CONTEXT; R-494
  narrowed to P3; R-502..R-504 filed; volunteer guide and day-0 A.2 aligned.
ISO_VERSION 1.27.0 (not built, not published).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-14 17:26:53 +02:00
parent 8c7f882d1c
commit 6fd8c87516
14 changed files with 269 additions and 34 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ set -euo pipefail
# does not exist: the ISO is a frozen artifact, while felhom-host-install.sh is fetched at RUN TIME
# from the website's git-sync of `main` (R-94/R-110), so whatever version an ISO carries, the script a
# box runs is always current. Coupling them would invent a constraint. The claim is corrected instead.
ISO_VERSION="1.26.1" # the ISO's own version. INDEPENDENT of felhom-host-install.sh's SCRIPT_VERSION,
ISO_VERSION="1.27.0" # the ISO's own version. INDEPENDENT of felhom-host-install.sh's SCRIPT_VERSION,
# which is fetched at run time from main and is not frozen into the image.
IMAGE="${FELHOM_ISO_ASSISTANT_IMAGE:-felhom-iso-assistant:trixie}"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+36 -2
View File
@@ -69,11 +69,42 @@ print_pairing_banner() {
printf ' Felhom — a doboz készen áll, és a párosításra vár.\n\n'
printf ' Párosító kód: %s\n\n' "$code"
printf ' Nyisd meg az e-mailben kapott linket, és add meg\n'
printf ' ezt a kódot és a jelszavadat.\n\n'
printf ' ezt a kódot és a Tulajdonosi jelmondatodat\n'
printf ' (az 5 szót a Felhom üzemeltetőjétől kaptad).\n\n'
printf ' Ez a képernyő magától frissül — nincs teendő a\n'
printf ' doboznál, és nyugodtan itt hagyhatod bekapcsolva.\n'
printf '================================================\n\n'
} > /dev/console 2>/dev/null || printf 'Párosító kód: %s\n' "$code"
} > "$CONSOLE_DEV" 2>/dev/null || printf 'Párosító kód: %s\n' "$code"
}
# install_felhom_issue (R-496, v1.27.0) — the text above the console login prompt is Felhom's, not
# Proxmox's. Measured 2026-09-14 (drill screen s29): the first thing a household read on a fresh box was
# `Welcome to the Proxmox Virtual Environment … connect to https://<ip>:8006/` — the operator admin UI,
# in English. pvebanner.service REWRITES /etc/issue on every boot (/usr/bin/pvebanner opens it '>'), so
# overwriting the file alone would last one boot: the unit is MASKED, then the file is written.
# Best-effort in every step — a banner must never block the boot or the pairing.
ISSUE_FILE="${FELHOM_ISSUE_FILE:-/etc/issue}"
install_felhom_issue() {
if command -v systemctl >/dev/null 2>&1; then
if systemctl mask pvebanner.service >/dev/null 2>&1; then
log "console: pvebanner.service masked (it would rewrite $ISSUE_FILE with the Proxmox admin URL on every boot)"
else
log "console: could not mask pvebanner.service — the Proxmox banner may return on the next boot"
fi
fi
local tmp="${ISSUE_FILE}.felhom-tmp"
if { printf '\n'
printf ' Felhom otthoni szerver\n\n'
printf ' Ezen a képernyőn nincs teendőd, bejelentkezni sem kell.\n'
printf ' A beállításhoz kövesd a Felhomtól kapott útmutatót.\n\n'
} > "$tmp" 2>/dev/null && mv -f "$tmp" "$ISSUE_FILE" 2>/dev/null; then
log "console: $ISSUE_FILE is the Felhom text (no admin URL)"
else
rm -f "$tmp" 2>/dev/null
log "console: could not write $ISSUE_FILE — continuing"
fi
command -v agetty >/dev/null 2>&1 && agetty --reload >/dev/null 2>&1
return 0
}
cleanup_pass() { [[ -e "$PASS_FILE" ]] && { shred -u "$PASS_FILE" 2>/dev/null || rm -f "$PASS_FILE"; }; return 0; }
@@ -528,6 +559,9 @@ emit("FELHOM_EXTRA_ARGS", d.get("extra_args"))
done
}
# --- R-496: Felhom's text above the console login, before anything can wait on the network --------
install_felhom_issue
# --- R-59/R-60 first-boot network gate: never proceed silently into a hub-unreachable install ------
network_gate
+20 -6
View File
@@ -97,7 +97,9 @@ exit 0
HI
exit 0 ;;
*"/appliance/register")
echo '{"appliance_token":"TESTTOKEN123456","poll_interval_sec":30}'
# pairing_code (R-27): without it print_pairing_banner returns early and the banner is never
# painted — which is how the banner went untested until v1.27.0 (R-496).
echo '{"appliance_token":"TESTTOKEN123456","poll_interval_sec":30,"pairing_code":"TST-CDE"}'
exit 0 ;;
*"/appliance/poll")
if [ "$mode" = "200" ]; then
@@ -113,15 +115,15 @@ exit 0
CURL
chmod +x "$FAKE/curl"
# fake systemctl (disable is a no-op)
printf '#!/bin/bash\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
# fake systemctl (disable is a no-op); logs every call so R-496's pvebanner mask is observable
printf '#!/bin/bash\necho "$*" >> /work/systemctl.log\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
reset_state() {
rm -rf /etc/felhom /run/felhom-bootstrap-pass /var/lib/felhom-install "$CALLS" /work/hostinstall.log \
/work/poll-mode /work/sleep.count /work/sleep.flip /work/sleep.abort \
/work/ip.log /work/ifreload.log /work/dhclient.log /work/hub-mode /work/dhcp-mode \
/work/sys /work/interfaces /work/interfaces.felhom-bak /work/console.out \
/run/felhom-interfaces.orig /work/run.log
/run/felhom-interfaces.orig /work/run.log /work/issue /work/systemctl.log
mkdir -p /etc/felhom
}
@@ -150,7 +152,7 @@ iface nicB inet manual
source /etc/network/interfaces.d/*
IFACES
}
GATE_ENV="FELHOM_NET_SYS=/work/sys FELHOM_INTERFACES_FILE=/work/interfaces FELHOM_CONSOLE_DEV=/work/console.out"
GATE_ENV="FELHOM_NET_SYS=/work/sys FELHOM_INTERFACES_FILE=/work/interfaces FELHOM_CONSOLE_DEV=/work/console.out FELHOM_ISSUE_FILE=/work/issue"
# ============================ Scenario D — direct mode, zero appliance calls =========================
# Runs WITH the gate fixture (NICs + fallback-shaped interfaces) and the hub reachable: the G1
@@ -179,6 +181,13 @@ check "G1: gate made zero ifreload calls" "[ ! -f /work/ifreload.log ]"
check "G1: gate made zero dhclient calls" "[ ! -f /work/dhclient.log ]"
check "G1: gate consumed zero sleeps" "[ ! -f /work/sleep.count ]"
check "G1: interfaces fixture untouched" "grep -q 'bridge-ports nicA' /work/interfaces && grep -q '192.168.100.2' /work/interfaces"
# R-496 (v1.27.0): the console's login banner is Felhom's, not Proxmox's — and it survives a reboot,
# because pvebanner.service (which rewrites /etc/issue on EVERY boot) is masked, not merely overwritten.
check "R-496: /etc/issue written" "[ -s /work/issue ]"
check "R-496: /etc/issue is the Felhom text" "grep -q 'Felhom otthoni szerver' /work/issue"
check "R-496: /etc/issue carries no admin URL (:8006)" "! grep -q '8006' /work/issue"
check "R-496: /etc/issue does not say Proxmox" "! grep -qi 'proxmox' /work/issue"
check "R-496: pvebanner.service masked" "grep -q 'mask pvebanner.service' /work/systemctl.log"
# ============ P: pairing loop (v1.21.0) — register, wait unbound INSIDE one invocation, deliver =====
say "P: pairing env -> register + in-script 204 wait -> 200 delivery -> host-install, ONE invocation"
@@ -188,7 +197,12 @@ FELHOM_HUB_URL=https://hub.example
ENV
echo 204 > /work/poll-mode
echo 3 > /work/sleep.flip # after 3 in-script waits the hub "binds" (poll flips to 200)
bash "$BSTRAP"; rc=$?
rm -f /work/console.out
env FELHOM_CONSOLE_DEV=/work/console.out FELHOM_ISSUE_FILE=/work/issue bash "$BSTRAP"; rc=$?
# R-496: the pairing banner names the secret the way the self-bind mail and page do (R-323).
check "R-496: banner painted to the console seam" "grep -q 'Párosító kód' /work/console.out"
check "R-496: banner names the Tulajdonosi jelmondat" "grep -q 'Tulajdonosi jelmondat' /work/console.out"
check "R-496: banner no longer says 'jelszavad'" "! grep -q 'jelszavad' /work/console.out"
check "single invocation ran to done (exit 0)" "[ $rc -eq 0 ]"
check "POSTed /appliance/register" "grep -q '/appliance/register' $CALLS"
check "appliance token persisted 0600" "[ -f /etc/felhom/.bootstrap-done ] || { [ -f /etc/felhom/appliance-token ] && [ \"\$(stat -c %a /etc/felhom/appliance-token)\" = 600 ]; }"