Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
WxAlerts.org, Community Driven Weather Data
Support us
Sign in
MeshMonitor

What it decides to send

The decision path for one alert, why repeats are suppressed, and how the airtime budget rations an outbreak.


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:

  1. Does it concern you? On an office subscription, alerts covering none of your counties are dropped; see office_alerts.
  2. 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.
  3. Is it news? The feed republishes a live warning on every follow-up statement. Repeats are suppressed unless something escalated.
  4. Is the rule inside its own min_interval?
  5. Render it to the byte budget.
  6. 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 MinorModerateSevereExtreme
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. burst is 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. priority on 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.