storage identity build B: live proof, R-1068/R-1079/R-1082/R-1084 closed, R-1093/R-1094 opened, D15, golden 0.318.0 record, backup page
gates / gates (push) Successful in 6m39s

A fresh one-disk install (512 GB VM, installer 1.33.0, agent 0.158.1 + golden 0.318.0 vouched): data volume 100 GiB, data
area 213 GiB registered as the default drive with no press, Jellyfin with no extra step, nothing of the area in a whole-box
copy; partitions formatted and used; the honest backup offer on a VM; /mnt/sys_drive never offered (no box had it).
16 §13 build B status; capability map D15; poster facts (drawing needs a refresh, STATUS); the backup page HU+EN now that
it is true; REPORT-storage-identity-buildB-2026-10-16.md.

Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
2026-10-16 10:07:13 +02:00
parent 8b0a4413cd
commit 920c1aaf84
35 changed files with 933 additions and 12 deletions
@@ -180,6 +180,7 @@
| <a id="d12"></a>[D12](capability-map-detail/D.md#d12) | Boot ordering: automount + networking survive reboot; appliance self-heal watchdog | agent v0.85 | **PROVEN-LIVE** | — | `audits/CAMPAIGN-4-2026-07-13.md` (2026-07-13); `audits/CAMPAIGN-6A-2026-07-14.md` (2026-07-14) |
| <a id="d13"></a>[D13](capability-map-detail/D.md#d13) | Media apps see EVERY drive; a drive added, pulled, back or safely removed is handled per app (notice, real restart, wiring, torrents paused/resumed) | controller v0.313.0, catalog `0188430`, hub v0.151.0 | **PROVEN-LIVE** | Tier-0 boxes (Tester 1, demo-hp 9201); the fleet with golden 0.313.0 + the floor | `audits/media-build-2026-10-15/G/` (2026-10-15); `audits/media-drive-events-2026-10-14/` (2026-10-14) |
| <a id="d14"></a>[D14](capability-map-detail/D.md#d14) | A second local copy COUNTS only on a proven different physical disk: the agent knows each place's disks; a VM box makes the copy but says it cannot check; a same-disk copy is not made; a NAS never hides the one-copy notice; a box with no drive refuses a data-folder app in words | agent v0.157.0, controller v0.317.0, hub v0.154.0 | **PARTIAL** | live: VM `unknown` (Tester 1) and two real-disk boxes `proven` (demo-hp, N100); unit only: the same-disk skip, the NAS notice, the no-drive sentence; `usb_bridge` never seen on a device | `audits/storage-identity-buildA-2026-10-15/README.md` (2026-10-15); `architecture/16-storage-physical-identity.md` §13 |
| <a id="d15"></a>[D15](capability-map-detail/D.md#d15) | A one-disk box gets its own data area („Belső tárhely") outside every whole-box copy, registered as the default drive with no press, so media and data-folder apps install with no extra step; a free disk's partitions can be formatted or used one by one | installer 1.33.0, agent v0.158.1, controller v0.318.0, golden 0.318.0 | **PROVEN-LIVE** | fresh 512 GB one-disk VM on demo-hp; partitions on a scratch disk; the old-box signed job and the D4 sentence unit-proven only; an empty part asks for confirmation (R-1093) | `audits/storage-identity-buildB-2026-10-15/README.md` (2026-10-16) |
## E. Access, networking & household use
@@ -6,8 +6,8 @@
> whether two storage places sit on the same physical disk." Triggered by the OMV test (`audits/omv-vm-2026-10-15/`,
> R-1068). Spike: `audits/storage-identity-2026-10-15/` (Part B there). Marks as in `01`: **[FACT]** read in a named
> file or measured; **[DESIGN]** a proposal. **Status: ACCEPTED 2026-10-15** — the operator ruled D1–D6 as recommended (§12). **Build A shipped 2026-10-15**
> (§3, §4, §5's D4 sentence, §8, §10); **§5's internal place, §6 and §7 wait for build B** — what is built, and where the
> build differs from this text, is §13.
> (§3, §4, §5's D4 sentence, §8, §10); **build B shipped 2026-10-16** (§5's internal place, §6, §7, §9) — what is built,
> and where the build differs from this text, is §13.
> Paths: A = `felhom-agent/`, C = `felhom-controller/controller/`, E = `felhom.eu/`.
## 1. Today, in one table (Part A)
@@ -216,5 +216,25 @@ fields) · website and guides (the OMV guide's second-disk line; the backup expl
- An older agent (no same-disk call): the controller keeps the pre-build `st_dev` rule and logs it once.
- §11 rejects `sys_drive` for household data, but the drives page still offers `/mnt/sys_drive` to attach (R-280) — R-1082.
**Build B — waits:** §5's internal place, §6 partitions, §7 the installer (and the D3 space check the operator asked for).
**Build B — the internal place, partitions, the installer (2026-10-16)** (agent v0.158.1 — v0.158.0 superseded; controller
v0.318.0; installer 1.33.0; evidence `audits/storage-identity-buildB-2026-10-15/`; the D3 space check: its `SPACE-CHECK.md`,
sizes ruled above in §7):
| Section | Built | Where it differs from the text above |
|---|---|---|
| §5 internal place | the root helper `felhom-internal-place` (bundle) makes `pve/felhom-belso` by the ruled rule; the installer's step 5b (appliance only) and the signed `add_internal_place` (an old box) run it; the agent mounts it at `/mnt/belso` like a drive (kind `internal`); the controller binds and registers it as the default drive | the place is made only in **appliance** mode (a byo host's pool is the owner's); every failing read in the helper refuses (security review of 0.158.0) |
| §6 partitions | each part of a free disk is listed (empty / filesystem / in_use); „format this part", „use this part"; a data-bearing part's wipe id binds disk id + part number + fs-UUID | the „filesystem with no files" state is folded into `filesystem` (no root read-only mount; the typed confirmation covers both). **A part can be formatted only while nothing on its disk is mounted**: the root mkfs wrapper refuses any mounted node and deliberately trusts no „it is Felhom's own drive" claim (the agent could fake it — security review of 0.158.0). So a household formats its free parts before using any part; a free part next to an in-use drive can still be USED, not formatted |
| §7 installer | `step_grows` by the ruled table; step 5b | the host root is left as Proxmox makes it (SPACE-CHECK §3) |
| §9 old boxes | the signed `add_internal_place` (no parameters; the size is the rule's) | — |
**Proven live 2026-10-16** (`audits/storage-identity-buildB-2026-10-15/README.md`): a fresh one-disk install (512 GB VM,
the public installer 1.33.0, agent 0.158.1 and golden 0.318.0 vouched) → data volume 100 GiB, internal place 213 GiB,
registered as the default drive with no press; Jellyfin installed with no extra step onto it; a whole-box copy holds nothing
of it; partitions formatted and used through the wizard; the honest backup offer on a VM. **Not as written:** an empty part
asks for the typed confirmation (the probe reads the parent's partition table — R-1093); a 200 GB disk gets no internal
place under the ruled sizes (15 GiB < 20).
Also in build B: R-1084 (the whole-box backup offer asks the same-disk question) and R-1082 (`/mnt/sys_drive` is never
offered to attach, by device). A bundle that adds a path needs a step bundle on boxes with an older wrapper (R16, R-880):
0.158.1 was delivered through `0.158.1-step1`.
@@ -268,3 +268,29 @@ the installer (R-1079).
---
<a id="d15"></a>
## D15 — The one-disk box's own data area; partitions as places
*Map row: [D15](../00-capability-map.md#d15) · added 2026-10-16.*
**Scenario:** a household with one disk installs a media app and expects it to work with no extra step — and expects its
films not to fill every whole-box copy. A household with a split disk wants to use the free part without wiping the rest.
**Components:** installer 1.33.0 (step 3 sizes the data volume by the ruled table; step 5b runs the bundle's root helper
`felhom-internal-place`), agent v0.158.1 (mounts the place like a drive, kind `internal`; lists partitions; the signed
`add_internal_place` for an old box), controller v0.318.0 (binds and registers it as the default drive; the partition
wizard). Design: `architecture/16-storage-physical-identity.md` §5–§7, §9, §13; sizes: `audits/storage-identity-buildB-2026-10-15/SPACE-CHECK.md`.
**Status:** **PROVEN-LIVE** (2026-10-16) on a fresh 512 GB one-disk VM from the public ISO and installer: data volume 100 GiB,
data area 213 GiB, registered as the default drive with no press, Jellyfin installed with no extra step onto it, a whole-box
copy holds nothing of it; partitions formatted and used through the wizard; formatting the whole disk under a used part
refused in words.
**Unit only:** the old-box signed job (no box with ≥ 20 GiB free thin space was at hand), the D4 sentence (R-1068's other
half). **Known gaps:** an empty part asks for the typed confirmation (R-1093); the whole-disk refusal sentence (R-1094); a
part next to a mounted part can only be used, not formatted (the root wrapper trusts no „Felhom's own" claim, `16` §13).
**Evidence:** `audits/storage-identity-buildB-2026-10-15/README.md`.
---
@@ -66,6 +66,9 @@ Inside, from bottom to top:
- the **system disk** (Proxmox + the guest),
- **data drive 1** (household data and the **Tier-1** backup folder),
- an optional **data drive 2** (the **Tier-2** copy).
- on a **one-disk box**, the **data area** („Belső tárhely", installer 1.33.0) — a part of the system disk the box uses as
data drive 1; it is **not** in the whole-guest backups, and a copy between it and the system data is never counted as a
second copy (it is the same disk).
Note on the drawing: the data drives are attached to the guest from the host; the **whole-guest backups do not include the data drives** (that is what the per-app tiers are for).
Arrows out of the box:
@@ -0,0 +1,47 @@
# Storage identity, build B — live evidence (2026-10-16)
Design: `architecture/16-storage-physical-identity.md` §5–§7, §9 (§13 = what was built). Releases: agent v0.158.1
(v0.158.0 superseded by a security review), controller v0.318.0, installer 1.33.0 (published), golden 0.318.0
(`documentation/tests/golden-0.318.0-2026-10-16/`). The operator vouched agent 0.158.1 + golden 0.318.0 and raised the
global floor to 0.318.0 / MinAgent 0.158.1.
**Method:** endpoint level (no browser on DooPlex) — the dashboard requests the pages make (`/api/stacks/<app>/deploy`,
`/api/storage/init`, `/api/storage/attach`, `/api/disks/candidates`, `/backups`, `/claim`, `/api/server/poweroff`), the agent's
local API with the guest's own token (`/disks`, `/disks/candidates`, `/disks/same-disk`, `/backup`), the VM console
(screendumps) for the Proxmox install. Times are UTC.
## Part 1 — the D3 space check
`SPACE-CHECK.md` + `space/` — ruled by the operator 2026-10-16: data volume 50/100/130 GiB, reserve max(16 GiB, 10 %) ≤ 48 GiB.
## Part 2 — live
| What | Result | File |
|---|---|---|
| A fresh one-disk install (scratch VM 342 on demo-hp, **512 GB** disk, ISO 1.29.0, the public installer 1.33.0, customer Tester4) | pool 383 GiB → data volume **100 GiB**, internal place **created, 213 GiB**; day-0 SUCCESS in 3 min | `05-vm342-create.txt`, `07-fresh-install.log` |
| The agent mounts it, the controller registers it | `/mnt/belso` (by fs-UUID) → bound at `/mnt/felhom-drives/belso`; the guest config has no mount point for it; controller 0.318.0: „registered /mnt/felhom-drives/belso (default=true)" | `15-box-logs.txt` |
| Jellyfin with no extra step | deploy with DOMAIN + SUBDOMAIN only → **202**, healthy in 30 s, `/media` = `/mnt/felhom-drives/belso/userdata/media` (the OMV run on 0.314.0: 400 „HDD_PATH … kötelező") | `08-jellyfin-deploy.txt` |
| Not in the whole-box copy | a 64 MB marker in the place → 0 entries in the archive (control: 29,512 `var/lib/felhom/docker` entries); vzdump „excluding bind mount point mp8" | `09-…` |
| The place is the system disk | same-disk internal vs sys_drive: **same disk, proven** (kind internal, `unknown/vm` — a VM; build A never counts such a copy) | `10-…` |
| R-1082 | the attach list is empty while the controller's own mount table holds `/mnt/sys_drive` (0.314.0 offered it) | `10-…` |
| Partitions (a 16 GB scratch disk, two parts, part 2 ext4) | both listed, formattable; part 1 formatted + registered (`/mnt/felhom-drives/resz1`); then nothing of the disk is offered for formatting, part 2 offered for USE only and attached (`resz2`); same-disk part 1 vs part 2: **same disk, proven**; formatting the whole disk: **409** in words | `11-`…`14-` |
| R-1084 | the offer on a VM: „Ezt a meghajtót kijelölheted a rendszermentés helyéül. A Felhom nem tudja ellenőrizni … ezért nem ígérhetjük, hogy egy lemezhiba után is vissza tudod állítani a rendszert." | `14-…` |
| Delivery to the three test boxes | Tester 1, demo-hp, N100: agent 0.158.1 + bundle 0.158.1 (through the step bundle `0.158.1-step1`), controller 0.318.0 by the floor | — |
## Not as the design said (filed or recorded)
- **An empty part asks for the typed confirmation** („device has a partition table"): `lsblk` reports the parent disk's table
for a partition, so the agent's probe reads an empty part as data-bearing. Safe direction, wrong friction → R-1093.
- **The whole-disk refusal sentence** ends „Csak a szabad részét formázhatod" — not true while another part is mounted (the
free part can then only be used) → R-1094.
- **A 200 GB disk gets no internal place** under the ruled sizes (pool 113 → 113 − 16 − 82 = 15 GiB < 20); the brief's
200 GB VM could not show it, so the VM was rebuilt with 512 GB (`05-…`).
- **Tester 1's dashboard password in the restored credentials file does not open Tester 1** — the dashboard checks ran on the
Tester4 scratch box instead (also a VM, same shape).
## Teardown
Machine: VM 342 switched off the household way (`/api/server/poweroff`, alarms held) and destroyed; Tester 1's 16 GB scratch
disk unlinked and deleted. Host: the helper `agentq.sh` removed from Tester 1's host (VM 342 is gone with its copy). Hub: the
three per-customer floors of build A cleared (303 `floor_set`); Tester4's host is off — the operator deletes the throwaway
customer. Secrets: scan of this folder for the phrase, the dashboard password, the setup code and the token prefix = none.
@@ -0,0 +1,8 @@
## 2026-10-16T02:11:42Z add a 16G scratch disk to Tester 1 (VM 341 on demo-hp)
Formatting '/mnt/hdd_1/images/341/vm-341-disk-7.raw', fmt=raw size=17179869184 preallocation=off
scsi6: successfully created disk 'nvme-scratch:341/vm-341-disk-7.raw,backup=0,size=16G'
scsi6: nvme-scratch:341/vm-341-disk-7.raw,backup=0,size=16G
## inside Tester 1 host
sde 50G drive-scsi4
sdf 20G drive-scsi5
sdg 16G drive-scsi6
@@ -0,0 +1,10 @@
blkid rc=2 (2 = nothing on it)
bash: line 1: parted: command not found
blkid rc=2 (2 = nothing on it)
NAME SIZE FSTYPE LABEL MOUNTPOINT
sdg 16G
|-sdg1 8G
`-sdg2 8G ext4 scratchp2
scsi-0QEMU_QEMU_HARDDISK_drive-scsi6 ../../sdg
scsi-0QEMU_QEMU_HARDDISK_drive-scsi6-part1 ../../sdg1
scsi-0QEMU_QEMU_HARDDISK_drive-scsi6-part2 ../../sdg2
@@ -0,0 +1,2 @@
gitea.dooplex.hu/admin/felhom-controller:0.317.0
gitea.dooplex.hu/admin/felhom-controller:0.318.0-rc.2 Up 25 seconds (healthy)
@@ -0,0 +1,6 @@
initialize /dev/sdg data_bearing=True
/dev/sdg1 empty fs= mountable=False formattable=True
/dev/sdg2 filesystem fs=ext4 mountable=True formattable=True uuid:ae2bd22b-3dc9-4be0-a713-c192b8d307a0
attach /dev/sdg data_bearing=True
/dev/sdg1 empty fs= mountable=False formattable=True
/dev/sdg2 filesystem fs=ext4 mountable=True formattable=True uuid:ae2bd22b-3dc9-4be0-a713-c192b8d307a0
@@ -0,0 +1,22 @@
Formatting '/mnt/hdd_1/images/342/vm-342-disk-0.raw', fmt=raw size=214748364800 preallocation=off
scsi0: successfully created disk 'nvme-scratch:342/vm-342-disk-0.raw,size=200G'
update VM 342: -boot order=scsi0;ide2
boot: order=scsi0;ide2
cores: 4
ide2: local:iso/felhom-installer-1.29.0-pve9.2-1.iso,media=cdrom,size=1665356K
memory: 8192
net0: virtio=BC:24:11:DE:A6:46,bridge=vmbr0
scsi0: nvme-scratch:342/vm-342-disk-0.raw,size=200G
status: running
2026-10-16T06:47:25Z PVE install pressed on VM 342 (ISO 1.29.0, /dev/sda 200G, ext4, PVE default layout)
## 2026-10-16T07:14:34Z VM 342 (200 GB) removed: with the ruled sizes a 200 GB disk gives only ~15 GiB for the data area (< 20 GiB) — rebuilt with 512 GB
name: buildb-onedisk
scsi0: nvme-scratch:342/vm-342-disk-0.raw,size=200G
purging VM 342 from related configurations..
scsi0: successfully created disk 'nvme-scratch:342/vm-342-disk-0.raw,size=512G'
update VM 342: -boot order=scsi0;ide2
boot: order=scsi0;ide2
net0: virtio=BC:24:11:B7:71:A8,bridge=vmbr0
scsi0: nvme-scratch:342/vm-342-disk-0.raw,size=512G
status: running
2026-10-16T07:17:30Z PVE install pressed on VM 342 (ISO 1.29.0, /dev/sda 512G, ext4, PVE default layout)
@@ -0,0 +1,3 @@
07:38:44 tester-1 cleared: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
07:38:44 demo-hp cleared: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
07:38:44 demo-felhom cleared: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
@@ -0,0 +1,147 @@
[INFO] auto-sized guest RAM: 4096 MiB (host 7939 MiB; clamp(host-4096, min 4096, max host-2048), ceiling host-1024)
[INFO] auto-sized guest cores: 3 (host 4 cores; host-1, min 2)
[INFO] felhom-host-install v1.33.0 — mode=appliance customer=Tester4 vmid=9201
[STEP] 1/8 pre-flight
[INFO] pve-manager/9.2.2/b9984c6d90a4bd80 (running kernel: 7.0.2-6-pve)
[INFO] node: buildb (auto)
[INFO] agent config: /etc/felhom-agent/agent.json
[INFO] agent: not installed yet — will be fetched + installed in step 5/8
[INFO] local-lvm free: ~383 GiB
[INFO] free RAM: ~6365 MiB
[INFO] existing guests on this host: 0 (pct+qm)
[INFO] archive storage 'local' present
[INFO] acl storage 'felhom-pbs' not present yet — expected: the PBS-DR tier creates it; the grant is pre-positioned deliberately
[INFO] dnsmasq: not present before Felhom — recorded; uninstall will remove it again if we install it
[INFO] hub reachable (https://hub.felhom.eu)
[OK] customer 'Tester4' exists + passphrase valid
[INFO] golden: none local — will fetch + verify from Gitea in step 7/8
[OK] pre-flight passed
[STEP] 2/8 Proxmox API token
[OK] token minted (secret captured, not logged)
[OK] scoped ACL applied (Base@/, Guest@/pool/felhom + /vms/990000..990009, Store@[local local-lvm felhom-pbs])
[SKIP] old broad role FelhomAgent already absent
[STEP] 3/8 compute volume grows
[INFO] auto-computed from a 383 GiB thin pool (16 §7: 50/100/130 GiB)
[INFO] grows: rootfs +0G (->32G), data +76G (->100G, ONE volume)
[STEP] 4/8 host enrollment (POST /host-enroll)
[OK] host REUSED (idempotent — existing credential)
[INFO] host_id: Tester4-68d1d8 (api_key captured, not logged)
[STEP] 4b/8 break-glass credential (root@pam console password → hub vault)
[OK] root@pam password set + vaulted to the hub (retrieve via the operator /admin path; never logged here)
[WARN] NOTE: the root@pam password just CHANGED — the old one now fails at the PVE web GUI (:8006).
[WARN] Retrieve the new one at hub → host page (vaulted recovery credential).
[STEP] 5/8 agent install (fetch + verify + install)
[INFO] disabled pve-enterprise.sources (Enabled: no)
[INFO] disabled ceph.sources (Enabled: no)
[INFO] added pve-no-subscription.sources (suite=trixie)
[OK] apt repos aligned to no-subscription (changed; apt-get update OK)
[WARN] no git credential in controller.yaml — fetching artifacts ANONYMOUSLY (they are world-readable; sha256 verification unchanged)
[INFO] manifest: agent v0.158.1 (sha 20264df64ef646a8…), golden v0.318.0
[INFO] fetching agent binary v0.158.1 from Gitea …
[OK] verified sha256 20264df64ef646a8… matches the hub manifest
[OK] installed /usr/local/bin/felhom-agent (felhom-agent 0.158.1)
[INFO] installing the 'sudo' package (required for the non-root agent model) …
[OK] sudo installed (Sudo version 1.9.16p2)
[INFO] installing the 'age' package (escrow ceremony identity-wrap dependency) …
[OK] age installed (1.2.1)
[OK] created service user felhom-agent
[OK] added felhom-agent to systemd-journal (unprivileged journal read for NAS verify)
[OK] verified sha256 c23c3d355fe70a6f… matches the hub manifest
os-apply: BUNDLE START agent=0.158.1 sha=c23c3d355fe70a6f authority=installer files=30 write=29 same=1 kept=0 skipped=0
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-mkfs-guarded (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-selfupdate-guarded (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-pbs-apply (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-backup-target-apply (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-vzdump-partial-clean (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-internal-place (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-crash-guard (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-priv-apply (new)
os-apply: BUNDLE WROTE /var/lib/vz/snippets/felhom-guest-hook.sh (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-shared-parent.sh (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-shared-parent.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard-check.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-crash-guard-check.timer (new)
os-apply: BUNDLE WROTE /etc/felhom/crash-guard.conf (new)
os-apply: BUNDLE WROTE /etc/grub.d/01_felhom_oneshot (new)
os-apply: BUNDLE WROTE /etc/grub.d/42_felhom_oneshot (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent-rollback.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-agent.service.d/felhom-agent-limits.conf (new)
os-apply: BUNDLE WROTE /usr/local/sbin/felhom-mgmt-watchdog (new)
os-apply: BUNDLE WROTE /etc/tmpfiles.d/felhom-privsep.conf (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-mgmt-watchdog.service (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-mgmt-watchdog.timer (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-sshd.service (new)
os-apply: BUNDLE WROTE /etc/felhom-oob.nft (new)
os-apply: BUNDLE WROTE /etc/systemd/system/felhom-oob-nft.service (new)
os-apply: BUNDLE WROTE /etc/sudoers.d/felhom-op (new)
os-apply: BUNDLE WROTE /etc/sudoers.d/felhom-agent (new)
os-apply: BUNDLE DONE agent=0.158.1 written=29 same=1 self-check=ok signers-created=False
[OK] installed every root-owned file from config bundle v0.158.1 (sha c23c3d355fe70a6f…; checked, previous copies kept)
[OK] OOB felhom-sshd instance + static belt from the bundle (agent renders config + fills sets once oob.enabled)
[STEP] 5b/8 internal data place (Belső tárhely)
[OK] internal place: created, 213 GiB (the agent mounts it at /mnt/belso)
[STEP] 6/8 agent config + service
[WARN] backup target: DEGRADED — no eligible second drive, so the whole-system backup stays on the SYSTEM drive.
[WARN] It protects against file corruption but NOT against a disk failure. Attach a second drive and assign it in the dashboard.
[INFO] creating island bridge vmbr9 (portless, 169.254.253.1/30)
[OK] vmbr9 up: 169.254.253.1/30
[INFO] R-50 island ON: local_api=169.254.253.1:8443 (vmbr9); guest net1=169.254.253.2/30; lan_resolver.host_ip=192.168.0.118
[INFO] node=buildb local_api=169.254.253.1:8443 tls_fp=77:14:E0:5D:21:B0…
[OK] wrote /etc/felhom-agent/agent.json (0600 felhom-agent)
[OK] agent --selftest (read-only) passed
[OK] felhom-agent service active (non-root felhom-agent reads the config OK)
[OK] wrote /etc/felhom/os-trust.json + operator-signers (felhom-op-1) — the root-owned slow-lane anchors
[STEP] 7/8 golden archive
[WARN] no git credential in controller.yaml — fetching artifacts ANONYMOUSLY (they are world-readable; sha256 verification unchanged)
[INFO] fetching golden v0.318.0 from Gitea → /var/lib/vz/dump/vzdump-lxc-9100-2026_10_16-09_45_36.tar.zst
[OK] verified sha256 18077b6a689a06f3… matches the hub manifest
[OK] golden imported + verified: local:backup/vzdump-lxc-9100-2026_10_16-09_45_36.tar.zst
[STEP] 8/8 provision guest 9201
[SKIP] pool felhom already exists
=== felhom-agent 0.158.1 selftest=provision (vmid=9201 customer=Tester4 hostname=tester4) ===
--- front half: bring-up (provision) local:backup/vzdump-lxc-9100-2026_10_16-09_45_36.tar.zst → vmid 9201 ---
time=2026-10-16T09:46:34.045+02:00 level=INFO msg="bring-up: pool membership re-asserted" vmid=9201 pool=felhom
[OK] front half: vmid 9201 up (boot+running) in 49s; MAC=BC:24:11:F9:27:7E
--- back half: mint per-guest token + populate bootstrap config mount ---
time=2026-10-16T09:46:39.169+02:00 level=INFO msg="provision: back-half complete" vmid=9201 mount=mp9 guest_path=/etc/felhom-bootstrap endpoint=169.254.253.1:8443
[OK] back half: bootstrap mount mp9 → /etc/felhom-bootstrap on vmid 9201 (host dir /var/lib/felhom-agent/guests/9201/bootstrap)
local-api endpoint 169.254.253.1:8443 · leaf fp ae0761dd4b8aa39a50ce43d350dfe1162a75b66f13494241cb681ee92ebf99e7 · token: minted (not printed)
=== selftest=provision OK — guest 9201 provisioned + bootstrap-mounted (KEPT) ===
next: reboot the guest → the golden's baked controller-bootstrap unit deploys the controller,
which PULLS its controller.yaml from the hub (retrieval passphrase) and merges in this local_api.
[OK] provision completed
[INFO] rebooting guest 9201 so the baked controller-bootstrap unit picks up the mount
[STEP] verify
[OK] pct status: running
[OK] onboot: 1
mp0: local-lvm:vm-9201-disk-1,mp=/var/lib/felhom,backup=1,size=100G
mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives
rootfs: local-lvm:vm-9201-disk-0,size=32G
[OK] pool: guest 9201 is a member of felhom
[OK] acl: FelhomAgentBase@/ present (user+token)
[OK] acl: FelhomAgentGuest@/pool/felhom present (user+token)
[OK] acl: FelhomAgentStore@/storage/local present (user+token)
[OK] acl: FelhomAgentStore@/storage/local-lvm present (user+token)
[OK] acl: FelhomAgentStore@/storage/felhom-pbs present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990000 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990001 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990002 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990003 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990004 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990005 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990006 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990007 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990008 present (user+token)
[OK] acl: FelhomAgentGuest@/vms/990009 present (user+token)
[OK] authz signers: 2 (operator-signed self-update armed)
[OK] controller: Up 20 seconds (healthy) (after ~0s)
[INFO] controller image: gitea.dooplex.hu/admin/felhom-controller:0.318.0
[WARN] cloudflared not visible yet
[INFO] (confirm in the hub UI that host Tester4-68d1d8 reports guest 9201)
[OK] Day-0 provision SUCCESS — vmid=9201 host_id=Tester4-68d1d8 customer=Tester4 golden=local:backup/vzdump-lxc-9100-2026_10_16-09_45_36.tar.zst
[INFO] root@pam was rotated + vaulted at step 4b — retrieve at hub → host page (the old GUI password no longer works).
@@ -0,0 +1,10 @@
jellyfin deploy 07:49:38 — values: DOMAIN, SUBDOMAIN only (no HDD_PATH)
{"ok":true,"message":"Telepítés elindítva – az állapot a kártyán követhető"}
HTTP 202
## 07:50:17 jellyfin: Up 17 seconds (healthy)
/var/lib/docker/volumes/jellyfin_jellyfin_cache/_data -> /cache
/var/lib/docker/volumes/jellyfin_jellyfin_config/_data -> /config
/mnt/felhom-drives/belso/userdata/media -> /media
/mnt/felhom-drives/belso/userdata/media -> /mnt/felhom-drives/belso/userdata/media
userdata
@@ -0,0 +1,13 @@
## whole-box copy, phase=done at 07:51:21
archive: vzdump-lxc-9201-2026_10_16-09_50_29.tar.zst 1225 MB
2026-10-16 09:50:29 INFO: including mount point rootfs ('/') in backup
2026-10-16 09:50:29 INFO: including mount point mp0 ('/var/lib/felhom') in backup
2026-10-16 09:50:29 INFO: excluding bind mount point mp8 ('/mnt/felhom-drives') from backup (not a volume)
2026-10-16 09:50:29 INFO: excluding bind mount point mp9 ('/etc/felhom-bootstrap') from backup (not a volume)
2026-10-16 09:51:18 INFO: archive file size: 1.20GB
marker entries in the archive: 0
belso entries in the archive: 2
control — mp0 entries (must be > 0): 29512
drwxr-xr-x 0/0 0 2026-10-16 09:49 ./var/lib/felhom/docker/overlay2/05cdeccc9f31a5095cd7da730f524ea4697bf80120ea3a15a12e09f406d77bf6/diff/mnt/felhom-drives/belso/
drwxr-xr-x 0/0 0 2026-10-16 09:49 ./var/lib/felhom/docker/overlay2/05cdeccc9f31a5095cd7da730f524ea4697bf80120ea3a15a12e09f406d77bf6/diff/mnt/felhom-drives/belso/userdata/
drwxr-xr-x 0/0 0 2026-10-16 09:49 ./var/lib/felhom/docker/overlay2/05cdeccc9f31a5095cd7da730f524ea4697bf80120ea3a15a12e09f406d77bf6/diff/mnt/felhom-drives/belso/userdata/media/
@@ -0,0 +1,9 @@
## 07:52:06 same-disk: the internal place vs sys_drive (agent call)
different=False proven=True reason=same_disk | a.kind=internal a.identity=unknown ['serial:scsi:QEMU_HARDDISK:drive-scsi0'] | b.kind=system
## GET /disks row of the internal place
{'name': 'belso', 'mount_path': '/mnt/belso', 'guest_path': '/mnt/felhom-drives/belso', 'kind': 'internal', 'role': 'user-data', 'identity': 'unknown', 'identity_reason': 'vm', 'bound_under_parent': True}
## R-1082: the attach list (dashboard /api/disks/candidates)
## R-1082: the attach list (dashboard /api/disks/candidates, controller 0.318.0)
{"data":{"vmid":9201,"initialize":[],"attach":[]},"ok":true}
## precondition: the controller's own mount table holds /mnt/sys_drive (what 0.314.0 offered in the OMV run, audits/omv-vm-2026-10-15)
/dev/mapper/pve-vm--9201--disk--1 /mnt/sys_drive ext4 rw,relatime,stripe=16 0 0
@@ -0,0 +1,7 @@
## 2026-10-16T07:52:36Z add a 16G scratch disk to VM 342
scsi1: successfully created disk 'nvme-scratch:342/vm-342-disk-1.raw,backup=0,size=16G'
blkid rc=2 (2 = nothing on it)
NAME SIZE FSTYPE LABEL MOUNTPOINT
sdb 16G
|-sdb1 8G
`-sdb2 8G ext4 scratchp2
@@ -0,0 +1,25 @@
## 07:52:51 candidates before (dashboard /api/disks/candidates)
initialize /dev/sdb whole_disk_in_use=False
/dev/sdb1 empty fs= formattable=True mountable=False
/dev/sdb2 filesystem fs=ext4 formattable=True mountable=True
attach /dev/sdb whole_disk_in_use=False
/dev/sdb1 empty fs= formattable=True mountable=False
/dev/sdb2 filesystem fs=ext4 formattable=True mountable=True
## 07:52:51 format part 1 (the wizard POST /api/storage/init)
{"data":{"phase":"formatting","started":true},"ok":true}
HTTP 200
## 07:52:58 init status
{"data":{"device":"/dev/sdb1","durable_id":"byid:scsi-0QEMU_QEMU_HARDDISK_drive-scsi1-part1","error":"","opsign":"","phase":"needs_confirmation","reason":"device has a partition table","started_at":"2026-10-16T07:52:51.782009734Z","updated_at":"2026-10-16T07:52:52.051700007Z","where":"/mnt/resz1"},"ok":true}
## candidates after part 1 is a drive
initialize /dev/sdb whole_disk_in_use=False
/dev/sdb1 empty fs= formattable=True mountable=False
/dev/sdb2 filesystem fs=ext4 formattable=True mountable=True
attach /dev/sdb whole_disk_in_use=False
/dev/sdb1 empty fs= formattable=True mountable=False
/dev/sdb2 filesystem fs=ext4 formattable=True mountable=True
## 07:53:13 confirmed format of part 1 (the wizard re-posts with confirmed + the durable id it was shown)
{"data":{"phase":"formatting","started":true},"ok":true}
HTTP 200
{"data":{"device":"/dev/sdb1","durable_id":"","error":"","opsign":"","phase":"done","reason":"","started_at":"2026-10-16T07:53:13.503303783Z","updated_at":"2026-10-16T07:53:15.360607509Z","where":"/mnt/felhom-drives/resz1"},"ok":true}
@@ -0,0 +1,7 @@
## 07:53:25 candidates with part 1 in use
initialize: []
attach /dev/sdb whole_disk_in_use=True [('/dev/sdb2', 'filesystem', 'formattable=False')]
## 07:53:25 use part 2 (the wizard POST /api/storage/attach)
{"data":{"registered":true,"where":"/mnt/felhom-drives/resz2"},"ok":true}
HTTP 200
@@ -0,0 +1,9 @@
## 07:53:38 same-disk: part 1 vs part 2 (agent call)
different=False proven=True reason=same_disk | a.kind=partition b.kind=partition | ['serial:scsi:QEMU_HARDDISK:drive-scsi1']
## 07:53:39 the server refuses formatting the whole disk under a registered part (POST /api/storage/init device=/dev/sdb)
{"error":"Ezen a lemezen egy használatban lévő meghajtó van, ezért a teljes lemez nem formázható. Csak a szabad részét formázhatod.","ok":false}
HTTP 409
## 07:53:39 /backups — the whole-box backup offer
A Felhom nem tudja ellenőrizni, hogy a második meghajtó valóban másik lemez-e (virtuális gépben és egyes USB-házakban ez nem látszik), ezért nem számolja második másolatnak.
Ezt a meghajtót kijelölheted a rendszermentés helyéül. A Felhom nem tudja ellenőrizni, hogy ez a meghajtó valóban másik lemez-e (virtuális gépben és egyes USB-házakban ez nem látszik), ezért nem ígérhetjük, hogy egy lemezhiba után is vissza tudod állítani a rendszert.
@@ -0,0 +1,28 @@
## agent (journal)
Oct 16 09:45:34 buildb felhom-agent[6589]: time=2026-10-16T09:45:34.644+02:00 level=INFO msg="internal place: mounted on the host" where=/mnt/belso fs_uuid=0347cd44-d4d9-4c02-94fe-3af6789e3368 gib=213 duration_ms=954
Oct 16 09:45:35 buildb felhom-agent[6589]: time=2026-10-16T09:45:35.837+02:00 level=INFO msg="wgtunnel: registered with hub" pubkey="NkTpVgvdFWq0cXODnqTBBDGvz2gZSd9wURYN4GnTuWk=" assigned_ip=10.77.0.6/32 existed=false generation=4 sync=ok
Oct 16 09:45:35 buildb felhom-agent[6589]: time=2026-10-16T09:45:35.840+02:00 level=INFO msg="wgtunnel: registered with hub" pubkey="NkTpVgvdFWq0cXODnqTBBDGvz2gZSd9wURYN4GnTuWk=" assigned_ip=10.77.0.6/32 existed=true generation=4 sync=unchanged
Oct 16 09:46:05 buildb felhom-agent[6589]: time=2026-10-16T09:46:05.422+02:00 level=INFO msg="wgtunnel: tunnel conf applied" endpoint=167.233.158.164:443 dns_name=ep0.felhom.eu assigned_ip=10.77.0.6/32 action=enable
Oct 16 09:46:38 buildb felhom-agent[6589]: time=2026-10-16T09:46:38.331+02:00 level=INFO msg="guest-attach: drive bound under shared parent (normalized to one bind, live)" vmid=9201 where=/mnt/belso stable=/mnt/felhom-drives/belso prior_binds=0
Oct 16 09:46:38 buildb felhom-agent[6589]: time=2026-10-16T09:46:38.786+02:00 level=INFO msg="local-api: guest-attach bound under shared parent" vmid=9201 where=/mnt/belso guest_path=/mnt/felhom-drives/belso
Oct 16 09:46:55 buildb felhom-agent[6589]: time=2026-10-16T09:46:55.526+02:00 level=INFO msg="guest-attach: drive bound under shared parent (normalized to one bind, live)" vmid=9201 where=/mnt/belso stable=/mnt/felhom-drives/belso prior_binds=1
Oct 16 09:47:01 buildb felhom-agent[6589]: time=2026-10-16T09:47:01.029+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/belso different=false proven=true reason=same_disk
Oct 16 09:49:23 buildb felhom-agent[6589]: time=2026-10-16T09:49:23.794+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/belso different=false proven=true reason=same_disk
Oct 16 09:49:41 buildb felhom-agent[6589]: time=2026-10-16T09:49:41.555+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/belso different=false proven=true reason=same_disk
Oct 16 09:52:07 buildb felhom-agent[6589]: time=2026-10-16T09:52:07.593+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/felhom-drives/belso/userdata b=/mnt/sys_drive different=false proven=true reason=same_disk
Oct 16 09:53:14 buildb felhom-agent[6589]: time=2026-10-16T09:53:14.891+02:00 level=INFO msg="local-api: disk assigned (host mount ensured)" vmid=9201 where=/mnt/resz1
Oct 16 09:53:14 buildb felhom-agent[6589]: time=2026-10-16T09:53:14.962+02:00 level=INFO msg="guest-attach: drive bound under shared parent (normalized to one bind, live)" vmid=9201 where=/mnt/resz1 stable=/mnt/felhom-drives/resz1 prior_binds=0
Oct 16 09:53:15 buildb felhom-agent[6589]: time=2026-10-16T09:53:15.333+02:00 level=INFO msg="local-api: guest-attach bound under shared parent" vmid=9201 where=/mnt/resz1 guest_path=/mnt/felhom-drives/resz1
Oct 16 09:53:27 buildb felhom-agent[6589]: time=2026-10-16T09:53:27.615+02:00 level=INFO msg="local-api: disk assigned (host mount ensured)" vmid=9201 where=/mnt/resz2
Oct 16 09:53:27 buildb felhom-agent[6589]: time=2026-10-16T09:53:27.706+02:00 level=INFO msg="guest-attach: drive bound under shared parent (normalized to one bind, live)" vmid=9201 where=/mnt/resz2 stable=/mnt/felhom-drives/resz2 prior_binds=0
Oct 16 09:53:28 buildb felhom-agent[6589]: time=2026-10-16T09:53:28.088+02:00 level=INFO msg="local-api: guest-attach bound under shared parent" vmid=9201 where=/mnt/resz2 guest_path=/mnt/felhom-drives/resz2
Oct 16 09:53:39 buildb felhom-agent[6589]: time=2026-10-16T09:53:39.199+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/felhom-drives/resz1 b=/mnt/felhom-drives/resz2 different=false proven=true reason=same_disk
Oct 16 09:53:39 buildb felhom-agent[6589]: time=2026-10-16T09:53:39.796+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/belso different=false proven=true reason=same_disk
Oct 16 09:53:40 buildb felhom-agent[6589]: time=2026-10-16T09:53:40.005+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/resz1 different=true proven=false reason=vm
Oct 16 09:53:40 buildb felhom-agent[6589]: time=2026-10-16T09:53:40.199+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/sys_drive b=/mnt/felhom-drives/resz2 different=true proven=false reason=vm
Oct 16 09:53:42 buildb felhom-agent[6589]: time=2026-10-16T09:53:42.263+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/felhom-drives/resz1 b=/mnt/sys_drive different=true proven=false reason=vm
Oct 16 09:53:42 buildb felhom-agent[6589]: time=2026-10-16T09:53:42.465+02:00 level=INFO msg="local-api: same-disk" vmid=9201 a=/mnt/felhom-drives/resz2 b=/mnt/sys_drive different=true proven=false reason=vm
## controller
2026/10/16 07:46:56 [INFO] [internal-place] registered /mnt/felhom-drives/belso (default=true) — media and data-folder apps can use it
2026/10/16 07:49:39 [INFO] [stacks] Deploy jellyfin: a media app — HDD_PATH (its own config) on the default drive "/mnt/felhom-drives/belso"; the media binds cover every present drive
2026/10/16 07:49:39 [INFO] [stacks] Deploying stack jellyfin with 3 env vars: [SUBDOMAIN, HDD_PATH, DOMAIN]
@@ -0,0 +1,14 @@
## 07:54:28 household power-off (POST /api/server/poweroff)
{"data":{"code":"confirm_required"},"error":"Írd be a megerősítő szót: KIKAPCSOLÁS.","ok":false}
HTTP 400
VM 342: running at 07:58:35
VM 342 is running - destroy failed
status: running
## 07:58:46 household power-off with the confirmation word
{"data":{"powering_off":true},"ok":true}
HTTP 202
VM 342: stopped at 07:59:18
purging VM 342 from related configurations..
Configuration file 'nodes/demo-hp/qemu-server/342.conf' does not exist
@@ -0,0 +1,7 @@
## 07:59:28 Tester 1 scratch disk removal
sdg not mounted
ls: cannot access '/tmp/agentq.sh': No such file or directory
scsi6: nvme-scratch:341/vm-341-disk-7.raw,backup=0,size=16G
update VM 341: -delete scsi6 -force 1
0
0
@@ -0,0 +1,8 @@
## 2026-10-16 R-1082: which boxes had attached /mnt/sys_drive — the hub's latest report per customer (reports.report_json
## "storage", a list of {mount, label, …}; hub database copied with its WAL)
demo-hp sys_drive mentions 0 | felhom-drives mentions 1 (control: its registered drive is listed)
Tester-2 sys_drive mentions 0 | felhom-drives mentions 0
tester-1 sys_drive mentions 0 | felhom-drives mentions 5
demo-felhom sys_drive mentions 0 | felhom-drives mentions 1
Tester4 sys_drive mentions 0 | felhom-drives mentions 1 (the data area, /mnt/felhom-drives/belso)
Result: no box has /mnt/sys_drive registered; nothing to keep working, nothing to detach.
+9
View File
@@ -35,6 +35,15 @@
---
## 2026-10-16 — storage identity build B (agent v0.158.1, controller v0.318.0, installer 1.33.0, golden 0.318.0)
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1068** | **On a one-disk box a media app's Install was refused with a raw field error.** (P3) | CLOSED 2026-10-16 — both halves built: a one-disk box now gets its own data area (installer 1.33.0 step 5b; an old box by the signed `add_internal_place`), registered as the default drive with no press — **live:** Jellyfin installed with DOMAIN + SUBDOMAIN only, 202, healthy, `/media` on the data area (the OMV run: 400 „HDD_PATH … kötelező"); a box with no drive at all gets the D4 sentence (controller 0.317.0, unit-proven). | `audits/storage-identity-buildB-2026-10-15/README.md` `08-jellyfin-deploy.txt` |
| **R-1079** | **Build „storage that knows its physical disks"** (`architecture/16-storage-physical-identity.md`). (P2) | CLOSED 2026-10-16 — build A (2026-10-15) + build B (2026-10-16) shipped; `16` §13 lists what was built and where it differs. Live: a fresh 512 GB one-disk install → data volume 100 GiB, data area 213 GiB; partitions formatted and used; the honest backup offer. Follow-ups filed: R-1083, R-1093, R-1094. | `audits/storage-identity-buildB-2026-10-15/README.md`; `audits/storage-identity-buildA-2026-10-15/README.md` |
| **R-1082** | **The drives page offered `/mnt/sys_drive` for attaching.** (P3) | CLOSED 2026-10-16 — operator ruling „hide it now"; **controller v0.318.0** excludes the system data volume by DEVICE (any second bind of it too); a box that registered it keeps it. Red-proved; **live:** the attach list empty while the controller's own mount table holds `/mnt/sys_drive`. Boxes that had attached it (the hub's latest report of all five boxes, 2026-10-16): **none** — nothing to keep working, nothing to detach (`live/18-r1082-hub-boxes.txt`). | `audits/storage-identity-buildB-2026-10-15/README.md` `10-same-disk-and-r1082.txt` |
| **R-1084** | **The whole-box backup offer promised disk-failure protection without checking the disk.** (P3) | CLOSED 2026-10-16 — **controller v0.318.0**: the offer asks the agent; proven different → the promise; unproven (a VM) → offered without it („…ezért nem ígérhetjük…"); same disk → not offered; older agent → today's offer. Red-proved; **live** on a VM box. | `audits/storage-identity-buildB-2026-10-15/README.md` `14-samedisk-refusal-offer.txt` |
## 2026-10-15 (night) — storage identity build A (agent v0.157.0, controller v0.317.0, hub v0.154.0)
| Row | What | Closed | Evidence |
+4 -6
View File
@@ -159,7 +159,7 @@ stopping line that lies.
| **R-683** | App updates | P3 | **[P3-LOW] Watch: after a power cut during an update's health check, the hold named an HOUR-OLD second-drive copy, not the one the update's own backup should have just made.** 2026-09-24 chaos round 3 (nextcloud, `backup_max_age: 1m`): no `backing-up` phase was seen and the hold named Tier 2 at 13:04 for an update pressed at 14:04; the pre-cut controller log was lost with the container (the runner now saves it at arm time — R-320). Round 11, the same action without a power cut, named a fresh 14:34 copy and logged the Tier-2 copy. The sentence was TRUE (it named the copy it offered); the question is why the update did not back up first. Not reproduced; watch the next power-cut drill. `audits/night-2026-09-24/E/round-03*.json`, `E/round-11-controller-pre.log` | **OPEN — P3; owner: CC (watch)** | — | — | CC |
| **R-785** | App updates | P3 | **[P3-LOW] SparkyFitness is pinned 11 releases and a major behind upstream (v0.17.3; upstream v1.7.3, v1.6.0 dated 2026-07-24).** READ 2026-10-01 (`audits/visitors-2026-10-01/C/bench/C1-previous-tag.txt`). **Needs:** an update walk 0.17 → 1.x through the ladder (bench + box), after R-784 is decided. | **OPEN — rank P3-LOW; owner: CC (after R-784)** | — | — | CC |
## Backup & restore — 32 rows (P2 3, P3 18, P4 11)
## Backup & restore — 31 rows (P2 3, P3 17, P4 11)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -183,7 +183,6 @@ stopping line that lies.
| **R-921** | Backup & restore | P3 | **When the off-site tier answers BUSY, the household still gets a short stop of every app — with no copy.** MEASURED 2026-10-08 night on demo-hp (read-back 2026-10-09, `audits/dooplex-survival-2026-10-09/partE/R-518.txt`): local tier 20:20–20:25Z (own stop, < 2 min), then at 20:26:14Z the agent refused the off-site request (`backup refused — a heavy operation is already in flight`, `busy=backup:local` — the night OS step ran 20:27–20:29Z), yet the controller's metrics show all 15 app containers down at 20:26:19Z and back at 20:27:19Z; the retry ran the off-site tier at 20:34Z with its own short stop. So a two-tier night costs THREE stops, one of them for nothing. This was R-518's noted "unmeasured" risk. Fix shape: ask the agent whether it is free (or reserve the slot) BEFORE quiescing, and resume at once on BUSY. | **NARROWED 2026-10-09 — built; RELEASED 2026-10-10 (controller 0.305.0 on demo-hp, demo-felhom, Tester 1):** before stopping apps the controller asks the agent which tiers have a job in flight (`GET /backup/status`); another tier's job in flight → no stop, the tier stays due (red-proved: 3 apps stopped for a refused tier before); a refusal after the stop still resumes the apps at once (pinned). Also fixes a stop after a long upload or a restart mid-upload. **LEFT: the measured case itself** — the agent's host-wide busy lock (the night OS step, restore test, fstrim) is served on NO endpoint, so the controller cannot see it before stopping; needs one agent field (e.g. `busy` on `GET /backup/status`) + a controller pre-check behind a feature probe. `07` §6.4. | an agent change | Build the agent `busy` field + its controller check; read back a two-tier night | CC |
| **R-933** | Backup & restore | P3 | **Mirror the images of every catalog app we support into our own registry, before scale.** Operator, 2026-10-12 (S8, `09` §3 decision 203): *„Will need to mirror the apps we support later, just to be sure."* Today a restore re-pulls each image from its maker; a version the maker deleted cannot start (the accepted limit, `07` §6.6, R-698 closed). Size it first: the supported catalog's images (every ladder version a box can hold) × their size; then where the copy lives (the DooPlex registry is Tier 2, precious — the copy must not make DooPlex a single point of failure for every household's restore) and how a restore falls back to it. | **OPEN** — NEW 2026-10-12 | the size measurement | Measure the total size of the supported catalog's images; then a one-page design (where, how a restore falls back, cost) | CC (measure + design), operator (the go) |
| **R-1019** | Backup & restore | P3 | **The live download-client templates put downloads into two backup tiers nobody chose.** MEASURED 2026-10-14 with the controller's own classifier over the live templates (`audits/media-all-drives-2026-10-14/``A-drives/A6-3-capture-sets.txt`): Transmission's `${USERDATA_PATH}/media/transmission/watch` bind is not listed in its `backup:` block, so it is classed MANDATORY and goes into the OFF-SITE copy (a writable unlisted bind defaults to mandatory); and qBittorrent's `media/torrents` and Transmission's `media/transmission` are `optional`, so on a box with a second drive every download is mirrored there nightly (Tier 2 = mandatory + optional) — it can fill the backup drive with films. | **NARROWED** — filed 2026-10-14; the `watch` half FIXED 2026-10-14 (declared optional, app-catalog `bc54c01`); open: the `optional` download folders still get a nightly Tier-2 mirror on a box with a second drive | — | Declare the media binds `excluded` (the watch folder too) in the live-fix task; `15` §7 states the rule for the per-drive bind | CC |
| **R-1084** | Backup & restore | P3 | **The whole-box backup offer promises protection against a disk failure without asking whether the drive is a different disk.** SEEN 2026-10-15 on Tester 1 (a box inside a VM, every place `unknown/vm`), controller 0.317.0, `/backups`: „Ezt a meghajtót kijelölheted a rendszermentés helyéül — így egy lemezhiba után is vissza tudod állítani a rendszert.” next to the new „A Felhom nem tudja ellenőrizni, hogy a második meghajtó valóban másik lemez-e…” — the page says both. Build A (`architecture/16-storage-physical-identity.md` §4) covered Tier-2 and the one-copy notice only; the whole-guest target offer (C/internal/web/backup_target_offer.go) still trusts any second drive. Evidence: `audits/storage-identity-buildA-2026-10-15/tester1-backup-pages-after.txt`. | OPEN | — | Make the offer ask the agent (`POST /disks/same-disk`, drive vs `/var/lib/felhom`): proven different → today's sentence; unknown → an honest one; same → no offer. A controller change with a test. | CC |
| **R-91** | Backup & restore | P4 | Old 13 GB datastore copy at `/srv/pbs-felhom` on ep0's root disk **Checked from source 2026-10-05 (burn-down round 2):** Gate is long past (row waits on demo-felhom's first post-migration PBS backup, migration 2026-07-27). Last positive record of the copy: audits/CAMPAIGN-9-restore-proof-2026-07-28.md:759 'ep0 : /srv/pbs-felhom rollback copy intact (13G)'; CONTEXT.md:3666 still says it is 13 G of dead weight awaiting R-91. No later record of deletion found (grep srv/pbs-felhom across felhom.eu). Deleting is on ep0 (protected) and needs an operator word. | WATCHING | demo-felhom's first **post-migration** PBS backup | Delete once it lands; fix `CONTEXT.md:1018` same commit | CC |
| **R-164** | Backup & restore | P4 | **C2's chain: the DB volume tar cannot be dropped until a SOUND dump predicate exists.** The unit carries both a volume tar and a SQL dump; the restore uses **both** — the dump is authoritative and replayed *after* the tar so it WINS (F17), with only the DB service up (R-47) — `internal/backup/restore_unit.go:262-266`. Dropping the DB container's tar would halve DB-app units **and** close the R-127(b) initdb-skip password trap (restored PGDATA ⇒ `POSTGRES_PASSWORD` ignored). | **BLOCKED** — on the predicate | a dump-validity predicate that is not `accounts has rows` | **The obvious gate is DEAD, measured:** `ValidateDump` warns when the `accounts` table is empty, and that warning was **correct** — the live DB genuinely had 0 accounts, and seeding one stopped the warning and put the row in the dump. But **a fresh appliance legitimately has zero accounts**, so promoting that predicate to a gate would **block every new customer's first backup**. Order: (1) a sound predicate — dump vs **live** per-table counts, not an absolute expectation; (2) warn→gate; (3) tar-drop. **Until (1), the tar is load-bearing** — not because dumps are bad, but because nothing can yet prove one is good. Pairs with **R-127** | CC |
| **R-213** | Backup & restore | P4 | **Putting files back in place — the half the recovery screen deliberately does not do.** The screen (R-193, controller v0.200.0) unlocks the repository and LISTS what is in it; restoring is per-app and lives in the backups area, and the operator ruled the two separate on 2026-08-05: *a screen that unlocks and then offers to overwrite is two decisions wearing one button*. What is missing is the step after the listing — a customer who can now SEE their files still has to work out, per app, which restore to choose. **The operator named its requirement: a live-versus-backup comparison** — the customer must be able to see what would change before anything is overwritten | **DEFERRED** — operator ruling 2026-10-12 (S18, `09` §3 decision 213): designed after the first customers. Stays P4; re-dated: look again when the first paying household has run 30 days. **OPEN — not started, deliberately** | the comparison design (nothing exists for it yet) | After the first customers | Operator + CC |
@@ -196,15 +195,13 @@ stopping line that lies.
| **R-32** | Backup & restore | P4 | **[P2-HIGH] RESET must purge the customer base dir; the orphan card must stay honest; unattributed bytes must be visible.** The rehearsal's S7 said in advance that an orphan card would BE a finding — and one appeared (16:58:14). Cause: RESET's `"hetzner":"ok"` leg destroys the sub-account, but **a Hetzner sub-account is an access-control object, not a data object** — its directory survives, so re-enabling offsite recreated an account over the previous lifecycle's ciphertext, encrypted under a key that same RESET had destroyed. **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-21, size M, roadmap state `idea`.** Moved verbatim; nothing added or reinterpreted. The roadmap keeps its copy as history, marked moved. | **OPEN — migrated from ROADMAP 2026-08-22, rank unchanged** **2026-10-06 night: design written** (`audits/night-burndown-2026-10-06/design-R-32.md`) — pick A: purge the repo + set-aside copies through the sub-account's own password login BEFORE deleting it (no main-account credential); the „repo data destroyed" comment at `hub/internal/offsite/offsite.go:276-280` is false for the shared tier; part (3) waits on a read-only measurement (`du` on port 23). Operator: agree to route A. **2026-10-07 07:58: `09` §3 decision 167 — option A yes, and delete the old RESET leftovers once.** **2026-10-07 (Part D step 2, read only):** the hub shows bx11 holding 3.3 GB of data while the four live customers account for ~0.6 GB → **~2.7 GB belongs to no live customer**; no live customer shows a set-aside folder, so the leftovers are in homes whose sub-account is gone, reachable only by the pool box's MAIN account. **The one-time clean-up is STOPPED:** no main-account login for bx11 is held on DooPlex (the API token in the credentials file sees no box). Needs: the main-account login, or the operator lists and deletes in the Hetzner console (`audits/day-2026-10-07/D/D2-listing-readonly.txt`). **2026-10-07: option A DELIVERED (hub 0.142.0):** RESET purges the repo and every `.orphaned-*` through the sub-account's own login before deleting it, and keeps the sub-account if the purge fails (red-proved, `audits/day-2026-10-07/D/`). Not yet seen live (no scratch RESET today). **The one-time clean-up of the ~2.7 GB of leftovers is STOPPED:** it needs the pool box's main-account login (see above). **2026-10-07 14:43 — `09` §3 decision 173: leave the ~2.7 GB of old leftovers for now; re-ranked P2 → P4.** Option A itself is delivered (hub 0.142.0). What stays open is ONLY the leftovers: they were computed (box total minus live customers), never listed, and deleting them needs the pool box's MAIN login, which reaches every customer's data. **Blocker: a safe main-account path.** | — | **Ruling from the run (three parts, deliberately separate):** (1) because RESET destroys custody, the ciphertext it leaves behind is unrecoverable **BY DESIGN** → RESET gains a **main-account purge of the customer base dir** (the existing operator ack already covers it); (2) the **move-aside guard STAYS** for reinstall-*without*-RESET — there custody survives and the card's "history recoverable" promise is true (R-26 depends on exactly that); (3) the operator **Restic tab shows per-customer directory bytes vs attributed snapshot bytes**, so dead data cannot hide. Measured on the pool box that night: **49 M attributed** (2 snapshots, 48.717 MiB) against **1.4 G + 3.0 M unattributed** across TWO `.orphaned-*` dirs. Evidence `restic-and-pool.txt` | CC |
| **R-1087** | Backup & restore | P4 | **Whole-box copies left on the OLD local target are never pruned after the target moves.** MEASURED 2026-10-15 (storage-identity build B space check): the N100's host root (`local`) holds three copies from 2026-07-28 (5.5–5.7 GB each, ≈ 16.8 GB); its agent's `local_backup_target` is now `felhom-backup` (the USB disk, retention 3), and retention prunes only the current target. On a small-root box such leftovers would starve the local copies (R-548). Evidence: `audits/storage-identity-buildB-2026-10-15/space/felhom-pve-9201.txt`. | OPEN | — | Decide whether the agent prunes its own `vzdump-lxc-<vmid>-*` files on a former target (after a newer copy exists on the new one), or the target move asks; until then, the N100's three July files can be removed by hand with the operator's word. | CC |
## Storage & devices — 13 rows (P2 1, P3 7, P4 5)
## Storage & devices — 12 rows (P3 6, P4 6)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
| **R-1079** | Storage & devices | P2 | **Build „storage that knows its physical disks” (`architecture/16-storage-physical-identity.md`, DRAFT): every place carries `physical_disks` + `identity`, computed by the agent; Tier-2 counts only on proven different disks; a „Belső tárhely” internal place on one-disk boxes; partitions as places.** Operator ruling 2026-10-15 asked for it. Spike proved the agent-side walk on real devices (one disk, two disks, two partitions, USB pass-through) and showed a VM can only be `unknown` (`audits/storage-identity-2026-10-15/README.md`). Closes, when built: R-1068, R-1074, R-1075; touches R-1069, R-1071, R-1073, R-548. **-- 2026-10-15:** the operator ruled D1–D6 as recommended, and asked to check the D3 space. **Build A SHIPPED** (agent v0.157.0, controller v0.317.0 with MinAgent 0.157.0, hub v0.154.0): identity per place, the same-disk call, Tier-2 + the one-copy notice ask the agent, the D4 sentence, the hub shows the fields — `16` §13, `audits/storage-identity-buildA-2026-10-15/`. Closed R-1072, R-1074, R-1075; narrowed R-1068. | NARROWED 2026-10-15 — build A shipped; build B (internal place, partitions, installer) not started. | A build B task file (installer + agent partitions); the operator's vouch of agent 0.157.0 for the global floor. | Build B per `16` §5–§7 (check the D3 space on real pool sizes first). | Operator |
| **R-1068** | Storage & devices | P3 | **On a box with one disk, a media app's (or any data-folder app's) Install is refused with a raw field error, and the way through is nowhere near the button.** MEASURED 2026-10-15 on the OMV-VM copy of the pilot's box (one passed NVMe, controller 0.314.0): `POST /api/stacks/jellyfin/deploy` with DOMAIN+SUBDOMAIN → **400 „a(z) "Médiatár útvonal" (HDD_PATH) mező kitöltése kötelező”** — the env var name reaches the household. R-1031's default-drive rule finds no drive because none is registered. The box DOES offer one: `/api/disks/candidates` listed `/mnt/sys_drive` (the installer's data volume, `already_mounted`), and `handleStorageRegisterMounted` (R-280) registers it — **seen in the candidate list and the code, NOT pressed** (the box was removed). With a second disk attached through „Csatlakoztatás”, the same Install succeeded in 30 s. Evidence: `audits/omv-vm-2026-10-15/README.md` „What happened”, `B4-deploy.txt`. **-- 2026-10-15 (storage-identity spike):** the `sys_drive` route is REJECTED by design — `sys_drive` is a folder of `mp0` (`backup=1`), so films there would ride in every whole-guest copy; the proposal is an internal data place outside vzdump, `architecture/16-storage-physical-identity.md` §5; folded into R-1079. **-- 2026-10-15 (build A, controller v0.317.0):** the raw-error half is DONE — on a box with no drive the install answers „Ehhez az alkalmazáshoz egy adattároló kell. A Meghajtók oldalon adhatsz hozzá egyet.” with a link to the Meghajtók page (HTTP 400, code `needs_drive`), never the field name; unit-proven and red-proofed, **not seen live** (no one-disk box at hand). The internal-place half waits for build B (`16` §5, §13). | NARROWED 2026-10-15 — the sentence is built (v0.317.0); the internal place is not. | Build B of R-1079 (the installer's internal place). | Build B; then see the sentence live once on a one-disk box (a scratch VM with one disk). | CC |
| **R-1069** | Storage & devices | P3 | **A disk passed into a Felhom VM on a virtio bus cannot be formatted, and the household reads the agent's raw English refusal.** MEASURED 2026-10-15 (OMV KVM plugin, „Add host disk”, default Bus `virtio`): `POST /api/disks/format` → 500 „agentapi: format: HTTP 409: format refused: device has no durable id to bind the format to (path-only formats are not permitted)”. A virtio-blk disk without a serial has no `/dev/disk/by-id` link inside the guest, so the anti-retarget guard refuses — correct and safe. The same disk on Bus `sata` got `byid:ata-QEMU_HARDDISK_QM00003` and formatted. Affects every hypervisor that passes disks as virtio without a serial (OMV KVM, Proxmox without `serial=`). The pilot guide says `sata`. Evidence: `audits/omv-vm-2026-10-15/B4c-second-disk-format.txt`. | OPEN | — | Translate the refusal for the household („Ezt a lemezt a Felhom nem tudja biztonságosan azonosítani — a virtuális gépben állítsd a lemez buszát SATA-ra”); optionally fall back to a filesystem-UUID binding after a blank-format. | CC |
| **R-1073** | Storage & devices | P3 | **Felhom in a virtual machine sees no disk health: SMART is UNKNOWN for its own system disk and any passed disk.** MEASURED 2026-10-15 on the OMV-VM copy: every storage target reported `smart.health = UNKNOWN`, all counters null (the guest sees QEMU disks). On the pilot's box the NVMe and the „old, not stable” Toshiba would be watched by OMV only, and Felhom's drive-health alarms (R-330/R-332's whole point) cannot fire. Evidence: the host report in `audits/omv-vm-2026-10-15/README.md` (memory/disk section) and `C-hub-events-Tester4.txt`. | WAITING-ON-OPERATOR | Operator: is a VM install a supported shape, and if so, is „OMV watches SMART” acceptable or must Felhom say on the dashboard that it cannot watch the disks? | Operator rules; then CC adds a household line on the Meghajtók page when SMART is unavailable. | Operator |
| **R-1082** | Storage & devices | P3 | **The drives page offers `/mnt/sys_drive` for attaching, while the storage-identity design rejects `sys_drive` as a drive for household data — and the new no-drive install sentence links to that page.** FOUND 2026-10-15 building `16` build A: `attachableStores` (felhom-controller `internal/web/attach_sources.go:79-136`) offers the guest data volume's `/mnt/sys_drive` on purpose — R-280, the way back after a reinstall so a restore has a destination, pinned by `TestMountedUnregisteredStores_OffersTheGuestDataVolume`. `architecture/16-storage-physical-identity.md` §11 rejects registering it for media (it rides in every whole-guest copy, local and off-site). Build A's D4 refusal (controller v0.317.0) never names it, but its link leads to the page that offers it. Not changed: the brief says to stop and report where source and design disagree, and removing it would reverse R-280. **-- 2026-10-16: operator ruling „hide it now”** (asked at the build B stop): the drives page stops offering `/mnt/sys_drive`; a box that already attached it keeps working; such boxes are listed from the hub, nothing is detached. | READY — ruled 2026-10-16: hide it now (build B). | — | Build B item 7: the drives page never offers `/mnt/sys_drive`; red-proved test; list boxes that attached it. | CC |
| **R-1093** | Storage & devices | P3 | **An EMPTY partition asks for the typed „data will be erased" confirmation.** MEASURED 2026-10-16 (build B, scratch VM, agent 0.158.1): `POST /api/storage/init` for a blank part → `needs_confirmation`, reason „device has a partition table" — `lsblk` reports the PARENT disk's table (PTTYPE) for a partition, so the agent's probe reads an empty part as data-bearing. Safe direction (more friction, never less), but `16` §6 says an empty part is self-serve and the wizard shows „adatot tartalmaz". Evidence: `audits/storage-identity-buildB-2026-10-15/live/12-partitions-format.txt`. | OPEN | — | In the agent's device probe, ignore a PTTYPE that a partition inherits from its parent (judge only the part's own signature and children); a red-proved test with a blank part fixture. Next agent release. | CC |
| **R-1062** | Storage & devices | P4 | **The N100's USB drive cannot be split in two without losing the box's whole-guest backup target.** The operator asked (2026-10-15 16:31) for two halves for multi-drive tests. Read first (`audits/n100-lived-in-2026-10-15/B/`): no family files — 2.7 GB nightly vzdumps of guest 9201 (`dump/`, PVE storage `felhom-backup` = the whole-guest backup target), 3.1 GB app backups + an off-site restore copy, 422 MB leftover app data of removed apps, empty media folders. The agent refuses a decommission of a backup target (`refuseIfBackupTarget`) and the N100 has no other drive to move that role to. Nothing changed. | **WAITING-ON-OPERATOR** — filed 2026-10-15 | operator decision | Keep one drive (CC's pick: the lived-in box keeps its local whole-guest backup), or give a word to drop that backup leg / add a second drive first | operator |
| **R-1063** | Storage & devices | P3 | **„Végleges leszerelés" leaves a half-state when the agent refuses: the dashboard marks the drive decommissioned first and never undoes it.** `handleStorageDecommission` → `finalizeDecommissionWith` calls `settings.SetDecommissioned` (and may move the default) BEFORE `agent.Decommission`; on the agent's 403 (protected role) or 409 (whole-guest backup target) it returns the error and keeps the mark — the dashboard would show a retired drive the agent kept serving. Read from code 2026-10-15 (`felhom-controller` `internal/web/storage_handlers.go`); NOT pressed on the lived-in N100 on purpose. | **OPEN** — filed 2026-10-15 | — | Measure on demo-hp (a backup-target drive), then: ask the agent first (or undo the mark and the default on a refusal); a test with a refusing agent seam, red first | CC |
| **R-1064** | Storage & devices | P4 | **Removing a storage path does not mention the cross-drive backup copies it holds.** demo-hp 2026-10-15: `/mnt/sizing-bench` removed through `/settings/storage/remove` (302, „Adattároló eltávolítva") while calibre-web's and paperless-ngx's `cross_drive.destination_path` pointed at it; the copies stay on disk, the records still name the removed path. The in-use check counts HDD_PATH only (`appsUsingPath`). | **OPEN** — filed 2026-10-15 | — | Read what tonight's tier-2 run (01:30) does with the two records; then: name the copies in the removal's confirmation (or count them as in use) | CC |
@@ -213,6 +210,7 @@ stopping line that lies.
| **R-331** | Storage & devices | P4 | **Disk health Phase 3 — growth-rate detection, and retiring the static 64.** The v0.215.0 count backstop (64 unreadable sectors → Hiba) is **a judgement from ONE drive**: the observed benign excursion peaked at 16 and cleared inside an hour, and the terminal run passed 64 at 13 Aug 11:28 and never came back. It is deliberately a backstop BEHIND the sustain rule, not the primary signal, but it is still a magic number tuned on a single sample and it will be wrong for some drive. With Phase 2's history the box can ask the question that actually matters — *is this count climbing, and how fast* — which distinguishes a drive with eight stable aging sectors from one adding forty a day, something no static threshold can do. Revisit 64 when that exists | **READY (M) — NEW 2026-08-14** | R-330 | Growth-rate rule over persisted samples; re-derive or delete the static 64 | CC |
| **R-1076** | Storage & devices | P4 | **A cheap SSD reports a vendor-less WWN, so a WWN-first disk key may not be unique.** MEASURED 2026-10-15 on the N100's system SSD (AirDisk 512 GB): `ID_WWN=0x5000000123456f1f` — NAA-5 with OUI `000000`, a pattern that looks like a placeholder; its serial `QDF922W009654S30EX` looks real. The agent's wipe binding prefers `wwn-` links (A/internal/storage/durable_device.go:32). Not known whether two units share the WWN. Evidence: `audits/storage-identity-2026-10-15/B-n100.txt`. | OPEN | — | Check a second unit of the same model (or vendor data); if shared, make the agent skip OUI-000000 WWNs (the design already keys on model+serial, `architecture/16-storage-physical-identity.md` §3). | CC |
| **R-1077** | Storage & devices | P4 | **No USB enclosure that hides or fakes a disk's serial has been measured.** The storage-identity spike (2026-10-15) had none at hand and found none in earlier evidence; the design's `usb_bridge` unknown rule (`architecture/16-storage-physical-identity.md` §3) is therefore an assumption. Cheap single- and dual-bay bridges are known to report one bridge serial for any disk. Evidence: `audits/storage-identity-2026-10-15/README.md` case 4b. | OPEN | — | Buy or borrow one cheap USB enclosure (single and dual bay), run `tools/physdisk_probe.py` against it, and set the rule from what it reports. | Operator (a device) then CC |
| **R-1094** | Storage & devices | P4 | **The whole-disk format refusal tells the household to format the free part — which is refused while the other part is in use.** SEEN 2026-10-16 (controller 0.318.0): 409 „Ezen a lemezen egy használatban lévő meghajtó van, ezért a teljes lemez nem formázható. Csak a szabad részét formázhatod." — since agent 0.158.1 a part next to a mounted part can only be USED (the root wrapper trusts no „Felhom's own" claim). Evidence: `audits/storage-identity-buildB-2026-10-15/live/14-samedisk-refusal-offer.txt`. | READY | — | Reword `err.web.meghajto_hasznalatban_formazas` (hu + en): „… nem formázható. A szabad részét használatba veheted." Next controller release; the copy gates run. | CC |
## Security & access — 21 rows (P1 2, P2 3, P3 10, P4 6)
@@ -0,0 +1 @@
booted 07:13:07
@@ -0,0 +1,3 @@
## round trip 2026-10-16T07:20:15Z
sha256 18077b6a689a06f3e2c08deefe939c631124f07ffc64533ec3896e75d61a4654 bytes 649065920
bake said 18077b6a689a06f3e2c08deefe939c631124f07ffc64533ec3896e75d61a4654
@@ -0,0 +1,6 @@
## teardown 2026-10-16T07:20:28Z
purging CT 9100 from related configurations..
0
3
qemu running: 0
disk back to virgin
@@ -0,0 +1,37 @@
# Golden 0.318.0 — bake + publish, 2026-10-16 (storage identity build B)
Why: the hub refuses to vouch agent 0.158.1 next to golden 0.313.0 (R-120: the golden is older than the controller the
fleet runs). The operator chose „bake a golden now" (2026-10-16), so build B's fresh-install proof can run on a vouched
agent. Procedure: `documentation/runbooks/RUNBOOK-manual-build.md` §4.0 and §4.1 steps 1–4, in the drill VM on DooPlex.
The script came from CC's clean `felhom-agent` clone at `origin/main` (`1a8bed1`).
| | Previous (`../golden-0.313.0-2026-10-15/`) | This bake |
|---|---|---|
| `build-golden.sh` | v3.2.0 | same file, unchanged |
| Controller | `felhom-controller:0.313.0` (MinAgent 0.155.0) | **`felhom-controller:0.318.0`** (**MinAgent 0.157.0**) |
| Docker engine | approved set (six pairs) | same six pairs, `GOLDEN_DOCKER_PKGS` set from the start |
| Template | `debian-13-standard_13.6-1_amd64.tar.zst` | same (after `pveam update`; `_amd64` filtered) |
## Pass markers (from `bake.log`, this folder)
```
[golden] build-golden.sh v3.2.0 — baking controller gitea.dooplex.hu/admin/felhom-controller:0.318.0
[golden] Docker engine set PINNED to the approved release: containerd.io=2.3.6-1~debian.13~trixie … docker-ce=5:29.8.2-1~debian.13~trixie …
docker OK (overlay2; data-root /var/lib/docker)
INFO: including mount point rootfs ('/') in backup
INFO: including mount point mp0 ('/var/lib/felhom') in backup
[golden] upload OK (HTTP 201)
GOLDEN_VERSION=0.318.0
GOLDEN_SHA256=18077b6a689a06f3e2c08deefe939c631124f07ffc64533ec3896e75d61a4654
```
No `excluding`, no `FATAL`, no `GOLDEN_DOCKER_PKGS not set`; the unit `Result=success`. Round trip: the registry's file
downloaded and hashed = the bake's sha, 649,065,920 bytes (`02-round-trip.txt`). Token: the unit's properties grep = 0;
the committed log grep = 0, with a working control (= 1 on a copy with the token appended, the copy shredded).
Teardown: `03-teardown.txt` (CT 9100 purged, token/script/log shredded in the VM, VM off, disk back to `virgin`).
## Vouch — the operator's act
Three fields together (runbook §4.1 step 5): golden **0.318.0**, agent **0.158.1** (NOT 0.158.0 — superseded by the
security review; NOT the `0.158.1-step1` entry), min_agent **0.157.0** (0.318.0's MinAgent). The agent's bundle is
`c23c3d355fe70a6fc6427c7996f76d72dab79a60f2c5497ea975de850cb1c41b`.
@@ -0,0 +1,340 @@
[golden] build-golden.sh v3.2.0 — baking controller gitea.dooplex.hu/admin/felhom-controller:0.318.0
[golden] creating build LXC 9100 (nesting=1,keyctl=1, unprivileged; rootfs 32G + ONE data volume 24G @ /var/lib/felhom, backup=1) …
Logical volume "vm-9100-disk-0" created.
Logical volume pve/vm-9100-disk-0 changed.
Creating filesystem with 8388608 4k blocks and 2097152 inodes
Filesystem UUID: 7e37a060-d7d2-475d-83e5-67eab3275078
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624
Logical volume "vm-9100-disk-1" created.
Logical volume pve/vm-9100-disk-1 changed.
Creating filesystem with 6291456 4k blocks and 1572864 inodes
Filesystem UUID: a3b42718-952b-4e23-8eee-52448fb9897b
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
extracting archive '/var/lib/vz/template/cache/debian-13-standard_13.6-1_amd64.tar.zst'
Total bytes read: 553512960 (528MiB, 99MiB/s)
Detected container architecture: amd64
Creating SSH host key 'ssh_host_ecdsa_key' - this may take some time ...
done: SHA256:0PNkfBVrUtRjVaiSrIlIUWHMMFUqzPIE2CSgi1rINvw root@felhom-golden
Creating SSH host key 'ssh_host_rsa_key' - this may take some time ...
done: SHA256:augNDRnuAosVOyx4y2gHFL8UZLLelWBMNn5COUeRgbQ root@felhom-golden
Creating SSH host key 'ssh_host_ed25519_key' - this may take some time ...
done: SHA256:C1akccRXwjrpEnO9h/kmkVlcoLxqbh6Ho7zYFeAngNw root@felhom-golden
[golden] starting + installing Docker (official repo, trixie channel) …
[golden] Docker engine set PINNED to the approved release: containerd.io=2.3.6-1~debian.13~trixie docker-buildx-plugin=0.37.1-1~debian.13~trixie docker-ce=5:29.8.2-1~debian.13~trixie docker-ce-cli=5:29.8.2-1~debian.13~trixie docker-ce-rootless-extras=5:29.8.2-1~debian.13~trixie docker-compose-plugin=5.6.0-1~debian.13~trixie
apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = (unset),
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MESSAGES = (unset),
LC_MONETARY = (unset),
LC_ADDRESS = (unset),
LC_IDENTIFICATION = (unset),
LC_MEASUREMENT = (unset),
LC_PAPER = (unset),
LC_TELEPHONE = (unset),
LC_NAME = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = (unset),
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MESSAGES = (unset),
LC_MONETARY = (unset),
LC_ADDRESS = (unset),
LC_IDENTIFICATION = (unset),
LC_MEASUREMENT = (unset),
LC_PAPER = (unset),
LC_TELEPHONE = (unset),
LC_NAME = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
installed: containerd.io 2.3.6-1~debian.13~trixie
installed: docker-buildx-plugin 0.37.1-1~debian.13~trixie
installed: docker-ce 5:29.8.2-1~debian.13~trixie
installed: docker-ce-cli 5:29.8.2-1~debian.13~trixie
installed: docker-ce-rootless-extras 5:29.8.2-1~debian.13~trixie
installed: docker-compose-plugin 5.6.0-1~debian.13~trixie
[golden] no approved guest release given - the template versions stay; first-night count vs an approved release: n/a
[golden] pending Debian upgrades in the baked guest (what a FUTURE approval may bring): 56
[golden] baking daemon.json: classic overlay2 driver (containerd-snapshotter OFF) + log rotation …
[golden] wiring the single data volume (R-165 variant V-c): /var/lib/felhom/{docker,sys_drive} -> binds …
[golden] verifying Docker works in the build guest (storage driver should be overlay2 on the ext4 data volume) …
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pulling fs layer
4f55086f7dd0: Verifying Checksum
4f55086f7dd0: Download complete
4f55086f7dd0: Pull complete
Digest: sha256:11a3eb1597e864c51b3f56733cb2e8a90bf0c284bfc05f9444d81c558bc848aa
Status: Downloaded newer image for hello-world:latest
docker OK (overlay2; data-root /var/lib/docker)
live-restore: on
/var/lib/docker is a real mount: /dev/mapper/pve-vm--9100--disk--1[/docker] ext4
/mnt/sys_drive is a real mount: /dev/mapper/pve-vm--9100--disk--1[/sys_drive] ext4
both paths are ONE filesystem: /dev/mapper/pve-vm--9100--disk--1 23317576
[golden] baking the in-guest controller image gitea.dooplex.hu/admin/felhom-controller:0.318.0 (no registry cred at deploy) …
WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/
0.318.0: Pulling from admin/felhom-controller
774043ccc8cc: Pulling fs layer
ab6b448d4be9: Pulling fs layer
23a5bfa58353: Pulling fs layer
862a57157567: Pulling fs layer
a7bd8aa6efbb: Pulling fs layer
0cbfef0f6e91: Pulling fs layer
862a57157567: Waiting
a7bd8aa6efbb: Waiting
0cbfef0f6e91: Waiting
774043ccc8cc: Verifying Checksum
774043ccc8cc: Download complete
862a57157567: Verifying Checksum
862a57157567: Download complete
23a5bfa58353: Verifying Checksum
23a5bfa58353: Download complete
a7bd8aa6efbb: Verifying Checksum
a7bd8aa6efbb: Download complete
0cbfef0f6e91: Verifying Checksum
0cbfef0f6e91: Download complete
ab6b448d4be9: Verifying Checksum
ab6b448d4be9: Download complete
774043ccc8cc: Pull complete
ab6b448d4be9: Pull complete
23a5bfa58353: Pull complete
862a57157567: Pull complete
a7bd8aa6efbb: Pull complete
0cbfef0f6e91: Pull complete
Digest: sha256:e0e788ae67adf3b60efdd042470cd4ea1d5e5b3a7fa77685f67dcb9892f8ea20
Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-controller:0.318.0
gitea.dooplex.hu/admin/felhom-controller:0.318.0
[golden] asking the controller which infra images it manages …
[golden] baking infra images (4): traefik:v3.7.13 cloudflare/cloudflared:2026.9.3 gtstef/filebrowser:1.5.6-stable gitea.dooplex.hu/admin/felhom-samba:1.1.0 …
v3.7.13: Pulling from library/traefik
e2de96513ba9: Pulling fs layer
b686a4f73445: Pulling fs layer
78cb21c375ca: Pulling fs layer
acb2f33459b1: Pulling fs layer
acb2f33459b1: Waiting
e2de96513ba9: Verifying Checksum
e2de96513ba9: Download complete
b686a4f73445: Verifying Checksum
b686a4f73445: Download complete
acb2f33459b1: Verifying Checksum
acb2f33459b1: Download complete
78cb21c375ca: Verifying Checksum
78cb21c375ca: Download complete
e2de96513ba9: Pull complete
b686a4f73445: Pull complete
78cb21c375ca: Pull complete
acb2f33459b1: Pull complete
Digest: sha256:24841fe2de7304c149343d877d2923b4c8800a38ba015dea9174c23b20e344a0
Status: Downloaded newer image for traefik:v3.7.13
docker.io/library/traefik:v3.7.13
2026.9.3: Pulling from cloudflare/cloudflared
2cc7ee286bf3: Pulling fs layer
c172f21841df: Pulling fs layer
218cf840d0d9: Pulling fs layer
f6069939f718: Pulling fs layer
d6b1b89eccac: Pulling fs layer
2780920e5dbf: Pulling fs layer
7c12895b777b: Pulling fs layer
3214acf345c0: Pulling fs layer
52630fc75a18: Pulling fs layer
dd64bf2dd177: Pulling fs layer
b839dfae01f6: Pulling fs layer
ebddc55facdc: Pulling fs layer
c4bc6f35ff5e: Pulling fs layer
b96fe2995f90: Pulling fs layer
58c0c263dc73: Pulling fs layer
bd8962e29291: Pulling fs layer
cac2ae0193cb: Pulling fs layer
f0383d5ebc47: Pulling fs layer
52630fc75a18: Waiting
dd64bf2dd177: Waiting
b839dfae01f6: Waiting
ebddc55facdc: Waiting
c4bc6f35ff5e: Waiting
b96fe2995f90: Waiting
58c0c263dc73: Waiting
bd8962e29291: Waiting
cac2ae0193cb: Waiting
f0383d5ebc47: Waiting
d6b1b89eccac: Waiting
2780920e5dbf: Waiting
7c12895b777b: Waiting
3214acf345c0: Waiting
f6069939f718: Waiting
2cc7ee286bf3: Verifying Checksum
2cc7ee286bf3: Download complete
c172f21841df: Download complete
218cf840d0d9: Download complete
f6069939f718: Verifying Checksum
f6069939f718: Download complete
d6b1b89eccac: Verifying Checksum
d6b1b89eccac: Download complete
2780920e5dbf: Verifying Checksum
2780920e5dbf: Download complete
2cc7ee286bf3: Pull complete
7c12895b777b: Verifying Checksum
7c12895b777b: Download complete
3214acf345c0: Verifying Checksum
3214acf345c0: Download complete
52630fc75a18: Verifying Checksum
52630fc75a18: Download complete
dd64bf2dd177: Verifying Checksum
dd64bf2dd177: Download complete
b839dfae01f6: Verifying Checksum
b839dfae01f6: Download complete
ebddc55facdc: Verifying Checksum
ebddc55facdc: Download complete
c4bc6f35ff5e: Download complete
c172f21841df: Pull complete
b96fe2995f90: Verifying Checksum
b96fe2995f90: Download complete
bd8962e29291: Verifying Checksum
bd8962e29291: Download complete
58c0c263dc73: Verifying Checksum
58c0c263dc73: Download complete
cac2ae0193cb: Verifying Checksum
cac2ae0193cb: Download complete
f0383d5ebc47: Verifying Checksum
f0383d5ebc47: Download complete
218cf840d0d9: Pull complete
f6069939f718: Pull complete
d6b1b89eccac: Pull complete
2780920e5dbf: Pull complete
7c12895b777b: Pull complete
3214acf345c0: Pull complete
52630fc75a18: Pull complete
dd64bf2dd177: Pull complete
b839dfae01f6: Pull complete
ebddc55facdc: Pull complete
c4bc6f35ff5e: Pull complete
b96fe2995f90: Pull complete
58c0c263dc73: Pull complete
bd8962e29291: Pull complete
cac2ae0193cb: Pull complete
f0383d5ebc47: Pull complete
Digest: sha256:072c067d25ccbe61d46e18f0d0723255f2bb5304f7317caa95b27031520ff92c
Status: Downloaded newer image for cloudflare/cloudflared:2026.9.3
docker.io/cloudflare/cloudflared:2026.9.3
1.5.6-stable: Pulling from gtstef/filebrowser
55afa1ecc21d: Pulling fs layer
8ed8f35f8d4f: Pulling fs layer
989b226a579c: Pulling fs layer
660aeead31d5: Pulling fs layer
4f4fb700ef54: Pulling fs layer
adce24567e4c: Pulling fs layer
f17ea56b313b: Pulling fs layer
6b6f3b3efe88: Pulling fs layer
4ed1ca4f3fce: Pulling fs layer
e6fc9c6a5757: Pulling fs layer
d47782d1182a: Pulling fs layer
6b6f3b3efe88: Waiting
4ed1ca4f3fce: Waiting
e6fc9c6a5757: Waiting
d47782d1182a: Waiting
4f4fb700ef54: Waiting
adce24567e4c: Waiting
f17ea56b313b: Waiting
660aeead31d5: Waiting
55afa1ecc21d: Verifying Checksum
55afa1ecc21d: Download complete
660aeead31d5: Verifying Checksum
660aeead31d5: Download complete
8ed8f35f8d4f: Verifying Checksum
8ed8f35f8d4f: Download complete
989b226a579c: Verifying Checksum
989b226a579c: Download complete
4f4fb700ef54: Verifying Checksum
4f4fb700ef54: Download complete
f17ea56b313b: Verifying Checksum
f17ea56b313b: Download complete
6b6f3b3efe88: Verifying Checksum
6b6f3b3efe88: Download complete
4ed1ca4f3fce: Verifying Checksum
4ed1ca4f3fce: Download complete
e6fc9c6a5757: Verifying Checksum
e6fc9c6a5757: Download complete
adce24567e4c: Verifying Checksum
adce24567e4c: Download complete
d47782d1182a: Verifying Checksum
55afa1ecc21d: Pull complete
8ed8f35f8d4f: Pull complete
989b226a579c: Pull complete
660aeead31d5: Pull complete
4f4fb700ef54: Pull complete
adce24567e4c: Pull complete
f17ea56b313b: Pull complete
6b6f3b3efe88: Pull complete
4ed1ca4f3fce: Pull complete
e6fc9c6a5757: Pull complete
d47782d1182a: Pull complete
Digest: sha256:7c5d7ac8ffda31294d278063cf9d2e04303b39e6dce1f4c691342240ca7703b8
Status: Downloaded newer image for gtstef/filebrowser:1.5.6-stable
docker.io/gtstef/filebrowser:1.5.6-stable
1.1.0: Pulling from admin/felhom-samba
897d797d2723: Pulling fs layer
3051591aa250: Pulling fs layer
ce57a3f93416: Pulling fs layer
fb94eeec2fe1: Pulling fs layer
fb94eeec2fe1: Waiting
ce57a3f93416: Verifying Checksum
ce57a3f93416: Download complete
fb94eeec2fe1: Verifying Checksum
fb94eeec2fe1: Download complete
897d797d2723: Verifying Checksum
897d797d2723: Download complete
3051591aa250: Verifying Checksum
3051591aa250: Download complete
897d797d2723: Pull complete
3051591aa250: Pull complete
ce57a3f93416: Pull complete
fb94eeec2fe1: Pull complete
Digest: sha256:1c17c09422bec0366d7cf0e0fcfc1486ba6c90334a0a5d5c851073a9342f8f10
Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-samba:1.1.0
gitea.dooplex.hu/admin/felhom-samba:1.1.0
[golden] baking the controller-bootstrap unit (deploys the BAKED controller from the config mount) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.service' → '/etc/systemd/system/felhom-controller-bootstrap.service'.
[golden] baking the controller-bootstrap PATH unit (starts the service on bootstrap-mount hot-plug — B1) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.path' → '/etc/systemd/system/felhom-controller-bootstrap.path'.
[golden] baking the first-boot SSH host-key regeneration unit (F3) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-regen-hostkeys.service' → '/etc/systemd/system/felhom-regen-hostkeys.service'.
[golden] identity-clean + minimize …
[golden] stop + archive …
INFO: including mount point rootfs ('/') in backup
INFO: including mount point mp0 ('/var/lib/felhom') in backup
INFO: archive file size: 618MB
INFO: Finished Backup of VM 9100 (00:00:40)
[golden] DONE. golden archive volid: local:backup/vzdump-lxc-9100-2026_10_16-09_18_45.tar.zst (rootfs 32G + ONE data volume 24G @ /var/lib/felhom, all in the archive)
[golden] publishing golden (649065920 bytes, sha256 18077b6a689a06f3…) → https://gitea.dooplex.hu/api/packages/admin/generic/felhom-golden/0.318.0/golden.tar.zst
[golden] pre-delete existing: HTTP 404 (404/204 expected)
[golden] upload OK (HTTP 201)
GOLDEN_VERSION=0.318.0
GOLDEN_SHA256=18077b6a689a06f3e2c08deefe939c631124f07ffc64533ec3896e75d61a4654
[golden] Record in the hub operator UI (Configs → Day-0 artifacts): golden 0.318.0 / 18077b6a689a06f3e2c08deefe939c631124f07ffc64533ec3896e75d61a4654
[golden] (the build guest 9100 is stopped; destroy it with: pct destroy 9100 --purge)