Gateways
–
Contacts
–
Repeaters
–
Messages 24h
–
Heard paths
–
Edges
–

Mesh map · locations are anonymized — markers are approximate to about a kilometre

loading map…

Roster · active window · all nodes ↗

name type received position · approxpubkey

⏰ Clock skew · advert clocks running ahead · expand for the list

nodetypeclock ahead byadvert clock saysroster updated

Chat · last 24 h · rows tinted by the mesh that heard them

whenwhomeshmessagesnrroutes

Topology graph

Click a node or link to inspect it · double-click a node to expand its neighbours · a hollow node is a relay we cannot name honestly

Busiest relays

Heard paths · click a row to trace it on the dashboard map

nodepathheard

Topology edges · relay a → b

a → b seen last
Clocks in sync
–
Drifting
–
Badly wrong
–
Window
–

Clock health · every advert carries the gap between that node's clock and ours

nodetype skew now median drift samples last heard
Positive means the node's clock runs behind ours. A node on the power-on seed reads about two years back; one that never reached NTP creeps. Drift is only shown when the readings actually trend — a clock that resets between adverts jumps, and a slope fitted across the jump would be fiction.

Adverts · heard packets only · click a row to inspect

no adverts captured yet. Enable set advert.dump on on a gateway
receivednodetyperegionskewsnrhopsheard by

Packet inspector · select an advert

Adverts are signed, not encrypted, so the whole packet decodes with no keys. Pick one on the left to see its decoded fields and raw bytes.

Bad coordinates · a node's position is self-reported and nothing verifies it. These cannot be right, so they are kept off the map — and listed here so they can be fixed.

Impossible
–
Outside North America
–
Null island
–
Every reported position is believable right now.
nodetypereported positionproblemheard bylast heard

Recent paths · !path traces reported by the mesh. Click one for the map + LOS profile

whenwhohops resolvedlength

Multi-path runs · !mpath floods: every gateway that heard the request, each over its own route. Click one for the map

whenwhogatewaysrouteshopsstate
Don't use openHop? See how you can still contribute with the radio you already run.

Run an observer · the radio your bots answer through

Observer access isn't on for this account yet. Enabling it creates your broker login, one credential for every gateway you run, and unlocks the setup below. A gateway is an openHop repeater running the txmesh plugin; here is what running one involves.

① Your MQTT login · one credential for every gateway you run

Every gateway you run signs in to txmesh with this login and publishes under its own node name. The password is stored and shown here, so set up as many nodes as you like without rotating. Only use Rotate if it leaked: every gateway using the current password stops connecting until you update its config.

login
–
endpoint
–

② Run the gateway · an openHop repeater plus the txmesh plugin

A gateway is an openHop repeater running the openhop-txmesh-plugin. The plugin adds one more identity to the radio you already have, reports what it hears here, and transmits the replies for your !commands. Nothing to flash and no dedicated hardware.

  1. On openHop Repeater 1.1.4 or newer, open the Plugins page and upload the wheel from the latest release. Older repeaters, or a separate machine: follow the manual setup.
  2. Run openhop-txmesh keygen and add the companion below to the repeater's config.yaml with the key it prints. Restart the repeater.
  3. Give the plugin the settings below (the Plugins page form, or config.json) and enable it. Your node appears under Devices within a few minutes.

Full walkthrough: run an observer (this site) · plugin manager install · every setting · operations & privacy.

node name · what the mesh sees · pick one unique to you
channels · comma-separated · #hashtags need no secret
Public is always joined. A private channel needs its secret as hex; add that in the file by hand.
companion · goes under identities: in the repeater's config.yaml
…
plugin config.json
…

③ No openHop repeater? · contribute what your radio already hears

If you run meshcore-ha, the Home Assistant integration, your node can feed txmesh without an openHop repeater. Its sightings, hop chains and channel messages land in the archive, your node appears under Devices, and every path trace on the site gets sharper for it. It is data only: meshcore-ha has no way to transmit a reply, so it will not answer !commands. That still needs the plugin above.

meshcore-ha broker settings · Settings → MQTT → add a broker · uses the node name from ② above
…

Your login may only publish under meshcore/<username>/, so the first segment of both topics has to be exactly that. Anything else is refused by the broker and nothing arrives. The second segment is your node name; {PUBLIC_KEY} works there too if you would rather not pick one, at the cost of a 64-character name on the Devices page.

or join MeshTexas instead

Nothing above has to be your route in. MeshTexas’ connect guide walks through putting a node on mqtt.meshtexas.org, whether that is flashing an ESP32 board, meshcoretomqtt on a RAK, or meshcore-packet-capture. No account needed. We subscribe to their feed, so a node set up there reaches txmesh with nothing to configure here. What you give up is ownership of it: those sightings are archived as meshtexas.org/<node>, which is analytics only: the node is not a gateway of yours, joins no mesh here, and can run no bots.

