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-onlyand 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-recreatefor 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
--optionsyntax (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-depsto 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)