920c1aaf84
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
241 lines
23 KiB
Markdown
241 lines
23 KiB
Markdown
# 16 — Storage that knows its physical disks
|
||
|
||
> **What this is:** the design that lets Felhom know, for every storage place, which physical disk(s) it sits on — and
|
||
> use that to make backup tiers honest, to give one-disk boxes a place for household data, and to allow partitions.
|
||
> Operator ruling 2026-10-15: „Felhom must support drives split into parts; the agent and the controller must know
|
||
> 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); **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)
|
||
|
||
| Place | Found by | Identified by | Mounted | What lives there | The backup page says |
|
||
|---|---|---|---|---|---|
|
||
| Host root / PVE `local` | PVE storage `local` [FACT] A/internal/storage/observe.go:424; never a candidate [FACT] A/internal/storage/role.go:53-79 | `path:` / `store:local` [FACT] A/internal/storage/durableid.go:54-67; role system | host `/`, `/var/lib/vz` | the **local whole-guest copies** (vzdump, retention 3) [FACT] E/scripts/felhom-host-install.sh:730-734; `07` §6.1 | `backup.target.degraded` „…ugyanazon a lemezen van, mint a rendszer…" [FACT] C/internal/web/backup_target_offer.go:78-100 |
|
||
| Guest `mp0` `/var/lib/felhom` with folders `docker/` and `sys_drive/` (bound to `/var/lib/docker`, `/mnt/sys_drive`) | config default `system_data_path=/mnt/sys_drive` [FACT] C/internal/config/config.go:166 | path only; „same disk" = `st_dev` [FACT] C/internal/system/mounts_linux.go:225-245 | thin LV in `local-lvm`, `backup=1` [FACT] A/configs/build-golden.sh:105, :221-225; measured on all 3 boxes | app volumes; Tier-1 of apps with no drive [FACT] C/internal/backup/backup.go:429-438; Tier-2 state-only fallback [FACT] C/internal/backup/tier2.go:157-178; **inside every whole-guest copy** | „Csak egy másolat készül (nincs második meghajtó)…" — a Go constant, not i18n [FACT] backup.go:359-361, :1387-1410 |
|
||
| Enrolled drive (`/mnt/<name>` on the host → `/mnt/felhom-drives/<name>`) | `GET /disks/candidates`: **whole disks only** [FACT] A/internal/storage/candidates.go:33-53 | mount: `uuid:<fs-uuid>`; wipe: `byid:` wwn > nvme-eui > … [FACT] A/internal/storage/durable_device.go:32-51 | systemd unit by fs-UUID through `FELHOM_MOUNT` [FACT] A/internal/storage/mountunit.go:50-65; host bind into `mp8` | `HDD_PATH` apps, media binds [FACT] C/internal/stacks/media_library.go:24-33; Tier-1/Tier-2; optionally the whole-guest target `felhom-backup`; **not in vzdump** (`mp8` is a host bind) [FACT] `07`:477-480 | Tier-2 badges; „Nincs 2. (off-drive) másolat" [FACT] C/internal/i18n/locales/hu.json:303 |
|
||
| Network share | customer input [FACT] A/internal/localapi/netstorage.go:36-40 | none („no durable-id, no SMART") [FACT] netstorage.go:15-19 | `.automount` under `/mnt/felhom-drives/<name>` [FACT] A/internal/storage/netmount.go:18-31 | browse only; never media, never Tier-2 [FACT] media_library.go:83; tier2.go:141-143 | `err.backup.halozati_tarolo_nem_lehet_a_2` [FACT] tier2.go:37-40 |
|
||
| USB disk | same as an enrolled drive; transport never decides [FACT] C/internal/settings/settings.go:609-614 | same; the bridge may pass the real identity (N100: yes) [FACT] spike case 4a | same | same | same; SMART may be missing → „Nincs adat" |
|
||
|
||
**[FACT] What compares physical disks today:** the controller compares only paths and `st_dev` (its own comment:
|
||
„two partitions on one physical disk look "different" here", mounts_linux.go:240-242). The agent has a whole-disk
|
||
compare (`SameWholeDisk`, role.go:207-216) but uses it only for eject/decommission roles. **[FACT] What knows about
|
||
partitions:** the agent accepts `/dev/sdb1` for inspect and mkfs (A/internal/storage/validate.go:28-32) but judges the
|
||
whole disk (claim.go:144-163), lists only whole disks, and calls a disk with a partition table or any filesystem
|
||
„data-bearing" (candidates.go:66-85). Measured: an empty partition next to another is never offered (spike).
|
||
|
||
## 2. The spike in five lines (Part B)
|
||
|
||
**[FACT]** A read-only walk (partition → parent; dm/LVM → slaves; loop → backing file → its disk) named the right
|
||
physical disk in every case on real devices: one-disk N100 and demo-hp (every system place → the one SSD), two disks
|
||
(system vs enrolled drive), two partitions of one disk (both → the same disk), a USB bridge that passes the identity.
|
||
**Inside a VM (Tester 1) it can only say „unknown"**: virtual disks on the same host disk and on a different host disk
|
||
look identical (`QEMU HARDDISK`, serial = the slot name `drive-scsiN`). Not measured: a USB box that hides the serial
|
||
(R-1077). Seen: a cheap SSD with a vendor-less WWN (OUI 000000, R-1076). Full table: `audits/storage-identity-2026-10-15/README.md`.
|
||
|
||
## 3. The model
|
||
|
||
**[DESIGN]** A **storage place** is anything Felhom stores on: the system data (`mp0`), the new internal data place
|
||
(§5), an enrolled whole disk, an enrolled partition, a network share. Each place carries:
|
||
|
||
| Field | Meaning |
|
||
|---|---|
|
||
| `kind` | `system` · `internal` · `disk` · `partition` · `network` |
|
||
| `physical_disks` | the ids of the whole physical disks under it, e.g. `["serial:ATA:SanDisk_X600:182195804614"]` |
|
||
| `identity` | `proven` — every disk under it has a stable id the agent trusts; `unknown` — otherwise, with a reason code: `vm`, `no_serial`, `weak_wwn`, `usb_bridge`, `network` |
|
||
|
||
**Disk id rule [DESIGN]:** `serial:<bus>:<model>:<serial>` from udev (`ID_BUS`, `ID_MODEL`, `ID_SERIAL_SHORT`); NVMe
|
||
`eui`/`nguid` when present. A WWN is used only when its NAA OUI is not `000000` (spike case 4c). Model + serial, not WWN,
|
||
is the primary key: two disks are „the same" only when the key is equal.
|
||
|
||
**Unknown rule [DESIGN]:** `systemd-detect-virt` ≠ `none` → every disk is `unknown/vm`. A disk with no serial → `no_serial`.
|
||
A USB disk whose serial is the bridge's (all the same digit, or equal to a known bridge list) → `usb_bridge`. A network
|
||
share → `network`, `physical_disks: []`.
|
||
|
||
**How it is computed [DESIGN]:** by the **agent**, as root, from `/sys` and udev only (no controller input): the walk
|
||
the spike used, built on what exists — `walkSlaves`/`physicalDisksOf` (role.go:81-131), `parentDisk`
|
||
(A/internal/storage/hostread.go:183-201) — plus the loop step and the mapping guest path → host mount (`/mnt/felhom-drives/<name>` →
|
||
`/mnt/<name>` → device; `mp0` → its LV).
|
||
|
||
**When [DESIGN]:** at agent start, on every disk hot-plug event the agent already reacts to, on enrol/attach, and
|
||
**again right before each Tier-2 run** (a disk swapped in the same bay must not inherit the old verdict).
|
||
|
||
**Reported [DESIGN]:** `GET /disks` gains `physical_disks` + `identity` per place; the hub report's `storage_targets`
|
||
gains the same two fields (A/internal/hub/report.go:379-438), so the hub sees the agent's verdict directly. The controller
|
||
stores nothing of it; it asks.
|
||
|
||
## 4. Backup tiers
|
||
|
||
**[DESIGN] The rule:** a Tier-2 copy **counts as a second copy** only when the source place and the target place share
|
||
**no** physical disk **and** both identities are `proven`. The decision is the agent's (`POST /disks/same-disk` with the
|
||
two guest paths → `{different: bool, proven: bool, reason}`), replacing `sameDevice`/`st_dev` in `tier2.go:83-179` and
|
||
`hasOffDriveTarget` (backup.go:1390-1402).
|
||
|
||
| Situation | Tier-2 | The one-copy notice | Household sentence (HU / EN) |
|
||
|---|---|---|---|
|
||
| Different disks, both proven | made, counted | hidden | — |
|
||
| Same disk (two partitions, internal place + system) | per decision D1 | shown | „Csak egy helyi másolat készül: nincs második lemez, vagy ugyanazon a lemezen van, mint az adatok. Ez a véletlen törlés ellen véd, lemezhiba ellen nem. Lemezhiba ellen egy másik lemez vagy a távoli mentés véd." / „Only one local copy is made: there is no second disk, or it is on the same disk as the data. This protects against mistakes, not against a disk failure. A different disk or the off-site copy protects against a disk failure." |
|
||
| Unknown (VM, hidden serial) | per decision D2 | shown | „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." / „Felhom cannot check that the second drive is really a different disk (inside a virtual machine and behind some USB enclosures this is not visible), so it does not count it as a second copy." |
|
||
| Network share | never (unchanged) | shown | unchanged |
|
||
|
||
The notice moves from a Go constant to i18n, in the informal voice (closes R-1072's half). Off-site (Tier-3, PBS) and
|
||
the local whole-guest copy are **unchanged**. **[FACT]** Today Tester 1 counts Tier-2 copies between virtual disks that
|
||
all sit on one host NVMe (R-1074).
|
||
|
||
## 5. One-disk boxes — the internal data place
|
||
|
||
**[FACT]** The install button for a data-folder app finds no drive on a one-disk box and refuses with the raw field
|
||
name (R-1068). **[FACT]** The only place on that disk today, `sys_drive`, is a folder of `mp0`, which is `backup=1`:
|
||
**anything put there rides in every local and off-site whole-guest copy** (`07` §6.1, §7.5.1). Films there would
|
||
overflow the local copy (R-548) and travel to ep0.
|
||
|
||
**[DESIGN]** The installer (new installs) creates a second thin LV on `local-lvm`, the **internal data place**
|
||
(„Belső tárhely"), formats it, and the agent mounts it **on the host** by fs-UUID at `/mnt/belso`, bound to
|
||
`/mnt/felhom-drives/belso` — exactly like an enrolled drive. So:
|
||
- it is **not** in vzdump (a host mount, like every drive) and not in PBS;
|
||
- the controller registers it as the default drive (`kind: internal`), so media and `HDD_PATH` apps install with no
|
||
extra step — **no sentence at all**; the media binds cover it like any drive;
|
||
- its `physical_disks` = the system disk, so a Tier-2 copy between it and `sys_drive` never counts (§4);
|
||
- the agent's format/wipe guard keeps refusing it as a **system** disk (role system, claim.go:72) — only the installer
|
||
makes or removes it.
|
||
|
||
On a one-disk box **without** an internal place (old boxes, §9), the install of a data-folder app answers in words:
|
||
„Ehhez az alkalmazáshoz egy adattároló kell. A Meghajtók oldalon adhatsz hozzá egyet." — never the field name.
|
||
Registering `sys_drive` as a drive (the „way through" R-1068 guessed) is **rejected** (§10).
|
||
|
||
## 6. Partitions
|
||
|
||
**[DESIGN]** A place may be one partition. The agent lists, under each whole disk, its partitions as separate
|
||
candidates with their own state:
|
||
|
||
| Partition state | Offered as | Who may |
|
||
|---|---|---|
|
||
| empty (no signature, no children) | „format this part" | the household, self-serve (it holds nothing) |
|
||
| a filesystem with no files (agent mounts read-only, counts entries) | „use this part" or „format this part" | the household |
|
||
| a filesystem with files | „use this part" (attach) only; format = the customer-confirmed data-bearing path, bound to the partition's durable id | the household with confirmation (today's user-data rule, A/internal/localapi/disks.go:912-946) |
|
||
| LVM / RAID / LUKS / swap / mounted elsewhere / on the system disk | not offered | nobody (claim.go:72-103, unchanged) |
|
||
|
||
The classifier judges **the partition** (its own signature, children, mounts) and still refuses on the whole disk's
|
||
system role and on any sibling that is foreign-mounted or a PV — those siblings make the **disk** unsafe, not the
|
||
partition. A partition's durable id = `<disk id>:part<N>` + its fs-UUID; the wipe re-resolve (wipe_reresolve.go:53-93)
|
||
binds both. **Creating or deleting partitions: never from the dashboard in this round — the installer only** (D5).
|
||
|
||
## 7. The installer
|
||
|
||
**[FACT]** Today `step_grows` gives the guest's one data volume (`mp0`) 70 / 130 / 250 GiB by the thin pool's free space
|
||
(<150 / ≥150 / ≥300 GiB), and nothing above 250 (E/scripts/felhom-host-install.sh:2261-2275). Measured pools: 128 GB
|
||
disk → 54 GiB, 200 GB → 113 GiB, 512 GB → 349 GiB (PVE default layout; root capped at 96 GiB).
|
||
|
||
**[DESIGN] The rule:** `mp0` = 70 GiB (pool < 300), 130 (300–800), 250 (≥ 800). Reserve = **max(16 GiB, 10 % of the
|
||
pool)**, kept free for thin snapshots during a whole-guest copy, image pulls during updates, and the restore test (which
|
||
sizes itself, A19). Internal place = pool − 32 (rootfs) − `mp0` − reserve, made only when ≥ 20 GiB. The local
|
||
whole-guest copies stay on host `local` (root), which the data place does not touch — R-548 is not made worse: `mp0`
|
||
does not grow.
|
||
|
||
| Disk | Thin pool (GiB) | Today: data volume / unused | Proposed `mp0` | Reserve | **Internal place** |
|
||
|---|---|---|---|---|---|
|
||
| 256 GB | ≈ 140 (estimate) | 70 / ≈ 38 | 70 | 16 | **≈ 22** (settings and small apps, not a film library) |
|
||
| 512 GB | 349 (N100, measured) | 250 / ≈ 67 | 130 | 35 | **≈ 152** |
|
||
| 1 TB | ≈ 800 (estimate) | 250 / ≈ 518 | 250 | 80 | **≈ 438** |
|
||
| 2 TB | ≈ 1,720 (estimate) | 250 / ≈ 1,438 | 250 | 172 | **≈ 1,266** |
|
||
|
||
**RULED 2026-10-16 (operator, after the build B space check `audits/storage-identity-buildB-2026-10-15/SPACE-CHECK.md`):
|
||
these numbers replace the table above.** `mp0` = **50 GiB** (pool < 300), **100** (300–800), **130** (≥ 800); reserve =
|
||
**max(16 GiB, 10 %), at most 48 GiB**; internal place = pool − 32 − `mp0` − reserve, only when ≥ 20 GiB; nothing promised
|
||
beyond pool − reserve. Result: ≈ 42 / 182 / 590 / 1,510 GiB for 256 GB / 512 GB / 1 TB / 2 TB. The host root is left as
|
||
the Proxmox installer makes it (SPACE-CHECK §3).
|
||
|
||
## 8. Virtual machines
|
||
|
||
**[FACT]** Inside a VM the agent cannot tell virtual disks apart by host disk (spike cases 5–6). **[DESIGN]** Every place
|
||
on a VM box is `identity: unknown/vm`; Tier-2 there never counts (D2), and the page says why. The OMV guide tells the
|
||
household: a second disk given to the Felhom VM must be **a different physical disk on the host**, passed whole, on bus
|
||
`sata` (R-1069); otherwise it adds space, not safety. Disk health is OMV's job there (R-1073).
|
||
|
||
## 9. Existing boxes
|
||
|
||
**[DESIGN]** The agent computes identities on its next update — no migration, no change to what is stored. A box with two
|
||
real disks keeps every Tier-2 copy and every sentence (both proven, different). A box where two places share a disk
|
||
(none known in the field; Tester 1 in its VM) shows the honest notice. One-disk boxes get no internal place by
|
||
themselves; adding one later is a signed operator job (an LV in free thin space), not a household button. Hub: the two
|
||
new fields appear in reports; nothing is backfilled.
|
||
|
||
## 10. Threat note
|
||
|
||
**[DESIGN]** „Different disks" is agent-computed evidence, like the data-bearing verdict (`03` §6): the controller can
|
||
only ask, never assert. A compromised controller could show a false sentence on its own page, but cannot make the agent
|
||
call two places different, cannot change the hub's copy of the verdict, and cannot format a partition the agent judges
|
||
data-bearing. The agent never trusts a guest path it cannot map to a host mount it made.
|
||
|
||
## 11. Rejected options (one line each)
|
||
|
||
- **Register `sys_drive` as the media drive** — it is inside `mp0`, so films enter every whole-guest copy, local and off-site.
|
||
- **Grow `mp0` without a cap** — the same problem, at full size.
|
||
- **Keep `st_dev` and add a partition check in the controller** — the controller cannot see LVM, loop or the host; the agent can.
|
||
- **WWN as the primary disk key** — cheap disks ship vendor-less WWNs (N100).
|
||
- **Ask the hypervisor (OMV/libvirt) which host disk backs a virtual disk** — Felhom has no access there, and it would trust a party it does not control.
|
||
- **Let the dashboard create partitions** — a new destructive power for the household with no recovery; not needed for the cases in hand.
|
||
|
||
## 12. Decisions for the operator (D1–D6) — RULED 2026-10-15: all six as recommended
|
||
|
||
| # | Question | Options | Recommendation | If you do nothing |
|
||
|---|---|---|---|---|
|
||
| D1 | A second copy on the **same** disk (two partitions, internal place → `sys_drive`)? | (a) do not make it; say „only one copy" honestly · (b) make it, label it „same disk: against mistakes, not against a disk failure" | **(a)** — the first copy already protects against mistakes; (b) spends space and invites a false sense of safety | today's `st_dev` rule stays: two partitions would count as two disks |
|
||
| D2 | **Unknown** identity (VM, hidden serial)? | (a) never count it; the copy is still made (it may be a real second disk) and the notice stays · (b) the household confirms „these are different disks" once, recorded with the disk ids | **(a)** for now; revisit (b) when a VM install is a supported shape | VM boxes keep counting same-disk copies as second copies (Tester 1 does today) |
|
||
| D3 | The installer's internal place on a one-disk box, and its reserve | (a) §7: `mp0` 70/130/250, reserve max(16 GiB, 10 %), the rest internal · (b) a larger reserve, 20 % · (c) no internal place; household adds a drive | **(a)** | new one-disk boxes keep refusing media apps (R-1068) and leave most of a big disk unused |
|
||
| D4 | Media and data-folder apps on a one-disk **old** box | (a) a plain sentence pointing to Meghajtók; internal place only by a signed operator job · (b) register `sys_drive` | **(a)** — (b) puts films into the whole-guest copies | the raw field error stays (R-1068) |
|
||
| D5 | Who creates or deletes partitions | (a) the installer only, this round · (b) also a dashboard wizard | **(a)** | — (nothing is built either way) |
|
||
| D6 | R-1071 — films from a NAS share, read-only, for Jellyfin | (a) a separate decision; this design only labels a NAS place `network`, never a copy target · (b) fold a read-only media bind kind into this build | **(a)** — the model makes it easier (a NAS becomes one more place kind with clear rules), but it is a media decision, not an identity one | stays open as R-1071 |
|
||
|
||
**After the rulings, the build task changes:** agent (place identity in `/disks` and the report, the same-disk call,
|
||
partition candidates, the loop step) · controller (Tier-2 and the notice ask the agent; i18n sentences; the install
|
||
refusal in words; register the internal place) · installer (`step_grows` + the internal LV) · hub (store and show the two
|
||
fields) · website and guides (the OMV guide's second-disk line; the backup explanation page).
|
||
|
||
## 13. Build status
|
||
|
||
**Build A — honest backup copies (D1, D2, D4), shipped 2026-10-15** (agent v0.157.0, controller v0.317.0 with MinAgent
|
||
0.157.0, hub v0.154.0; evidence `audits/storage-identity-buildA-2026-10-15/`):
|
||
|
||
| Section | Built | Proven |
|
||
|---|---|---|
|
||
| §3 model | the agent computes `kind`, `physical_disks`, `identity` + reason for every PVE storage and every own mount; `GET /disks` and the hub report carry them | live: N100 and demo-hp `proven`, Tester 1 `unknown/vm`, the PBS store `network`; the hub stores and shows them |
|
||
| §4 tiers | `POST /disks/same-disk`; Tier-2 choice, the pre-run guard and the one-copy notice ask it (D1: same disk → no copy; D2: unknown → copy made, not counted); a NAS never counts and never hides the notice (R-1075) | live: Tester 1 shows the „cannot check" notice and its copies are made, not counted; demo-hp and N100 count theirs, no notice. **Unit only:** the same-disk skip (no box has two places on one disk), the NAS notice (no box with a NAS can reach the agent, R-1083) |
|
||
| §5 D4 | the install of a data-folder app on a box with no drive answers „Ehhez az alkalmazáshoz egy adattároló kell. A Meghajtók oldalon adhatsz hozzá egyet." with a link to the Meghajtók page; never the field name | **unit only** — no one-disk box was at hand |
|
||
| §8 VMs | every place on a VM box is `unknown/vm` | live, Tester 1 |
|
||
| §10 | the agent maps only paths to mounts it made; an absent drive (its folder on the root disk) is `unmapped`, never „the system disk" | unit, and live (`unmapped` for an absent drive on demo-hp) |
|
||
|
||
**Where the build differs from the text above:**
|
||
- §3 „When": the identity is computed **on every read** (each `GET /disks`, each report, each same-disk call), not cached
|
||
at start and on hot-plug. The rule's purpose — a swapped disk never inherits an old verdict — holds with no cache.
|
||
- `usb_bridge`: built from the spike's rule, **never seen on a real device** (the N100's USB enclosure passes the real serial).
|
||
- §4 said the notice closes „R-1072's half": it closed all of R-1072 — the other sentence was already informal.
|
||
- 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 — 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`.
|
||
|