peripherals
| What it does | USB, PCI and Thunderbolt/USB4 device inventory |
| Version | 1.0.0 |
| Kind | Collector · read-only · gathered (crossplatform.peripherals.usb, crossplatform.peripherals.pci, crossplatform.peripherals.thunderbolt) |
| Platforms | Windows ✅ · macOS ✅ · Linux ✅ |
| Actions | pci (definition crossplatform.peripherals.pci) · thunderbolt (definition crossplatform.peripherals.thunderbolt) · usb (definition crossplatform.peripherals.usb) |
| Security | securable Inventory · operation Read · risk Low · dispatch ReadOnly · approval gate None |
| Roles | execute: endpoint-admin, endpoint-operator · author: content-author |
How it works
usb, pci and thunderbolt each read one bus’s device tree from the operating system and emit one row per device or node. All three are pure reads: nothing is configured, authorised or written. A bus with no devices answers with one <kind>|none row and a complete status; a read that could not be attempted answers with one <kind>|unavailable|<token> row and a CONSTRAINED / PARTIAL status, so an empty list is never standing in for a failure.
Windows asks SetupAPI (SetupDiGetClassDevsW) for present devices under the USB or PCI enumerator, and thunderbolt is the PCI walk filtered to devices whose description names Thunderbolt or USB4. macOS asks IOKit for IOUSBHostDevice, IOPCIDevice or IOThunderboltSwitch services and reads their registry properties. Linux lists /sys/bus/usb/devices, /sys/bus/pci/devices or /sys/bus/thunderbolt/devices and reads the attribute files in each. No leg starts a subprocess.
The plugin does not translate ids into names: no pci.ids or usb.ids database is shipped, so vendor and product text appears only where the operating system already supplies it. It does not cover displays, Bluetooth, audio or camera devices, and it does not report whether a device is working or what is plugged in physically beyond what each bus tree exposes.
flowchart LR
OP["Operator or workflow"] --> SRV["Server authz: Inventory.Read"]
SRV -- "gRPC mTLS" --> HOST["Agent plugin host"] --> EX["peripherals.execute"]
EX --> WIN["Windows leg: SetupAPI"]
EX --> MAC["macOS leg: IOKit registry"]
EX --> LIN["Linux leg: sysfs bus directories"]
WIN --> ROWS["Rows and typed result status"]
MAC --> ROWS
LIN --> ROWS
ROWS --> RS[("ResponseStore")] --> API["REST api/responses"]
What you can decide with it
| Question | Action | Windows | macOS | Linux |
|---|---|---|---|---|
| Which USB devices are attached? | usb | Every present node under the USB enumerator, including each interface of a composite device (instance ids ending &MI_nn) and root hubs. Serial and speed are always -. | One row per USB device (IOUSBHostDevice), with serial and negotiated speed. | One row per USB device; interface entries are skipped. Serial and speed come from sysfs. |
| Which PCI devices does the host have (GPU, NIC, storage controller)? | pci | Vendor, device and class ids and a description; subsystem ids always 0000 and driver always -. | Ids, class, subsystem ids, the registry name as description and a child class name as driver. | Ids, class, subsystem ids and the bound driver; description always -. |
| Is there a Thunderbolt or USB4 controller, and are attached devices authorised? | thunderbolt | The host controller only, found by a text match on the description. No attached devices, no authorisation state. | Controllers and attached switches; authorized is not exposed (-). | Domains (host controller) and attached devices with authorized from sysfs; no live Thunderbolt bus has run this leg. |
| Is this specific model of dock, token or adapter in use? | usb, match vendor_id and product_id | Yes, via the hardware id. | Yes. | Yes. |
Bus by OS, with what each leg cannot show:
| Bus | Source | Row identity | Not visible |
|---|---|---|---|
| USB | SetupAPI, IOKit, sysfs | Windows: device instance id, one row per interface on composite devices. macOS: IOKit location id. Linux: sysfs name such as 1-2.1. | Windows: serial, speed. All: devices behind a hub the OS has not enumerated, and unplugged devices. |
| PCI | SetupAPI, IOKit, sysfs | Windows: instance id. macOS: the registry pcidebug location, which several bridges can share. Linux: 0000:bus:slot.fn. | Windows: subsystem ids and driver. Linux: description. |
| Thunderbolt | SetupAPI text match, IOKit, sysfs | Windows: instance id. macOS: route string. Linux: domainN or N-M. | Windows: any downstream device. macOS: authorisation. Linux: retimer children. |
What a result does and does not prove: a row is a node in the OS device tree, not a physical device, so one dock or keyboard can appear as several USB rows on Windows. usb|none or pci|none with OK means the tree was read and was empty. Two machines with the same peripheral report the same ids, so ids can be joined across the fleet, but only serial (where present) distinguishes one unit from another.
Diagrams
Every action ends in one of three outcomes:
stateDiagram-v2
[*] --> Read
Read --> Rows: devices found
Read --> None: tree read, no devices
Read --> Unavailable: tree could not be read
Rows --> OK
None --> OK
Unavailable --> Constrained
OK --> [*]
Constrained --> [*]
OK carries either device rows or a single <kind>|none row. Constrained carries a single <kind>|unavailable|<token> row naming the cause (Windows getclassdevs_failed with the Win32 error, macOS matching_failed, Linux eacces or read_failed). An absent bus directory on Linux, such as no Thunderbolt bus, is none, not a failure.
OS capability
| Action | Windows | macOS | Linux |
|---|---|---|---|
pci | ✅ supported · rung 1 · SetupAPI (PCI enumerator) | ✅ supported · rung 1 · IOKit IOServiceMatching(IOPCIDevice) | ✅ supported · rung 1 · /sys/bus/pci/devices sysfs attribute reads |
thunderbolt | 🟡 constrained · rung 1 · SetupAPI PCI enumerator, DEVICEDESC contains Thunderbolt/USB4 | ✅ supported · rung 1 · IOKit IOServiceMatching(IOThunderboltSwitch) | 🟡 constrained · rung 1 · /sys/bus/thunderbolt/devices sysfs reads |
usb | ✅ supported · rung 1 · SetupAPI SetupDiGetClassDevsW(USB enumerator) + SPDRP_HARDWAREID/COMPATIBLEIDS | ✅ supported · rung 1 · IOKit IOServiceMatching(IOUSBHostDevice) | ✅ supported · rung 1 · /sys/bus/usb/devices sysfs attribute reads |
Declared limits per leg (descriptor fallback text, verbatim):
thunderbolt/ Windows — string-heuristic identification; no Thunderbolt device class in SetupAPIthunderbolt/ Linux — walk verified against a sysfs fixture tree only; no live Linux venue with a Thunderbolt bus in this run
Privileges and prerequisites
| OS | Runs as | Extra grant needed | Measured | If the read is refused |
|---|---|---|---|---|
| Windows | agent service account (LocalSystem today) | None documented for this plugin (docs/agent-privilege-model.md) | Bare-metal Windows 10.0.26200, LocalSystem (elevated), 2026-09-08; see Sample output | SetupDiGetClassDevsW fails: the action reports <kind>|unavailable|windows:setupapi:getclassdevs_failed:<GetLastError> |
| macOS | agent daemon, root today (the LaunchDaemon has no UserName key; see docs/agent-privilege-model.md) | None: IOKit registry property reads are unprivileged | Bare-metal macOS, unprivileged (uid 501), 2026-09-14; no root-privileged run has been captured, so root is the documented production identity, not a measured one | IOServiceGetMatchingServices fails: <kind>|unavailable|macos:iokit:matching_failed. A query that succeeds but matches nothing is a clean <kind>|none |
| Linux | agent daemon, dedicated unprivileged account, never root by design (docs/agent-privilege-model.md) | None: the sysfs bus directories under /sys/bus/{usb,pci,thunderbolt}/devices are world-readable by default | Debian container, root (euid 0), 2026-09-14; the Thunderbolt walk is exercised only against a fixture tree | A bus directory that exists but cannot be read reports <kind>|unavailable|linux:sysfs:eacces or linux:sysfs:read_failed. An absent bus directory is a clean <kind>|none |
Binaries and subprocesses: none. Every leg is an in-process call (SetupAPI, IOKit, or a direct filesystem read of sysfs). Network: none.
Data contract
Inputs
No action takes parameters.
Outputs
Pipe-delimited rows, one per device or node, with the action name as the first field. Every free-text field goes through the shared untrusted-output escaper, and a field the platform does not supply is -. A bus with no devices reports a single <kind>|none row; a read that could not be attempted reports <kind>|unavailable|<token> instead of device rows. A USB or PCI row is a node in the OS device tree, not necessarily a physical device: Windows lists each interface of a composite USB device separately.
crossplatform.peripherals.pci — bus_path|vendor_id|device_id|class|subsystem_vendor|subsystem_device|driver|description
| Field | Type | Values | Available | Example | Description |
|---|---|---|---|---|---|
bus_path | string | - | Windows, Linux, macOS | 0000:00:02.0 | Platform-native PCI bus location (e.g. a SetupAPI device instance path, a sysfs bus path, or an IOKit registry path). |
vendor_id | string | - | Windows, Linux, macOS | 8086 | PCI vendor id, 4 lowercase hex digits. |
device_id | string | - | Windows, Linux, macOS | 9a49 | PCI device id, 4 lowercase hex digits. |
class | string | - | Windows, Linux, macOS | 030000 | PCI class code (base class + subclass + programming interface), 6 lowercase hex digits. |
subsystem_vendor | string | - | Windows, Linux, macOS | 1028 | PCI subsystem vendor id, 4 lowercase hex digits; “0000” where unread. |
subsystem_device | string | - | Windows, Linux, macOS | 0a5c | PCI subsystem device id, 4 lowercase hex digits; “0000” where unread. |
driver | string | - | Windows, Linux, macOS | i915 | Bound driver name, where the platform exposes one; ”-” otherwise. |
description | string | - | Windows, Linux, macOS | Intel Iris Xe Graphics | Human-readable device description, where the platform exposes one; ”-” otherwise. |
crossplatform.peripherals.thunderbolt — path|role|vendor|model|unique_id|generation|authorized
| Field | Type | Values | Available | Example | Description |
|---|---|---|---|---|---|
path | string | - | Windows, Linux, macOS | domain0/0-0 | Platform-native location for this node (e.g. a SetupAPI device instance path, a sysfs bus path, or an IOKit registry path). |
role | string | - | Windows, Linux, macOS | host_controller | Reported node role. Values: host_controller, device. |
vendor | string | - | Windows, Linux, macOS | Apple Inc. | Vendor string, where the platform exposes one; ”-” otherwise. |
model | string | - | Windows, Linux, macOS | Thunderbolt 4 Pro Dock | Model/device name string, where the platform exposes one; ”-” otherwise. |
unique_id | string | - | Windows, Linux, macOS | 00340000-0044-4123-8000-000103ffffff | Node-unique identifier, where the platform exposes one; ”-” otherwise. |
generation | string | - | Windows, Linux, macOS | 4 | Reported Thunderbolt/USB4 generation, where the platform exposes one (e.g. “4”); ”-” otherwise. |
authorized | string | - | Windows, Linux, macOS | 1 | Security-level authorization state, where the platform exposes one. Values: 1 (authorized), 0 (not authorized), - (not exposed by this platform mechanism). |
crossplatform.peripherals.usb — bus_path|vendor_id|product_id|class|subclass|vendor|product|serial|speed|is_hub
| Field | Type | Values | Available | Example | Description |
|---|---|---|---|---|---|
bus_path | string | - | Windows, Linux, macOS | 1-2.1 | Platform-native bus location for this device (e.g. a SetupAPI device instance path, a sysfs bus path, or an IOKit registry path). |
vendor_id | string | - | Windows, Linux, macOS | 046d | USB vendor id, 4 lowercase hex digits. |
product_id | string | - | Windows, Linux, macOS | c52b | USB product id, 4 lowercase hex digits. |
class | string | - | Windows, Linux, macOS | 09 | USB device class code, 2 lowercase hex digits. |
subclass | string | - | Windows, Linux, macOS | 00 | USB device subclass code, 2 lowercase hex digits. |
vendor | string | - | Windows, Linux, macOS | Logitech | Vendor string, where the device or platform exposes one; ”-” otherwise. |
product | string | - | Windows, Linux, macOS | USB Receiver | Product string, where the device or platform exposes one; ”-” otherwise. |
serial | string | - | Windows, Linux, macOS | - | Device serial number, where exposed; ”-” otherwise. |
speed | string | - | Windows, Linux, macOS | 480 | Negotiated link speed, platform-native token (e.g. “480” Mbps); ”-” where unread. |
is_hub | boolean | - | Windows, Linux, macOS | 0 | Whether this node is itself a USB hub. Values: 1 (hub), 0 (not a hub) — a digit, never true/false. |
Result status
| Status | Completeness | Provenance | When |
|---|---|---|---|
OK | FULL | — | The leg attempted the read and either found devices or found none (<kind>|none) — an empty bus is a complete answer, not a degradation. |
CONSTRAINED | PARTIAL | linux:sysfs:eacces | Linux: the bus’s devices directory exists but could not be opened (permission denied). |
CONSTRAINED | PARTIAL | linux:sysfs:read_failed | Linux: the bus’s devices directory exists but listing it failed for a reason other than EACCES. |
CONSTRAINED | PARTIAL | macos:iokit:matching_failed | macOS: IOServiceGetMatchingServices itself returned a non-success kern_return_t — the IOKit main port was unreachable, not merely “no matches”. |
CONSTRAINED | PARTIAL | windows:setupapi:getclassdevs_failed | Windows: SetupDiGetClassDevsW itself failed; the row and status reason carry the literal token with GetLastError()’s numeric code appended. |
Where the data goes
- Instruction result. Rows travel over the agent’s mTLS gRPC channel as the command response and land in the ResponseStore, queryable at
/api/responses/{id}. Each of the three actions is also a gathered definition (crossplatform.peripherals.usb/.pci/.thunderbolt, 300s TTL). - Not consumed by daily-sync, Echo, DEX, or metrics.
- Sensitivity. Rows carry hardware identifiers (USB/PCI vendor and product ids, serial numbers, Thunderbolt unique ids) that can fingerprint a specific device, and vendor/product strings that name installed peripheral hardware — a software/hardware inventory by another route. No row carries a username or account name.
- Siblings:
hardware(system/BIOS/CPU/memory/disk inventory — this plugin covers the bus-attached device treehardwaredoes not).
Sample output
Windows — captured: windows Windows 10.0.26200 x86_64 · bare-metal · 2026-09-08 · LocalSystem (elevated) · leg-hash 25220299bc79
== action=usb
usb|USB/VID_046D&PID_C548&MI_03/7&247BA993&0&0003|046d|c548|03|00|(Standard system devices)|USB Input Device|-|-|0
usb|USB/VID_0B05&PID_18F3/9876543210|0b05|18f3|00|00|(Standard USB Host Controller)|USB Composite Device|-|-|0
usb|USB/VID_0B0E&PID_2E56&MI_03/9&3135CDCD&0&0003|0b0e|2e56|03|00|(Standard system devices)|USB Input Device|-|-|0
usb|USB/VID_0B0E&PID_2E56/6CFBEDC4770B|0b0e|2e56|00|00|(Standard USB Host Controller)|USB Composite Device|-|-|0
usb|USB/VID_8087&PID_0029/8&384B90AF&0&6|8087|0029|e0|01|Intel Corporation|Intel(R) Wireless Bluetooth(R)|-|-|0
usb|USB/VID_0B05&PID_18F3&MI_02/A&2EFC6B98&0&0002|0b05|18f3|03|00|(Standard system devices)|USB Input Device|-|-|0
usb|USB/ROOT_HUB30/7&14C6E8AD&0&0|0000|0000|00|00|(Standard USB HUBs)|USB Root Hub (USB 3.0)|-|-|0
usb|USB/VID_174C&PID_3074/8&1B878445&0&8|174c|3074|00|00|(Standard USB HUBs)|Generic SuperSpeed USB Hub|-|-|0
usb|USB/VID_046D&PID_C548&MI_01/9&32226396&0&0001|046d|c548|03|01|(Standard system devices)|USB Input Device|-|-|0
usb|USB/VID_046D&PID_C548&MI_02/9&32226396&0&0002|046d|c548|03|00|(Standard system devices)|USB Input Device|-|-|0
usb|USB/VID_046D&PID_C548&MI_00/7&247BA993&0&0000|046d|c548|03|01|Logitech (x64)|Logitech USB Input Device|-|-|0
usb|USB/VID_0B0E&PID_2E56&MI_00/9&3135CDCD&0&0000|0b0e|2e56|01|01|(Generic USB Audio)|Jabra Link 390|-|-|0
… 12 of 24 rows shown
[result_status] OK / FULL
== action=pci
pci|PCI/VEN_1022&DEV_1444&SUBSYS_00000000&REV_00/3&11583659&0&C4|1022|1444|060000|0000|0000|-|PCI standard host CPU bridge
pci|PCI/VEN_1022&DEV_1486&SUBSYS_88081043&REV_00/4&231A312E&0&0141|1022|1486|108000|0000|0000|-|AMD PSP 11.0 Device
pci|PCI/VEN_8086&DEV_1539&SUBSYS_85F01043&REV_03/6&2AD155D1&0&0028000A|8086|1539|020000|0000|0000|-|Intel(R) I211 Gigabit Network Connection
pci|PCI/VEN_1022&DEV_1441&SUBSYS_00000000&REV_00/3&11583659&0&C1|1022|1441|060000|0000|0000|-|PCI standard host CPU bridge
pci|PCI/VEN_1022&DEV_1485&SUBSYS_88081043&REV_00/6&313998C&0&0040000A|1022|1485|130000|0000|0000|-|AMD PCI
pci|PCI/VEN_1022&DEV_790E&SUBSYS_87C01043&REV_51/3&11583659&0&A3|1022|790e|060100|0000|0000|-|PCI standard ISA bridge
pci|PCI/VEN_1022&DEV_1484&SUBSYS_88081043&REV_00/3&11583659&0&39|1022|1484|060400|0000|0000|-|PCI-to-PCI Bridge
pci|PCI/VEN_1022&DEV_1484&SUBSYS_88081043&REV_00/3&11583659&0&41|1022|1484|060400|0000|0000|-|PCI-to-PCI Bridge
pci|PCI/VEN_1022&DEV_149C&SUBSYS_88081043&REV_00/6&313998C&0&0140000A|1022|149c|0c0330|0000|0000|-|USB xHCI Compliant Host Controller
pci|PCI/VEN_10DE&DEV_228B&SUBSYS_40761458&REV_A1/4&1D81E16&0&0119|10de|228b|040300|0000|0000|-|High Definition Audio Controller
pci|PCI/VEN_1022&DEV_57A3&SUBSYS_88081043&REV_00/5&2BFB86CE&0&08000A|1022|57a3|060400|0000|0000|-|PCI-to-PCI Bridge
pci|PCI/VEN_1022&DEV_1446&SUBSYS_00000000&REV_00/3&11583659&0&C6|1022|1446|060000|0000|0000|-|PCI standard host CPU bridge
… 12 of 48 rows shown
[result_status] OK / FULL
== action=thunderbolt
thunderbolt|none
[result_status] OK / FULL
macOS — captured: macos macOS 26.6.2 arm64 · bare-metal · 2026-09-14 · euid 501 (USB hub serial and Thunderbolt controller IDs replaced with placeholders) · leg-hash 25220299bc79
== action=usb
usb|02200000|05ac|800c|09|00|Apple|USB3 Gen2 Hub|0000001|super+|1
usb|02100000|05ac|800b|09|00|Apple|USB2 Hub|0000001|high|1
[result_status] OK / FULL
== action=pci
pci|0:0:0(1:128)|106b|1017|060400|0000|0000|ApplePCIECHostBridge|pci-bridge
pci|0:0:0(1:128)|106b|1017|060400|0000|0000|ApplePCIECHostBridge|pci-bridge
pci|0:0:0(1:128)|106b|1017|060400|0000|0000|ApplePCIECHostBridge|pci-bridge
pci|0:0:0(2:2)|106b|100c|060400|0000|0000|ApplePCIEHostBridge|pci-bridge
pci|0:2:0(1:1)|106b|100c|060400|0000|0000|ApplePCIEHostBridge|pci-bridge
pci|1:0:0|14e4|1682|020000|14e4|1682|BCM5701Enet|ethernet
pci|2:0:0|14e4|4434|028000|106b|4388|IOUserService|pci14e4,4434
pci|2:0:1|14e4|5f72|028000|106b|4388|AppleConvergedPCI|pci14e4,5f72
[result_status] OK / FULL
== action=thunderbolt
thunderbolt|0|host_controller|Apple Inc.|iOS|05ac000000000001|-|-
thunderbolt|0|host_controller|Apple Inc.|iOS|05ac000000000002|-|-
thunderbolt|0|host_controller|Apple Inc.|iOS|05ac000000000003|-|-
[result_status] OK / FULL
Linux — captured: linux Debian GNU/Linux 13 (trixie) aarch64 · container · 2026-09-14 · euid 0 · leg-hash 25220299bc79
== action=usb
usb|usb1|1d6b|0002|09|00|Linux 7.0.12-linuxkit vhci_hcd|USB/IP Virtual Host Controller|vhci_hcd.0|480|1
usb|usb2|1d6b|0003|09|00|Linux 7.0.12-linuxkit vhci_hcd|USB/IP Virtual Host Controller|vhci_hcd.0|10000|1
[result_status] OK / FULL
== action=pci
pci|0000:00:08.0|1af4|105a|018000|1af4|005a|virtio-pci|-
pci|0000:00:0d.0|1af4|105a|018000|1af4|005a|virtio-pci|-
pci|0000:00:10.0|1af4|1045|058000|1af4|0045|virtio-pci|-
pci|0000:00:01.0|1af4|1041|020000|1af4|0041|virtio-pci|-
pci|0000:00:07.0|1af4|1042|018000|1af4|0042|virtio-pci|-
pci|0000:00:0c.0|1af4|105a|018000|1af4|005a|virtio-pci|-
pci|0000:00:0f.0|1af4|1044|100000|1af4|0044|virtio-pci|-
pci|0000:00:00.0|106b|1a05|060000|0000|0000|-|-
pci|0000:00:06.0|1af4|1042|018000|1af4|0042|virtio-pci|-
pci|0000:00:0b.0|1af4|105a|018000|1af4|005a|virtio-pci|-
pci|0000:00:09.0|1af4|105a|018000|1af4|005a|virtio-pci|-
pci|0000:00:0e.0|1af4|1053|078000|1af4|0053|virtio-pci|-
… 12 of 14 rows shown
[result_status] OK / FULL
== action=thunderbolt
thunderbolt|none
[result_status] OK / FULL
Caveats and known gaps
- Windows Thunderbolt identification is a text match and sees only the host controller. SetupAPI has no Thunderbolt device class, so the leg matches a PCI device description containing “Thunderbolt” or “USB4” and reports every match as role
host_controller. Downstream Thunderbolt devices and their authorisation state are not visible on Windows. - Linux Thunderbolt is verified against a fixture tree only. The
/sys/bus/thunderbolt/deviceswalk has been exercised against a synthetic sysfs tree; no live Linux host with a Thunderbolt bus has run it. The Linux sample is a container showing only virtual USB and virtio PCI devices, so it does not show real hardware rows. - Some fields are always unfilled. Linux
pcidescriptionis always-because sysfs carries none. Windowspcihas subsystem ids0000anddriver-, and Windowsusbhas serial and speed-; the Windows USBvendoris the driver manufacturer string, so it can read like(Standard USB Host Controller)rather than a company. - macOS
pciandthunderboltidentities are weak.driveris the registry child’s class name (for example a bridge class), not a driver bundle identifier, and thepcidebuglocation used asbus_pathcan repeat across bridges, as the sample shows.authorizedis always-on macOS. - Coverage is limited to the three buses. There is no vendor-name lookup and no coverage of displays, Bluetooth, audio or camera devices. A name that looks incomplete, such as a blank vendor, means the operating system did not supply one.
Source and tests
- Plugin:
agents/plugins/peripherals/src/peripherals_legs.hpp·agents/plugins/peripherals/src/peripherals_linux.cpp·agents/plugins/peripherals/src/peripherals_linux_parsers.hpp·agents/plugins/peripherals/src/peripherals_macos.cpp·agents/plugins/peripherals/src/peripherals_macos_parsers.hpp·agents/plugins/peripherals/src/peripherals_parsers.hpp·agents/plugins/peripherals/src/peripherals_plugin.cpp·agents/plugins/peripherals/src/peripherals_win.cpp·agents/plugins/peripherals/src/peripherals_win_parsers.hpp - Definitions:
content/definitions/peripherals.yaml - Capability rows:
server/core/src/capability_decls/plugin_action_catalogue_peripherals.hpp - Tests:
tests/unit/test_peripherals_linux_parsers.cpp·tests/unit/test_peripherals_local_dispatcher.cpp·tests/unit/test_peripherals_macos_parsers.cpp·tests/unit/test_peripherals_parsers.cpp·tests/unit/test_peripherals_win_parsers.cpp - Privilege row:
docs/agent-privilege-model.md