yuzu

disk_actions

What it doesReports physical drive health and the mapping between drives and the logical volumes they back
Version1.0.0
KindCollector · read-only · on-demand
PlatformsWindows ✅ · macOS 🟡 constrained · Linux 🟡 planned
Actionspartitions (definition crossplatform.storage.partitions) · smart (definition crossplatform.storage.smart) · topology (definition crossplatform.storage.topology) · volumes (definition crossplatform.storage.volumes)
Securitysecurable Inventory · operation Read · risk Low · dispatch ReadOnly · approval gate None
Rolesexecute: endpoint-admin, endpoint-operator · author: content-author

How it works

Both actions are reads and change nothing. smart lists the physical drives the OS exposes and reports each one’s model, transport (bus) and media type; on Windows, NVMe drives also answer a SMART/Health query whose critical-warning flags become a health verdict, with the raw wear and spare-capacity percentages alongside. volumes lists logical volumes and, for each, the physical drive that backs it and the mount points or drive letters it serves. On Windows smart probes PhysicalDrive0 to PhysicalDrive63; on macOS both actions walk the IOKit registry; Linux returns a single placeholder row and an UNAVAILABLE status.

volumes is deliberately not a third volume inventory: hardware.disks already lists physical devices and filesystem_posture.mounts already lists mounts. This action carries only the join between them, which is what lets a failing-drive alert name the drive letters or mount points it affects. Where a mechanism is unavailable (Linux) or is reachable only through a private OS interface (macOS health), the leg says so in its result rather than guessing.

partitions and topology read nothing yet: on every OS each answers with exactly one status|<action>|unsupported|<os>:planned row from the shared planned-leg reporter (agents/shared/planned_leg.hpp), until their leg PRs land (partitions on every OS; topology on Linux for LVM/md/multipath and on macOS for APFS containers; Windows has no topology source ruled yet).

flowchart LR
  OP["Operator or workflow"] --> SRV["Server: Inventory Read, concurrency per device"]
  SRV -- "gRPC mTLS" --> HOST["Agent plugin host"] --> EX["disk_actions.execute"]
  EX --> WIN["Windows: storage IOCTLs on drive and volume handles"]
  EX --> MAC["macOS: IOKit registry reads and getmntinfo_r_np"]
  EX --> LIN["Linux: placeholder row and UNAVAILABLE"]
  WIN & MAC & LIN --> ROWS["Pipe rows and typed result status"]
  ROWS -- "CommandResponse" --> RS[("ResponseStore")]
  RS --> API["REST /api/responses"]

What you can decide with it

QuestionAction and columnWindowsmacOSLinux
Is a drive reporting a failure or warning?smart healthfailing or warning for NVMe drives only (derivation below)Never; health is always unknownNothing
How worn is the drive?smart pct_used, spare_pctNVMe drives only. pct_used runs 0 to 255 and can exceed 100-Nothing
What kind of drive is it?smart model, bus, mediaAll drives the OS listsAll block devices, disk images included (bus virtual)Nothing
Which drive letters or mount points does this drive serve?volumes device, mount_pointsOne row per volume; a spanned volume lists one entry per disk extent, comma-separated, so the same drive can appear more than onceOne row per partition or container member that maps a mount or a drive; device is the physical disk, for example disk0Nothing
How full is it, or what is its filesystem usage?not this pluginUse crossplatform.storage.mountsUse crossplatform.storage.mountsn/a

How health is derived. For an NVMe drive the plugin reads the SMART/Health log page and looks only at its critical-warning byte. If bit 0 (available spare below threshold) or bit 2 (reliability degraded) is set, health is failing. If any other bit is set (temperature, media read-only, volatile-memory backup failed, or a bit the plugin does not know), it is warning. If no bit is set it is ok. pct_used and spare_pct are reported but never change the verdict, so a drive at 100 percent or more of its rated endurance with no warning flag still reads ok; set your own wear threshold on pct_used if you want one.

What a missing failing does and does not prove. Only Windows NVMe drives are ever judged. SATA and USB drives, virtual machine disks and every drive on macOS report unknown, and Linux reports unsupported, so the absence of a failing row says nothing about those devices. Alert on failing and warning, and track the unknown and unsupported rows separately to see which devices the fleet cannot judge. ok means the drive reported no critical warning at read time, not that it will not fail.

