Device naming: what /dev/serial/by-id/ covers, and what it does not¶
Generated 2026-10-03 from catalog/hardware/. 34 devices.
This project's stated highest-value hardware feature was persistent udev
symlinks by serial. A Proxmark3 capture put that in doubt, because
systemd's 60-serial.rules already composes /dev/serial/by-id/ paths
from the descriptor strings, per unit, with no help from us — and if that
covers the catalog, our symlink work is redundant.
So this is an accounting rather than an argument. Three questions per device, and the third column is the one that changed the plan.
The counts¶
| Devices | |
|---|---|
Get a /dev/serial/by-id/ path for every confirmed identifier |
9 |
| Get one for some identifiers and not others | 3 |
| Get none at all — nothing they present is a serial interface | 15 |
| Not yet recorded either way | 7 |
| Where by-id is insufficient for at least one reason | 29 of 34 |
| Carry a udev symlink from this catalog | 7 |
| …of which duplicate a path by-id would have given anyway | 0 |
The last row is the finding. Every symlink this catalog emits is on a
device with no /dev/serial/by-id/ entry at all. The two mechanisms have
not overlapped once — not by design, which makes it worth stating: the
symlinks were written for SDRs, and SDRs are libusb devices that systemd's
serial rule never sees. Nothing here is redundant, and nothing here was
the main event either.
Why by-id is insufficient, by reason¶
A device can hit more than one, so these do not sum to the row above.
| Reason | Devices | What it means |
|---|---|---|
no-serial-subsystem |
16 | No /dev/serial/ entry exists. The device is claimed by libusb, or by a storage/HID class driver, and systemd's serial rule never sees it. |
no-unit-serial |
1 | It is a serial device, but supplies no per-unit serial. by-id composes its path from manufacturer, product and serial, so two of these collide there exactly as they would under a naive symlink. |
unlabelled-ports |
4 | One interface presents several ports. by-id hands out a stable path for each and labels none of them; a stable path to a port you cannot identify is not an answer. |
unrecorded |
9 | Nobody has recorded what kind of interface this is, so the question cannot be answered yet. Counted as uncovered rather than assumed away. |
Per device¶
by-id — does systemd give it a stable path. Ours — what this catalog
adds on top. Insufficient because — empty means by-id genuinely settles it
and we should not be inventing work.
| Device | by-id | Ours | Insufficient because |
|---|---|---|---|
airspy |
no | symlink /dev/airspy, access, packages |
no-serial-subsystem |
bladerf |
no | symlink /dev/bladerf, access, packages, firmware mode |
no-serial-subsystem |
btech-uv-50pro |
yes | (distro names it: /dev/serial/by-id/, from systemd's own 60-serial.rules), access, packages, documented gap | unlabelled-ports |
c5-wardriver |
yes | access, packages, firmware mode | — |
catsniffer-v3 |
yes | access, packages, documented gap | — |
clip-boy |
yes | access, packages, firmware mode | — |
dell-dw5821e |
yes | — | unlabelled-ports |
dell-dw5930e |
unknown | documented gap | unrecorded |
flipper-zero |
partly | access, packages, firmware mode, documented gap | no-serial-subsystem |
fobos-sdr |
no | symlink /dev/fobos-sdr, access, packages, firmware mode, documented gap |
no-serial-subsystem |
free-wili-2 |
partly | access, interface map, packages, documented gap | no-serial-subsystem, unlabelled-ports |
funcube-dongle |
no | packages | no-serial-subsystem |
hackrf-one |
no | symlink /dev/hackrf, access, packages, firmware mode |
no-serial-subsystem |
hackrf-pro |
no | symlink /dev/hackrf-pro, access, packages, firmware mode |
no-serial-subsystem |
hydrasdr-rfone |
no | symlink /dev/hydrasdr-rfone, access, packages, firmware mode, documented gap |
no-serial-subsystem |
intel-ax210-bluetooth |
no | — | unrecorded |
krakensdr |
unknown | access, packages, documented gap | unrecorded |
librevna |
unknown | access, packages, documented gap | unrecorded |
limesdr |
no | access, packages, documented gap | no-serial-subsystem |
meshtastic |
partly | access, packages, firmware mode, documented gap | no-serial-subsystem |
minino |
yes | access, packages, firmware mode | — |
orbic-rc400l |
unknown | access, packages, documented gap | unrecorded |
plutosdr |
unknown | access, packages, documented gap | unrecorded |
portapack-h4m |
unknown | access, packages, documented gap | unrecorded |
proxmark3 |
yes | access, firmware mode, documented gap | no-unit-serial |
rnode |
yes | access, packages, firmware mode, documented gap | — |
rtl-sdr |
no | access, packages | no-serial-subsystem |
sdrplay-rsp |
no | access, packages, documented gap | no-serial-subsystem |
sunplus-integrated-webcam-fhd |
no | — | unrecorded |
ubertooth-one |
no | symlink /dev/ubertooth, access, packages |
no-serial-subsystem |
uconsole |
unknown | documented gap | unrecorded |
usrp |
no | access, packages | no-serial-subsystem |
wi-spy |
no | access, packages | no-serial-subsystem |
yaesu-ft-991a |
yes | (distro names it: /dev/serial/by-id/, from systemd's own 60-serial.rules), access, packages, documented gap | unlabelled-ports |
Classes¶
A class is a naming subject too, and one of them carries the finding this table exists to make visible: some devices are already named by the package that drives them.
| Class | by-id | Named by | Devices in it |
|---|---|---|---|
badgelife |
yes | nothing device-specific | 5 |
bluetooth-controller |
no | nothing device-specific | 1 |
camera |
no | nothing device-specific | 1 |
dmr-radio |
partly | nothing device-specific | 0 |
gps-receiver |
yes | the distribution — /dev/gpsN, from gpsd's own 60-gpsd.rules |
0 |
nfc-reader |
no | us — /dev/nfc |
0 |
programmer |
no | nothing device-specific | 0 |
rig |
yes | the distribution — /dev/serial/by-id/, from systemd's own 60-serial.rules |
2 |
wwan-modem |
yes | nothing device-specific | 1 |
What this changes¶
by-id gives a stable path. It does not give:
- Permissions. A device only root can open is unusable however stable
its path. This is what actually stops people, and by-id does nothing for
it. Group membership and
uaccesstagging are ours. - Non-serial devices. Every SDR in this catalog, the Ubertooth and the
Proxmark in client mode are libusb devices with no
/dev/serial/entry at all. - Identical units. A device supplying no per-unit serial collides in
by-id exactly as it would under a naive symlink.
/dev/serial/by-path/separates them and is topology, so it changes when the cable moves. -
Knowing which interface is which. A multi-port device gets a stable path per port and a label on none of them.
-
A name somebody else already provides.
gpsdshipsSYMLINK+="gps%n", so a GNSS receiver is/dev/gps0with nothing from us. Writing our own on top would be a D-022 displacement with no benefit; recording that the distribution did it is the useful act.
So the hardware layer's value is permissions, composite-device mapping, firmware-mode identification, and honest documentation of the cases nothing solves. Symlinks are one tactic, used where the evidence supports one — which, so far, is exactly where by-id cannot reach.