Regions · last 24 h · a repeater set with region put only forwards floods scoped to a region it holds · each node is drawn in its region's colour

Named regions · click one to list its nodes

loading…
case-sensitive, as set with region put; matching packets are named within a minute

Nodes

pick a region to list the nodes flooding in it

Mesh coverage · the Gulf region (TX · LA · MS · AL · FL), colored by the mesh that watches each county · overlaps blend · hover a county

Known meshes

loading…

All nodes · every node heard in the last 24 hours (contact cards + adverts) · click a row for the node page

node type mesh seen by last heard position pubkey

My commands · !commands that run on your observers — they fire only on traffic your gateways hear and reply through your radios

Send via my observers · transmitted as your gateway, rate-limited 6/min on-device · fleet-wide sends are admin-only

My transmissions · your sends + your commands' replies

whenviachtextsource

Site settings · what this deployment calls itself, and where it points

Stored in the database, so they outlive a redeploy. A field still on its deployment default follows the environment variable in the manifests; changing it here takes over, and Reset hands it back. Pages pick changes up within ~10 s, the collector within ~30 s.

Credentials are deliberately not editable here. The broker password, the device tokens and the database DSN stay environment variables fed from cluster secrets, so an admin account can never read them back out of a web form.

NWS active feed · live National Weather Service alerts for Texas (the same cached feed !nws and the auto-poller read) ·

severityeventcountiesmatches meshonsetends

Announcement history · warnings this system has transmitted to a mesh (NWS + river-flood), newest first

whenkindmeshdetail

External data sources · analytics-only connectors: foreign MQTT brokers (meshcore-mqtt-broker packet family) or community map APIs (CoreScope, PotatoMesh) · never feed bots · changes go live within ~30s

