Documentation
¶
Overview ¶
Package serialdev enumerates host serial devices and records which ones a run has been approved to use.
Approval is trust-on-first-use: the first run to use a device name records that device's identity (see Pin), and later runs must present the same device. Enforcement lives here rather than in the kernel because the devices cgroup can only filter on major:minor, which cannot distinguish two boards of the same model. See docs/plans/2026-08-15-serial-devices-design.md.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoMatch means no attached device matched the config's vid/pid. ErrNoMatch = errors.New("no matching serial device") // ErrAmbiguous means several devices matched. Match still returns the // candidates so callers can name them in the error shown to the user. ErrAmbiguous = errors.New("multiple matching serial devices") )
var ErrPinMismatch = errors.New("serial device does not match its pin")
ErrPinMismatch means the attached device is not the one previously approved under this name. It is always fatal rather than a warning: a swapped device is the exact case pinning exists to catch.
var ErrPinNameTaken = errors.New("a device is already pinned under that name")
ErrPinNameTaken is returned by Rename when the new name already holds a pin.
var ErrPinNotFound = errors.New("no device is pinned under that name")
ErrPinNotFound is returned when an operation names a pin that does not exist.
Functions ¶
func DefaultPinPath ¶
func DefaultPinPath() string
DefaultPinPath is where pins live under the moat home directory. MOAT_HOME overrides the location the same way it does for every other moat state.
Types ¶
type Device ¶
type Device struct {
Path string // host device node, e.g. /dev/ttyUSB0 or /dev/cu.usbserial-14200
VID string // 4-hex-digit USB vendor ID, lowercase
PID string // 4-hex-digit USB product ID, lowercase
Serial string // USB iSerial, empty on devices that ship without one
PortPath string // physical topology: sysfs port path (Linux) or IOKit locationID (macOS)
Description string // human label for `moat device list`
// Interface distinguishes the UARTs of a multi-interface bridge (FT2232H,
// CP2105, ESP-Prog): one USB device, several ttys, everything else about
// them identical. It is the interface's number within the device — "0",
// "1" — and empty when the platform cannot see it.
Interface string
// contains filtered or unexported fields
}
Device is a serial port attached to the host.
type Enumerator ¶
Enumerator lists serial devices attached to the host.
It is an interface so tests can supply a fixed device list: every automated test in this area runs without hardware.
func NewEnumerator ¶
func NewEnumerator() Enumerator
NewEnumerator returns the host's serial device enumerator.
type EnumeratorFunc ¶
EnumeratorFunc adapts a function to the Enumerator interface.
type Matcher ¶
type Matcher struct {
VID string
PID string
// Interface narrows a match to one UART of a multi-interface bridge, where
// several ttys share a USB ID. Empty matches any interface, which keeps
// single-UART devices as they were.
Interface string
}
Matcher selects devices by USB vendor and product ID.
type Pin ¶
type Pin struct {
Name string `json:"name"`
VID string `json:"vid"`
PID string `json:"pid"`
Serial string `json:"serial,omitempty"`
PortPath string `json:"port_path,omitempty"`
// Interface is the UART index of a multi-interface bridge (FT2232H,
// CP2105). Empty on pins written before the field existed, which skips
// the interface check rather than demanding "0": CDC modems expose their
// single tty on interface 1.
Interface string `json:"interface,omitempty"`
FirstSeen time.Time `json:"first_seen"`
}
Pin records the identity a device name was first approved with.
func (Pin) PinnedByPortPath ¶
PinnedByPortPath reports whether this pin identifies its device only by physical port, which happens when the device ships without a serial number (common on cheap CH340 and CP2102 clones).
Callers must surface this in the consent prompt: such a pin approves whatever is plugged into that port, not that particular device.
type PinStore ¶
type PinStore struct {
// contains filtered or unexported fields
}
PinStore persists pins as JSON. It is safe for concurrent use within a process, and across processes via an advisory lock on a sidecar lock file: two moat processes approving devices concurrently would otherwise race — each reads the file at open, each writes its own map back, and the second write drops the first's pins, silently re-arming trust-on-first-use for every device the loser had pinned.
func OpenPinStore ¶
OpenPinStore loads the store at path, creating an empty one if absent.
A malformed file is an error rather than an empty store: silently discarding pins would silently disable the approval check.
The store keeps the file's lock sidecar open so mutations can flock it; a store whose lock file cannot be opened still works single-process (lf is nil, lockFile is skipped) rather than failing every run.
func (*PinStore) Forget ¶
Forget removes a pin so the next run re-approves the device. Forgetting an unknown name is not an error.
func (*PinStore) Rename ¶
Rename moves a pin from one name to another under a single lock.
Doing this as Put+Forget from the caller would leave a window where the hardware is pinned twice, and a crash inside it would make that permanent — the state this exists to avoid, since a device pinned under two names is invisible in `moat device list` (it reports the first pin that verifies). Renaming onto an existing name fails rather than overwriting: that pin belongs to some other hardware, and silently replacing it would un-approve a device the user never mentioned.
type USBEnumerator ¶
USBEnumerator lists USB devices the serial broker cannot serve: ones with no serial interface at all (SDR dongles, keyboards, storage). It exists so `moat device list` can show a plugged-in-but-not-serial device instead of implying nothing is attached. Its devices carry a USB identity (VID/PID, serial, port path) but Path is always empty.
type USBEnumeratorFunc ¶
USBEnumeratorFunc adapts a function to the USBEnumerator interface.