Files
felhom.eu/scripts
admin 063ba83e41
gates / gates (push) Failing after 7m1s
website: two hidden mockups - M1 "the site is the dashboard", M2 hybrid
Design studies only. Nothing live changes, nothing is linked from a live page, both
carry noindex and /preview/ is already Disallow-ed in robots.txt. felhom-controller
was read ONLY (baseline dcd89364a4); nothing in it was touched.

M1  website/preview/m1/  - the website wearing the controller's shell
    index (Indítópult)   welcome card + all 58 launcher tiles
    alkalmazasok         the catalog in the site's own five buckets, vanilla search
    mentesek             the three backup levels, every number marked "minta"
    hogyan / teszteles / kapcsolat   today's copy inside the dashboard shell
M2  website/preview/m2/  - short hero beside a window holding a clickable launcher

Real data, not invented:
  * 58 apps from app-catalog-felhom.eu @ 8ea72aa: real names, logos, Hungarian
    taglines, use cases and first steps out of each .felhom.yml app_info.
  * Tile colours reproduce the controller's OWN tileColor (funcmap.go): FNV-1a 32
    of the slug -> hue, 45% 38%. No brand_color exists in the catalog, so every
    tile here is the colour a real launcher would paint.
  * Page copy is today's live website only.
  * Category labels are the five buckets alkalmazasok.html already uses. None invented.

The 58 is now measured, not quoted: 60 templates minus plant-it (abandoned) and
wger (hidden) = 58 offered, which is exactly what the site's prose says. See the
R-929 note in the audit folder.

Mockup rules honoured:
  * No value pretends to be live - every sample wears a "minta" badge.
  * No control does nothing: the disabled sidebar entries (Tárhely, Megosztás,
    Rendszermonitor, Beállítások) say "a saját dobozodon működik" rather than
    404-ing, and the contact form's submit is disabled with the same kind of hint.
    The consent text is the live one, unchanged.
  * App panels open with :target, so they work with JavaScript switched OFF and
    their text is real HTML a crawler can read - not JS-built content.
  * No login form anywhere. No external request, no AI or stock image.

CSS: website/preview/mockup.css is three labelled blocks so the shared-design
report can count them - adapted base (font URLs repointed /static -> /assets),
188 rules copied VERBATIM from the controller, and the mockup-only rules. The
burger is the website's own three-span control, copied rather than hand-rolling an
SVG: the shared sprite carries no i-menu.

scripts/site_gates.py gains a MOCKUPS set, with the reasoning in a comment. The
mockups keep every correctness check (BOM, emoji, banned strings and CDN hosts, no
<style>, cache-busting, <html lang>, clean tail, dashboard-picture language, the
picture-viewer rules) and are excluded from exactly the three chrome-consistency
checks a dashboard-shell study must fail by construction: nav/footer identical to
index.html, the NO_TWIN language globe, and analytics - the last deliberately, so a
hidden mockup cannot pollute the real stats. Delete the set with the directories.

site_gates.py green.
2026-10-11 12:13:23 +02:00
..

Felhom host scripts

Operator-side scripts for standing up a Felhom Proxmox host.

felhom-host-install.sh — Day-0 host bootstrap (operator-deploy)

Run on a freshly-PVE-installed box to fully automate Day-0: Proxmox API token → hub host enrollment (single secret) → agent install (fetch + verify + install) → agent config → golden → guest provision → verify. It composes already-proven mechanisms (the pveum role/token sequence, the hub POST /host-enroll enrollment from option C, and felhom-agent --selftest=provision). The agent renders bootstrap.json into the guest and the controller pulls its own controller.yaml in-guest — the script never fetches that.

Since v1.1.0 (BUNDLE slice) the script also installs the agent itself: it fetches the agent binary + golden from Gitea generic packages and verifies each artifact's sha256 against the hub-vouched manifest (GET /api/v1/artifacts/{id}) before installing/using it. The fetch credential is the git token already inside the customer's controller.yaml (config-retrieve) — no new credential, and the checksum trust root is the hub, not Gitea.

Grounding: documentation/audits/SPIKE-day0-firstboot-handshake-2026-06-26.md.