namekindurltopic / pollregions map owner msgslast msglast connect
MQTT: subscriber credentials come from the foreign broker's operator (for meshcore-mqtt-broker instances like meshtexas, the device JWT model is publishers-only — ask for subscriber credentials). Map APIs: give the map's base URL (e.g. https://corescope.example.org); nodes are polled on the chosen interval, MeshCore nodes only, and the first region code labels the sightings' mesh. Bots always run on our own gateways, never on foreign traffic.

External observers · per-observer sighting stats from the sources above (packet sources appear per origin, map sources as one row)

observerviaregionstatus last advertadverts 24hnodes heard

Submit tokens · hand one to an integrator who asks: their submissions become a named sender, pre-trusted for precise coordinates · shown once, stored hashed

labelnoteissued byissuedlast used uses

Path senders · who's POSTing !path traces to the open submit API · tick trusted to store a sender's exact endpoint coordinates instead of the ~1 km privacy coarsening · static-token senders are always trusted · tick short links to run one sender's /p/ links through the shortener

no path submissions yet
senderkindpathsfirst seenlast seenlast path trusted short links

Origins · who has transmitted from behind a repeater · a packet's first hop is the repeater that heard the sender directly, so pick the ones around a place and see every name and radio that started there

Every lookup is logged with your name and the reason you give, and admins can read that log.

Radios · from adverts: a public key is the radio's identity, and it keeps it across every name change · positions are the newest the radio shared

no advert started at these repeaters
keynames advertisedtype from here viafirstlastposition

Senders · from channel messages: a name is typed by the sender and unsigned, so it is a lead, not an identity · click a row for the messages and every route they were heard on

no channel message started at these repeaters
sender from here shareviafirstlast

Lookup audit · every origin lookup ever run, newest first · admins only

whenwhoscoperepeatersrangereason found

Locate · estimate where a radio is from the signal it leaves behind · gateways that heard it directly give a distance from the SNR (radios run ~22 dBm); indirect hearings place it within one hop of the repeater that relayed it first · the answer is a fuzzy area on purpose

Every lookup is logged with your name and the reason you give, and admins can read that log.

Location · when the radio reports its own position that is the answer (red pin) and the RF estimate is only a cross-check; with no report the RF estimate stands alone · shaded = RF area, faint rings are the evidence (solid = a direct hearing's distance, dashed = within one hop of a relaying repeater)

Hearing gateways · every gateway that heard this radio directly, strongest first · distance is estimated from the median SNR, and all of them (plus relayed hearings) feed the estimate, each weighted by precision

no direct hearings in this window
gatewaySNR readings est. distanceposition

Lookup audit · every locate lookup ever run, newest first · admins only

whenwhoscoperadiowindowreason result

Dropped packets · adverts the collector refused at ingest · never reach the dashboard, roster, map, or any node page · after the 2026-09-17 incident with the MikeHawk / Suds observers, this is where their corruption would surface instead

Every row is an advert that validate_advert rejected before touching the archive: broken clocks (adv_ts=2, year-2105 timestamps), out-of-range positions (lat=566), control-byte names, unknown roles. Visible here, admin-only; the operator can see the pattern without any garbage leaking into live views.

ingested observerreasonpubkeyname adv_ts skew latlon

Registered users · read-only sees data only · region manager runs one mesh's bots + sends (assign the mesh below) · admin manages users

userrolemeshobserveremailactivityregisteredlast loginsessions

Send to mesh · via the observer send bridge · sent as the gateway, rate-limited 6/min on-device

Recent transmissions · UI sends + bot replies

whenviachtextsource

Observers · every gateway we have registered, with live health (telemetry, battery, boots) · click a card for the full observer page

Inactive · no live publish for h; click to list them · they come back to the cards on their next publish · archives now, deletes
nodeownermeshlast seenlast telemetryarchives
Archived · hidden from the live views; click to list them · a gateway that publishes again restores itself · removes it and clears its retained MQTT topics
nodeownerfirst seenlast seenmessagesarchived

Device MQTT credentials · per-device broker logins, scoped to their own meshcore/<user>/# · password shown once

device provisioning not configured (dynsec admin secret missing)
labelusernamecreated bycreatedstatus

Automated alerts · master switches for the pollers that transmit to the mesh · each mesh still needs an alert channel (and counties / gauges) below · takes effect within a minute

Meshes & alerts · one card per mesh: channels, !nws counties, auto-alert channel, !flood river gauges · public alerts page ↗

Gateway sites · group co-located gateways under one site name · commands marked one reply per gateway answer once per site per distinct RF path, so two radios in the same room don't both transmit while a gateway across the mesh still reports its own path · blank = the node stands alone

gatewayownermeshsitelast message

Core & mesh commands · shared fleet + mesh commands · via: all / mine / gateway / node

namekindscope / mesheschannelstriggerreplyvia every/cooldownfireslast fired
📖 reply template reference
context
{sender}who triggered the bot
{args}text after the trigger prefix
{channel}channel it arrived on
{gateway}gateway that heard it
{snr} {hops}reception SNR dB / hop count
{time} {date}HH:MM UTC / YYYY-MM-DD
archive lookups
{seen NAME}last heard age of best name match ({seen} = use {args})
{whois HEX}node names for a path-hash prefix
{stats}nodes / repeaters / msgs 24h
{top_relays N}busiest N relays (max 5)
{pathbot}store the trigger message's RF path, reply with hops + /p/ link · silent when the request arrived direct (no relays, nothing to trace) · sends a second frame ~2s later (back-to-back the radio drops it) asking 1-byte senders to switch to 2-byte hashes, at most once a day each
{mpath}the same packet as every observer heard it, on one map · acks at once, then collects for 30s (our gateways plus external sources on other meshes) and sends a second frame with an /m/ link, each route a different colour · one run per requester at a time
{vitals NODE}gateway battery / uptime / rssi ({vitals} = the gateway that heard you)
{online}gateway fleet status; names the down ones
{active N}nodes heard in the last N hours (default 24)
{new_nodes N}nodes first seen in the last N hours
{lastmsg NAME}last channel message from a node + age
{route NAME}how a node's advert reached us — hop chain with names
{skew NAME}node clock offset vs NTP from its last advert
{top_talkers N}busiest senders in 24h (max 5)
{lag}sender→rx time delta of the trigger message
{gauge LID}one NWPS river gauge by id, e.g. {gauge CFTT2}
{nws}active NWS alerts for the mesh's counties (set per-mesh in Weather & flood alerts)
{flood}river gauges at/near flood in the mesh region (NOAA NWPS)
utilities · examples
{roll NdM}dice; nest for user dice: {roll {args}}
{pick a|b|c}random choice
{8ball}magic 8-ball
{help}list all functions
!ping → pong {sender}! rx {snr} dB via {gateway}
!seen → {seen} · !mesh → {stats}
rendered at fire time, 160-char cap · unknown {names} stay literal · failing lookups show ∅ · full docs: kubernetes/internal/apps/meshscope/README.md

Dropped packet · …

loading…

device credential

⚠ the password is shown only once, copy it now
device setup (MeshCore observer CLI)

    

account

recovery email · optional
change username
change password · other sessions are signed out

new bot

what should this bot do?
↩ command reply
answers when a channel message starts with a trigger like !ping · any channel except Public
⏱ scheduled announcement
posts on a fixed schedule (at most every 15 min) · the only kind allowed on Public
scope
Core = shared across every mesh (toggle it per-mesh from the bots table). Scope to one mesh to use that mesh's channels.
channels
custom channels are hashtags; # is added for you
bot name
trigger prefix
reply template · click a function to insert · 160 chars max
nesting works one level ({roll {args}}) · unknown {names} stay literal · failing lookups render ∅
transmit via
all = every gateway in this command's scope (fleet-wide only for a Core command) · gateway = the one that heard the trigger
cooldown (seconds)
summary
txmesh · Community-run MeshCore bot service · Texas · [email protected]