Hub

MeshSat Hub changelog: fleet management platform releases.

Generated from the commit history of meshsat-hub: features and fixes, newest first, one section per tag. Untagged work on the main branch is listed under Unreleased. The Hub is open: create an account and we review it before it goes live.

Unreleased (main, last change 2026-09-16)

Features

  • relay: publish the bridge CA at GET /api/relay/ca for the Bridge relay client (0a75c18d)
  • certauth: bridge certificates can serve a relay tunnel as the TLS server (21f6289c)
  • relay: Hub side of the WebSocket relay, a rendezvous over the bus (79a17da2)
  • store: slow-query log names the statement; pg_stat_statements on the cluster (517bf1a6)
  • k8s: nightly in-cluster production verification (336bd8af)
  • cloudloop: a tenant can bring its own Cloudloop MQTT certificate (5137ae12)
  • k8s: Reloader annotations on hub, stunnel and tak-operator (c86a808c)
  • k8s: onion heartbeat for the status page; admin-only OIDC client for it (95094347)
  • web: HUB status badge links to status.meshsat.net (93c4db87)
  • health: report Stripe reachability as an informational dependency (0788b57d)
  • hawkbit: move the OTA database from bundled H2 to CNPG Postgres (d4f861ae)
  • api: tell the customer when a feature will not do anything (c508f0f0)
  • tor: publish the Hub’s .onion address (5add20ad)
  • apprise: deploy the notification relay that never existed (461e6424)
  • tor: run the hidden service on Kubernetes at last (6edd131e)
  • hawkbit: per-tenant OTA, and guard every tenant-supplied URL (f321d43e)
  • wireguard: per-tenant wg-easy, and deploy the platform’s own (68f892b2)
  • oob: per-tenant command policy, migration v24 (dc7ac374)
  • email: per-tenant PGP gateway, and contacts stop colliding (e0a932be)
  • notify: per-tenant Apprise and ntfy delivery backends (54b3bad4)
  • aprsis: each tenant transmits under its own amateur callsign (f704414d)
  • geo: a fence stays quiet for a while after it alerts (fa732825)
  • geo: geofences actually fire now (bbfc047b)
  • ratelimit: the send budget follows the plan (279fdc75)
  • tenant: audit retention belongs to the tenant (3d686635)
  • tenant: the bridge offline timeout belongs to the tenant (828d6a8a)
  • signups: tell someone when their account request is refused (dd30d53d)
  • signups: email the operator when an account request is waiting (c27a6c92)
  • tak: switch the hosted TAK front on (90f09f31)
  • tak: switch the hosted TAK front on (d84051df)
  • tak: a TAK page for tenants, with one-time enrolment and a QR (9365c46e)
  • tak: one-time enrolment packages for a tenant’s TAK users (5a7857e4)
  • tak: build ATAK, WinTAK and iTAK enrolment packages (afd7ea24)
  • tak: a tenant can forward CoT to their own TAK server (afcf0d29)
  • tak: open the hosted TAK leg at the edge on 8089 (cb3b3920)
  • tak: a tenant can turn TAK on and manage who sees the map (a1dcc8ad)
  • tak: operator creates OpenTAKServer accounts on request (55ee6c2f)
  • tak: destroy a closed tenant’s TAK server when its data is purged (d0b90394)
  • tak: forward device positions to each tenant’s own TAK server (48a5b50b)
  • tak: wire the Hub to hosted per-tenant TAK (8d7db133)
  • tak: per-tenant TAK schema and the TAK account ceiling (3cbcde86)
  • k8s: foundations for hosted per-tenant TAK (b4b8a898)
  • db: a stuck Backup no longer holds the slot until somebody notices [IFRNLLEI01PRD-2833] (80523a0c)
  • billing: the donation page is MeshSat’s, with Stripe’s form inside it (27a0af30)
  • auth: customers can change their password and turn on two-factor (7dcaf0ee)
  • webhook: watch where satellite deliveries come from, never refuse one (47e27c69)
  • tenant: a customer can take their data and leave (e16bcb24)
  • stripe: a moved Stripe field can no longer pass unnoticed (ed335d05)
  • billing: the checkout page says whose it is (07f29c3e)
  • billing: a renewal that fails is no longer invisible (741e595b)
  • billing: a public donate link, and the last of Ko-fi (1b3648f5)
  • billing: donations, and they are outside the scope of BTW (f2b7873c)
  • billing: move Stripe to the dedicated MeshSat Hub account (cf1d11eb)
  • billing: Stripe is the payment provider; Ko-fi is decommissioned (baa6ab8c)
  • billing: switch Stripe on (c50c90c7)
  • billing: checkout starts in the Hub, and a cancellation ends a plan (1331d9ec)
  • stripe: the payment provider, inert until it is configured (e7aa50aa)
  • mail: write money the way the customer’s document writes it (21efd1ec)
  • mail: give Hub email the billing system’s brand shell (4d4dfad5)
  • invoiceninja: watch the sender setting, not just the tax one (3c565510)
  • refunds: money given back now leaves a credit note behind it (d8f29b70)
  • vat: a receipt follows where the buyer is, and the threshold is measured (aab7195d)
  • enrollment: CAPTCHA is live, and the stage has to be interactive (1f017bca)
  • admin: money that upgraded nobody is now visible and recoverable (b337ed1f)
  • mail: the Hub can tell a customer what happened to their account (109ab4fa)
  • k8s: wire the Invoice Ninja token so receipts actually go out (2c89aad8)
  • kofi: issue a VAT receipt for every paid subscription (a312ffa7)
  • login: the Hub sign-in gets the meshsat.net hero (5c0c4576)
  • auth: harden the enrollment flow now that it is public (ffd0bab5)
  • auth: the enrollment page gets meshsat.net’s rolling hero (13e9ddda)
  • ux: carry the claim code to the upgrade, and link password recovery (6d75e67a)
  • plans: four subscription tiers metered by device count (f0436220)
  • signups: the approval panel (9d03902e)
  • signups: give the Hub its own identity at the provider (98647f79)
  • signups: approve a beta request from the Hub (d6d15421)
  • tenant: let a tenant take its data and have it destroyed (964a39dd)
  • auth: a password reset flow, and MeshSat mail that says MeshSat (04c0737f)
  • webhook: give every tenant its own inbound endpoint (909b9941)
  • map: basemap replicas copy the archive from each other (d0a6335d)
  • map: all of Europe at street-name detail, on its own disks (003bc420)
  • k8s: a second Reticulum terminator, and the placement rules rewritten (c93e4e25)
  • k8s: the broker becomes a three node JetStream cluster on the control-plane tier (57902a25)
  • k8s: KeyDB replaces the single Redis, two masters on two machines (b7c9ee03)
  • k8s: the Hub can land on any worker, and gets there faster (fa3f07f5)
  • map: self-hosted MapLibre vector basemap, no third-party tile host (58a2924d)
  • k8s: two Hub replicas with rolling updates, a PDB and a per-pod leader identity (1d6ef137)
  • edge: gated registration, file-backed IP allowlist and enrollment exemption (ea4e9547)
  • routing: satellite route destination delivers a text to a bridge over its Iridium modem (413d0384)
  • oob: bridge commands over SMS, Iridium IMT and SBD as sealed OOB frames with replies correlated by counter (b05a163a)
  • fleet: uplink frames over SMS, SBD and IMT become fleet state and SOS alerts, last report bearer on the bridge (86ba57ab)
  • routing: optional sender list per route so kit A reaches kit B without an echo (30ddf857)
  • providers: Twilio, Rock7, RockBLOCK and Globalstar per tenant (fb0cb60e)
  • cloudloop: use the tenant’s own Cloudloop account for MT sends, thing lookup, credits and the MO webhook (043c5903)
  • tenant: per-tenant provider accounts: encrypted Cloudloop, Twilio, Rock7, RockBLOCK and Globalstar credentials with a tenant API and Integrations page (d22dad2a)
  • audit: archive purged entries to S3 for every tenant (a686f294)
  • nats: per-bridge NATS users and permissions rendered into a Secret (56d08734)
  • mqtt: tenant-prefixed topic namespace with dual-shape subscriptions (7f864f07)
  • routing: evaluate routes and persist inbound traffic for the device’s tenant (02b7dea9)
  • k8s: run one Hub replica on notrf01 for the cutover (49210628)
  • k8s: bootstrap owner attaches to the default tenant; operators count as verified (f52ade0e)
  • k8s: edge HAProxy patcher for the auth, cutover and rollback changes (492fb54b)
  • cli: --migrate-only and the MariaDB to Postgres rehearsal tooling (4c12c480)
  • k8s: authentik bootstrap for the MeshSat brand, enrollment and approval (d2741afc)
  • k8s: kustomize tree for MeshSat Hub on notrf01cl01k8s (43c62a6d)
  • bridge: keep the bridge CA certificate in a Kubernetes Secret (01090eb5)
  • api: tenant self-service and platform-admin tenant directory (45290690)
  • web: SSO-first UX pass, first-run onboarding, tenant panel, light-theme parity (5655b5ce)
  • web: MeshSat brand tokens, IBM Plex fonts, lockup and light theme (dfa90534)
  • web: sign in with MeshSat ID (OIDC) and auth callback view (e3b48174)
  • auth: OIDC authorization-code login with JIT tenant provisioning (47d973bc)
  • store: tenants and tenant invites, default tenant seeded (d113d97f)
  • leader: run pollers, reapers, retention and evaluators on the leader only (970b4fc6)
  • store: dispatch claims and CAS so N replicas send each message once (48794544)
  • config: HUB_DB_DRIVER selects sqlite/mariadb/postgres, configurable sqlite path (28af0fb5)
  • store: Postgres port of users, tokens, API keys, device keys, routes, escalation, alerts, prefs (c075cfed)
  • store: Postgres port of devices, messages, webhooks, positions, audit, device configs (2363a088)
  • store: Postgres implements store.Store, conformance suite runs on Postgres (81c58de7)
  • store: shared helpers and conventions for the Postgres domain files (ddfb0ae1)
  • store: Postgres port of bridges, bond groups, costs, groups, templates, alert rules, credentials (49b4d04a)
  • store: Postgres store skeleton, versioned migrations, conformance harness (59558c2c)
  • auth: EC JWKS keys, discovery endpoints, slash-tolerant issuer check (d91d56aa)
  • health: readiness probe classes, drain on shutdown, startup marker (0e233860)
  • api: provider-aware MT send routes with Cloudloop/IMT support (f8c584b3)
  • directory: Hub precedence-default policy seeding (fdfe9e1f)
  • directory,api: vCard 4.0 + CSV import/export (Hub) (cc446886)
  • directory: Hub directory foundation + signed push (22cf806f)
  • dtn: wire CustodyManager for hub-side custody acceptance (481a6375)
  • ui: add bond groups Vue UI, Fleet HeMB badges, and E2E tests (4533612c)
  • hemb: add bond group CRUD API + MQTT config push (b5c1134e)
  • ui: complete API-to-UI coverage for keys, costs, alerts, credentials (ac5f946e)
  • ui: add alert rules Vue view and Playwright E2E tests (bf6f914a)
  • ui: wire cost tracking view with route, nav, and E2E tests (560ec6e2)
  • tak: store OTS markers as devices+positions in Hub DB (d92187cb)
  • tak: add OTS REST API poller for inbound CoT relay (4415c840)
  • tak: broadcast CoT XML to bridges via MQTT for map integration (c0078587)
  • tak: redesign TAK Operations Center with tactical design system (40f0d84f)
  • map: interactive CoT type filter on Position Map legend (8a0d271e)
  • tak: Hub TAK Operations Center + enhanced KPIs + fleet badges (1cbe2c4f)
  • tak: mission proxy endpoint GET /api/tak/missions (71edbd15)
  • tak: federation peers API + TAK KPI dashboard widget (cabd6cf0)
  • map: TAK/CoT-compliant SVG markers for fleet devices (712a9103)
  • map: TAK/CoT-compliant markers: diamonds, squares, stale fade (fefe5179)
  • tak: Federation v2: Hub as bidirectional TAK federation bridge (b5636673)
  • tak: Marti proxy for fleet DataPackage/Mission sync (80a42b17)
  • api: add TAK integration status to Integrations page (98c51a8f)
  • keys: Hub channel key rotation + MQTT distribution to bridges (d43d3790)
  • hemb: Hub-side HeMB bearer integration: all 3 webhooks (29165501)
  • reticulum: add SMS interface for Reticulum packet relay (737a4369)
  • sms: bring Twilio webhook to parity with Rock7/Cloudloop pipeline (5c7410c7)
  • hemb: port HeMB codec + bond group management + telemetry to Hub (189471be)
  • bridge: verify birth message ECDSA signatures to prevent MQTT spoofing (fa34848b)
  • nats: simple shared-password auth: no accounts, no cycles (3571a647)
  • settings-ui: Service Security section with auth status + password rotation (2ef9ffbb)
  • nats: NATS accounts: separate auth domains for Hub (password) and bridges (mTLS) (82724cf2)
  • security: auto-generate service passwords on first boot (f46afd01)
  • fleet-ui: Provision QR button + modal on Fleet page (c7d277e9)
  • provision: single-use nonce + deduplicated generateBundle helper (43500916)
  • fleet: one-step QR provisioning for bridges and Android apps (12dde998)
  • protocol: port 4 protocol enhancements from bridge: FEC, Time Sync, DTN, RLNC (9c146722)
  • reticulum: Hub announces identity to TCP clients + broadcast on announce (847539fd)
  • reticulum: broadcast announces to all interfaces (flood transport) (b8df4316)
  • reticulum: add TCP interface for external RNS node connectivity (3eb7806c)
  • nats: mTLS WebSocket on port 9443 + Hub CA cert export (3dcb81eb)
  • nats: cross-site leaf nodes for bidirectional MQTT routing (3a5e3fbf)
  • nats: add MQTT-over-WebSocket via NATS native websocket (57ab729d)
  • deploy: GitOps for mqtt-ws sidecar + nginx WebSocket proxy (7434f60f)
  • infra: Mosquitto WebSocket bridge sidecar for MQTT over port 443 (d0b8f0e5)
  • settings: UI-configurable MQTT public URL for bridge onboarding (0db02fd6)
  • fleet: full bridge management UI with add/edit/delete, credentials, commands, and onboarding (0ba6f53c)
  • observability: add comprehensive instrumentation across 12 issues (d34da2fd)
  • dashboard: credential management page (84f3aceb)
  • bridge: credential_push MQTT command for distribution (6f5fc212)
  • credentials: centralized credential management for Hub (bb2f26f2)
  • reticulum: wire Reticulum router to live MQTT bridge traffic (c51b7193)
  • dashboard: use unified topology API, add path discovery and hints stats (f7b2eeff)
  • hub: add configurable alerting rules engine with device_not_seen condition (2be90a68)
  • hub: add scheduled message delivery with background scheduler (ffd081cd)
  • hub: add scheduled message delivery with background scheduler (a364ce8e)
  • hub: add changelog generation script and CI job (7bc44c3e)
  • hub: add device groups Vue page with CRUD and member management (d30f0732)
  • hub: add message templates with variable substitution (4d2be91a)
  • hub: add cost tracking ledger for satellite message sends (d531b2f9)
  • hub: add server-side CSV export for messages, positions, and audit (3b8f5ae3)
  • hub: add map breadcrumb tracks with time range selector (3cd22053)
  • hub: add Prometheus /metrics endpoint with HTTP and message metrics (a3da5457)
  • hub: add /api/v1/ versioning prefix as alias for /api/ (246388a9)
  • cloudloop: ThingResolver: auto-learn IMEI-to-thingID from MO + API refresh (779c1347)
  • constellation: upgrade IridiumBackend to official Cloudloop Data API (a27254ed)
  • cloudloop: Sender auto-selects SBD vs IMT API, supports imt_topic and ring_style (29f10d6d)
  • cloudloop: upgrade MT client to official Data API: SendSBD, SendIMT, GetDeliveryStatus (0f8a323e)
  • dashboard: Integrations page: inbound channel status and webhook URLs (859c3f45)
  • cloudloop: LingoMO webhook + MQTT subscriber for 9704 IMT (c8a0f141)
  • dashboard: show bridges in Device Fleet widget with interface badges (12c22d34)
  • bridge: bridge-to-hub uplink protocol: registry, subscriber, CoT projection, fleet dashboard, commands, security (c6ca9038)
  • reticulum: Hub Super Transport Node: path discovery + routing hints (ca4d85d8)
  • help: polish Help & Documentation page with consistent styling (d6ce77ee)
  • ui: add Help/Documentation page (f85b861a)
  • ui: grouped dropdown nav menus (5 groups, 20 items) (1e2a2887)
  • dashboard: add EmptyState to Devices view, rebuild dist (220ab7f1)
  • dashboard: unify Hub UI with Bridge design patterns (e5fc7e0e)
  • ci: run OWASP scan automatically on every push to main (f5b29f31)
  • ci: auto-deploy with Galera health gates before and after (140dc1c1)
  • reticulum: add topology dashboard with relay stats and interface map (d1932db9)
  • dashboard: add toast notifications, empty states, mobile nav drawer, widget customization (955c753f)
  • reticulum: wire Iridium SBD as Reticulum transport interface (ab4421fc)
  • geofences: add geofence API + map-based polygon drawing UI (6299005b)
  • dashboard: add WebSocket real-time events, sparklines, theme toggle, search, CSV export (827a3f0b)
  • dashboard: add Device Detail, Email Gateway, Backup views + rewrite Settings + new nav (4714258b)
  • dashboard: redesign operations dashboard with professional layout and full data surface (88c30378)
  • routing: wire all 5 missing destination handlers: TAK, APRS, webhook, notification, MQTT (7e885b34)
  • globalstar: add Globalstar satellite constellation as Reticulum interface (75ca531c)
  • sms: add MQTT inbound subscriber for Android SMS relay (e164c62d)
  • dashboard: add Reticulum topology view with identity, routes, and stats (2d5d0d83)
  • reticulum: add Astrocast and Globalstar satellite interfaces (5c392ada)
  • reticulum: add packet relay with hop counting, loop prevention, and rate limiting (4accd856)
  • reticulum: add transport interfaces: Iridium, MQTT, Tor, WireGuard (37c1959a)
  • reticulum: add global routing table with cost-aware path selection (023eaf75)
  • reticulum: add Hub identity generation, persistence, and API endpoint (5c69e684)
  • reticulum: add RNS wire-compatible packet format library (5f035cf8)
  • sms: decrypt inbound SMS using global or per-sender key (d6930c83)
  • crypto: add key import endpoint + hydrate global SMS key on startup (5c29c65d)
  • sms: Twilio SMS send from dashboard with compression + encryption (08bf73d0)
  • mt: Rock7 MT send with SMAZ2 compression + AES-256-GCM encryption (d47595b4)
  • rock7: add Rock7 RockBLOCK MT sender: live tested OK,5037977 (576dc7a8)
  • security: TLS certificate pinning + encryption architecture doc (c6482936)
  • astrocast: wire MSVQ-SC decoder for Android-compressed messages (0db3d32b)
  • demo: MSVQ-SC decoder + email test endpoint + demo setup script (17d301b5)
  • scripts: add demo setup script for Hub provisioning (00844eb0)
  • compat: add protocol version byte support (b123ed3a)
  • compat,security: Astrocast 1-byte fragment + MQTT mutual TLS (37783872)
  • compat: add bridge GPS codec (0x50/0x44) + canned codebook (0xCA) (30537bea)
  • ipougrs: add experimental IP-over-satellite tunnel adapter (dd770892)
  • codec: add pluggable sensor payload decoder framework (566d1bb6)
  • sim: add MT delivery simulation, movement patterns, Astrocast base64 fix (e3cc6cbb)
  • sim: add MeshSat device simulator and dev environment (bbad792d)
  • routing: add route test endpoint and enhanced routing UI (6e3e3e07)
  • ui: add routing rule management view (ddc83cb1)
  • routing: add SMS and Email as routing destinations (44d332b6)
  • routing: add configurable message routing engine with default routes (cf96e003)
  • wg,tor: WireGuard auto-provisioning + Tor .onion API (b94a4446)
  • email: add PGP email gateway with key management API (c1be4ee9)
  • sms: add SMS gateway and escalation notifier (c1e85665)
  • astrocast: add MO webhook receiver for Astrocast satellite messages (cce16b10)
  • swagger: generate OpenAPI spec with swaggo and serve Swagger UI (b9c289f5)
  • deadman: wire dead man’s switch to device heartbeats (80299680)
  • crypto: wire E2E encryption + key management API (04c3960f)
  • safety: wire fragment reassembly and SOS detection (4563f226)
  • integration: wire reassembler in testStack and add fragment reassembly test (09d5fa56)
  • api: add strict JSON request validation, update roadmap and README (627b9b90)
  • ops: add Ansible playbooks, runbook, and updated README (dd7efb19)
  • cluster: add Galera cluster health monitoring + remediation UI (0f231bde)
  • health: add Galera write-readiness probe for HAProxy health checks (573f927c)
  • store: replace PostgreSQL with MariaDB Galera store layer (d0cfc2b2)
  • login: email/password login UI with token refresh + API token fallback (99c32026)
  • auth: add built-in user management with Argon2id + JWT sessions (8bae0235)
  • auth: add built-in user management with local accounts and JWT sessions (3801e42b)
  • dashboard: add views for all v0.3-v1.0 features + Playwright e2e tests (a7cf499a)
  • mptcp: add bandwidth monitoring, failover logic, and endpoint management (43abfc86)
  • hawkbit: add Eclipse hawkBit OTA integration + REST API (fa1623e7)
  • mptcp: add MPTCP concentrator monitor + API endpoints (3620eac1)
  • astrocast: add Astrocast Astronode S REST API client + constellation backend (35ef5c39)
  • geofence: add polygon geofencing engine + position MQTT subscriber (3ba336c8)
  • position: extend position model + Douglas-Peucker simplification (8a303316)
  • crypto: add AES-256-GCM encryption and per-device key management (a9559f99)
  • dedup: wire message deduplication into MO ingestion pipeline (e35b9807)
  • fragment: add SBD fragmentation and reassembly (2aeb0af7)
  • api: add notification preferences CRUD endpoints (819dc9d0)
  • deadman: add per-device dead man’s switch with snooze (24c7fa7e)
  • ntfy: add ntfy push notification client + multi-notifier fanout (91784bbe)
  • apprise: add Apprise notification client for escalation alerts (ee74aac5)
  • escalation: add SOS escalation chain engine with alert state machine (2ef5852b)
  • security: add OWASP compliance testing for public-facing Hub URLs (98f067f3)
  • api: add security headers middleware (HSTS, CSP, X-Frame-Options) (bf663086)
  • security: add SAST/SCA pipeline stages and security-first documentation (96f2d37f)
  • k8s: add Kubernetes manifests and Helm chart (604fc6b3)
  • e2e: add post-deploy smoke tests and verify CI stage (1faaa844)
  • leader: implement Kubernetes Lease API leader election (294b0640)
  • cluster: add cluster-mode docker-compose with PG, Redis, NATS (fefd1654)
  • health: add active dependency probes to readyz endpoint (d8454625)
  • web: auth UI with API key management and role-based nav (4e8fd286)
  • web: enhance device, message, and credit views with Tailwind (3e33dd5f)
  • ratelimit: per-device monthly budget caps alongside daily limits (7f6542dd)
  • audit: wire message_sent events and add audit dashboard (1f1ebc0f)
  • audit: tamper-evident hash-chain audit log with verification API (fe0c2a09)
  • device: per-device config versioning with history API (5b34d772)
  • web: add Tailwind CSS, auth store, login view, router auth guard (38a84a34)
  • auth: device API keys with RBAC enforcement (93eb365f)
  • auth: tenant isolation layer with per-tenant data scoping (ba1c0e43)
  • auth: JWKS-based JWT signature verification for OIDC mode (475212f2)
  • auth: OAuth2/OIDC + token auth middleware (68cb8f0e)
  • Vue.js dashboard with position map, device registry, messages (9b49a34c)
  • wireguard: wg-easy API client + peer management REST endpoints (c1e67be9)
  • constellation: unified satellite send interface with routing strategies (201c065e)
  • credits: Iridium credit balance polling + REST endpoint (93805887)
  • tri-mode wiring: all subscribers use bus.MessageBus, main.go mode switch (a2d930c6)
  • log mode on startup (74be5f4a)
  • tri-mode foundation: MessageBus, Leader, PostgreSQL store, mode config (d00fe9c4)
  • ratelimit,dedup: Limiter interface + Redis impl + idempotent dedup (19e2042f)
  • store: SQLite storage layer: Store interface + device/message/webhook/position/audit CRUD (7595cbcb)
  • backup: full state export/import with diff preview (b7dda92f)
  • webhook: outbound webhook API with HMAC signing and retry (438513aa)
  • ratelimit: per-device token bucket rate limiting for MT sends (f6fb1210)
  • aprsis: Hub APRS-IS IGate: satellite positions on aprs.fi (17bc50df)
  • tak: Hub TAK gateway: MQTT subscriber → CoT → OpenTAKServer (e5eda94e)
  • test: add integration test suite with embedded MQTT broker (3dcad4a5)
  • sbd: add RockBLOCK webhook handler and Cloudloop MT sender (cc2ff9ed)
  • add MQTT client, topic namespace, and SMAZ2 compression (ed8b93cf)
  • infra: add Docker Compose stack with MQTT and Tor (3d0c3db9)
  • initial repository setup (28310b6e)

