# felhom-agent sudoers allowlist — the NARROW host-root surface (slice 5 Phase B, doc 03 §3/§7).
#
# Install as a drop-in: /etc/sudoers.d/felhom-agent (mode 0440, root:root), validated with
# `visudo -cf`. The agent runs as the non-root `felhom-agent` service user and shells out via
# `sudo -n` with FIXED argument vectors (no shell). The fine-grained validation is done IN
# the agent BEFORE exec (internal/storage/validate.go): UUIDs against a strict hex regex,
# mount paths confined+traversal-checked, SMART devices whitelisted to raw disks, LVM names
# charset-checked. These sudoers wildcards are the COARSE allowlist; the agent is the fine
# gate, so a wildcard can never be abused by a value the agent didn't already validate.
#
# Binary paths MUST match the agent config (privileged.systemctl/install/smartctl/lvs). Adjust
# for your distro (Debian/PVE shown). A missing/declined entry degrades the agent with a
# warning (SMART→UNKNOWN, mount→logged error), it does not crash.

Cmnd_Alias FELHOM_MOUNT = \
  /usr/bin/install -o root -g root -m 0644 -- /var/lib/felhom-agent/units/* /etc/systemd/system/*.mount, \
  /usr/bin/systemctl daemon-reload, \
  /usr/bin/systemctl enable --now -- *.mount, \
  /usr/bin/systemctl disable -- *.mount, \
  /usr/bin/systemctl stop -- *.mount

Cmnd_Alias FELHOM_DISK = \
  /usr/sbin/smartctl -a -j /dev/sd[a-z]*, \
  /usr/sbin/smartctl -a -j /dev/nvme[0-9]*n[0-9]*, \
  /usr/sbin/smartctl -a -j /dev/vd[a-z]*, \
  /usr/sbin/smartctl -a -j /dev/hd[a-z]*, \
  /usr/sbin/lvs --reportformat json --units b -o lv_name\,data_percent\,metadata_percent -- *, \
  /usr/sbin/pvs --reportformat json --noheadings -o pv_name, \
  /usr/sbin/zpool status -P

# Provisioning back-half (slice 8A, doc 03 §6): populate a guest's bootstrap config mount
# host-side (internal/provision). These are host-root ops the API token cannot do — a bind mount
# is root@pam-only, and the chown maps the 0600 bootstrap.json to the unprivileged-LXC guest-root
# (uid/gid 100000, spike gotcha 1). The host dir is AGENT-OWNED state under /var/lib/felhom-agent/
# (the wildcard only ever names a path the agent itself created), and the bootstrap file the agent
# writes there is the only thing these touch. ':' is escaped per sudoers grammar.
Cmnd_Alias FELHOM_PROVISION = \
  /usr/bin/chown -R 100000\:100000 /var/lib/felhom-agent/guests/*, \
  /usr/sbin/pct set [0-9]* -mp[0-9]* /var/lib/felhom-agent/guests/*, \
  /usr/sbin/pct set [0-9]* -onboot 1

# Disk inspection + format (slice 8C + Impl-1). blkid/lsblk read the device's data-bearing evidence
# (the agent decides data-bearing-ness from THIS, never the caller's claim). Format goes ONLY through
# felhom-mkfs-guarded (Impl-1 Part B): raw mkfs.* is NO LONGER allowlisted, so even a bad agent cannot
# mkfs the OS disk — the wrapper re-checks the catastrophic cases (system disk / LVM PV / foreign mount)
# as root and refuses, and the agent's unclaimed-disk filter (claim.go) is the primary guard above it.
Cmnd_Alias FELHOM_FORMAT = \
  /usr/sbin/blkid -p -o export /dev/*, \
  /usr/bin/lsblk -J -o NAME\,FSTYPE\,PTTYPE\,MOUNTPOINT /dev/*, \
  /usr/local/sbin/felhom-mkfs-guarded /dev/* *

# LAN split-horizon resolver (internal/lanresolver): the agent manages a host-side dnsmasq that
# answers *.<customer-domain> with each guest's live LAN IP. install only ever writes felhom-*.conf
# drop-ins (from agent-written /tmp temp files); the two `pct exec` reads are FIXED command vectors
# (the guest's eth0 IPv4 + the controller's pulled controller.yaml for the domain) — NOT a general
# `pct exec`. systemctl is scoped to the dnsmasq unit only. The agent never edits /etc/resolv.conf.
Cmnd_Alias FELHOM_DNSMASQ = \
  /usr/bin/apt-get install -y -q dnsmasq, \
  /usr/bin/install -m 0644 /tmp/felhom-resolver-*.conf /etc/dnsmasq.d/felhom-*.conf, \
  /usr/bin/systemctl enable --now dnsmasq, \
  /usr/bin/systemctl reload dnsmasq, \
  /usr/bin/systemctl restart dnsmasq, \
  /usr/bin/rm -f /etc/dnsmasq.d/felhom-*.conf, \
  /usr/sbin/pct exec [0-9]* -- ip -4 -o addr show dev eth0, \
  /usr/sbin/pct exec [0-9]* -- docker exec felhom-controller cat /opt/docker/felhom-controller/controller.yaml

# Guest mountpoint lifecycle (intermediary-mount re-architecture + C1 net). The pre-start self-heal hook
# wrapper is installed once into the PVE snippets dir (from an agent-written /tmp file) and registered
# per-guest; decommission/eject DELETE the dead mountpoint slot so a missing bind source can't brick the
# guest at next boot (the B3 C1 fix). The agent fine-validates the vmid (numeric) + slot (mp[0-9]+) and
# the snippet path is fixed — the wildcards are the coarse allowlist. The install SOURCE is a
# random-named agent temp (os.CreateTemp, audit B1 — a fixed /tmp name was a local TOCTOU), hence the
# glob; the DESTINATION stays pinned. The `mkdir -p` creates the snippets dir on a FRESH box —
# `install` won't create parents, so without it the hook install failed silently on Day-0 boxes
# (B2, DRILL-day0-cleanroom-2026-07-03; fixed agent v0.63.0).
Cmnd_Alias FELHOM_GUESTHOOK = \
  /usr/bin/mkdir -p /var/lib/vz/snippets, \
  /usr/bin/install -m 0755 -- /tmp/felhom-guest-hook-*.sh /var/lib/vz/snippets/felhom-guest-hook.sh, \
  /usr/sbin/pct set [0-9]* --hookscript local\:snippets/felhom-guest-hook.sh, \
  /usr/sbin/pct set [0-9]* --delete mp[0-9]*, \
  /usr/sbin/pct reboot [0-9]*

# Intermediary mount model (the drive hot-swap re-architecture). The agent keeps a SHARED host parent
# /mnt/felhom-drives (self-bind + make-shared + a boot-persistence systemd unit) and binds/unbinds each
# drive's felhom-data namespace UNDERNEATH it so the change propagates into the running guest live (no
# pct, no reboot). The agent fine-validates the drive name + confines paths before any exec; the trailing
# `*` (matching the comma-laden mp spec) mirrors the existing FELHOM_PROVISION pattern.
# `lxc-info -n <vmid> -p -H` resolves the guest init PID for the GuestSeesMount / bound_under_parent check
# (a READ — the drive-gate's "is the drive live in the guest?" signal); WITHOUT it the non-root agent gets
# an empty PID and reports every drive absent (multi-drive flapping, audit 2026-06-29). `make-private`
# isolates the parent's peer group on FIRST setup only (EnsureSharedParent guards on mountpoint, so it
# never re-churns a live parent); without it the parent stays in root's group and submounts double.
Cmnd_Alias FELHOM_INTERMEDIARY = \
  /usr/bin/mkdir -p /mnt/felhom-drives, \
  /usr/bin/mkdir -p /mnt/felhom-drives/*, \
  /usr/bin/mkdir -p /mnt/*/felhom-data, \
  /usr/bin/chown 100000\:100000 /mnt/*/felhom-data, \
  /usr/bin/mount --bind /mnt/felhom-drives /mnt/felhom-drives, \
  /usr/bin/mount --make-shared /mnt/felhom-drives, \
  /usr/bin/mount --make-private /mnt/felhom-drives, \
  /usr/bin/mount --bind /mnt/*/felhom-data /mnt/felhom-drives/*, \
  /usr/bin/umount /mnt/felhom-drives/*, \
  /usr/bin/install -m 0755 -- /tmp/felhom-shared-parent-*.sh /usr/local/sbin/felhom-shared-parent.sh, \
  /usr/bin/install -m 0644 -- /tmp/felhom-shared-parent-*.service /etc/systemd/system/felhom-shared-parent.service, \
  /usr/bin/systemctl enable felhom-shared-parent.service, \
  /usr/bin/lxc-info -n [0-9]* -p -H, \
  /usr/sbin/pct set [0-9]* -mp8 /mnt/felhom-drives*

# Controller-swap / managed auto-update (Option A, non-root). The agent owns the in-guest controller
# image SWAP (it survives the controller being killed mid-swap): read the baked image ref, check the
# pre-pulled target is present, rewrite /etc/felhom-controller-image, restart the bootstrap unit,
# health-check, roll back on failure. Each grant is bounded — NO general `pct exec` and NO `bash -c`:
#   cat <fixed file>            — read the current image ref (read-only)
#   docker image inspect *      — is the pre-pulled target present? (read-only)
#   docker inspect -f *         — container running/health/image (read-only; `*` spans the -f template
#                                 + container across spaces, spike-confirmed)
#   systemctl restart <fixed unit> — re-run the golden's bootstrap (the only state change)
#   tee <FIXED image file>      — WRITE the ref; content is fed on STDIN (no shell, no interpolation),
#                                 the agent strict-validates the ref (controllerImageRe) before the write.
# Validated GO: felhom.eu/documentation/audits/SPIKE-controllerswap-narrow-grants-2026-06-29.md.
Cmnd_Alias FELHOM_CONTROLLERSWAP = \
  /usr/sbin/pct exec [0-9]* -- cat /etc/felhom-controller-image, \
  /usr/sbin/pct exec [0-9]* -- docker image inspect *, \
  /usr/sbin/pct exec [0-9]* -- docker inspect -f *, \
  /usr/sbin/pct exec [0-9]* -- systemctl restart felhom-controller-bootstrap.service, \
  /usr/sbin/pct exec [0-9]* -- tee /etc/felhom-controller-image

# Stale-lock recovery (F2-b, v0.49.0). A host reboot DURING a vzdump backup leaves the guest with a
# `snapshot-delete`/`backup` lock + `onboot:1` then can't start it → the customer box stays DOWN. The
# agent clears the STALE lock at startup (only when no vzdump is in-flight). `pct unlock` is the one op
# with no API equivalent (snapshot-delete + start go through the API token); the agent fine-validates the
# vmid (numeric) before exec — the `[0-9]*` is the coarse allowlist.
Cmnd_Alias FELHOM_STALELOCK = \
  /usr/sbin/pct unlock [0-9]*

# Restore-test scratch teardown (F-LEAK, Campaign 8, v0.110.0). A restore-test whose restore FAILS
# leaves a scratch guest the API token CANNOT destroy: `FelhomAgentGuest` is granted at /pool/felhom and
# a guest joins that pool only when its restore COMPLETES, so a failed restore leaves a pool-less guest
# out of reach (403 VM.Allocate) holding its disks until a human removes it.
#
# TWO API-SIDE FIXES WERE TRIED AND BOTH REFUTED LIVE on 2026-07-28, which is why this grant exists:
#   1. Adopt the stranded guest into the pool, then retry. `PUT /pools/{pool}` ALSO requires
#      VM.Allocate on the VM being added — pool membership cannot bootstrap its own authority.
#   2. Grant FelhomAgentGuest per-path at /vms/990000..990009. Durable for exactly one use per slot:
#      PVE's own destroy path calls `AccessControl::remove_vm_access($vmid)` (LXC.pm:906), which DELETES
#      every ACL at /vms/<vmid> (AccessControl.pm:1898). The grant is consumed by the operation it
#      authorises, so after ten teardowns the band is ungranted and the defect returns.
#
# WHY THIS IS THE TIGHTEST AVAILABLE FENCE, not a widening: sudo matches the vmid LITERALLY, so
# `99000[0-9]` is exactly the ten-slot scratch band the restore-test picks from — nothing else. There is
# no `[0-9]*` coarse allowlist here on purpose: unlike `pct unlock`, this op DESTROYS, so the band must
# be in the policy and not merely validated in the agent. Even a compromised agent asking for
# `pct destroy 9201` is refused by sudo itself. Unlike an ACL, a sudoers rule is not consumed by use.
# The agent re-checks the band in code before exec (defence in depth); this is the outer fence.
Cmnd_Alias FELHOM_SCRATCH_TEARDOWN = \
  /usr/sbin/pct destroy 99000[0-9] --purge

# Network storage / NAS (Part A1, SPIKE-nas-storage-2026-06-29). The agent mounts a customer NAS share
# HOST-SIDE under /mnt/felhom-drives/<name> via a systemd .automount (+ .mount) pair so it propagates
# into the guest through the existing shared bind (an unprivileged LXC cannot mount NFS/CIFS itself).
# A NAS is NOT a drive — no durable-id, no SMART, no wipe; these grants only install/enable/remove the
# unit pair. The agent fine-validates every value (share name, server, export, uid/gid, creds path) before
# any unit is rendered (internal/storage/netmount.go ValidateNetworkMountSpec); the trailing globs are the
# COARSE allowlist. The `.mount` install/enable/disable/stop reuse FELHOM_MOUNT; this alias adds the
# `.automount` variants + the unit-file removal. The unit FILE name is the systemd-escaped mountpoint,
# which always begins `mnt-felhom` (the mountpoint is /mnt/felhom-drives/<name>), so the rm glob is scoped
# to felhom mount units only. mkdir of the mountpoint reuses FELHOM_INTERMEDIARY's /mnt/felhom-drives/*.
# CAMPAIGN-3 additions (loud, per the no-widening rule):
#  - `systemctl reset-failed -- mnt-felhom*`: F10 (CRITICAL) — a NAS automount that hit
#    mount-start-limit-hit during an outage was re-armable by NO platform path; the reassert now
#    reset-failed's the stuck unit before `enable --now` (which the start-limit otherwise refuses),
#    and RemoveNetworkMount clears failed-state residue (F2). Scoped to felhom mount units (the unit
#    name is the systemd-escaped mountpoint, always beginning `mnt-felhom`). reset-failed only clears
#    a unit's failed latch — it cannot start/stop/alter anything.
#  - `rmdir /mnt/felhom-drives/*`: F1 — remove the now-empty mountpoint dir a removed share leaves
#    behind (the campaign accumulated 10 stub-shaped leftovers). rmdir ONLY (never rm -rf): it refuses
#    a non-empty dir, so unexpected data is preserved, not destroyed — a fail-safe grant.
Cmnd_Alias FELHOM_NETMOUNT = \
  /usr/bin/install -o root -g root -m 0644 -- /var/lib/felhom-agent/units/* /etc/systemd/system/*.automount, \
  /usr/bin/systemctl enable --now -- *.automount, \
  /usr/bin/systemctl disable -- *.automount, \
  /usr/bin/systemctl stop -- *.automount, \
  /usr/bin/systemctl reset-failed -- mnt-felhom*, \
  /usr/bin/rmdir /mnt/felhom-drives/*, \
  /usr/bin/rm -f /etc/systemd/system/mnt-felhom*

# Offsite WG tunnel (S3, doc 06 §3.3). The agent manages wg-quick@wg-felhom as an agent-managed
# host service (the dnsmasq/lanresolver shape): conf staged in the agent-owned StateDir (never
# /tmp), installed 0600 to the FIXED destination, unit enable/restart/disable. The ONLY wg read
# is `latest-handshakes` — `wg show <if> dump` is FORBIDDEN everywhere (its interface line
# carries the PRIVATE KEY; the S1 session-log incident). Both install paths are FIXED (no glob):
# the agent has exactly one tunnel conf to manage.
Cmnd_Alias FELHOM_WG = \
  /usr/bin/apt-get install -y -q wireguard-tools, \
  /usr/bin/install -o root -g root -m 0600 -- /var/lib/felhom-agent/wg/wg-felhom.conf /etc/wireguard/wg-felhom.conf, \
  /usr/bin/systemctl enable --now wg-quick@wg-felhom, \
  /usr/bin/systemctl restart wg-quick@wg-felhom, \
  /usr/bin/systemctl disable --now wg-quick@wg-felhom, \
  /usr/bin/wg show wg-felhom latest-handshakes

# Agent self-update (TASK D1, SPIKE-agent-selfupdate-2026-07-05). The agent downloads the
# operator-SIGNED binary (sha256 pinned in the signed op — neither hub nor Gitea compromise can
# substitute it), verifies the sha in-process, then hands off to the guarded wrapper, which
# RE-verifies the sha as root, confines the staged path to /var/lib/felhom-agent/selfupdate/,
# performs the A/B flip (atomic same-fs rename, .prev retained) and schedules a detached restart.
# The apply args are a COARSE glob (spike S4b: sudoers fnmatch makes a [a-f0-9]* sha pattern
# first-char-only anyway) — the wrapper's own sha re-verify + path confinement is the real gate.
# `rollback` is normally run by felhom-agent-rollback.service (root, OnFailure=), not via sudo;
# granting it here keeps the verb probe-able (capability self-check) and operator-invokable.
Cmnd_Alias FELHOM_SELFUPDATE = \
  /usr/local/sbin/felhom-selfupdate-guarded apply /var/lib/felhom-agent/selfupdate/* *, \
  /usr/local/sbin/felhom-selfupdate-guarded commit, \
  /usr/local/sbin/felhom-selfupdate-guarded rollback

# Dedicated OOB sshd (TASK H1). The agent manages felhom-sshd like wg-felhom/dnsmasq: it RENDERS the
# config (Port from its claim) + the operator's authorized_keys, validates with `sshd -t`, and reloads
# (never restart-on-change [SF-2]). Both install SOURCES are the agent-owned staged files under
# StateDir; both DESTINATIONS are FIXED. `sshd -t/-T` are the validate/discover reads. The
# systemctl verbs are SCOPED to felhom-sshd only. reset-failed precedes a deliberate restart [SF-5].
# NOTHING here can touch the stock sshd, :22, or /etc/ssh.
Cmnd_Alias FELHOM_SSHD = \
  /usr/bin/install -o root -g root -m 0644 -- /var/lib/felhom-agent/felhom-sshd/sshd_config /etc/felhom-sshd/sshd_config, \
  /usr/bin/install -o root -g root -m 0644 -- /var/lib/felhom-agent/felhom-sshd/authorized_keys.felhom-op /etc/felhom-sshd/authorized_keys/felhom-op, \
  /usr/sbin/sshd -t -f /var/lib/felhom-agent/felhom-sshd/sshd_config, \
  /usr/sbin/sshd -t -f /etc/felhom-sshd/sshd_config, \
  /usr/sbin/sshd -T -f /etc/felhom-sshd/sshd_config, \
  /usr/bin/systemctl enable --now felhom-sshd, \
  /usr/bin/systemctl reload felhom-sshd, \
  /usr/bin/systemctl restart felhom-sshd, \
  /usr/bin/systemctl reset-failed felhom-sshd, \
  /usr/bin/wg show wg-felhom latest-handshakes

# PBS DR tier apply (slice 2, SPIKE-pbs-tier-provisioning-2026-07-10 §2b). Storage-entry
# lifecycle is /storage-ROOT-gated in the PVE API (spike Probe 1: create/modify/delete all check
# Datastore.Allocate on /storage), so the agent token cannot do it — this wrapper is the pinned
# vector. THE SET-ONLY LAW: the wrapper contains NO deletion path (entry deletion destroys the
# client encryption key = un-decryptable backups); verbs are create/reconcile/grant only. The
# token secret rides the wrapper's STDIN — sudo logs argv, so it must never appear here. The
# agent fine-validates every field (charset + descriptor equality) before exec; these globs are
# the coarse allowlist.
#
# `read` (R-39 leg b, agent v0.91.0) is the ONE added verb. It prints a token secret to stdout and
# performs no mutation. It exists because the agent writes that file through this wrapper but could
# never read it back (/etc/pve/priv is 0700 root:www-data), leaving its PBS verify loop permanently
# blind to an `applied`-but-401 tier. It is NOT a general file-read: the wrapper pins the directory
# and prefix-asserts the resolved path, and the id grammar admits no slash. The secret goes to
# STDOUT, never argv — sudo logs argv.
# E-2a: the backup-target storage shim. Creating a PVE storage needs Datastore.Allocate at /storage
# and the grant needs Permissions.Modify -- the agent holds NEITHER by design (blast-radius
# containment; Permissions.Modify would let it rewrite its own authority). Both live behind this
# fixed-vocabulary root shim instead, exactly like the mkfs and pbs-apply wrappers. The wrapper has
# NO storage-removal path, enforces is_mountpoint 1, and refuses a target on the root device.
Cmnd_Alias FELHOM_BACKUPTARGET = \
  /usr/local/sbin/felhom-backup-target-apply create *, \
  /usr/local/sbin/felhom-backup-target-apply grant *

Cmnd_Alias FELHOM_PBSDR = \
  /usr/local/sbin/felhom-pbs-apply create *, \
  /usr/local/sbin/felhom-pbs-apply reconcile *, \
  /usr/local/sbin/felhom-pbs-apply grant *, \
  /usr/local/sbin/felhom-pbs-apply read *

# OOB nft belt (TASK H1). The STATIC table `inet felhom_oob` is installed once by host-install; the
# agent mutates ONLY its two SETS — @operator_ips (the operator /32) + @ssh_port (the claimed port).
# SET ELEMENTS ONLY [trap 4]: NO `nft add rule`, NO `nft -f`, NO `flush ruleset/table` — a rule grant
# would let the agent firewall anything. The agent fine-validates every element (netip / int range)
# before exec; the trailing wildcards are the coarse allowlist (values only).
Cmnd_Alias FELHOM_OOB = \
  /usr/sbin/nft list set inet felhom_oob operator_ips, \
  /usr/sbin/nft list set inet felhom_oob ssh_port, \
  /usr/sbin/nft flush set inet felhom_oob operator_ips, \
  /usr/sbin/nft flush set inet felhom_oob ssh_port, \
  /usr/sbin/nft add element inet felhom_oob operator_ips *, \
  /usr/sbin/nft add element inet felhom_oob ssh_port *

# Escrow ceremony (controller-driven, TASK 2026-07-13; mechanics validated by
# SPIKE-controller-escrow-2026-07-13). ONE fixed argv — sudoers matches the argument vector
# byte-for-byte (spike §2.2): any alteration (value, extra flag, order, config path) is refused.
# --config pinned: env_reset strips FELHOM_AGENT_CONFIG and the pin closes alternate-config
# injection. R rides the subprocess stdout pipe only; sudo logs argv = secrets-free. The argv
# MUST stay byte-identical to escrow.CeremonyArgs() (internal/escrow/ceremony.go) — the
# capability manifest entry + TestManifestCoveredBySudoers lock the three copies together.
Cmnd_Alias FELHOM_ESCROW = \
  /usr/local/bin/felhom-agent --config /etc/felhom-agent/agent.json --selftest=escrow-create --upload --output=json

# Node self-heal (CAMPAIGN-3 Part 6, F12-class defense in depth). The ONE fixed unit the appliance
# watchdog may (re)start when a boot leaves networking down — the exact command the morning recovery
# ran by hand after the F12 host loss. FIXED unit, no glob: this grant alone cannot harm — starting
# networking.service is precisely what the boot should have done. The remedy is ALSO code-gated on
# deployment_mode="appliance" (the Manager refuses to invoke it on a byo host); the sudoers grant is
# the coarse floor, the mode gate is the fine one.
Cmnd_Alias FELHOM_SELFHEAL = \
  /usr/bin/systemctl start networking.service

# Guest-network watchdog (internal/guestnet, R-54). The guest's DHCP client is unsupervised — when it
# died on 2026-07-20 the box lost its address ~80 minutes later and went off the internet for 1h15m
# (INCIDENT-guest-dhclient-killed-2026-07-20). Four FIXED read vectors plus ONE fixed heal vector; the
# heal is the incident's own restored invocation, byte for byte. This is NOT a general `pct exec`: every
# argument after the numeric vmid is a literal, so the grant cannot be widened by anything the guest or
# the hub says. The address read is deliberately NOT duplicated here — it is already FELHOM_DNSMASQ's,
# and the same command must not be granted twice under two names.
Cmnd_Alias FELHOM_GUESTNET = \
  /usr/sbin/pct exec [0-9]* -- ip route show default, \
  /usr/sbin/pct exec [0-9]* -- cat /etc/network/interfaces, \
  /usr/sbin/pct exec [0-9]* -- pgrep -x dhclient, \
  /usr/sbin/pct exec [0-9]* -- dhclient -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases eth0

felhom-agent ALL=(root) NOPASSWD: FELHOM_MOUNT, FELHOM_DISK, FELHOM_PROVISION, FELHOM_FORMAT, FELHOM_DNSMASQ, FELHOM_GUESTHOOK, FELHOM_INTERMEDIARY, FELHOM_CONTROLLERSWAP, FELHOM_STALELOCK, FELHOM_NETMOUNT, FELHOM_WG, FELHOM_SELFUPDATE, FELHOM_SSHD, FELHOM_OOB, FELHOM_PBSDR, FELHOM_BACKUPTARGET, FELHOM_SELFHEAL, FELHOM_ESCROW, FELHOM_GUESTNET, FELHOM_SCRATCH_TEARDOWN
