Skip to content

Reference — CLI, configuration, packaging ​

One C binary, two roles: muxen-portal --daemon runs the daemon — the service and socket units are named muxen-portal too, one name everywhere — and any other invocation is the thin ctl client.

The daemon follows the MUXEN standards: Meson build, .deb packaging, systemd service under muxen.target, running as user muxen. It depends on libstdmuxen and libmosquitto, and talks to muxen-uds over its WebSocket API.

CLI ​

CommandEffect
open <deviceId|uid> --rate <bps|baud> [--channel <1-4>] [--listen-only] [--serial-cfg 8N1] [--lin-checksum <rule>] [--fd] [--name portal0] [--timeout <s>] [--wait]Full session bring-up: upload, handshake, OPEN, create the mirror. The mirror type is derived from the gateway, not passed by you. The target is a deviceId or the 16-hex-character muxen UID — the stable identity, which also matches an orphaned portal directly. --channel selects the serial UART channel (default 1). --fd requests the negotiated FD transport. Submits and returns; --wait blocks until active or failed.
open-channel <1-4> [--rate <baud>] [--serial-cfg 8N1] [--lin-checksum <rule>] [--wait]Open an additional channel on the running session (additive OPEN, no re-upload) — creates <name>-chN, or promotes a monitor channel.
set-rate <bps|baud> [--channel <1-4>] [--listen-only on|off] [--serial-cfg 8N1] [--lin-checksum <rule>] [--name portal0]Hot reconfiguration via re-OPEN — milliseconds, a brief restart of that channel only, no re-upload. The CAN equivalent of stty on a PTY; makes bitrate hunting on an unknown remote bus cheap.
set-term on|off [--channel <1-4>] [--name portal0]Drive the remote line termination. Hot, and the state survives re-OPENs. Refused locally with a clear message if the gateway did not advertise termination control.
close [--name portal0] [--wait]CLOSE, verify the revert, destroy the mirrors. Also aborts a still-opening session. --wait blocks until the revert is verified.
status [--json]Session state, remote line state, termination state, counters, sequence-loss statistics, PTY reader presence, local drop counters. Serial and LIN sessions list every port the gateway has, open or not.
stats [--watch] [--json]Per-port TX/RX table from the gateway's point of view. --watch repaints once a second.
timing <1-4> [--window-ms N] [--arm|--collect] [--json]Measure the shape of the traffic on a serial channel: arms the gateway's wire-timing collector for --window-ms (100–60000, default 5000) and prints the gap and burst histograms plus a plain-language reading. Reads a port without opening its PTY. Serial gateways only.
list-targets [--json]Gateways on the MUXEN bus that support the portal, with their type.
-h, --help, -V, --versionThe two MUXEN-standard global flags, answered by both roles without touching the daemon socket.

--rate defaults: 250000 for CAN gateways (typical NMEA 2000 / J1939 networks run at 125 k or 250 k) and 19200 for serial and LIN gateways. A board may state its own default where the peer is soldered to the gateway — muxen_can-enocean opens at 57600.

--lin-checksum takes classic, enhanced or auto (the default). --listen-only selects listen-only on CAN and monitor mode on LIN.

Every command submits, prints the immediate result and exits: the shell always comes back at once, never held for the session's lifetime. timing is the one deliberate exception, because it is a measurement — the connection is held for the whole window, bounded at 60 s.

At open, the CLI names the resolved firmware image and prints a warning listing the product identities that will go offline. On a bus-extender gateway the warning is generic: the entire segment behind it goes offline, and those devices cannot be enumerated.

The ctl socket ​

The CLI is a thin control client over a Unix stream socket /run/muxen-portal/ctl.sock (systemd RuntimeDirectory, with an $XDG_RUNTIME_DIR fallback for non-systemd runs), carrying newline-delimited JSON:

request  {"cmd": "open" | "close" | "status" | "stats" | "list-targets" | "timing", …}
reply    {"status": "ok" | "error", "data": …, "error": …}

One connection per request. The daemon owns all state. muxen-detect is a second client of this same socket.

The ids of a request are checked before anything changes, and one out of range is answered with "status": "error": deviceId must be a JSON integer 1–4095 (function << 6 | instance, 12 bits; 0 addresses no gateway), and channel a JSON integer 1–4 (then also one the gateway announced, and open where the command needs it). A string such as "57", a float, a boolean or null is refused like a value out of range. Earlier daemons cut them to 16 and 8 bits instead, so deviceId 4153 opened device 57 and channel 257 was channel 1. The CLI sends what it is given and lets the daemon decide, except that open still checks its target itself; muxen-detect refuses a deviceId above 4095.

timing accepts three modes: measure (default — arm, hold the connection, reply with the report), arm (arm and reply at once, holding the report) and collect (take the held report, or wait if the window is still open).

Daemon configuration ​

/etc/muxen/portal.conf, all keys optional. Every key is also accepted as a --key=value command-line flag of muxen-portal --daemon, and the flag wins over the file.

