Bridges
Bridges are small companion programs, maintained by the Vexillum developers, that connect a Vexillum reflector to other networks, or connect two Vexillum modes to each other. Each bridge is a separate program with its own repository under repo.kc1awv.net/Vexillum. None of them is built into Vexillum.
How bridges connect
Section titled “How bridges connect”Vexillum has no special “peer” or “interlink” concept. A bridge logs into a Vexillum instance the way an ordinary client does:
- into a DMR instance as a Homebrew repeater,
- into a YSF or NXDN instance as a hotspot,
- into a DExtra instance as a linking client,
- into a VAFM instance as a VAFM client.
That means Vexillum treats the bridge as a normal connected client until you
designate it. You don’t need to change
the Vexillum configuration to allow it beyond what any client needs, such as the DMR
instance password. It also means the bridge has to
follow the same rules as any other client. For example, a DMR instance only
forwards group calls to its configured Talk TG on slot 2, so a DMR bridge’s
-vex-talkgroup must match that value exactly.
Available bridges
Section titled “Available bridges”| Bridge | Connects Vexillum to | Audio handling |
|---|---|---|
| vex-dmr-bridge | One or more Homebrew/MMDVM DMR networks (Brandmeister, TGIF, FreeDMR, FreeSTAR SystemX, DMR+, another Vexillum or urfd) | Frames relayed unchanged |
| vex-openbridge-bridge | Brandmeister, over its OpenBridge server-to-server protocol | Frames relayed unchanged |
| vex-xlx-bridge | A module on an XLX or urfd reflector (D-Star interlink) | Frames relayed unchanged |
| vex-dmr-ysf-bridge | Vexillum DMR ↔ Vexillum YSF | AMBE frames repacked, no transcoding |
| vex-nxdn-ysf-bridge | Vexillum NXDN ↔ Vexillum YSF | AMBE frames repacked, no transcoding |
| vafm-homebrew-bridge | Vexillum VAFM ↔ a Homebrew DMR master | Full transcode (Opus ↔ AMBE+2) |
Cross-mode mesh
Section titled “Cross-mode mesh”A YSF instance sends every voice frame it receives to every connected session,
including bridges. If you run both vex-dmr-ysf-bridge and vex-nxdn-ysf-bridge
against the same YSF instance, you get a three-way conversation:
DMR and NXDN users reach each other through YSF, so no direct DMR ↔ NXDN
bridge is needed. Add vex-dmr-bridge or vex-openbridge-bridge to the same
DMR instance, and outside DMR networks join the conversation too.
Building
Section titled “Building”No bridge publishes binaries yet, so you build them from source. The bridges are written in Go and need Go 1.25 or newer. Each one builds the same way:
Replace the repository and command name with the bridge you want. The only
exception is vafm-homebrew-bridge, which needs native audio codec libraries
and cgo. See its page.
Every bridge takes command-line flags, and -help prints the full list. None of
them uses a configuration file.
Things every bridge has in common
Section titled “Things every bridge has in common”Secrets come from environment variables
Section titled “Secrets come from environment variables”Passwords and passphrases are never passed as flags. Each bridge reads them
from a named environment variable, and a flag like -vex-password-env lets you
change the variable’s name. If a required secret is missing, the bridge exits
at startup with a message naming the variable.
Give the bridge its own identity
Section titled “Give the bridge its own identity”Vexillum credits “last heard” activity to the callsign the connecting session registered with. If a bridge registers with a real operator’s callsign, every relayed call looks like it came from that operator. Give each bridge a distinct identity (for example, a callsign with a suffix, or a dedicated DMR ID) instead of your personal one.
This matters again when you designate the bridge: a designation matches every session with that callsign or DMR repeater ID on the instance. See One designation, one session.
Designate each bridge in Vexillum
Section titled “Designate each bridge in Vexillum”Vexillum can guess which sessions are bridges, but it doesn’t act on the guess. Until you designate a bridge’s session on the admin Bridges page, Vexillum treats it as an ordinary station:
- it’s listed and counted as a connected station,
- calls it relays aren’t labelled as bridged,
- the activity feed, including the VexDV public directory, reports it as a client, and reports its calls as if the talker had connected directly.
Once designated, the session is reported as a bridge under the label you give it, and every call it carries says “via” that label. That way, a call bridged from DMR onto YSF reads as the same conversation rather than a second talker.
To designate a bridge:
- Start the bridge, then open Bridges in the admin interface.
- Under Detected Bridge Connections, find the bridge’s session. Vexillum
lists any session connected from a loopback address, and any DMR session
whose software ID starts with
vex-. Rows marked not designated have a Designate button. - Click Designate. It fills in the instance, and matches by the bridge’s callsign, or on DMR by its repeater ID.
- Type a Label and click Add. Labels are published, so choose
something readers will understand, such as
Brandmeister TG 3170603orDMR-YSF.
A bridge that runs on another host doesn’t connect from loopback, so it isn’t detected. Add its designation by hand in the same form, matching the callsign or DMR repeater ID the bridge presents to Vexillum.
Designate every Vexillum instance a bridge connects to. A cross-mode bridge
has a session on each side, so vex-dmr-ysf-bridge needs one designation on
the DMR instance and one on the YSF instance. Give both the same label.
That’s how Vexillum knows the two sessions are one bridge, and it’s what lets it
link each call the bridge relays to the call it came from. A DMR call relayed
onto YSF is then reported as one transmission heard on two instances, not as two
talkers. Chains work too: in the cross-mode mesh, a DMR call
relayed to YSF and then on to NXDN is reported as one transmission on all three.
Calls from outside the server, such as Brandmeister calls, are labelled “via”
their bridge but aren’t linked to anything.
Tick Required if the instance shouldn’t be considered healthy without the bridge. A required bridge that isn’t connected marks its instance degraded in the admin interface and in the activity feed.
One designation, one session
Section titled “One designation, one session”A bridge has exactly one session on each instance it joins, so each designation should match exactly one session. If the bridge shares a callsign or DMR ID with anyone else connected to the same instance, typically your own hotspot or radio, the designation matches them too:
- their calls are reported as coming through the bridge rather than from them, and
- Vexillum can’t link the bridge’s relayed calls to those calls, because it never treats a bridge’s own traffic as the source of what it relays. Every leg of the transmission shows up separately again.
The Designated Bridges table warns about this with matches N sessions next to the connection status. To fix it, restart the bridge with its own identity (a DMR ID and callsign that nothing else uses on that instance), remove the designation, and designate the bridge’s detected connection again. On DMR, Designate matches by repeater ID, so a dedicated ID is what keeps your hotspot out of it.
Local status endpoint
Section titled “Local status endpoint”Each bridge serves GET /status on -status-addr. It returns JSON with the
state of each leg of the bridge:
This is for local monitoring (curl, uptime checks, systemd health scripts). It does not appear in the Vexillum admin interface.
Running as a service
Section titled “Running as a service”The bridges run in the foreground and log to standard error, so they work well under systemd. A minimal unit looks like this:
Put the passwords in the EnvironmentFile (for example VEX_DMR_PASSWORD=...),
and make that file readable only by root.
License
Section titled “License”All bridges are licensed GPL-2.0-or-later. Each repository’s NOTICE.md and
THIRD_PARTY_NOTICES.md credit the projects they draw on.