Diagrams

From a drive to a health value

flowchart TD
  S["smart: one row per drive"] --> O{"Which OS?"}
  O -- "Linux" --> L["Placeholder row, health unsupported"]
  O -- "macOS" --> M["health unknown, detail says whether the device advertises SMART"]
  O -- "Windows" --> B{"Is the bus NVMe?"}
  B -- "no: SATA, USB, SAS, virtual or unknown" --> U1["health unknown, no wear figures"]
  B -- "yes" --> P{"Did the SMART/Health log page come back?"}
  P -- "no" --> U2["health unknown, CONSTRAINED windows:nvme_health"]
  P -- "yes" --> C{"Critical-warning byte"}
  C -- "bit 0 or bit 2 set" --> F["failing"]
  C -- "any other bit set" --> W["warning"]
  C -- "no bit set" --> OK["ok"]

Wear (pct_used) and spare (spare_pct) come from the same log page and are printed on the row, but the decision uses the critical-warning byte alone. Every path that cannot read the page ends in unknown, never in ok.

How volumes joins volumes to drives

flowchart TD
  WV["Windows: volume GUID path from FindFirstVolumeW"] --> WM["Drive letters and mount folders for the volume"]
  WV --> WE["Disk extents of the volume"]
  WE --> WD["PhysicalDriveN, several when the volume is spanned"]
  MP["macOS: IOMedia partition, for example disk3s1"] --> MW["Walk the IOKit providers to the first whole IOMedia"]
  MW --> MD["Physical disk, for example disk0, not the synthesized APFS container"]
  MP --> MM["Mount points matched by BSD name through getmntinfo_r_np"]
  WM & WD --> ROW["volume row: volume, mount_points, device, fstype, total_bytes"]
  MM & MD --> ROW

On Windows the physical drive comes from the volume’s disk extents, so a spanned or striped volume names every drive it touches. On macOS the drive comes from walking the IOKit provider chain, because trimming the BSD name would stop at a synthesized APFS container that is not a physical device. A whole disk that serves no mount point is left out, since it carries no join.

OS capability

