Most of what this bridge does is decline to transmit. Understanding the order it decides in is most of operating one, because nearly every question you will be asked is a variant of “why did that not go out”.
The decision path
For a single alert, in order:
- Does it concern you? On an office subscription, alerts covering none of
your counties are dropped; see
office_alerts. - Does a rule match it? No rule, no send. The config is an allowlist, which means a new phenomenon code appearing upstream cannot start transmitting on its own.
- Is it news? The feed republishes a live warning on every follow-up statement. Repeats are suppressed unless something escalated.
- Is the rule inside its own
min_interval? - Render it to the byte budget.
- Queue it by priority, and let the token bucket decide when it goes.
Every step has a counter in /stats, so the answer to “why” is always a
subtraction.
Only news is sent
A warning is republished roughly every fifteen minutes for the life of the
hazard, as a CON statement saying it is still in effect. That is exactly what
a duty-cycled channel cannot afford, so a hazard already announced is
re-transmitted only when:
| Change | |
|---|---|
| Severity rose | Minor → Moderate → Severe → Extreme |
Action became UPG |
The hazard was upgraded |
Action changed to EXA, EXB or EXT |
Extended in area or in time |
CAN and EXP are not treated as updates: a hazard ending arrives on the
tombstone path instead.
State is keyed on VTEC, not on topic. A warning covering three counties is published on three county topics plus its office topic with identical payloads, so keying on the topic would put it on the mesh three times.
Airtime, not bandwidth
LoRa is shared and half-duplex, and Meshtastic rebroadcasts everything, so a chatty bridge degrades the channel for every node in range, including people whose traffic is an actual emergency. Two mechanisms keep it in bounds:
- A token bucket spends from
max_per_hour, refilled continuously rather than all at once. The budget is a rate, not a burst at the top of each hour.burstis how many can go out back to back before the rate takes over. - A priority queue, not FIFO. During an outbreak the feed produces more
than the budget allows, and FIFO would drain flood advisories issued at 2:01
while a tornado warning issued at 2:02 waited behind them.
priorityon each rule is what inverts that.
Anything that waits past queue_max_age is discarded rather than delivered
late. A warning that arrives after it expired is worse than silence, because it
teaches people to ignore the channel.
Restarts are quiet
The feed’s alert topics are retained, so subscribing replays every hazard
currently in effect, and on a busy day dozens at once. For warmup_seconds after
each connect the bridge records that backlog into state without sending any of
it.
This is also why the service keeps no database: the broker is the state store, and it is re-read on every connect. The cost is that a hazard already in effect when the bridge starts is never announced: it is treated as something already said. Its all-clear still goes out when it ends.
An empty payload on an alert topic is a tombstone: the hazard is over and its retained copy is being deleted. The retained set is therefore exactly the live set, and a client connecting after a hazard ends never sees it at all.
The size limit comes from the source
MeshMonitor fronts both Meshtastic and MeshCore, and they are not the same radio:
| Source | Channel message | Direct message |
|---|---|---|
| Meshtastic | 237 bytes | 237 bytes |
| MeshCore | 130 bytes | 150 bytes |
The server rejects an oversized message with a 400 rather than truncating it,
so the source type is detected at startup and max_bytes is lowered to match,
rather than being discovered one failed alert at a time. Setting max_bytes
higher than the source allows does nothing; setting it lower is respected.
On a MeshCore source that 130-byte ceiling is doing real work: an event name, a place and a time is most of it, which is why the message tiers drop the description first and the instruction last.
All-clears
A hazard ending is announced only if this bridge announced the hazard itself,
and only if the matching rule has all_clear on. Telling a mesh that a warning
it never heard about is over is pure noise, and on a busy evening it would
roughly double the traffic.
All-clears queue at ten below the rule’s own priority: worth sending, never worth displacing a live hazard from a constrained queue.
