# Felhom pilot — tester agreement (Peti)

> The terms of the first external pilot: what Peti runs, what Felhom can and cannot do on his
> hardware, the honest limitations of the pilot, and his exit rights. Lives at
> `felhom.eu/documentation/pilot/PETI-tester-agreement.md`.
> Created 2026-07-09 (GL-7). Operator: Viktor. Tester: Peti.
>
> This is the operator-side record of the bargain. The points in §3, §4 and §5 are what Peti is told
> and asked to acknowledge at onboarding; that conversation happens in Hungarian — this document is
> the English source of truth those points are drawn from.

---

## 1. What the pilot is

Peti runs **Felhom as a guest on his own Proxmox server** — the **BYO-host** model. He stays root on
his hardware; Felhom is one LXC guest plus a narrow, non-root management footprint. This is not the
appliance model (Felhom-owned hardware); the whole point of the pilot is to prove the BYO trust
boundary on someone else's box.

**Peti's box:** an 80-core / 128 GB rack server already running his own workloads. The Felhom guest
is capped at **12 cores / 32768 MiB** (noisy-neighbour containment — the only thing standing between
Felhom and his other guests, since we do not control the host).

**Name:** `sajatfelhom.hu`. The existing Cloudflare tunnel for that domain must be re-pointed at the
new guest as part of onboarding (§7).

---

## 2. What runs on Peti's box

| Component | Footprint | Privilege |
|-----------|-----------|-----------|
| Felhom guest (LXC) | one container in the `felhom` pool, capped 12c/32 GB | unprivileged LXC |
| Host agent | one binary + systemd unit on the host | **non-root** (`felhom-agent` user) |
| Sudoers allowlist | `/etc/sudoers.d/felhom-agent` | narrow, `pct`/`vzdump`/mount verbs only |
| PVE API token | scoped to the `felhom` pool + named storages only | `Pool.*`/`VM.*`/`Datastore.*` on those objects |
| Local API | binds `vmbr0:8443` on his LAN | TLS + per-guest bearer token |

Nothing else. In particular, on a BYO host the installer **does not** touch these appliance-only
mechanisms:

- **Break-glass is OFF.** Felhom never runs `chpasswd` on `root@pam` and never vaults a host root
  credential to the hub. Peti's root password is his own and is never seen or changed.
- **Out-of-band operator SSH is OFF** (`oob.enabled=false`). Felhom opens no operator tunnel into
  his box.
- **WireGuard tunnel DISCLOSURE (changed 2026-07-12, DR-tier-by-default decision 5):** the install
  DOES enable an **outbound** WireGuard tunnel (`wg_tunnel.enabled=true`) — it is base
  infrastructure like the Cloudflare tunnel, carries the offsite/DR backup traffic, and is the
  future out-of-band management transport. It is strictly box-dials-out (no Felhom-side connection
  reaches back into the network; the peer has no inbound route), it is listed in the installer's
  typed host-mutation acknowledgement, and it can be disabled per-customer hub-side on request —
  with the honest consequence that the DR tier and offsite backup stop working without it.

These are asserted at install time (the byo config-write asserts `oob`/`lan_resolver` are off
before the install is marked complete — `wg_tunnel` moved to the disclosed-default list in
installer v1.15.0) and were live-validated on the demo host during the GL-6 drill.

---

## 3. What Felhom can and cannot do (the trust boundary)

**Can:** provision and manage its own guest, run backups of its own guest to the storages named in
its ACL, report guest/host health to the hub, and — only with an operator signing key that Peti's
box has pinned — apply a signed agent self-update. Every host action goes through the narrow sudoers
allowlist, scoped to the `felhom` pool.

**Cannot:** read or modify Peti's other guests (the pool-scoped token is refused outside `felhom`);
change his root password; open an inbound operator session; touch his DNS service (see F6 below);
or reach storages outside its named ACL. The local API authorises every request strictly against the
calling token's own guest — a token minted for one guest is refused for any other
(`localapi/server.go`; spot-verified).

---

## 4. Honest limitations of the pilot

This is the data-sovereignty pitch stated plainly, not hidden:

- **No offsite backup yet (local-only).** Backups stay on Peti's own hardware. **A dead or stolen
  box, or a dead backup drive with no second local target, loses data.** The offsite tier
  (Tier-1/Tier-2 split + production WG endpoint) is a fast-follow, not part of this pilot. Local
  backups being green is asserted at onboarding (§7).
- **Backups live on a physically removable drive on his own hardware (F2).** This is the flip side
  of "no offsite" and is the sovereignty guarantee: his data never leaves his premises, and he can
  physically remove the drive. It also means the resilience of the backup is exactly the resilience
  of that one local drive/pool.
- **The `felhom` pool membership is asserted at bring-up only, not continuously healed (F7).** If
  something outside Felhom moves the guest out of the pool while it runs, Felhom won't silently
  re-grab it; a re-run (or `--adopt-pool`) repairs it. No autonomous host-level reassertion.
