Skip to content

v1.6.0

Latest

Choose a tag to compare

@github-actions github-actions released this 24 Jul 20:37
· 11 commits to dev since this release
201cd9e

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 data the REST API exposes, gated behind a per-session config toggle. (a14a0ac, 975f2f3)
  • Presence write coalescing — high-frequency "this node/observer was just seen" writes are now batched in memory and flushed periodically (presence.flush_interval, presence.packet_ttl) instead of hitting the database on every packet, with ingest routed through the coalescer end-to-end. (2f12480, 5804524, 41a3aa4, d113036)

Performance

  • Top observers, payload-type breakdown, top talkers, and top advertisers are now all served from materialized views, hourly-bucketed for efficient windowed (24h/7d/30d-style) queries, instead of scanning live tables per request. (b93ce7a, d8ff7d0, bc3a375, cbde3d0)
  • Channel and trace activity per IATA are now tracked in dedicated tables rather than derived per-request. (dff9c6c, ba0e85c, 457747b, a84112f)
  • Scope stats no longer cross-join the full packets table. (42edcde)
  • New indexes: known_routes(last_seen), packets(scope_id) (scope stats), packets(last_heard_at). (63c0b97, 013/014 migrations, b8665eb)
  • IATA-filtered packet lists are now driven off the observation index rather than a broader scan, and IATA filters are passed as native bpchar[] arrays instead of CSV strings. (30772da, 508337b)
  • Path hash resolution is split by prefix width for more targeted lookups. (0afba79)

Fixes

  • Radio frequency precision — node, observer, and observer-list radio strings were being truncated to one decimal place (%.1f) before reaching clients; now formatted with full precision (%g), matching the underlying float32 value. Found and fixed across three separate call sites, not just the first one noticed. (d8c6223, plus follow-up instances caught during the node-staleness work)
  • Trace packet paths — the live map and packet-detail resolved path were resolving TRACE packets against the wrong bytes (the physical packet header's path field, which for TRACE holds per-hop SNR readings, not hashes) instead of the trace payload's own embedded path hashes — meaning trace packets never rendered on the live map at all. (7a58a07)
  • Advert signature verification — adverts are now verified before being trusted for node upserts, closing a gap where an unsigned/invalid advert could otherwise update a node's identity. (ee2fac1)
  • Duplicate/unknown channel hash dedup — unknown channel hashes were being recorded more than once instead of deduped. (420a431)
  • Empty /status payloads from observers were being logged as if they were real, non-empty status updates. (ac67308)
  • A subscribe-logic bug and an associated small race condition in the WebSocket hub. (0ca3c95)
  • Broader parsing coverage for observer-reported heard_at timestamp formats. (ca65ed1)
  • Various review-driven fixes for the new per-IATA channel/trace activity tables. (ba0e85c, a84112f)
  • Missing DB test stub and a swagger doc omission caught in follow-up commits. (d7fe41c, 3d4f1a2)
  • Test file formatting cleanup. (572f14b)

Chores / Dependencies

  • Go bumped to 1.26.5 for the GO-2026-5856 crypto/tls fix. (70c8a87)
  • chi router bumped to 5.3.0. (976a38c)

Migration note: this range includes several new/changed materialized views and indexes (top observers/talkers/advertisers, payload breakdown, per-IATA channel/trace activity, plus the new nodes/iata_codes columns for staleness, clock drift, and borders). Run migrations and refresh the materialized views before rolling traffic to a new instance — a stale view will under-report stats until the next scheduled refresh.

Full Changelog: v1.5.4...v1.6.0