ActionWindowsmacOSLinux
partitions🟡 planned · rung 1 · Win32_DiskPartition + Win32_LogicalDisk🟡 planned · rung 1 · diskutil list + getmntinfo🟡 planned · rung 1 · /proc/self/mountinfo + /sys/block/*/
smart✅ supported · rung 1 · IOCTL_STORAGE_QUERY_PROPERTY (StorageDeviceProperty + StorageDeviceSeekPenaltyProperty + StorageDeviceProtocolSpecificProperty, NVMe log page 0x02)🟡 constrained · rung 1 · IOKit IOBlockStorageDevice device characteristics⛔ unsupported
topology🟡 planned · rung 1 · no source ruled; stays planned🟡 planned · rung 1 · diskutil apfs list🟡 planned · rung 1 · /sys/block/dm-/dm/{name,uuid}, md/md, slaves
volumes🟡 constrained · rung 1 · FindFirstVolumeW + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS + GetVolumePathNamesForVolumeNameW🟡 constrained · rung 1 · IOKit IOMedia provider walk + getmntinfo_r_np(3) MNT_NOWAIT⛔ unsupported

Declared limits per leg (descriptor fallback text, verbatim):

Privileges and prerequisites

OSRuns asExtra grant neededMeasuredIf the read is refused
Windowsagent service account (LocalSystem today; the packaged service has not yet moved to the virtual service account)None measured for smart: the drive handle is opened with zero access rights, not GENERIC_READ, and both IOCTLs are FILE_ANY_ACCESS, so it keeps working when the agent runs as a lower-privileged account. volumes opens each volume handle the same way, but that has not been measured under an unprivileged account; a refusal there is reported as denied, never as “the device did not answer”smart: 2026-09-03 on bare metal as NT AUTHORITY\LOCAL SERVICE. volumes: elevated only (2026-09-06)PERMISSION_DENIED / partial, provenance windows:physicaldrive:denied (a drive handle) or windows:volume_enum:denied (a volume handle or the volume enumeration)
macOSLaunchDaemon, root today (docs/agent-privilege-model.md)None. IOKit property reads only; no user client is opened, so the leg needs nothing root has2026-09-03 and 2026-09-06 at euid 501 (an unprivileged user, a stricter test than the daemon’s)never PERMISSION_DENIED: a refused or failed IOKit read, including a failed service match, is CONSTRAINED (partial, macos:iokit or macos:iomedia and their sub-tokens), and UNAVAILABLE only when the enumeration returned nothing at all
Linuxn/an/a, leg not implementedn/aalways UNAVAILABLE

The two planned actions read nothing on any OS and need no grant. No external binaries, no subprocesses, no network access. Do not change the Windows drive open to GENERIC_READ: that breaks the leg under an unprivileged account.

Data contract

Inputs

No action takes parameters.

Outputs

Pipe-delimited rows, one per drive or volume. Field 0 is a literal discriminator (smart or volume; the planned actions answer one status row, status|<action>|unsupported|<os>:planned, and their final partition and topology row schemas are declared in disk_actions_legs.hpp) that precedes the columns below; every row of a kind has the same field count, with - where a value is inapplicable or was not read. - never means zero, and a macOS smart row always has - for pct_used and spare_pct. Every free-text field (device, model, volume, mount points, filesystem, detail) passes through safe_output_field, which rewrites \ as /; bus, media and health are fixed vocabularies and the numeric fields are digits, so they need no escaping. Windows volume GUID paths read //?/Volume{…}/ and drive letters C:/. A leg that finds nothing still emits one placeholder row so zero rows can never be misread as “no disks”: a smart placeholder carries health=unsupported, a volume placeholder carries volume=-, and both are accompanied by a typed status, which is the authoritative signal.

crossplatform.storage.partitions — row_kind|disk|partition|size_bytes|type|mount_points|detail

FieldTypeValuesAvailableExampleDescription
row_kindstringstatus partitionWindows, Linux, macOSstatusRow shape discriminator (the wire row’s leading tag). Values: status (exactly one per result, always first), partition (one per record read).
diskstring-Windows, Linux, macOSdisk0Physical disk the partition belongs to. Status rows: the action name.
partitionstring-Windows, Linux, macOSdisk0s2Partition identifier. Status rows: the level (unsupported).
size_bytesstring-Windows, Linux, macOS494384795648Partition size in bytes, or ”-” when unread. Status rows: the reason token (:planned).
typestring-Windows, Linux, macOSapfsPartition type or filesystem hint, or ”-”. Absent on status rows.
mount_pointsstring-Windows, Linux, macOS/Comma-separated mount points or drive letters, or ”-”. Absent on status rows.
detailstring-Windows, Linux, macOS-Free text, or ”-”. Absent on status rows.

crossplatform.storage.smart — device|model|bus|media|health|pct_used|spare_pct|detail

FieldTypeValuesAvailableExampleDescription
devicestring-Windows, macOSPhysicalDrive0OS device name of the physical drive
modelstring-Windows, macOSSamsung SSD 970 EVO Plus 1TBDrive model string as reported by the OS, escaped
busstringnvme sata usb sas virtual unknownWindows, macOSnvmeTransport, a closed vocabulary
mediastringssd hdd unknownWindows, macOSssdMedia type, a closed vocabulary
healthstringok warning failing unknown unsupportedWindowsokHealth verdict. unknown means the device did not say; unsupported means this OS leg cannot ask. Windows reports a verdict for NVMe drives only; macOS always reports unknown; Linux always unsupported.
pct_usedstring-Windows5NVMe “Percentage Used” (0-255 by specification, never clamped), or ”-” when not read.
spare_pctstring-Windows100NVMe “Available Spare” (0-100), or ”-” when not read.
detailstring-all-Free text, or ”-”.

crossplatform.storage.topology — row_kind|kind|name|uuid|members|detail

FieldTypeValuesAvailableExampleDescription
row_kindstringstatus topologyWindows, Linux, macOSstatusRow shape discriminator (the wire row’s leading tag). Values: status (exactly one per result, always first), topology (one per record read).
kindstring-Windows, Linux, macOSlvm_vgLayer kind. Values: lvm_vg, lvm_lv, md, multipath, apfs_container. Status rows: the action name.
namestring-Windows, Linux, macOSvg0Layer name. Status rows: the level (unsupported).
uuidstring-Windows, Linux, macOS-Layer UUID, or ”-”. Status rows: the reason token (:planned).
membersstring-Windows, Linux, macOSsda2,sdb2Comma-separated member devices, or ”-”. Absent on status rows.
detailstring-Windows, Linux, macOS-Free text, or ”-”. Absent on status rows.

crossplatform.storage.volumes — volume|mount_points|device|fstype|total_bytes|detail

FieldTypeValuesAvailableExampleDescription
volumestring-Windows, macOSdisk3s1s1Volume GUID path (Windows) or BSD name (macOS); ”-” only on the empty-result placeholder row.
mount_pointsstring-Windows, macOS/System/Volumes/DataComma-separated mount points or drive letters, or ”-” when the volume serves none.
devicestring-Windows, macOSdisk0Backing physical drive; several, comma-separated, on a spanned volume.
fstypestring-Windows, macOSapfsFilesystem of the volume (NTFS, FAT32, apfs, …), or ”-” when unmounted or unknown.
total_bytesint64-Windows, macOS494384795648Raw media capacity (sum of disk extents on Windows, IOMedia size on macOS), not filesystem capacity. Not comparable with crossplatform.storage.mounts.total_bytes.
detailstring-all-Free text, or ”-”.

Result status

Surfaced as plugin_result_status on the command response.

StatusCompletenessProvenanceWhen
OK (derived from UNDECLARED)--clean read: volumes on Windows and macOS, smart on Windows. A macOS smart run is never clean (next row)
CONSTRAINEDpartialWindows: windows:physicaldrive · windows:physicaldrive_cap · windows:storage_device_property · windows:nvme_health · windows:volume_enum · windows:volume_extents:unread · windows:volume_extents:truncated · windows:volume_mount_paths · windows:volume_information. macOS: macos:iokit · macos:iokit:no_bsd_name · macos:iokit:health_unread · macos:iomedia · macos:iomedia:no_bsd_name · macos:provider_walk · macos:getmntinfo_r_npa per-device read degraded and the row carries - for what was not read. Every macOS smart run reports macos:iokit:health_unread (identity read, health not) unless a stronger cause outranks it. windows:physicaldrive_cap means the probe of PhysicalDrive0 to PhysicalDrive63 did not end in a run of eight absent indices, so a host with more drives may be reported short. windows:volume_extents:truncated means a volume reported more disk extents than the reply could carry, so its device lists only some drives
UNAVAILABLEpartiallinux:smart · linux:volumes · windows:physicaldrive · macos:iokit · macos:iomediaLinux, always (placeholder row). Windows smart and macOS (both actions) when the enumeration ran but produced no device or mapping row at all. Windows volumes never reports it. On macOS, a failed IOKit service match is CONSTRAINED (macos:iokit or macos:iomedia), not UNAVAILABLE
UNAVAILABLEunknownwindows:planned · linux:planned · macos:plannedpartitions or topology on any OS: a planned action, one unsupported status row, nothing read
PERMISSION_DENIEDpartialwindows:physicaldrive:denied · windows:volume_enum:deniedWindows only: a drive or volume handle open, or the volume enumeration, was refused; outranks every other degradation. The macOS legs never report it

Where the data goes

Sample output

Windows — captured: windows Windows 11 Pro 10.0.26200 · bare-metal · 2026-09-07 · interactive user (elevated) · leg-hash 3abb2323b0be

== action=smart
smart|PhysicalDrive0|Samsung SSD 970 EVO Plus 250GB|nvme|ssd|ok|5|100|-
smart|PhysicalDrive1|Samsung SSD 970 EVO Plus 1TB|nvme|ssd|ok|1|100|-
[result_status] UNDECLARED / UNKNOWN

== action=volumes
volume|//?/Volume{0a35801e-3f40-4114-a073-3d7ea1dd7664}/|-|PhysicalDrive0|NTFS|524288000|-
volume|//?/Volume{c9a5f911-4689-41b2-b774-5b0e25b60e10}/|C:/|PhysicalDrive0|NTFS|248158093312|-
volume|//?/Volume{daa59534-884a-4ce4-8fc8-4244f7bbe7ae}/|-|PhysicalDrive0|NTFS|967835648|-
volume|//?/Volume{785383ef-b682-41d9-9b28-27c4b8882d65}/|D:/|PhysicalDrive1|NTFS|1000187363328|-
volume|//?/Volume{b188886e-39bb-4915-a893-d61a3ca3c707}/|-|PhysicalDrive0|FAT32|272629760|-
[result_status] UNDECLARED / UNKNOWN

macOS — captured: macos macOS 26.6.2 arm64 · bare-metal · 2026-09-07 · euid 501 · leg-hash 3abb2323b0be

== action=smart
smart|disk0|APPLE SSD AP0512Z|nvme|ssd|unknown|-|-|device advertises SMART; health attributes are not read on macOS (they require Apple's private IONVMeSMARTUserClient interface)
[result_status] CONSTRAINED / PARTIAL / macos:iokit:health_unread

== action=volumes
volume|disk0s1|-|disk0|-|524288000|-
volume|disk0s3|-|disk0|-|5368664064|-
volume|disk0s2|-|disk0|-|494384795648|-
volume|disk1|-|disk0|-|524288000|-
volume|disk2|-|disk0|-|5368664064|-
volume|disk3|-|disk0|-|494384795648|-
volume|disk1s1|/System/Volumes/iSCPreboot|disk0|apfs|524288000|-
volume|disk1s4|-|disk0|-|524288000|-
volume|disk1s2|/System/Volumes/xarts|disk0|apfs|524288000|-
volume|disk1s3|/System/Volumes/Hardware|disk0|apfs|524288000|-
volume|disk2s1|-|disk0|-|5368664064|-
volume|disk2s2|-|disk0|-|5368664064|-
… 12 of 19 rows shown
[result_status] UNDECLARED / UNKNOWN

Linux — captured: linux Debian GNU/Linux 13 (trixie) aarch64 · container · 2026-09-06 · euid 0 · leg-hash 3abb2323b0be

== action=smart
smart|-|-|unknown|unknown|unsupported|-|-|drive health is not implemented on Linux in this release; no mechanism was bound against real hardware
[result_status] UNAVAILABLE / PARTIAL / linux:smart

== action=volumes
volume|-|-|-|-|-|physical-to-logical volume mapping is not implemented on Linux in this release; it ships with the smart leg it exists to support
[result_status] UNAVAILABLE / PARTIAL / linux:volumes

Caveats and known gaps

  1. health is the drive’s own critical-warning flag, and only Windows NVMe drives have one. Wear and spare percentages never feed the verdict, so a heavily worn drive with no warning flag reads ok. SATA, USB and virtual machine disks report unknown because IOCTL_STORAGE_PREDICT_FAILURE is deliberately not used as a fallback (it fails on NVMe), so a missing failing is not evidence of a healthy drive.
  2. macOS reports identity only. health is always unknown and pct_used and spare_pct are always -, because the attributes sit behind Apple’s private IONVMeSMARTUserClient interface, which the plugin does not use; every macOS smart run is also CONSTRAINED with macos:iokit:health_unread.
  3. Linux is declared, not implemented. Both actions return one placeholder row (health=unsupported, or volume=-) and UNAVAILABLE, and the definitions target Windows and macOS only. A Linux leg would bind the NVMe admin get-log-page call, whose log page the Windows decoder already reads, and has not been measured on real storage. partitions and topology are advertised planned on all three OSes, though Windows has no LVM/md topology source and macOS has no multipath; the leg PRs move such legs to a typed unsupported with a reason, so until then <os>:planned means “not read”, never “this host has no partitions”.
  4. No deadline on Windows device I/O. DeviceIoControl is synchronous with no timeout, so a wedged device or a disconnected network LUN can park the dispatch worker that called it. The 64-drive probe limit and suppression of the not-ready-volume dialog bound the ordinary case, not a driver that never returns.
  5. total_bytes is media capacity, not filesystem capacity. On macOS every APFS volume in a container reports the container’s size, and unmounted members and containers appear with - mount points; on Windows it is the sum of the volume’s disk extents. Use crossplatform.storage.mounts for real capacity.

Source and tests