Prerequisites (manual, before running)

  1. Install Proxmox VE 9.x on the box. During the installer, use Advanced → LVM sizing so local-lvm (the pve/data thin pool) has enough room for the appliance volumes — a useful box wants ≥ ~120 GiB free on local-lvm (rootfs 32G + Docker-data ~200G + user-data ~50G after grows). The script refuses below the hard minimum.
  2. SSH into the box as root.
  3. Create the customer in the hub first (hub UI → new customer). The customer's retrieval passphrase (a 5-word Hungarian phrase) is the only secret you carry to the box.

That's it. The agent binary + golden are fetched + verified + installed by the script (provided the operator has recorded the current artifact set in the hub UI → Configs → Day-0 artifacts, and published them via felhom-agent/scripts/publish-agent.sh + configs/build-golden.sh). A local golden, if present, is still used as a fallback.

Usage

curl -fsSL https://felhom.eu/scripts/felhom-host-install.sh -o felhom-host-install.sh
chmod +x felhom-host-install.sh

# secure no-echo passphrase prompt:
sudo ./felhom-host-install.sh --customer-id <customer>

# or from a 0600 file (no prompt):
sudo ./felhom-host-install.sh --customer-id <customer> --passphrase-file /root/.pass

# preview every mutating command without executing:
sudo ./felhom-host-install.sh --customer-id <customer> --dry-run

# resume after a fixed mid-way failure (skips completed steps):
sudo ./felhom-host-install.sh --customer-id <customer> --resume

The passphrase is read no-echo or from a 0600 file — never a CLI argument, never echoed, never written to the state file or logs. The minted Proxmox-token secret and the per-host hub api_key live only in the agent config (0600, root).

Key options

Option Default Purpose
--customer-id ID (required) customer (must already exist in the hub)
--vmid N 9201 guest VMID to provision
--golden VOLID newest vzdump-lxc-<golden-vmid> golden archive
--rootfs/--datavol/--sysdata-grow N auto-compute volume grows (GiB over the golden base 32/16/8)
--passphrase-file PATH no-echo prompt read passphrase from a 0600 file
--preserve-from PATH — merge non-Day-0 sections (PBS/local_api/privileged/authz) from an existing config
--dry-run / --resume / --force off preview / resume / clobber an existing vmid
--mode provision|dr provision dr is a documented 10D stub (not implemented)

Behaviour notes

  • Idempotent + resumable. A step-state file (/var/lib/felhom-install/state.json) records completed steps; --resume skips them. A plain re-run refuses to clobber an existing --vmid (pass --force to override).
  • Single-secret enrollment. POST /host-enroll mints on first call (201) and reuses the credential on later calls (200) — re-running never orphans a running agent's key. The global operator key is never used.
  • Token automation. Creates/normalises the 16-priv FelhomAgent role, the felhom-agent@pve user + privsep token, and both ACL grants (user and token — the ACL is applied after the token exists, because pveum user token remove purges it).
  • Agent install (v1.1.0). Fetches the binary from Gitea (/api/packages/admin/generic/felhom-agent/<ver>/felhom-agent), verifies its sha256 against the hub manifest, then installs the non-root felhom-agent service user + binary + sudoers (0440, visudo -cf-validated) + the canonical systemd unit. Idempotent: same version already installed + service active → skips. A sha256 mismatch aborts the install (verify-before-use). The agent runs non-root (privileged.mode: "sudo" + the sudoers allowlist), never as root.
  • Golden (v1.1.0). Uses a local golden when present; otherwise fetches it from Gitea (/api/packages/admin/generic/felhom-golden/<ver>/golden.tar.zst), verifies its sha256, and imports it into the archive storage's dump dir for the restore. --force-gitea-golden forces the Gitea path even when a local golden exists.
  • DR mode (--mode dr) is a documented seam only — it restores the customer's own PBS whole-CT snapshot instead of the golden. Not implemented (10D).

Productionization hooks (not done here)

  • Serving: place this file where the felhom.eu site serves it at https://felhom.eu/scripts/felhom-host-install.sh (a static route; verify on deploy).
  • Per-customer artifact pinning: the hub manifest currently returns the global current artifact set for every customer; per-customer pinning is a future hook (GET /api/v1/artifacts/{id} already takes the customer id).
  • Unit/sudoers integrity: the binary + golden are sha256-verified against the hub; the unit + sudoers are fetched from the agent repo main (canonical text) and the sudoers is visudo -cf-validated.