# SPIKE — why the v0.169.0 disk-health card shows "Nincs adat" on real hardware, 2026-07-25

**Question:** the new "Lemezek állapota" card (controller v0.169.x) renders **Nincs adat** for both
physical disks on the live demo host (`demo-felhom`, a real N100 — NOT virtualized). Is SMART actually
unavailable on this hardware, or does the agent fail to *read* SMART it could read? If the latter,
where, and what are the fix directions?

**Verdict up front:** SMART is **fully available** on both disks via the *exact* form the sudoers
allowlist already grants (`smartctl -a -j <whole-disk>`) — the system SSD and the USB HDD both report
**PASSED**. The card says "Nincs adat" because the **agent never asks**, for two independent reasons:

1. **`local` (the system SSD) → UNKNOWN:** its storage is a `dir` on `pve-root`, an **LVM logical
   volume on `sda3`**. The agent leaves `backing_device` empty for it, and even if it didn't,
   `smartDeviceFor` has **no device-mapper/LVM handling** — so the dir → LV → PV → whole-disk (`/dev/sda`)
   chain is never resolved and `enrich` skips the SMART read.
2. **The USB HDD (`/dev/sdb`) → ABSENT:** it is surfaced only via the **`driveTargets.Known` union path**
   in `handleDisks`, which sets `backing_device=/dev/sdb` but **never calls `enrich`** (no SMART read at
   all) → the target's `Smart.Health` stays `""` → the controller (correctly) omits it → "Nincs adat".

**Neither disk needs `smartctl -d sat`** — the USB-SATA bridge passes SMART through with plain
`-a -j`. The `-d sat` candidate is **disproven on this hardware**, and the sudoers allowlist does not
grant it anyway (recorded below). **This is findings-only — no fix implemented; the fix gets its own spec.**

---

## Topology (Probe 1 — `lsblk -o NAME,TYPE,TRAN,MOUNTPOINT,MODEL`)

```
NAME                          TYPE TRAN   MOUNTPOINT MODEL
sda                           disk sata              AirDisk 512GB SSD
|-sda1                        part
|-sda2                        part        /boot/efi
`-sda3                        part                              (LVM PV)
  |-pve-swap                  lvm         [SWAP]
  |-pve-root                  lvm         /                     ← the "local" dir storage
  `-pve-data-tpool  …         lvm                               ← pve-data thin pool (VM disks 9201)
sdb                           disk usb    /mnt/hdd_1 TOSHIBA MQ04ABF100   ← the USB data drive
```

The customer's OS **and** every VM disk live on **`sda`** (SATA SSD, via LVM). The data drive is
**`sdb`** (USB). Both are real, SMART-capable disks.

## The `/disks` payload (Probe 2 — controller-side curl to the agent localapi)

```
local-lvm    | type=lvmthin | backing=''         | mount=''          | smart=UNKNOWN
local        | type=local   | backing=''         | mount=''          | smart=UNKNOWN
felhom-pbs   | type=pbs     | backing=''         | mount=''          | smart=UNKNOWN
47a3361a-…   | type=usb     | backing='/dev/sdb' | mount='/mnt/hdd_1' | smart=ABSENT
```

(The controller v0.169.1 already excludes `pbs`/`lvmthin` from the card, so only `local` and the USB
render — both "Nincs adat".)

## Backing-path resolution (Probe 3 — `readlink -f`)

```
readlink -f /dev/sdb  → /dev/sdb           (already a clean whole-disk path; NOT a symlink)
readlink -f /dev/sda  → /dev/sda
/dev/disk/by-uuid/47a3361a-… → ../../sdb   (the durable_id's symlink; the payload already carries the resolved /dev/sdb)
```

→ **The "USB backing is a `by-uuid` symlink the regex can't match" hypothesis does NOT apply here** —
the payload's `backing_device` is already the resolved `/dev/sdb`.

## SMART actually works on both (Probe 4 — the EXACT allowlisted form)

```
# smartctl -a -j /dev/sda   (system SSD)
model: AirDisk 512GB SSD
smart_status.passed: True
messages: []

# smartctl -a -j /dev/sdb   (USB TOSHIBA, behind its USB-SATA bridge)
model: TOSHIBA MQ04ABF100
smart_status.passed: True
messages: ['Warning: This result is based on an Attribute check.']
```

Both return **PASSED** with the plain `-a -j` form — **no `-d sat` needed**. (The USB result is
attribute-based, which is normal for a HDD behind a bridge; still a valid PASSED verdict.)

## Sudoers / capability-manifest (Part 5.4 recording)

