* feat(planguard): read sqlc statement text and load a strict plan corpus * fix(planguard): fail on unextracted statements, keep fragments mode-neutral * feat(planguard): plan assertions, scoped sort exemptions and narrow retired-index verdicts * fix(planguard): an errored run always fails * fix(planguard): keep subplan conds off their parent scan; fail on unanalyzed budgets * feat(planguard): read-only plan runner, reports and the run command * fix(planguard): validate identifiers in emitted scripts; prove read-only runs * feat(planguard): end-state corpus, planted regressions and the generated cached-plan policy * fix(planguard): derive observer cases from data; mark legacy generic plans * fix(planguard): bound the dense-region route resolvers; test policy refusals * fix(planguard): re-measure the dense-region route cases; cover every policy refusal * feat(planguard): synthetic fixture built through the migrations, checked in CI * ci(planguard): build the fixture once; guard fixture data for identifiers * fix(planguard): hash every fixture generator input; harden the fixture guard * feat(planguard): snapshot diff over every column, named result classes and cursor walks * fix(planguard): bound the relaxed diff classes; derive the cursor tie from data * fix(planguard): refuse multi-statement text in the corpus runner * feat: read-only server mode and an HTTP page diff between two builds * fix(planguard): refuse escape strings and psql meta-commands in corpus text * fix: storediff follows each build's own cursor; prove read-only gates via the log * fix(planguard): match the PostgreSQL lexer on high-bit bytes; check every emitted line * feat(admin): daily read-only plan check on the maintenance pool, its metric and report * feat(planguard): daily prod check behind planguard.daily_check * fix(planguard): cancel fresh runs at shutdown; expose a never-ran check * ci(planguard): pin the fixture server and collation; refresh baselines * fix(planguard): calibrate route budgets on one dataset; skip entries whose index is absent
MeshCore Beacon
MeshCore Beacon is a MeshCore network observation backend. It connects to one or more MeshCore MQTT brokers, ingests LoRa packet traffic in real time, stores it in PostgreSQL, and streams live events to WebSocket clients.
What it does
- Subscribes to MeshCore MQTT brokers and decodes incoming LoRa packets using meshcore-go
- Stores packets, observations, nodes, observers, traces, routes and channel messages in PostgreSQL
- Deduplicates observations across multiple brokers (the same packet heard by two brokers is one observation per observer)
- Decrypts group text messages for known channel keys
- Detects firmware capability flags from path hash sizes
- Streams live events to WebSocket clients with subscription filtering by IATA, region, payload type, and event type
- Serves a REST API for querying stored data
- Seeds regions, IATA display names, and channel keys from a YAML config file on startup
This repo is the code. Deploying, configuring and operating Beacon is documented in beacon-docs; see Documentation below.
Stack
| Component | Technology |
|---|---|
| Language | Go 1.26 |
| Router | Chi v5 |
| Database | PostgreSQL 16 |
| Caching | Redis 7 |
| DB queries | sqlc + pgx/v5 |
| MQTT | paho.mqtt.golang |
| WebSocket | coder/websocket |
| Packet decode | meshcore-go |
| Config | YAML via gopkg.in/yaml.v3 |
| Env | godotenv |
Running it locally
You need Go 1.26+ and a PostgreSQL 16 database. Docker is the easy way to get the database.
git clone https://github.com/MeshCore-Beacon/beacon-server.git && cd beacon-server
cp env.example .env
cp config.yaml.example config.yaml
docker run -d --name beacon-postgres -p 5432:5432 \
-e POSTGRES_USER=beacon -e POSTGRES_PASSWORD=beacon -e POSTGRES_DB=beacon postgres:16-alpine
go run ./cmd/beacon
Fill in POSTGRES_DSN and your MQTT broker credentials in .env. Every variable is described
in Configuration,
and the fully annotated config.yaml lives in beacon-docs as well; the copy here is a working
starter. Migrations run on startup. The API listens on LISTEN_ADDR (default :8080) and
Swagger is at http://localhost:8080/swagger/index.html.
MeshMapper authentication
MeshMapper APIs require either a regional API key or a grouped-region API key covering multiple regions. Your local MeshMapper regional or grouped-region admin can generate these keys.
Set MESHMAPPER_API_KEY in each deployment's private environment or
gitignored .env; it overrides meshmapper.api_key in config.yaml, including
when empty. The examples leave the key empty. Never put it in browser settings
or use the mobile app's App key. Restart Beacon after changing the key.
A regional deployment needs access to its IATA; a group deployment needs access
to every member IATA, including members added by import_groups. API keys do not
remove rate limits or change the get_zones.php?country=... request shape.
IP exemptions are not authentication.
Beacon sends X-API-Key to get_zones.php, get_geojson.php, get_scopes.php,
get_channels.php, and get_repeaters.php.
Requests are restricted to those HTTPS MeshMapper endpoints and never follow
redirects. Coverage consumers keep their separate keys, scopes, quotas, and
supported ?key= authentication; this client does not call coverage.php.
Missing keys are reported as unconfigured without stopping Beacon or deleting
cached imports. Failed refreshes record last_error, retain the last successful
data and freshness timestamp, and report 401 as an authentication failure or
403 as a permission failure. Inspect the meshmapper.zones, meshmapper.scopes,
meshmapper.channels, and meshmapper.repeaters logs for freshness and the next attempt. No anonymous
fallback occurs. Existing refresh limits still apply, including longer
Retry-After delays on 429/503.
Use the environment setting when backups are enabled. Backup exports preserve
saved YAML verbatim and refuse a nonempty meshmapper.api_key in that file so
the credential cannot enter a backup download. Clear that YAML value after
moving the key to the environment; environment secrets are excluded from backups.
Deployment coordination is required with MeshMapper Server before enforcement:
confirm X-API-Key support, obtain and verify the integration key's API and IATA
permissions, and confirm these requests do not consume Coverage quota. Header
support must not be assumed deployed. The first four endpoints can enforce
authentication before the app rollout; repeater enforcement waits for app 1.4.1
and its forced update. Key issuance, permissions, quotas, and enforcement switches
remain MeshMapper Server work, tracked in
MeshMapper_Server#431.
To run the web frontend against it, see Running the full stack locally.
The published image is ghcr.io/meshcore-beacon/beacon-server. latest tracks stable
releases, dev follows the development branch, and each release is also tagged X.Y.Z and
X.Y.
What you see on an empty database
Path resolution, capability detection and known routes depend on nodes having advertised to a
local observer. On a fresh database every hop shows "confidence": "none" and
supportsMultibytePaths is false until adverts populate node_short_ids. That is expected
and fills in as the mesh is observed.
GRP_TXT packets whose channel key is not known yet are stored hash-only. After adding the key to
config.yaml, the next restart decrypts the matching history; look for
config: backfilled N previously-undecrypted channel message(s) in the log.
Documentation
In beacon-docs:
- Deploy with Docker
- Getting packets in: brokers, the subscriber account, topics
- Configuration: environment variables and
config.yaml - API contract: REST, WebSocket, admin endpoints
- Reverse proxy and rate limits
- Upgrading
- Operations, backup and export and CPU profiling
- High level design
- Releases and versioning
In this repo: Historical stats explains the hourly rollups behind
the stats endpoints, for anyone changing them. docs/swagger.yaml is the generated OpenAPI
description.
Contributing
CONTRIBUTING.md covers code style, tests, and database and API changes. Branches, commits and the release flow are shared across the Beacon repos and live in beacon-docs/CONTRIBUTING.md. Please read the Code of Conduct. Security reports go through SECURITY.md.
Acknowledgements
See CONTRIBUTORS.md for the people who have helped build Beacon, and SHOULDERS.md for the open source projects it stands on.