host-install v1.17.0: appliance guest auto-sizing (F5) + stale operator-key comment fix (R-16)

- F5: appliance mode auto-sizes guest RAM=clamp(host-4096,min 4096,max host-2048,ceil host-1024) + cores=host-1 min 2 when no explicit cap; explicit --memory/--cores always win. Harness F5 red-proof (8/16/32GB + edge + explicit).
- R-16 doc-drift: operator signing keys 'EMPTY by default' comment corrected (keys are PINNED).
This commit is contained in:
2026-07-17 09:21:24 +02:00
parent 0c708a3bd0
commit 698fac04a3
3 changed files with 108 additions and 6 deletions
+52 -6
View File
@@ -182,7 +182,7 @@
set -euo pipefail
SCRIPT_VERSION="1.16.0" # the SINGLE version source (F-1): -h, the run banners, and the hub
SCRIPT_VERSION="1.17.0" # the SINGLE version source (F-1): -h, the run banners, and the hub
# Setup-tab copy (hub internal/web/configs.go hostInstallVersion —
# scripts/hostinstall_gates.py asserts the two stay equal) all follow it.
# 1.16.0: the FELHOM_ESCROW sudoers alias (controller-driven escrow
@@ -190,11 +190,11 @@ SCRIPT_VERSION="1.16.0" # the SINGLE version source (F-1): -h, the run banners
# CANONICAL sudoers fetch below (configs/felhom-agent.sudoers from the
# agent repo, visudo-gated), no separate installer step.
# Operator signing keys pinned at day-0 (GL-4; doc 04 §3 two-key model). EMPTY by default — the pin
# CEREMONY is an operator step: generate the real keypairs OFFLINE, then fill these four constants
# in one commit (or pass --operator-pubkey-file at install time, which overrides them). Empty =
# no authz.signers written = agent self-update stays DORMANT (the safe default; the verify step
# warns). PUBLIC keys only — this script never generates, reads, or references private key material.
# Operator signing keys pinned at day-0 (GL-4; doc 04 §3 two-key model). PINNED below to the real
# ceremony keypairs (the pin ceremony is DONE — felhom-op-1 / felhom-rec-1); --operator-pubkey-file
# at install time overrides them. If these were ever cleared to empty, no authz.signers is written
# and agent self-update stays DORMANT (the safe fallback; the verify step warns). PUBLIC keys only —
# this script never generates, reads, or references private key material.
OPERATOR_KEY_OPERATIONAL_ID="felhom-op-1"
OPERATOR_KEY_OPERATIONAL_LINE="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8z0qCNgA3x2xxAB0Qj5ro8waFjGZ8Ta/sWB63tlLw+ felhom-op-1"
OPERATOR_KEY_RECOVERY_ID="felhom-rec-1"
@@ -1152,6 +1152,49 @@ fi
# --resume variant), so the profile is decided HERE, before the passphrase prompt and any step. The
# refusals are deliberately argv-time (the harness relies on them firing on a non-PVE machine too).
#===============================================================================
# --- F5 (VALIDATION-n100): appliance guest auto-sizing --------------------------
# The golden default (2 GB RAM) was too small on a 16 GB host. In APPLIANCE mode, when the operator
# passes no explicit cap, size the guest generously from the host — LXC limits are cheap, so err
# generous. An explicit --cores/--memory ALWAYS wins (never overwritten). byo mode requires explicit
# caps (never auto-sized). The host reads are overridable for the mode harness (no real /proc dep).
host_total_mib() {
if [[ -n "${FELHOM_FAKE_MEMTOTAL_MIB:-}" ]]; then echo "$FELHOM_FAKE_MEMTOTAL_MIB"; return; fi
awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0
}
host_core_count() {
if [[ -n "${FELHOM_FAKE_NPROC:-}" ]]; then echo "$FELHOM_FAKE_NPROC"; return; fi
nproc 2>/dev/null || echo 0
}
autosize_guest_caps() {
[[ "$MODE" == "appliance" ]] || return 0
if [[ -z "$MEM_MIB" ]]; then
local total; total=$(host_total_mib)
if [[ "${total:-0}" -gt 0 ]]; then
# clamp(host-4096, min 4096, max host-2048), then the hard ceiling host-1024 (never
# over-commit). Order matters: the min floor is applied AFTER the generous max so that on
# a host too small for both the MIN wins (edge rule), bounded only by the host-1024 ceiling.
local mem=$(( total - 4096 ))
local max2=$(( total - 2048 ))
(( mem > max2 )) && mem=$max2
(( mem < 4096 )) && mem=4096
local ceil=$(( total - 1024 ))
(( mem > ceil )) && mem=$ceil
MEM_MIB=$mem
log_info " auto-sized guest RAM: ${MEM_MIB} MiB (host ${total} MiB; clamp(host-4096, min 4096, max host-2048), ceiling host-1024)"
fi
fi
if [[ -z "$CPU_CORES" ]]; then
local cores; cores=$(host_core_count)
if [[ "${cores:-0}" -gt 0 ]]; then
local c=$(( cores - 1 ))
(( c < 2 )) && c=2
CPU_CORES=$c
log_info " auto-sized guest cores: ${CPU_CORES} (host ${cores} cores; host-1, min 2)"
fi
fi
}
case "$MODE" in
appliance|byo) ;;
"")
@@ -1165,6 +1208,9 @@ case "$MODE" in
*) die "Unknown --mode: $MODE (appliance|byo)" ;;
esac
# F5: fill the appliance guest caps from the host when the operator gave none (explicit flags win).
autosize_guest_caps
# BYO argument refusals (C1/C2) — before the passphrase prompt, before any step.
if [[ "$MODE" == "byo" ]]; then
if [[ -z "$CPU_CORES" || -z "$MEM_MIB" ]]; then