- `capability/manifest.go:94` probes with `{"/usr/sbin/smartctl", ["-a","-j","/dev/sda"]}`.
- `/etc/sudoers.d/felhom-agent` grants **only** the `-a -j` form:
  ```
  /usr/sbin/smartctl -a -j /dev/sd[a-z]*,
  /usr/sbin/smartctl -a -j /dev/nvme[0-9]*n[0-9]*,
  /usr/sbin/smartctl -a -j /dev/vd[a-z]*,
  /usr/sbin/smartctl -a -j /dev/hd[a-z]*,
  ```
  → `/dev/sda` **and** `/dev/sdb` are both permitted (the agent CAN read both today). **`-d sat` is
  NOT granted** — a `-d sat` invocation would be denied by sudoers, so any `-d sat` fix needs a
  sudoers **and** manifest widening.

## Per-disk table — why the card says what it says

| storage | backing (payload) | whole disk | `smartctl -a -j` | agent read path | card | root cause |
|---|---|---|---|---|---|---|
| `local` (dir/`pve-root`) | `''` | `sda` (via `/`→`pve-root`→`sda3`, **unresolved**) | **PASSED** | `enrich`: `catDir` but `backing_device==''` → SMART skipped | Nincs adat (UNKNOWN) | dir-on-LVM backing never resolved; `smartDeviceFor` has no dm/LVM branch |
| USB `47a3361a` | `/dev/sdb` | `sdb` | **PASSED** | `driveTargets.Known` union path — **`enrich` never called** | Nincs adat (ABSENT) | union path sets `backing` but runs no SMART read; `smartDeviceFor('/dev/sdb')` *would* resolve it |
| `felhom-pbs` / `local-lvm` | `''` | — (logical/network) | n/a | not `catDir` | (excluded, v0.169.1) | correct — not physical disks |

`smartDeviceFor` (source): maps `…/nvme0n1pN → …/nvme0n1` and `…/sdbN → …/sdb`, then
`ValidateSMARTDevice`. A **whole disk** `/dev/sdb` passes through unchanged and validates → it would
resolve fine if `enrich` were ever called on the union path. **There is no branch for `/dev/mapper/*`
or `/dev/dm-*`,** which is why an LVM-backed dir can never resolve.

---

## Fix directions (graded — DO NOT IMPLEMENT; each needs its own spec)

### A — Resolve dm/LVM → whole disk (reaches the SYSTEM SSD `local`)
Resolve the dir's mount → LV → PV → whole disk (e.g. `lsblk -s -no pkname <dev>`, or
`/sys/block/dm-*/slaves`), set `backing_device` accordingly, and add a **dm/LVM branch to
`smartDeviceFor`**.
- **Reach: HIGH** — the system SSD holds the OS *and* all VM data; it is the single most important disk
  to monitor. Also covers any other dir-on-LVM storage.
- **Risk: MEDIUM** — an LVM VG can span **multiple** PVs (a dir → N disks, not one); the resolver must
  handle the multi-slave/RAID case (probe each, report per-disk) rather than assume one. Must not
  mis-resolve stacked dm (thin pools). Needs a focused test over single-PV and multi-PV shapes.

### B — Enrich the `driveTargets.Known` union path (reaches the USB / felhom data drives)
Run the same SMART read on `handleDisks`' union branch when `backing_device != ""`.
- **Reach: GOOD** — the felhom-enrolled *data* drives (the USB here), which is exactly what a customer
  cares about most for a health card.
- **Risk: LOW** — the backing path is already a clean whole disk, `smartDeviceFor` already resolves it,
  and sudoers already grants `/dev/sd[a-z]*`. Care points: the union path lives in `localapi` while
  `enrich`/`SMART` lives in `storage` (reuse the `o.ops.SMART` seam or a small helper, no duplication),
  and don't pay smartctl on *every* `/disks` call — the controller's 60s TTL cache already blunts the
  storm, but consider a short agent-side memo if `/disks` is hit hard. **Cheapest win; do this first.**

### C — `smartctl -d sat` retry (+ sudoers/manifest widening) — REJECT for now
- **Reach: NONE on this hardware** — the USB bridge already returns PASSED with plain `-a -j`
  (disproven above). Would only matter for a *future* bridge that yields UNKNOWN with `-a -j`.
- **Risk: HIGH-relative** — requires adding `smartctl -d sat -a -j /dev/sd[a-z]*` (+ nvme/vd/hd) to
  **both** `/etc/sudoers.d/felhom-agent` and `capability/manifest.go` (widening the root-command
  allowlist — a privilege-surface change), plus a retry loop. **Keep as a documented fallback**, gated
  on an actual UNKNOWN-with-`-a -j` disk appearing in the field.

### D (minor) — `EvalSymlinks` before the partition regex
Not needed here (`backing_device` is already `/dev/sdb`), but cheap insurance for a future payload that
carries a `/dev/disk/by-*` symlink. **Low priority**, fold into A/B if convenient.

**Recommended order (for the follow-up spec):** **B** (low-risk, covers the data drive) → **A**
(higher value, medium risk, covers the system disk) → skip **C** until a bridge demands it.

---

**STOP.** No agent code, sudoers, or capability-manifest change was made in this spike (read-only
probes only). The fix is a separate task.
