MeshCore Beacon

MeshCore Beacon · Releases

from GitHub · updated 2026-07-24

13 releases

  1. v1.6.0 # 14 days ago · 2026-07-24 20:37 UTC

    New Features

    • Channel message backfill at boot — when a channel's decryption key is added to config, packets for that channel that arrived before the key was known (and were stored as hash-only, undecrypted rows) are now retried against the keystore on the next startup. Previously that history just sat in the database unused forever; now it surfaces automatically. Backfilled messages don't emit live WebSocket events (they're history, not new activity), and the process is non-fatal — a decrypt failure logs and moves on rather than blocking boot. (3cad2df)
    • Node staleness & automatic deletion — new config: nodes.stale_threshold (default 24h) marks a node stale: true in the API once it hasn't been seen in that long; nodes.delete_after (default 30 days, same default as packet retention) removes nodes from the database entirely once they've been gone that long, via the existing cleanup background job. Node deletion correctly excludes any node with a manually-recorded owner record, so operator-curated ownership data is never silently orphaned. (99d7eb0)
    • IATA region borders (GeoJSON)GET /api/v1/iatas/{iata}/border returns a region's border as a GeoJSON Feature (Polygon or MultiPolygon, with a server-computed bounding box) for map rendering — 204 when a region has no border configured, 404 for an unknown IATA. Borders are supplied per-IATA via config (borderFile: path/to.geojson) and validated at boot — malformed geometry, out-of-range coordinates, or unclosed rings fail startup rather than shipping bad data silently. Deliberately kept off the main /iatas list response so it stays lightweight for callers that don't need it. (3f2506f)
    • Node clock drift detection — repeaters and room servers self-report a device clock in every advert; the server now compares that against its own receive time and surfaces the delta (clockDriftSeconds, clockOutOfSync, clockCheckedAt) on the node API, with the out-of-sync threshold configurable (nodes.clock_drift_threshold, default 5m). Useful for spotting devices with a broken or badly-drifted RTC. (8e27986)
    • Top advertisers & top talkers stats — two new endpoints, GET /stats/top-advertisers and GET /stats/top-talkers, following the same since/limit/iatas shape as the existing top-observers endpoint. Top advertisers counts distinct ADVERT packets per node (not raw hearings, so a broadcast heard by five observers doesn't inflate the count); top talkers aggregates by decrypted sender display name. Now served from materialized views (see Performance) rather than live queries. (b38663d, later matview-backed in cbde3d0/bc3a375)
    • Node public-key prefix searchGET /nodes?pubkeyPrefix=... matches nodes by a partial hex prefix of their public key, alongside the existing exact-match pubkey filter — useful when a UI only shows a node's key truncated. (0ca28cc)
    • Plural filter params on packet listGET /packets now accepts comma-separated payloadTypes, routeTypes, and scopes alongside the existing singular params, so multi-select filters can be expressed in one request instead of requiring client-side post-filtering. (b33ebc0)
    • Source/destination endpoints in resolved path data — packet path data (both the live WebSocket feed and the REST packet detail) now resolves and includes the sender and, where determinable, the recipient — not just the intermediate hops — for payload types where that's meaningful (ADVERT, TEXT_MESSAGE, PATH, REQUEST/RESPONSE, ANON_REQ's destination). This is what makes the live map able to draw a packet's full source→destination path instead of stopping at the first/last known repeater. (f07edf9)
    • ?neighbors on node listGET /nodes?neighbors opts into including each node's known neighbor IDs in the list response. (4e2ee62)
    • Resolved path in WebSocket observation events — live packet observation events over the WebSocket now carry the same resolved-path …
  2. v1.5.4 # 2 months ago · 2026-06-24 15:11 UTC

    Fixes

    • Packet Observation Duplication - The packet observations were being constrained as unique on observer, packet hash and the heard at TS. The TS has been removed and a migration is in place to clean up bad data.

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.5.3...v1.5.4

  3. v1.5.3 # 2 months ago · 2026-06-20 17:41 UTC

    Features

    • Discovery request/response parsing — Beacon now parses DISCOVER_REQ/DISCOVER_RESP control payloads from the mesh, feeding directly into neighbor data.
    • Neighbor SNR tracking — a new optional snr column on node_neighbors, now populated from three independent sources:
      • discovery responses observed by Beacon
      • zero-hop adverts seen directly by observers
      • hop pairs derived from TRACE packets
    • Observer online check uses last seen — ingest now factors in an observer's last-seen timestamp when determining online status, rather than relying solely on connection state.

    Fixes

    • Node summary neighbor counts — deduplicated neighbor counts in node summaries that were previously inflated by duplicate rows.
    • Release image publishing — fixed the GitHub Actions release workflow so built images are pushed to the public registry correctly. (Thanks, @MrAlders0n!)

    Maintenance

    • Bumped meshcore-go to v1.0.8.
    • SECURITY: Bumped github.com/go-chi/chi/v5 from v5.2.2 to v5.2.4 (dependabot).
    • Documented the remaining parsed ingest payload structs.
    • Added the new neighbor snr field to the swagger docs.

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.5.2...v1.5.3

  4. v1.5.0 # 2 months ago · 2026-06-15 22:28 UTC

    Testing

    This release is primarily a testing overhaul, bringing project coverage from minimal to ~46% with a solid foundation across all major layers.

    Infrastructure

    • Introduced sqlc.Querier interface in db.Store, enabling mock-based testing of the database layer without a real PostgreSQL connection
    • Added gomock-generated mock for sqlc.Querier at db/sqlc/mock
    • Converted stubReader in handlers tests to a configurable struct with function fields, eliminating the need for a separate funcReader type
    • CI coverage filtering now correctly excludes generated code (db/sqlc, docs, iatadb/gen) for accurate badge reporting

    Coverage by package

    • db — store mapping logic, pagination, nil pointer handling, conditional field mapping, type conversions across channels, observers, nodes, packets, traces, routes, scopes, stats, and IATAs
    • internal/api/handlers — validation paths and happy paths across all handlers including messages, packets, observers, nodes, stats, routes, scopes, and traces
    • internal/cachegetOrSet cache hit/miss/error/corrupt-entry behaviour, TTL resolution fallback chain, node and observer invalidation
    • internal/config — YAML loading, config seeding, default resolution extracted into testable Resolve function
    • internal/hub — full runtime behaviour including broadcast, subscription lifecycle, lagged notification, OR-scope semantics
    • internal/ingest — pure function coverage for payload parsing, transport code derivation, capability detection, observer type normalisation
    • internal/keystore — key derivation, fingerprinting, store lookup, EntryExists (moved from main)
    • internal/scopestore — load, replace, copy-on-read semantics
    • internal/background — scheduler task execution and context cancellation
    • internal/ws — IP limiter acquire/release/cleanup logic

    Refactoring

    • config.Resolve() extracted from main.go to derive runtime defaults with fallback chain — now tested and used at startup
    • keystore.EntryExists() moved from main.go into the keystore package where it belongs

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.4.1...v1.5.0

  5. v1.4.1 # 2 months ago · 2026-06-12 18:29 UTC
  6. v1.4.0 # 2 months ago · 2026-06-12 18:00 UTC

    Features

    • Background task scheduler — periodic maintenance tasks now run on independent configurable intervals via a new internal/background package. Intervals are set per-task under the background: config key; all default to 1h.
    • Route and neighbor reconfirmation — a new background task periodically prunes known_routes and node_neighbors rows that have become stale or ambiguous: routes where any hop has departed node_short_ids or whose prefix now resolves to multiple nodes, and neighbors under the same conditions.

    Fixes

    • Neighbor recording — neighbors are now only recorded when the resolved first-hop node is a repeater or room. Previously a prefix collision could cause a companion or other non-infrastructure node to be written as a neighbor. Thanks to @gadgethd for the contributing fix to IATA seeding that triggered this investigation.
    • IATA seedingUpsertIATADetails is now a true upsert, correctly updating existing records rather than silently doing nothing on conflict. Contributed by @gadgethd .
    • Graceful shutdown — added a timeout to the shutdown sequence to prevent possible hangs on exit.

    Maintenance

    • Switched to dedicated CodeQL action.
    • Corrected route list handler cursor documentation.
    • Fixed broken README API reference chart.
    • Minor code cleanup: renamed mustEnvgetEnv, fixed view refresh comment, added note on node type helper.

    Contributors

    Welcome and thank you to @gadgethd — first contribution to Beacon!

  7. v1.3.1 # 2 months ago · 2026-06-11 23:00 UTC

    Traces & Pings

    Single-hop traces are now classified as pings and tagged accordingly. The /traces list response now includes the path and SNR values from the most complete observation of each trace, making it possible to assess signal quality and hop progression at a glance without opening the detail view. A new type query parameter allows filtering traces by TRACE or PING.

    Routes

    Single-hop routes are no longer stored — direct links are already captured by neighbor relationships, making them redundant.

    Nodes

    knownNeighborCount added to node summaries, and publicKey added to node neighbor responses.

    Database Migrations

    Migrations are now embedded in the binary and applied automatically on startup, removing the dependency on external tooling or container entrypoints. Existing deployments will bootstrap cleanly on first run.

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.3.0...v1.3.1

  8. v1.2.1 # 2 months ago · 2026-06-10 20:49 UTC

    Just a silly bug in a query for radio preset stats.

  9. v1.2.0 # 2 months ago · 2026-06-10 20:23 UTC

    New features

    CORS support

    • Configurable CORS headers for cross-origin frontend deployments

    Node type breakdown

    • New GET /api/v1/stats/node-types endpoint returning node counts by type (companion, repeater, room server, sensor)
    • Supports the same multi-IATA and region filtering as all other stats endpoints

    Multi-IATA and region filtering on stats endpoints

    • All /stats/* endpoints now accept iatas, iata, regionId, and region query params
    • Previously only a single exact-match IATA was supported
    • Top nodes chart now returns data correctly in the default all-regions view

    Bug fixes

    Observer telemetry

    • Timestamps were being returned in seconds instead of milliseconds — now consistent with the rest of the API
    • Bucketed telemetry (6h, 24h intervals) was grouping by hour-of-day rather than by time window, causing incorrect bucket boundaries and near-zero deltas in the 30-day view
    • Running total fields (airtime_tx_pct, airtime_rx_pct, receive_errors) were being averaged across buckets instead of computing the delta — now correctly returns increase per bucket

    Maintenance

    • Removed unused queries
    • Updated dependencies (SHOULDERS.md)
    • CodeQL security scanning via GitHub integration

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.1.0...v1.2.0

  10. v1.1.0 # 2 months ago · 2026-06-09 21:15 UTC

    v1.1.0 — Redis caching layer

    This release adds an optional Redis-backed caching layer that reduces redundant PostgreSQL reads for read-heavy, slow-changing endpoints.

    What's new

    • Redis cache layer (internal/cache) — a CachedReader wraps the database store and transparently caches responses using a generic getOrSet helper with graceful degradation. If Redis is unavailable, all reads fall through to PostgreSQL with no impact on availability.
    • Explicit cache invalidation — node and observer cache entries are invalidated immediately on upsert from the ingest path, keeping detail responses accurate despite caching.
    • TTL-based expiry for stats and reference data, with a per-category override system and a global fallback. Defaults to 1h, aligned with the materialized view refresh cycle.
    • Fully optional — set REDIS_ADDR to enable. Unset, Beacon behaves exactly as before.

    Configuration

    Add to .env:

    REDIS_ADDR=localhost:6379
    REDIS_PASSWORD=
    REDIS_DB=0
    

    Add to config.yaml (all optional, defaults to 1h):

    cache:
      ttl: "1h"
      ttls:
        stats: "1h"
        reference: "1h"
        nodes: "1h"
        observers: "1h"
    
  11. v1.0.4 # 2 months ago · 2026-06-09 16:23 UTC

    [v1.0.4] — 2026-06-09

    Security

    Fix no bounds check on region IDs.

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.0.3...v1.0.4

  12. v1.0.2 # 2 months ago · 2026-06-09 15:42 UTC

    [v1.0.2] — 2026-06-09

    Features

    • Traces: raw path hashes included in responses — trace responses now include raw (unresolved) path hashes alongside the resolved node data, giving clients access to the full hop information even when nodes aren't known to the observer.

    Bug Fixes

    • Fixed typo in subscribeScope struct that could cause unexpected subscription behaviour.
    • Fixed missing Swagger annotation for the traces rawPath field.

    Documentation

    • Updated schema migration notes.

    Full Changelog: https://github.com/MeshCore-Beacon/beacon-server/compare/v1.0.1...v1.0.2

  13. v1.0.0 # 2 months ago · 2026-06-08 23:16 UTC

    v1.0.0

    This is the first public release of Beacon, a MeshCore network observation backend built by MeshCore Canada.

    What is Beacon

    Beacon connects to one or more MeshCore MQTT brokers, ingests LoRa packet traffic in real time, stores it in PostgreSQL, and serves it via a REST API and WebSocket stream. It is designed to be the data backbone for MeshCore network monitoring frontends and tools.

    Features

    Packet ingestion and storage

    • Subscribes to multiple MeshCore MQTT brokers simultaneously
    • Decodes all MeshCore packet types using meshcore-go
    • Deduplicates observations across brokers — the same packet heard by multiple brokers produces one observation per observer
    • Stores packets, observations, nodes, observers, traces, known routes, neighbors, and channel messages in PostgreSQL
    • Configurable retention for packets and telemetry

    Node and observer tracking

    • Upserts nodes from advert payloads with name, type, location, and radio settings
    • Tracks observers from /status messages including hardware model, firmware version, battery level, uptime, and radio configuration
    • Detects multibyte path and trace firmware capability from path hash sizes
    • Records node neighbors from forwarded adverts
    • Builds and maintains known routes — fully resolved multi-hop paths where all nodes are identified with high confidence
    • Observer telemetry snapshots with configurable resolution and bucketed query support (1h, 6h, 24h intervals)

    Channel messages

    • Decrypts group text messages for configured channel keys
    • Supports hashtag-derived PSKs (SHA256("#tag")[:16]) and explicit hex keys
    • Records channel hash observations even for unknown channels

    Transport scopes

    • Matches TRANSPORT_FLOOD and TRANSPORT_DIRECT packets to configured scope keys
    • Tags nodes and observers with their default scope
    • Scope-filtered queries across packets, nodes, observers, channels, and traces

    Geographic filtering

    • Optional ingest filter by country or continent using a compiled-in IATA → country/continent database sourced from OurAirports
    • Regions group IATAs for display and filtering

    REST API

    • Full paginated REST API with cursor-based pagination across all resources
    • Endpoints for packets, observations, nodes, observers, channels, messages, traces, routes, regions, IATAs, scopes, and stats
    • Cross-IATA route search
    • Observer telemetry with bucketed aggregation
    • Swagger UI at /swagger/index.html

    WebSocket

    • Real-time event stream with per-connection subscription filtering by IATA, region slug, region ID, payload type, channel hash, and event type
    • Events: packetObservation, observerStatus, nodeUpdate, channelMessage
    • Backpressure handling with lagged notifications and REST backfill support
    • Per-IP connection limits

    Path resolution

    • Resolves path hash prefixes to node UUIDs using node_short_ids
    • Confidence levels: high (unambiguous), ambiguous (hash collision), none (unknown)
    • Resolved hop nodes included in packet observations, traces, and known routes

    Project

    • AGPL-3.0-or-later license
    • Contributor Covenant code of conduct
    • Full contributing guide including database change, API change, and testing requirements
    • GitHub Actions CI with build, format, vet, test, and swagger doc checks
    • Docker image published to GitHub Container Registry on every push to main and dev