go-ctap/hid is a cgo-free Go library for discovering and communicating with HID devices on Windows, macOS, and Linux. It uses native operating-system facilities and requires neither libhidapi nor a C toolchain.
The library was created primarily as the HID backend for go-ctap/ctap, but it is protocol-agnostic and can be used independently with other HID devices. The module is currently pre-v1, so its API may continue to evolve between minor releases.
| Capability | Windows | macOS | Linux |
|---|---|---|---|
| Enumeration and filtering | Yes | Yes | Yes |
| Connection events | Yes | Yes | Yes |
| Input/output reports | Yes | Yes | Yes |
| Feature reports | Yes | Yes | Yes |
| Configurable read timeout | Yes | — | — |
| Native backend | HID, SetupAPI, Configuration Manager | IOKit and Core Foundation via purego |
hidraw, sysfs, and kernel uevents |
The module requires Go 1.25 or newer.
go get github.com/go-ctap/hidEnumerate returns a Go iterator. Filters are exact matches and can be combined; this example selects the FIDO HID usage collection used by go-ctap:
for info, err := range hid.Enumerate(
hid.WithUsagePage(0xf1d0),
hid.WithUsage(0x01),
) {
if err != nil {
log.Printf("enumerate HID: %v", err)
continue
}
log.Printf(
"path=%q vid=%04x pid=%04x product=%q",
info.Path,
info.VendorID,
info.ProductID,
info.ProductStr,
)
}Pass DeviceInfo.Path to OpenPath to get a device with Read, Write, SendFeatureReport, GetFeatureReport, and Close. Output and feature-report buffers begin with the report ID; use 0 for an unnumbered report. Higher-level framing, such as CTAPHID, is intentionally left to packages such as go-ctap/ctap.
Events first publishes a connected event for every HID device already present, then continues with live connected and disconnected events.
receiver, err := hid.Events()
if err != nil {
log.Fatal(err)
}
defer receiver.Close()
for event := range receiver.Listen() {
if event.DeviceInfo != nil {
log.Printf("%s: %s", event.Type, event.DeviceInfo.Path)
}
if event.Err != nil {
log.Printf("HID event metadata: %v", event.Err)
}
}Events cover all HID devices and should be filtered by the caller. Delivery is ordered and queued, so the channel should be consumed continuously or the receiver closed when it is no longer needed. A non-nil DeviceEvent.Err means that the state change occurred but some metadata may be incomplete.
- Device paths are opaque and platform-specific.
DeviceInfometadata is best-effort, and fields unavailable on a platform remain empty or zero. - Enumeration and event monitoring do not guarantee I/O access;
OpenPathremains subject to operating-system, driver, and sandbox policy. - Reads block by default.
WithReadTimeoutis available only in Windows builds; there is no cross-platform context or deadline API. - On macOS, enumeration and events do not open devices, but opening protected devices for I/O may still be denied by system or sandbox policy.
- On Linux, access to
/dev/hidrawNdepends on udev rules and permissions. A connection event may arrive before the device node and its final permissions are ready.
go test ./...
CGO_ENABLED=0 go test ./...
go vet ./...Licensed under the Apache License 2.0.