Fixes

  • nats: a bridge credential can no longer read the Hub’s internal bus or another tenant (1915d362)
  • sms: the Android inbound subscriber ignores the Hub’s own sms/inbound topic (cee990ec)
  • api: a bridge command outlives the server’s 15 s write timeout (8f275573)
  • oob: correlate command replies across replicas and assemble multi-segment replies (d7e00c31)
  • tor: the onion heartbeat is a warm Tor client, not a cold bootstrap every ten minutes (5ad4654b)
  • verify: notify suite may read hub-config, and keeps the log tail its last check needs (98854270)
  • http: WebSocket upgrades survive the metrics and logging wrappers (eb34a40f)
  • verify: notify suite reads the ConfigMap, not a startup line that scrolls away (d7b8f6e0)
  • verify: the journey probe drops the exec power it never used (706dcaa5)
  • signups: a verification probe is approved without mailing anyone (9c908239)
  • verify: restore run.sh, which the previous fix truncated to nothing (42586278)
  • verify: the all-green run crashed before reporting (da407750)
  • verify: replica suite survives a rollout that swaps a pod mid-run (c5ac51a1)
  • verify: journey suite reads both replicas; MQTT broker field gets the public-address guard (a7133ccb)
  • verify: suites that assumed the runner or the Ko-fi era (822249e2)
  • k8s: verify job pulls with the registry secret; image pinned by digest; Secret canary (81d50ebc)
  • k8s: verify suites ConfigMap without a name hash (a67328b3)
  • replicas: one send per MT and SMS request, one HeMB publish, one inbound SMS row (5c20472d)
  • store: scoping ratchet parses files without the deprecated ParseDir (2c61d0a8)
  • store: tenant-scoping ratchet; UpdateMessageStatus ignored its tenant (92a79315)
  • k8s: onion heartbeat: DataDirectory tor owns (3abd5aa5)
  • k8s: onion heartbeat: tor needs a regular torrc, written to /tmp (227efa3b)
  • k8s: onion heartbeat: tor must not read the image torrc on a read-only root (85bc4044)
  • hawkbit: activate the postgres profile the way the image can actually hear (c5d3ff19)
  • scripts: the digest gate must also see no OLD pod left (ddb42efe)
  • store: a comment inside an applied migration is a text change (d07f7e30)
  • hawkbit: OTA was non-functional since deploy; H2 2.x removed IDENTITY() (26c31108)
  • scripts: no grep -m1 under pipefail in the digest check (efc465e0)
  • devices: tell the second tenant whose IMEI it is, and keep the global key (880a0ba4)
  • bus: a broker blip at startup no longer silences a replica for good (0e3a2a24)
  • integrations: retry the replica subscription instead of giving up once (81ebe3fb)
  • integrations: a saved provider account reaches both replicas at once (5443cdaa)
  • api: rewrite the customer-facing capability messages (c29704f2)
  • tor: drop fsGroup, which made the onion key too readable to start (37a6b35f)
  • tor,wireguard: the onion key survives losing the volume; tighten wg-easy (372732da)
  • k8s: cap apprise’s worker pool; memory was never the problem (ea1893e9)
  • k8s: apprise needs more than 256Mi (71753d7c)
  • k8s: apprise creates its own user, so it cannot run with no capabilities (1ba8e43e)
  • k8s: stop overriding the tor image’s command (2127702c)
  • wireguard: log in on demand, not once at startup (c7f8327f)
  • k8s: wg-easy needs root and NET_RAW, not the kernel modules (cec89115)
  • k8s: hawkBit’s user property is a map keyed by username (99ea9c3a)
  • k8s: wg-easy and hawkBit name a storage class that exists (edf3332a)
  • email: persist PGP contacts and share them across replicas (82c39c50)
  • config: always seal OOB frames, classify every setting’s owner (b57e9c7d)
  • aprsis: both replicas would transmit the same packet (7490f0c7)
  • position: every position was stored twice (9de4c1be)
  • webhook: a customer’s endpoint was being POSTed to twice (61515f9d)
  • geo: one crossing, one page, on two replicas (607f2de0)
  • web: pick a geofence’s escalation chain, don’t type it (5e21483b)
  • webhook: a created webhook could never be deleted, and had no timeout (7f2e1f99)
  • geo: a device could cross another tenant’s fence (98edde4c)
  • ratelimit: one tenant could lift another’s send budget (bcb4f5fa)
  • webhook: every tenant’s events went to every tenant’s webhook (e1d1813d)
  • deadman: the dead man’s switch was every tenant writing yours (9210f6dd)
  • security: gate the remaining platform-only routes (67c8768d)
  • security: stop the backup export leaking live credentials (8ae92917)
  • escalation: let a tenant configure an SOS chain, and make it hold (ed490ec1)
  • ots: own the httpx timeout, and test the icon patch (68de93f3)
  • web: stop asking for credits once the answer is 404 (cff1ef6d)
  • takfront: audit the one refusal that names a real client (4a54881a)
  • tenancy: evict a purged tenant from every cache, on every replica (352e1d82)
  • security: one-time provisioning material must not outlive its claim (965b265c)
  • security: seal the Hub’s own long-lived keys at rest (0a47b7fa)
  • takfront: tell plaintext on the TAK port apart from a refused client (1a47d2b2)
  • ots: guard close_connection, which is the call site that mattered (9cc3a558)
  • tak: the Hub asks for the identity name the CRD admits (78d75eaa)
  • ots: stop the probe traceback flood, and time out every request (bd117c51)
  • tak: give each instance an OTS account for the identity the front uses (740d4bac)
  • tak: quiet the probe WARN, and recover from a refused identity (868c0a92)
  • web: point Help at the docs, and offer the iTAK package (654c44e4)
  • tak: give each Hub replica its own upstream identity (425b79e9)
  • tak: switch the hosted TAK front back off until the identity race is fixed (90c29689)
  • tak: let the Hub ask for its own upstream identity (a588323f)
  • tak: reload the front’s server certificate when it is renewed (e385d97d)
  • tak: put the front’s chain in the enrolment truststore, not the tenant CA (9ab486d2)
  • tak: point the operator at the icon-fix OTS image (fc7d3df5)
  • ots: load marker icons from the image instead of a blocked network fetch (4244752a)
  • tak: point the operator at the CVE-clean OTS image (4920b89f)
  • tak: a TAK server that refuses the Hub no longer loses positions in silence (02082e65)
  • ots: apply Debian security updates to the hosted TAK image (199d4333)
  • ci: the image pin comment names the build it pins (d83a9cef)
  • tak: change the administrator password from OpenTAKServer’s default (69c4f37d)
  • ots: drop pip from the runtime image, it vendors a vulnerable msgpack and setuptools (07c5802b)
  • ots: patch the vulnerable dependencies OpenTAKServer pins, and gate branch builds on CVEs (27a79833)
  • tak: only the platform’s own traffic reaches the operator’s TAK server and APRS-IS (e6da23b2)
  • tak: federation refuses to start without its own CA, and is off until it has one (dd6e0d4f)
  • scripts: no early-exiting pipe reader survives pipefail (8273bccc)
  • stripe: raising the pinned API version no longer breaks donations (80f9dd0a)
  • ci: the pin guard blocked every deploy with SIGPIPE (5734ba12)
  • billing: the donation page stops arguing against its own ask (3d52bd7a)
  • metrics: the money alert should not wait for money to go missing (ee25ec20)
  • scratchpad: a probe must not manufacture the alerts it helps prove (a9896c3b)
  • stripe: only real money counts as an unattributed payment (4e158bcf)
  • stripe: Subscribe stopped working for a day after the first attempt (b6bcaa51)
  • refunds: refunding a donation took a month off the customer’s subscription (fd9d588e)
  • stripe: a refunded subscription issued no credit note (d0a41ae0)
  • invoiceninja: a tenant paying from a second address got no document at all (9e6e8646)
  • stripe: a plan expired on a date the Hub invented, not the one Stripe bills on (f32de103)
  • stripe: the first subscription payment took the money and issued no document (282a18fe)
  • billing: a donation returned the giver to a sign-in wall and a subscription receipt (8be80fd7)
  • mail: the plan-changed email described a cancellation that does not happen (a4218530)
  • api: unattributed payments have been invisible since the Stripe migration (37f269c1)
  • billing: the Subscribe button 403’d for every real customer (2d9d26f0)
  • routing: seeded fan-out routes listen to satellite channels only, SOS over SMS escalates, duplicate persist is not a warning (66cb9d4c)
  • billing: the half of the Stripe wiring that never reached the commit (10014fbe)
  • mqtt: a phone number as device id no longer puts an MQTT wildcard in a publish topic (56babac9)
  • sms: a plain-text inbound SMS reaches the routing engine, so the kit to Hub to kit relay fires (f02108f5)
  • refunds: the two drainers share a receipt row, and could both lose (a702ffd0)
  • refunds: record the refund before taking the paid period back (b3e52b41)
  • invoiceninja: the inclusive-taxes guard was checking a URL that does not exist (06a10543)
  • vat: a donation is not a subscription and not the free plan (27ccc22e)
  • billing: the tenant owner keeps owner, and a receipt is claimed before it is issued (ba315733)
  • enrollment: the flow title wrapped to an orphan and broke the card’s axis (6cb265ba)
  • enrollment: the bootstrap never checked the template it repoints to (e8666d95)
  • k8s: the grace period was shorter than the shutdown it has to allow (3147ba28)
  • oidc: never discard the tenant this request just won (92e5a7e5)
  • billing: an operator-set plan could lapse, and a handover could double-issue (9be09a90)
  • oidc: two sign-ins for one new account left an orphaned tenant (06b01619)
  • tenant: a claim code shown to a customer was not always the stored one (80c60521)
  • mail: state the exact moment a plan ends, not “tomorrow” (fde3ee3b)
  • auth: an API key resolved no tenant, so every key answered 403 (6896aa1a)
  • enrollment: the first mail a customer gets was branded authentik (9eab4759)
  • enrollment: stop the bootstrap silently deleting the CAPTCHA (3f56605d)
  • signups: approving a request could activate any account on the shared IdP (fdd31244)
  • kofi: close four ways a subscription could be granted wrongly (96982325)
  • auth: the enrollment hero was only half painting (096cec09)
  • security: close what the IP allowlist was hiding, part 2 (0e5628d4)
  • security: close what the IP allowlist was hiding, part 1 (b46f5b09)
  • kofi: a retried delivery must not buy a second month (e54ee748)
  • kofi: remember the payer, or every renewal loses the claim code (0f456382)
  • plans: a new tenant starts on free, not on the unlimited beta plan (77c8e9ee)
  • bridge: a birth cannot choose its own tenant (18c67518)
  • auth: stop sending MeshSat alerts from another brand’s address (25e9b64d)
  • rockblock: actually verify the signature Ground Control sends (8504c09c)
  • tenant: apply a suspension on every replica at once (be6d538a)
  • signups: decode a real authentik user, not an imagined one (38907ab5)
  • tenant: keep secrets out of the export (d412ce9e)
  • webhook: stop the IP allowlist refusing a tenant’s own webhook (a9d88220)
  • k8s: give the platform tenant a RockBLOCK webhook secret (0e1dd787)
  • map: make the map labels readable on the brand background (ec67e370)
  • map: give the basemap server somewhere to write (5805cdf5)
  • map: let the basemap build collect garbage instead of being killed (d10c60ba)
  • map: the deep basemap covers Greece too, where half the fleet is (63f87cb8)
  • map: put the streets back, with a second deeper basemap archive (3f965b7a)
  • cloudloop: subscribe to the satellite feed on the leader only (6aacf229)
  • k8s: mount the Cloudloop MQTT client certificate, the Iridium feed was dead (4729c538)
  • edge: let provider callbacks through the beta gate (0514a72c)
  • k8s: roll the Hub one pod at a time, two eligible workers cannot host a surge pod (a2c0725d)
  • bus: MQTT client id per pod so two replicas do not replace each other’s broker session (eee2ed66)
  • escalation: notify with the alert’s tenant in the context so SMS use the tenant’s Twilio account (64d7fb4f)
  • ui: OTA and Email pages say the feature is not enabled on this Hub instead of an empty list (67f3e068)
  • oob: bounded conversions for the stored peer id, role and replay window (gosec G115) (833d5532)
  • ui: allow the OpenStreetMap apex host in the CSP, refresh the session once per burst, phone layouts for Messages and the Tenant panel (41b275c9)
  • webhook: store inbound messages in the device’s tenant, gate the email webhook and the wildcard Cloudloop allowlist (2663517a)
  • auth: refreshed sessions keep the platform-admin flag, IdP name updates the user row (ca026716)
  • auth: the Hub owner is the dedicated MeshSat identity admin@meshsat.net, not an omoikane account (075a7f21)
  • cloudloop: key MT cost rows and mt/status by device IMEI, not the Cloudloop thing ID (c51cb895)
  • ui: ship the rebuilt SPA bundle, keep the authentik card below the language switcher on phones (4d9237b9)
  • ui: geofence map on OpenStreetMap tiles too, Credentials buttons on the brand tokens (ef722440)
  • ui: browser audit round 1 after the cutover (2d22a588)
  • authentik: dark card container, hide the injected omoikane sky canvas (ed3f6d6e)
  • nats: explicit permissions for the shared user so a reload does not crash nats-server (12fee660)
  • ci: keep the deploy stage for bump_k8s_pin; lint fixes from the post-cutover MRs (6d7881e2)
  • audit: verify the retained chain segment after retention purged its head (e0441c86)
  • store: audit chain verification read zero entries on mariadb and postgres (09137a30)
  • authentik: give the signup notification rule a destination (8a002b2c)
  • authentik: report the enrollment email-verified marker in the email scope (e98a635a)
  • k8s: NATS password variable, OAuth2 grant types, tolerant approval mail (67506189)
  • bus: redact broker credentials in logs and stop overriding the MQTT URL on k8s (4d8277df)
  • deps: bump golang.org/x/crypto to v0.55.0 and x/net to v0.57.0 (890685b5)
  • authentik: validate the Matrix ID from either prompt_data key shape (4f35ce47)
  • k8s: authentik 2026.8 ClientType import, wrapper grep exit codes, phase 3 notes (5491282e)
  • k8s: edge auth backend health checks a static asset, not /-/health/live/ (959a5735)
  • k8s: relay health checks via the NATS monitor port (fd0b3f5c)
  • security: annotate operator-path file access for gosec v2.29.0 G703 (3095a490)
  • security: inline path cleaning so gosec v2.29.0 sees the sanitizer (12a52f05)
  • security: make the gosec HIGH gate real and resolve its 84 findings (0be356ab)
  • message: stable inbound message IDs, duplicate insert is a no-op (f7390932)
  • auth: on-curve check via crypto/ecdh, lint cleanups (8597ac26)
  • leader: compile the Lease elector unconditionally, never leader on error (20445365)
  • deps: bump x/text to v0.39.0 and x/net to v0.55.0 (GO-2026-5970, GO-2026-5026) (e42672ae)
  • dbwrap: bounded driver-agnostic DB retry, fix int64 backoff overflow (5634c128)
  • cloudloop: resolver learns IMT things from live API shape + env seed (52163c8b)
  • bus: fan out same-filter MQTT subscriptions: stop paho router clobbering (ffdce804)
  • security: drop deprecated middleware.RealIP: restore direct-peer RemoteAddr (1496e148)
  • deps: bump chi to v5.3.0: RealIP IP-spoofing CVEs (GO-2026-5774/5775) (b07f26aa)
  • tak,aprsis: stop bufio.Scanner busy-loop on idle TCP read timeout (f1c245bc)
  • ci: bump package job to docker:28 for runner daemon API 1.44 (a9e43c61)
  • deps: bump cloudflare/circl to v1.6.3 (GO-2026-4550) (ec7a01a0)
  • email: migrate openpgp to maintained ProtonMail fork (GO-2026-5932) (cb636e15)
  • tak,aprsis: remove read deadlines that poison bufio.Scanner into a CPU spin (d2b052c8)
  • cloudloop: tolerate subscriberCertus string-or-object (IFRNLLEI01PRD-906) (dc7c4d1b)
  • main: drop directory group/policy route registrations (aa0c443e)
  • directory: explicit errcheck on deferred rows.Close (a203153b)
  • ui: nav dropdown menus hidden behind Leaflet map z-index (890c4bcc)
  • galera: align garbd EVS timeouts with MariaDB data nodes (5ffa4ba0)
  • galera: align garbd EVS timeouts with MariaDB data nodes (7a607a8a)
  • galera: increase EVS timeouts for WAN SST, prevent seqno -1 auto-bootstrap (7546a579)
  • galera: prevent garbd solo PRIMARY proto 127/127 poisoning (1a686264)
  • galera: auto-bootstrap broken: parse node address from args, add retry path (cb1d6217)
  • galera: WSREP 1047 retry, smart auto-bootstrap, UUID cleanup (cb48a122)
  • ui: use bridge_id in BondGroupsView selector and E2E tests (6cb12d2c)
  • db: add bond_groups table to MariaDB migrations (fe9539d8)
  • e2e: use ESM imports and handle MQTT timeout in credential rotation test (d7e00e17)
  • security: harden v1.2 channel APIs and mark v1.2 complete (f2fc5fcc)
  • owasp: use exec-based ZAP setup with correct uid 1000 ownership (114b5400)
  • owasp: add required rule name column to ZAP baseline config (6d54aac8)
  • owasp: set world-readable permissions on ZAP config for non-root user (53ec6bd6)
  • owasp: copy wrk directory structure into ZAP container (85729e26)
  • owasp: use docker create+cp instead of volume mounts for ZAP (4d6bbcb5)
  • owasp: resolve REPORT_DIR to absolute path for Docker bind mount (6cdeaeee)
  • owasp: fix ZAP config mount and path traversal false positives (1d3e17ac)
  • tak: add errcheck nolint for OTS poller resp.Body.Close (abff5a2f)
  • tak: start TAK client unconditionally, not in leader election (a091a054)
  • tak: Hub IS a TAK gateway: show as always active, not disabled (81bc1708)
  • galera: include garbd arbitrator in cluster address (d79e4202)
  • galera: hardcode cluster address in compose template (7085619f)
  • galera: watchdog must use --force-recreate for Docker Compose v5 (cde03c12)
  • tak: remove unused wg field from federationPeer (fb77a359)
  • tak: nolint directive for federation wg field (eedf2ae4)
  • tak: restore wg field for federation goroutine coordination (97cfd31b)
  • tak: lint: unused field, unchecked Close() returns (8d8432c9)
  • routing: skip matchFilter for SMS/email recipients + API key auth (f4b2a55b)
  • cloudloop: LingoIMT.MessageID as json.Number (was string) (c63d50af)
  • sms: Hub SMS encrypt uses channel key (sms:*) first (d09ed10b)
  • api: check json.Encode error (errcheck lint) (20d93828)
  • api: commit device key distribute + rotate methods (06f49d5f)
  • rockblock: accept unsigned webhooks when secret not configured (dbd583ad)
  • cloudloop: apply De Morgan’s law to satisfy staticcheck QF1001 (cd8b3e4b)
  • cloudloop: support wildcard (*) in webhook IP allowlist (697a4616)
  • sms: remove undefined retSMSIface reference (6d224a3a)
  • hemb: backport generation cleanup from bridge E2E fixes (1ba50d84)
  • lint: check errcheck on rows.Close and AddSymbol returns (cdbba99d)
  • lint: check json.Unmarshal return in subscriber tests (5af76f72)
  • lint: check json.Unmarshal return in birthverify tests (a80da3ba)
  • provision-qr: add auth refresh to fetchBlob + better error messages (38229aa9)
  • auth: exempt QR provision claim from auth middleware (07ecc288)
  • csp: add blob: to img-src for QR provisioning + Playwright tests (cb87b1a7)
  • lint: remove unused os import (8c84c6e5)
  • lint: use t.Setenv in security headers test (66bac805)
  • security: address all audit findings: MPTCP injection, webhook auth, CORS, rate limiting (0c685200)
  • mqtt: parse credentials from broker URL for NATS auth (864a90ee)
  • security: harden NATS auth, stunnel mTLS, Redis password, Galera UFW (0417d2bf)
  • provision: two-step QR: short URL in QR, full bundle via claim endpoint (f1fbd803)
  • lint: check w.Write error return in QR provisioning handler (25c108d1)
  • lint: use time.Until per staticcheck S1024 (8d6857ab)
  • lint: resolve errcheck, staticcheck, unused lint findings (43d6a5de)
  • deploy: HAProxy SNI routing for Reticulum TCP + stunnel Docker fix (4f052526)
  • reticulum: RNS-compatible random_hash + wire compat test + TCP debug logging (a171c677)
  • bridge: retry on Galera deadlock during bridge birth registration (189889d8)
  • store: include credential columns in GetBridge and ListBridges (b7047628)
  • bridge: health messages re-set online status after reaper timeout (e8e1b528)
  • galera: eliminate all bare gcomm:// writes to .env (25717f46)
  • nats: prevent leaf node loop by restricting remotes to spoke nodes (c16d5c85)
  • fleet: clear retained MQTT messages on bridge delete (2541eb4d)
  • test: add GetSystemConfig to mock store for credential test (1e5a77f6)
  • docker: bundle MSVQ-SC codebook assets in container image (6c937559)
  • infra: add hub service to Galera compose + fleet Playwright tests (0723372a)
  • lint: handle errcheck warnings in credentials, mariadb, sqlite stores (c3fef1b7)
  • galera: add garbd Dockerfile and fix --option syntax (85c2e5ca)
  • galera: enforce cluster_size=3 with garbd arbitrator (d17c0611)
  • galera: add garbd arbitrator, entrypoint wrapper, and pc.recovery for self-healing (26e8bd2c)
  • bridge: ignore stale retained birth messages on Hub restart (a4bc4002)
  • bridge: add reaper to mark stale bridges offline (94b0bf72)
  • hub: escalation engine now respects tenant isolation in alert processing (50d027d5)
  • hub: WebSocket auth: require token, fix CheckOrigin to same-origin (d14fd9f2)
  • cloudloop: add debug fields to no-IMEI warning for test messages (676bd26f)
  • dashboard: Hub status shows ‘OK’ in uppercase (7c525284)
  • store: MariaDB JSON columns reject empty strings, use ‘{}’ fallback (d6cf719f)
  • store: MariaDB NOT NULL columns missing DEFAULT values (e1993952)
  • bridge: staticcheck SA9004 lint error in satdecoder const group (6d302a71)
  • ui: map gap, delete confirmations, message chart, constellations (8637a37e)
  • ui: add fullscreen background logo matching Bridge (9b779603)
  • ui: single-row header, compact status bar, watermark empty states (1b4cad79)
  • ui: unify Hub design tokens with Bridge codebase (8d5ac2ca)
  • owasp: remove python3 dependency, accept SAMEORIGIN from nginx (a35ba509)
  • ci: use docker exec for healthz checks (port not exposed to host) (15f17a91)
  • ci: add DEPLOY_SSH_KEY, use IPs for DMZ hosts, fix pre-deploy SSH (c590fc27)
  • dashboard: unify all 19 views with consistent design language and TAK color scheme (9476bd0b)
  • deploy: fix SSH user in health check, use container exec for Ansible healthcheck (3cbbd1fe)
  • galera: pass wsrep settings via command-line args, fix file permission issue (868e767b)
  • sms: persist inbound SMS to messages table + show in dashboard (a27933a8)
  • auth: exempt all /api/webhook/* paths from auth (Twilio/Astrocast/email can’t send bearer tokens) (49671603)
  • sms: use base64 encoding for encrypted SMS (matches Android AesGcmCrypto) (06760652)
  • sms: auto-generate encryption key on first use, force compress when checked (525ec84b)
  • core: persist MO messages directly in webhook handler (bypass MQTT loop-back) (2a090368)
  • ci: replace AWX deploy with direct SSH: AWX was restarting MariaDB (77225ba9)
  • auth: add OIDC cert pinning support + gitignore sim binary (708cd2cb)
  • core: add message persistence subscriber: MO messages now saved to DB (7f14e11f)
  • ci: swagger job validates generation succeeds, skip diff check (non-deterministic across Go versions) (e918211a)
  • ci: compare only swagger.json not yaml (yaml formatting varies by Go version) (839996d4)
  • ci: add go mod download before swag init (8af04847)
  • ci: pin swag version to v1.16.4 for reproducible swagger generation (d061cc1e)
  • ci: install git in swagger CI job, regenerate spec (92f4faf0)
  • codec: fix GPSDecoder magic byte (0x47→0xA5), add bridge format unit tests (49ce2b41)
  • security: use constant-time comparison for webhook JWT verification (46934ac5)
  • test: use larger fragment test message to match MinFragmentPayload=100 (1f78001d)
  • ui: standardize all timestamps to UTC 24h format (6566b09b)
  • deploy: use --no-deps to prevent MariaDB restart on Hub deploy (449fa54e)
  • cluster: make /api/cluster/node auth-exempt for peer health queries (0fbf1bad)
  • mariadb: handle NULL datetime columns with sql.NullTime + add garbd Dockerfile (56f0febe)
  • mqtt: retry initial MQTT connection with backoff (46c19cd6)
  • mqtt: remove NATS auth username, use anonymous MQTT connections (a1dedcaa)
  • mqtt: add NATS MQTT adapter auth username for cluster mode (b338aeb4)
  • auth: add legacy token fallback in local auth mode (0822a267)
  • dashboard: fix healthz path, CSP for map tiles, improve settings page (be86c399)
  • store: add NotificationPref type/methods missing from prior commits (ebdf6645)
  • main: bounded worker for API key last_used + remove dead config field (38e55830)
  • bus: replay MQTT subscriptions on broker reconnect (549fd4e3)
  • health: use live probe for MQTT instead of one-shot check (55f3fd51)
  • bus: set MQTT 3.1.1 protocol version for NATS compatibility (4fa1204b)
  • web: validate login token against authenticated endpoint (db83d2ba)
  • lint: handle errcheck on fmt.Fprintf in auth error writer (f59067fd)
  • lint: replace nil context with context.Background in auth test (78ce424b)
  • dedup: use SET NX PX instead of deprecated SetNX (6b936a5b)
  • last errcheck in cloudloop client (7764c3f6)
  • resolve 21 golangci-lint v2 issues across 7 files (29b2367b)
  • ci: install golangci-lint on go1.25 image (986e0ae0)
  • ci: merge duplicate variables blocks (2ad469fc)
  • aprsis: handle SetReadDeadline error return values (3fa1fe54)
  • resolve golangci-lint errcheck and unused warnings (6ae0df27)

Performance

  • store: migrate the sqlite conformance template once and copy it per sub-test (5149f331)
  • bridge: only the Lease holder writes a health report to the store (e2d64864)
  • bridge: a health report is one UPDATE, not four queued on the same row (7102bfd0)
  • ci: test databases skip fsync; the fixed cost was never Argon2 (74460cd4)