KeyDefaultMeaning
interfacecan0the MUXEN CAN bus
uds-url—muxen-uds WebSocket endpoint
mqtt-host, mqtt-port—broker for the portal/session topic
nameportal0 / tty-portal0default mirror name
allow-fdfalsepermit attempting the CAN-FD data plane. The daemon then probes the bus before switching and falls back to ISO-TP at runtime if the FD plane stops delivering. Keep it off unless the installation is meant to be FD-tolerant.
mirror-linger180 s (0 disables)how long a mirror survives a session loss while the daemon re-establishes the session
pty-buffer64 KiBper-channel daemon-side RX ring between the tunnel and the PTY
stmin0 ms (valid 0–10)ISO-TP STmin for the tunnel pairs, both directions. 0 keeps full tunnel throughput; raise it (1 ms is the muxen-uds convention) to soften the dispatch load on bus nodes at the cost of mirroring throughput. Bounded at 10 because beyond that a single data transfer would starve STATUS and false-trip the loss detector.
ping-timeoutprotocol default (~30 s)gateway keep-alive, in 100 ms units
monitor-channelstrue (bare flag --no-monitor-channels)on serial and LIN sessions, open every channel the gateway advertises so its counters run from t₀

What the daemon needs from muxen-uds ​

Everything goes through the existing WebSocket API — scan, uid, readconfig, firmware, reset — and rests on four properties:

  1. Function code 0x3E is listed in scans. During a session the gateway broadcasts life frames as function 0x3E with the assigned instance. muxen-uds must list such a device, UID included, without filtering it out as unknown.
  2. The firmware store accepts the portal images. They are registered by the muxen-portal package and must be selectable for the firmware upload command like any product image, although they ship outside the muxen-firmware blob.
  3. A UID may switch identity. Within one session the same UID appears as the product identity, then as portal 0x3E, then as the product again. Revert verification relies on the 0x3E entry disappearing and the product entry returning under the recorded function and instance.
  4. The capability table belongs to the portal. The HardwareId → board/image map is owned by the portal daemon; muxen-uds only supplies the scan and readconfig values.

The link to muxen-uds counts as up only once the daemon has sent its hello, the first frame of every connection since muxen-uds 10.0.0. The portal logs the daemon's name, version and host name from it, and logs an error for any of uid, readconfig, firmware and reset missing from its features. A rejected request ({"type":"error"}) fails like a transport error; an error frame without a uuid is logged. Frames of any other type are ignored.

A daemon that opens with anything but a hello, or says nothing within 5 s, predates 10.0.0: the portal logs muxen-uds 10.0.0 or later required and keeps the link down, so open and list-targets fail with muxen-uds is not reachable.

The package declares Depends: muxen-uds (>= 10.0.0~), and muxen-uds 10.0.0 in turn Breaks: muxen-portal (<< 1.2.0~).

The daemon does not write UDS traffic itself, with one exception: the UDS reset (0x11), a stateless single frame on the target's own ID pair. It sends that itself to its portal device (teardown fallback and orphan unblocking) and to the product device (aborting an in-flight upload on close). Those frames never collide with muxen-uds's own pairs.

Bridging internals ​

The Brain-side ISO-TP endpoints use the kernel can-isotp module, one socket per plane — the control and data ID pairs. Sockets bind arbitrary tx/rx ID pairs, so the ACCESS = 3 tunnel coexists with muxen-uds traffic without coordination. On transmit the daemon services the control socket ahead of the data socket, so keep-alive and teardown latency stay bounded regardless of mirror load.

Packaging ​

The package muxen-portal contains the daemon/ctl binary, muxen-detect, the seven firmware images, the systemd units and bash completion for both binaries.

  • Socket activation. muxen-portal.socket owns the ctl socket and the first connection starts the daemon, so there is no resident process while the portal is unused. The MQTT expireAfterSec convention already reads "daemon not running" as "no session".
  • Firmware travels in this package, not in muxen-firmware. The images are installed into /usr/lib/muxen/firmware/portal/, the muxen-uds firmware store directory. Both the daemon and muxen-uds read that directory directly, so installing the files is registering them — visible in listfirmware, selectable by the firmware command. There is no postinst registration step, dpkg replaces them on upgrade and removes them on purge, and nothing is pushed to any gateway at install time.
  • The srec versions track the package version, so an apt upgrade muxen-portal bumps binaries and images together. A gateway is only ever touched during an open, so "the upgrade updated the firmware" means the next session uses the new image. The fleet is never mass-flashed and no gateway carries portal firmware persistently.
  • A source-only build without the CI firmware artifacts still yields a valid package; the daemon reports "no portal image installed for this board" until the images are present.
  • Recommends: pulls can-utils, a serial terminal (picocom | minicom | tio | screen) and bash-completion — the tools that actually drive the mirrors. A --no-install-recommends install still works.

Versioning and release flow are identical to the other MUXEN daemons: meson.build + debian/changelog + git tag.

Integration of multiplexed solutions
MUXEN and the MUXEN logo are trademarks of MUXEN SAS.