Whilst researching this, I had a look at some of the libraries out there to see how they do it on different operating systems.
Microsoft Windows and Apple MacOS X provide system APIs for doing this.
On Linux, some libraries go poking around in `/dev/serial/*`, others go nosing through `/sys/class/tty/*`. Some just look for specific names in `/dev` and hope for the best. `pyserial` also checks `/proc/tty/drivers` on Linux, but everywhere else in POSIX land it uses `glob`.
Most do not consider BSD at all. You're on your own to learn what serial ports your computer has. It's a mess.
#NetBSD has a `/kern` virtual filesystem which is similar to `/sys` in Linux… so there *is* scope there to maybe put some sort of virtual text file that dumps a mapping of devices and what device nodes they're at. My mind also thinks about OpenFirmware's DeviceTree.
BSD in general, has the same problem Linux had years ago: it assumed the hardware never changed. In the 80s and 90s, that was mostly true, your devices were hard-wired into "busses". You mostly ignored that there was a ISA bus, you just "knew" you had a device at address 0x300 and "talked" to it. It was there at power on, and there at shutdown.
SCSI was a little more complicated, you spoke to your disk drive *through* this HBA card.
Then came along Firewire, USB and Bluetooth. Devices could come and go as they pleased. Moreover, USB is a tree structure and Bluetooth is a mesh radio network. A flat structure doesn't quite describe the situation. I'm seeing a lot of wisdom behind the idea of Open Firmware's DeviceTree… and it seems to me, the kernel of a modern OS needs to track a live changing representation of this. A big part of keeping track will be providing a mechanism for the userspace to learn of changes in this tree, and to find where things are.
While I don't expect them to wholesale copy Linux, I think there is merit in the kernel calling a userspace application to "set up" devices when they are detected, and "tear down" those devices when they are removed.
This is `udev` in Linux and it is very handy even if it can be a pain in the bum to debug.
I've also seen the virtual `/dev` filesystem route used. Linux used to have `devfs`, and SyllableOS also used such a system. Not saying that's the best approach, but it is another way of exposing this information to the system.
Not sure which is the "right" approach, but I don't think a static "nothing ever changes" approach is viable here.