hub v0.133.0 (R-859 test approvals end with the test; R-840 bundle on the System page, manifest, alarm); installer 1.31.0 (root files from the config bundle); bundle bootstrap script; golden 0.293.0 evidence; rulings 96–99; drill-r50 removed (evidence)
gates / gates (push) Successful in 29s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-04 20:18:37 +02:00
parent 6b820143f8
commit ff1db11db4
45 changed files with 1707 additions and 42 deletions
+78 -8
View File
@@ -184,7 +184,7 @@
set -euo pipefail
SCRIPT_VERSION="1.30.0" # the SINGLE version source (F-1): -h and the run banners follow it.
SCRIPT_VERSION="1.31.0" # the SINGLE version source (F-1): -h and the run banners follow it.
# The hub used to carry a copy for its Setup tab; R-94 DELETED it
# (2026-08-02) because the hub cannot know which version a box runs —
# the Setup command fetches this script at run time. scripts/
@@ -444,6 +444,9 @@ resolve_artifacts() {
ART_AGENT_SHA=$(python3 -c "import json,sys;print(json.loads(sys.argv[1])['agent']['sha256'])" "$body" 2>/dev/null || echo "")
ART_GOLDEN_VER=$(python3 -c "import json,sys;print(json.loads(sys.argv[1])['golden']['version'])" "$body" 2>/dev/null || echo "")
ART_GOLDEN_SHA=$(python3 -c "import json,sys;print(json.loads(sys.argv[1])['golden']['sha256'])" "$body" 2>/dev/null || echo "")
# 1.31.0 (R-840): the vouched agent's CONFIG BUNDLE — every root-owned file below, as one checked unit. Absent when
# the vouched agent carries none (older than 0.143.0); step 5 then fetches the files one by one as before.
ART_BUNDLE_SHA=$(python3 -c "import json,sys;print((json.loads(sys.argv[1]).get('bundle') or {}).get('sha256',''))" "$body" 2>/dev/null || echo "")
}
# Resolve the Gitea fetch credential (git username + token) from the customer's controller.yaml —
@@ -1166,10 +1169,13 @@ run_uninstall() {
local cgf
for cgf in /usr/local/sbin/felhom-crash-guard /etc/systemd/system/felhom-crash-guard.service \
/etc/systemd/system/felhom-crash-guard-check.service /etc/systemd/system/felhom-crash-guard-check.timer \
/etc/felhom/crash-guard.conf /etc/felhom/os-trust.json /etc/felhom/operator-signers; do
/etc/felhom/crash-guard.conf /etc/felhom/os-trust.json /etc/felhom/operator-signers \
/etc/felhom/config-bundle.json; do
if [[ -e "$cgf" ]]; then run rm -f "$cgf"; fi
done
if [[ -d /var/lib/felhom-crash-guard ]]; then run rm -rf /var/lib/felhom-crash-guard; fi
# 1.31.0 (R-840): the bundle's previous copies and the wrapper's nonce record.
if [[ -d /var/lib/felhom-os-apply ]]; then run rm -rf /var/lib/felhom-os-apply; fi
if [[ -f /var/lib/vz/snippets/felhom-guest-hook.sh ]]; then run rm -f /var/lib/vz/snippets/felhom-guest-hook.sh; fi
local dconf _dnsmasq_touched=false
for dconf in /etc/dnsmasq.d/felhom-*.conf; do
@@ -2328,6 +2334,76 @@ step_agent_install() {
fi
fi
# 1.31.0 (R-840): the root-owned files come from the vouched agent's CONFIG BUNDLE — the SAME file a signed
# agent_config_update brings to an installed box, installed by the same code (the bundle's own felhom-os-apply), so
# a new box and an updated box cannot drift. A vouched agent older than 0.143.0 carries none: the per-file path.
if [[ -n "$ART_BUNDLE_SHA" ]]; then
install_root_files_bundle
else
log_warn " the vouched agent v$ART_AGENT_VER carries no config bundle (older than 0.143.0) — fetching the root files one by one"
install_root_files_legacy
install_mgmt_watchdog
# H1: dedicated felhom-sshd OOB instance + static belt (appliance default since v1.25.0; --no-oob opts out).
install_oob
fi
_state_mark agent_install
}
# install_root_files_bundle (1.31.0, R-840): fetch the vouched config bundle, verify it against the hub manifest, take
# out its own felhom-os-apply (checked against the bundle's entry), install that, and let it install every root-owned
# file with its checks (visudo, sh/bash -n, python, unit sections, the RuntimeDirectory guard, nft -c), its self-check
# and its undo. Then the parts that are not files: the OOB host key, the belt loader, enabling the units.
install_root_files_bundle() {
local url="$GITEA_BASE/api/packages/$GITEA_OWNER/generic/felhom-agent/$ART_AGENT_VER/felhom-config-bundle.json"
if $DRY_RUN; then
log_dry "fetch $url ; verify sha256=$ART_BUNDLE_SHA ; felhom-os-apply --install-bundle (every root-owned file, one checked unit)"
return 0
fi
# The bundle writes the OOB belt's files only on a box with the belt: create its directory and user FIRST.
if $ENABLE_OOB; then
install -d -o root -g root -m 0755 /etc/felhom-sshd /etc/felhom-sshd/authorized_keys
id felhom-op >/dev/null 2>&1 || useradd --create-home --shell /bin/bash felhom-op
fi
local btmp ostmp out rc=0
btmp=$(mktemp -t felhom-bundle.XXXXXX); ostmp=$(mktemp -t felhom-os.XXXXXX)
fetch_verify "$url" "$btmp" "$ART_BUNDLE_SHA"
python3 - "$btmp" "$ostmp" <<'PY' || { rm -f "$btmp" "$ostmp"; die "the config bundle carries no valid felhom-os-apply — refusing"; }
import ast, base64, hashlib, json, sys
b = json.load(open(sys.argv[1]))
e = [f for f in b["files"] if f["path"] == "/usr/local/sbin/felhom-os-apply"][0]
data = base64.b64decode(e["content_b64"])
assert hashlib.sha256(data).hexdigest() == e["sha256"], "felhom-os-apply does not match its sha in the bundle"
ast.parse(data.decode())
open(sys.argv[2], "wb").write(data)
PY
install -m 0755 -o root -g root "$ostmp" /usr/local/sbin/felhom-os-apply
rm -f "$ostmp"
# The wrapper refuses --install-bundle from a sudo caller (the agent's route is the signed job); this script is
# root already, so a `sudo bash` run must not look like one.
out=$(env -u SUDO_UID -u SUDO_GID -u SUDO_USER -u SUDO_COMMAND \
/usr/local/sbin/felhom-os-apply --install-bundle "$btmp" --sha256 "$ART_BUNDLE_SHA" 2>&1) || rc=$?
rm -f "$btmp"
grep '^os-apply: BUNDLE' <<<"$out" | sed 's/^/ /' || true
[[ $rc -eq 0 ]] || die "the config bundle did not install (rc=$rc) — nothing half-done stays (the wrapper put the previous files back): $(grep -E 'REFUSED|FAILED' <<<"$out" | head -2)"
systemctl daemon-reload
systemctl enable felhom-agent >/dev/null 2>&1 || true
log_success " installed every root-owned file from config bundle v$ART_AGENT_VER (sha ${ART_BUNDLE_SHA:0:16}…; checked, previous copies kept)"
if $ENABLE_OOB; then
if [[ ! -f /etc/felhom-sshd/ssh_host_ed25519_key ]]; then
ssh-keygen -t ed25519 -N "" -f /etc/felhom-sshd/ssh_host_ed25519_key -C felhom-sshd-hostkey -q
chmod 600 /etc/felhom-sshd/ssh_host_ed25519_key
fi
systemctl enable --now felhom-oob-nft.service >/dev/null 2>&1 || true # load the static belt now
systemctl enable felhom-sshd >/dev/null 2>&1 || true # NOT start — the agent renders the config first
log_success " OOB felhom-sshd instance + static belt from the bundle (agent renders config + fills sets once oob.enabled)"
else
log_skip " OOB (felhom-sshd) off (byo, or appliance --no-oob) — the bundle skipped its files"
fi
}
# install_root_files_legacy is the per-file path (before 1.31.0, and for a vouched agent older than 0.143.0).
install_root_files_legacy() {
# Guarded-mkfs wrapper (Impl-1 Part B) — the ONLY mkfs path the sudoers permits. Install it BEFORE
# the sudoers (which allowlists it), 0755 root:root under /usr/local/sbin. bash -n before install.
if $DRY_RUN; then
@@ -2503,12 +2579,6 @@ step_agent_install() {
# Non-fatal if the agent repo predates them (raw fetch 404s → break-glass just stays manual).
# HARD GUARD: refuse ANY fetched unit that declares RuntimeDirectory= — that directive is the very
# incident G1 closes (a second sshd's `RuntimeDirectory=sshd` removed the shared /run/sshd).
install_mgmt_watchdog
# H1: dedicated felhom-sshd OOB instance + static belt (appliance default since v1.25.0; --no-oob opts out).
install_oob
_state_mark agent_install
}
# install_mgmt_watchdog fetches + installs the G1 break-glass host artifacts (idempotent; enables the