Skip to content

Mode Protocol Reference

Vexillum is a multi-protocol reflector runtime. Each mode it supports has its own wire protocol, its own packet types, and its own session and stream behavior.

This section documents, mode by mode, what actually goes over the wire: packet formats, byte offsets, state machines, timeouts, and worked examples backed by this implementation’s own test vectors. It exists so that client authors, hotspot and gateway developers, and anyone building an interoperable tool have a concrete reference instead of a Go source tree and a guess.

Mode Transport Default port(s) Authentication Spec fidelity
DMR UDP 62031 RPTK / SHA-256 challenge Homebrew/HBP-style convention
DExtra UDP 30001 None Real DExtra/DSVT wire format, plus a Vexillum-specific control layer
M17 UDP 17000 None M17 project wire conventions
NXDN UDP 41400 None Vexillum-specific, inspired by NXDN concepts
P25 UDP 41000 None Vexillum-specific, minimal opaque relay
VAFM UDP, TCP, TLS, DTLS, WS, WSS 43000/43001 (+ TLS/DTLS/WS variants) Optional shared passphrase Vexillum-native protocol (beta)
YSF UDP 42000 None Vexillum-specific, tag names and framing follow MMDVM/YSFReflector convention

Not every reflector runs every mode, and default ports are defaults, not promises. Always confirm the actual configuration with the reflector operator. See Supported Modes for the user-facing version of this table.

These pages document Vexillum’s own reflector-side implementation of each protocol. They are not, in every case, the official over-the-air or reflector-network specification for that mode.

Fidelity to any external standard varies considerably by mode:

  • DMR, DExtra, and M17 implement conventions with real external lineage - Homebrew/HBP-style repeater protocol, real DExtra/DSVT wire frames, and the M17 project’s own packet formats, respectively - with Vexillum-specific session parameters (timeouts, session caps) layered on top.
  • NXDN, P25, and YSF are simplified, Vexillum-specific reflector protocols in this codebase. They borrow tag names, field concepts, or bit positions from their namesake modes, but they are not implementations of the official NXDN, P25, or YSF/FCS specifications. P25 in particular does no NID/DUID or talkgroup parsing at all, and NXDN uses a single shared stream rather than real per-talkgroup multiplexing.

Each mode’s reference page repeats this distinction briefly near the top, in an aside you can’t scroll past by accident. If you need full interoperability with an existing NXDN, P25, or YSF network rather than with Vexillum specifically, these pages will tell you where this implementation diverges, but they are not a substitute for the official specification of that mode.

All six modes documented here (VAFM included) share the same underlying runtime shape, so it’s described once, here, instead of six times:

  • Each mode runtime keeps a session map keyed by the sender’s UDP address. A session is created on the first recognized login/poll/connect packet from that address and is refreshed on every subsequent packet from the same peer.
  • A cleanup ticker runs on a fixed interval and reaps any session that hasn’t been heard from within that mode’s session timeout. A reaped session’s active stream, if any, is ended at the same time.
  • A stream represents one active transmission. Each mode admits at most one active stream per “channel” (a module, a talkgroup, or a single shared room, depending on the mode) at a time: the first sender to start a stream holds it exclusively until either an explicit end-of-transmission marker arrives from that same sender, or the stream goes idle past that mode’s stream idle timeout, whichever comes first. Competing senders on the same channel are silently ignored while a stream is active.
  • Every mode also bounds its session map at a fixed maximum (typically 4096 entries) so that a flood of unauthenticated login/poll packets from unseen addresses can’t grow memory usage without bound; once the cap is hit, polls from genuinely new addresses are dropped until existing sessions time out.

Each mode’s own reference page gives that mode’s specific timeout values and its specific end-of-transmission signal, rather than re-deriving this shape from scratch.

This is separate from the generic per-instance lifecycle (disabled, stopped, starting, running, stopping, failed) that governs starting and stopping a mode instance at the application level. That lifecycle is shared infrastructure across all modes and isn’t part of the wire protocol - it’s out of scope for these pages.