- **Port 53 must be free on his host (F6).** Felhom's guest DNS wants `:53`. If Peti already runs a
  DNS service there, the installer **refuses and tells him** — it will not stop, mask, or reconfigure
  a DNS service on a host Felhom does not own. He resolves the conflict; Felhom mutates nothing. This
  is checked before anything is written (§7 pre-flight).

---

## 5. Exit rights (data sovereignty)

Peti can leave at any time and keep his data. The uninstaller:

- **Keeps** all drive data under `/mnt/felhom-drives/*`, the backup storage contents, the hub record,
  and any vaulted recovery credential — and prints a KEPT-vs-WIPED statement naming each, in both
  modes. Drives are unmounted (plain `umount`; a busy drive is warned, never force-unmounted).
- **Removes** only Felhom's own footprint: its guest, agent, sudoers, self-update artifacts, and
  config (including `.bak*` siblings that could hold a hub API key).
- Refuses to touch any guest that is not a Felhom guest (it targets only guests carrying the
  `/etc/felhom-bootstrap` bind mount).

Result: Peti walks away with his hardware untouched and his data on his own removable drives. This
was live-validated twice on the demo host (two real uninstalls, drive data intact each time, host
audit clean) during the GL-6 drill.

---

## 6. Pre-install checklist (confirm with Peti before day-0)

- [ ] **PVE major version 9.x** (the byo preflight refuses non-9.x unless forced).
- [ ] **Port 53 is free** on the host — no existing DNS service bound there (F6; preflight refuses
      otherwise).
- [ ] **The `felhom` pool** either does not exist or is Felhom's to use.
- [ ] **VMID 9201 is free** (or pick another; the installer auto-selects a free one if 9201 is taken).
- [ ] **⚠ OPEN — the backup target.** `felhom-pbs` (the demo offsite datastore) is unreachable from
      Peti's LAN, so the default `--acl-storages "local local-lvm felhom-pbs"` is wrong for him. We
      must know **whether Peti's server has a second disk/pool** to use as the local backup target:
      - If **yes** → name it in `--acl-storages` (e.g. `"local local-lvm <his-backup-storage>"`) so
        backups land on a *different* physical drive than the live guest — real local resilience.
      - If **no** (single pool) → backups land on the same pool as the guest; this is a degraded
        posture (one drive failure loses both live and backup) and must be stated to Peti explicitly.
      **Resolve this before finalising the install command below.**
- [ ] **`sajatfelhom.hu` Cloudflare tunnel** — confirm we can re-point it at the new guest (§7).
- [ ] **Customer `peti-felhom` exists in the hub** with real Gitea read creds filled in
      (`git.username`/`git.token`) — the artifact fetch needs them, or relies on the anonymous path.

---

## 7. The install command

Built from the hub's customer page (`peti-felhom` → **Setup Command** → BYO). The BYO shape is
deliberately minimal — a fresh box auto-fetches the golden template from Gitea, so no
`--force-gitea-golden` is needed:

```
sudo ./felhom-host-install.sh \
  --customer-id peti-felhom \
  --mode byo \
  --cores 12 \
  --memory 32768 \
  --acl-storages "local local-lvm <PETI-BACKUP-STORAGE>"
```

`<PETI-BACKUP-STORAGE>` is resolved by the §6 open question. Add `--vmid`/`--node` only if 9201 is
taken or the host is multi-node. The command is **download-then-run** (the script is fetched and
inspected, never piped straight into a shell).

**Onboarding sequence:**

1. Run pre-flight first: same command with `--preflight-only` — prints PASS/FAIL, writes no state.
   Confirms 9.x, `:53` free, ACL storages exist, pool membership.
2. Run the install. Peti sees the host-mutation disclosure and types his hostname to acknowledge.
3. **Claim the dashboard (customer-claim model, controller v0.122.0 + hub v0.50.0 — supersedes the
   old operator-set G9).** The dashboard is NOT open before the customer claims it: an unclaimed box
   serves only the claim page. On the box's first report the hub emails a one-time **claim code** to
   Peti's registered address; he opens `felhom.sajatfelhom.hu`, enters the code, and sets his OWN
   password. The operator never sets or sees it. If the code didn't arrive, the "Új kód kérése"
   button (or the operator's "Kód újraküldése" on the hub customer page) emails a fresh one. Set the
   geo-restriction at onboarding as before. Onboarding is complete once Peti has claimed + logged in.
4. **Re-point the `sajatfelhom.hu` Cloudflare tunnel** at the new guest.
5. **Verify local backups are green** on his box (§4 — the honesty gate). Confirm a backup completes
   to `<PETI-BACKUP-STORAGE>` and a restore-test passes mount-parity.

---

## 8. Acknowledgement

At onboarding, Peti is asked to acknowledge (in Hungarian):

1. Felhom runs as a guest on his hardware; he stays root and Felhom never changes his root password
   or opens an inbound operator session.
2. **There is no offsite backup in the pilot.** His data stays on his premises on a removable drive;
   a lost box or a failed backup drive without a second local copy loses data.
3. He can uninstall at any time and keep his data and hardware; Felhom removes only its own footprint.
4. This is a pilot: rough edges are expected, and the honest limitations above are the trade for full
   data sovereignty.
