180 Commits

Author SHA1 Message Date
agessaman 72e874f838 fix(regions): scope flood replies to the request's region
Build a core RegionMap from the node's served regions and wire it into
the dispatcher and every companion bridge, so a flood reply is re-scoped
to the region its request arrived under (or left plain for a wildcard /
direct request) -- matching firmware simple_repeater::sendFloodReply.
Previously replies went out plain, so a reply to a request in region B
was dropped by B-only repeaters.

The map is built once from the node-wide transport_keys table (each named
region -> RegionEntry, flags=REGION_DENY_FLOOD for deny-flood regions; the
'*' wildcard is deliberately not an entry so plain floods reply plain). A
single shared instance reaches the dispatcher and all bridges. Public
regions rely on name-hashing for their key; a stored key is carried only
when it is genuinely custom material the name would not reproduce, keeping
reply-matching aligned with the forwarding transport-code check.

Region edits at runtime (CLI, web API, Glass sync) all funnel through the
transport_keys CRUD methods, which now fire a post-commit change callback;
the daemon rebuilds the map and reassigns a fresh instance to the
dispatcher and every live bridge (atomic rebind, safe against an in-flight
find_match on the RX thread).

Requires openhop_core with Dispatcher.region_map / CompanionBridge.region_map.
2026-07-24 07:52:16 -07:00
Rightup 6e5c4e1b0d fix: refactor SQL queries for better readability and maintainability 2026-07-20 21:25:56 +01:00
Rightup b3e4649f54 fix: add nosec comments for intentional LAN bind defaults and controlled SQL fragments 2026-07-20 21:10:45 +01:00
Rightup efb7e4a319 feat(metrics): implement metrics data retrieval with RRDtool fallback 2026-07-20 13:44:14 +01:00
agessaman 2432332f87 refactor(airtime): introduce refresh_radio_params method for dynamic modulation updates
Added a new method to the AirtimeManager class to refresh modulation parameters without clearing transmission history. Updated ConfigManager to call this method after applying live radio configurations. Enhanced tests to verify that modulation updates occur correctly during live updates.
2026-07-17 17:38:11 -07:00
agessaman a620433312 Merge upstream/fix/all-the-things into fix/all-the-things
Keep both migration 12 (companion message signal/channel data) and
migration 13 (packet upstream hash for neighbour links), and retain
ACK/MULTIPART plus TRACE imports in engine tests.
2026-07-15 22:51:15 -07:00
agessaman 2d6b1131ac fix(storage): evict offline-queue rows set-based in insertion order
The companion queue's capacity path ran a Python loop of single-row
SELECT+DELETE pairs ordered by created_at. Wall-clock ordering meant a
backwards clock step (NTP correction) could make the just-inserted
channel row sort as oldest and wrongly reject the incoming message while
older channel rows remained evictable.

Replace the loop with one set-based DELETE ordered by id (AUTOINCREMENT,
i.e. insertion order, immune to clock steps), with an evictable-count
pre-check preserving the all-or-nothing rejection rule: never displace a
direct message, never evict the incoming row to make room for itself,
roll back the insert entirely when channel rows cannot make room. The
queue load and pop queries move to id ordering for the same reason.
2026-07-15 22:02:18 -07:00
agessaman 034cd6f566 fix(companion): persist queued message signal and channel data 2026-07-15 17:06:00 -07:00
Rightup 9688fe70e4 feat(neighbour-links): implement neighbour link tracking and history retrieval 2026-07-15 17:08:40 +01:00
agessaman 79cba76b4d fix(companion): protect direct offline messages 2026-07-14 16:59:34 -07:00
agessaman 1fe3fb1779 fix(storage): retain signed advert packets 2026-07-14 15:51:40 -07:00
Rightup 6085a06369 fix(mqtt_handler): change log level from info to debug for connection messages 2026-07-14 16:02:24 +01:00
Rightup f49c29874c fix(mqtt_handler): change log level from info to debug for connection messages 2026-07-14 08:51:33 +01:00
Lloyd b2eb45b199 feat: Add LBT diagnostics endpoint with correlation analysis
- Implemented `lbt_diagnostics` API endpoint to return aggregated Listen Before Talk (LBT) diagnostics aligned with RF metrics.
- Introduced methods for calculating Pearson correlation coefficients and auto-bucket sizing for diagnostics.
- Enhanced data aggregation logic in `StorageCollector` for LBT diagnostics.
- Updated OpenAPI specification to include new endpoint and response schemas.
- Added comprehensive unit tests for LBT diagnostics, including validation of correlation calculations and data integrity.
2026-07-10 13:36:49 +01:00
Rightup 1906f576bb feat: add region/default-scope / cli commands update
standardize default region on mesh.default_region (remove legacy region_default_scope usage)
add/align CLI support for owner.info, path.hash.mode, and loop.detect with validation
wire UI terminal get/set + autocomplete/help for owner.info, path.hash.mode, loop.detect
extend update_radio_config to persist owner_info for UI set owner.info
add and use shared packet utility for advert creation + default-region transport scoping
refactor repeater and room-server advert paths to reuse shared packet logic
2026-07-08 17:25:35 +01:00
Rightup 92310235ae fix: correct model name in MeshCoreToMqttPusher class 2026-07-08 15:34:12 +01:00
agessaman e22514882f perf: force timestamp range scan for windowed packet-stats GROUP BYs
The GROUP BY type and GROUP BY drop_reason sub-queries in
get_packet_stats (and get_packet_type_stats) filter by a time window but
let the planner pick idx_packets_type / idx_packets_transmitted to get
grouping for free. It then heap-checks the timestamp filter across the
entire table, turning a bounded window into a full scan: on a 1.5M-row
packets table the 24h stats load spent ~4.8s (type) + ~2.3s
(drop_reason) instead of ~0.1s each.

Pin these to idx_packets_timestamp with INDEXED BY so they range-scan the
window (~50k rows) and group via a small temp b-tree. Verified on the
live DB: 4.80s -> 0.10s and 2.32s -> 0.10s. Unlike a covering index this
adds no write-path cost on the packet-insert hot path.
2026-07-08 10:13:28 +01:00
agessaman 676e2cea30 perf: add covering index for airtime chart queries
The airtime/utilization chart queries (get_airtime_data and
get_airtime_buckets) range-scan and order packets by timestamp,
selecting only timestamp/length/payload_length/transmitted. On a large
packets table this forced a full scan of the row heap, saturating slow
storage (e.g. a Pi SD card): each dashboard poll took longer than the
client timeout, aborted polls stacked, and sustained I/O starved the
transmit queue.

Add a covering index on packets(timestamp, length, payload_length,
transmitted) so these queries run index-only, dropping the read from the
full row heap to just the index range. Verified via EXPLAIN QUERY PLAN
(COVERING INDEX idx_packets_airtime). Additive and idempotent.
2026-07-08 10:13:28 +01:00
Will Dillon d1a9230ab5 feat: allow API key authentication in WebSocket handler
The PacketWebSocket.opened() method only accepts JWT tokens for
authentication. API keys (used via X-API-Key header for REST endpoints)
are silently rejected, forcing WS clients to obtain a JWT through a
separate login flow.

Add fallback to token_manager.verify_token() when JWT verification
fails or when an X-API-Key header is present. This lets API token
holders connect directly to the WebSocket without needing a JWT.

Auth priority:
1. JWT in ?token= query parameter (existing behavior)
2. API key in X-API-Key header
3. API key in ?token= query parameter (falls through from JWT reject)
2026-07-08 10:12:47 +01:00
Rightup 9177d1aacc feat: add repeat on/off to status for mqtt 2026-07-07 22:41:32 +01:00
agessaman 954150b2d8 merge: reconcile companion cleanup with fix-general-tidy
Merge the maintainer's fix-general-tidy branch (neighbor discovery,
keygen, API endpoints, web-asset rebuild, HTTP server config/control
commands, and an independent #286 room-server push/ACK/guest fix) into
the companion cleanup branch.

Both branches fixed #286 in parallel with byte-identical push-ACK CRC
logic. In the six overlapping files the maintainer's implementation is
kept (ACL replay-detection/session helpers, encoded path-len with legacy
fallback, expected_crc/ack_timeout_s injector API, dispatcher ACK
helpers); this branch's unique companion work is preserved on top
(sender_prefix persistence + migration, boot-state hardening /
CompanionStateLoadError, MessageQueue.max_size, older-core fallbacks).

Conflict resolution took the maintainer's side across the overlap, then
fixed two integration seams the merge introduced and updated this
branch's tests to the maintainer's API:
- room_server: timeout used undefined `hops`; aligned to `path_len`.
- packet_router: PATH helper was invoked twice (maintainer's
  unconditional call plus this branch's conditional local-identity
  call); dropped the now-redundant conditional block.

Pin openhop_core to @dev (was @feature/publish-workflow-message-handling)
so this can merge to the repeater's dev; core dev carries the required
sender_prefix and PathUtils.is_valid_path_len APIs.

Full suite green (1040 passed) against openhop_core dev and
refactor/companion-housekeeping; ruff clean.
2026-07-07 12:56:43 -07:00
Rightup 43a112ce7c feat: add HTTP server configuration and control commands 2026-07-07 20:38:22 +01:00
agessaman e6d4b68d01 fix: fail companion init loudly when persisted state cannot be loaded
A transient SQLite error during boot made companion_load_channels/
contacts/messages swallow the exception and return [], which is
indistinguishable from "no data". The Public-channel backfill then ran
over the empty store, so clients saw their channels wiped and later
saves could overwrite the persisted state.

- companion_load_{contacts,channels,messages} now return None on error
  vs [] for genuinely empty, and log the companion hash
- add companion_count_{channels,messages} helpers
- extract shared _restore_companion_state used by both the boot and
  hot-reload companion paths; each load is cross-checked against the
  table's row count for the hash, retried once after a short delay,
  and raises CompanionStateLoadError if it still cannot load, aborting
  companion init (and the Public backfill) instead of starting empty
- log restored row counts per companion; log when the channel store
  rejects a persisted channel (unchecked channels.set return)
- trim_companion_contacts_to_fit refuses to trim on a failed load
2026-07-07 07:43:27 -07:00
agessaman fd43d86ea8 fix: persist sender_prefix for signed room posts
TXT_TYPE_SIGNED_PLAIN room posts carry a 4-byte author pubkey prefix
(QueuedMessage.sender_prefix, added in openhop_core). The SQLite
persistence path dropped it, so posts replayed from persistence showed
a zero-padded author prefix in the app frame while posts synced from
the live in-memory queue were correct.

- add sender_prefix column (hex text, default '') to companion_messages
  with an ALTER TABLE migration for existing databases
- store/return the prefix in companion_push/pop/load_messages
- pass sender_prefix when rebuilding QueuedMessage from persistence in
  the frame server and the startup preload paths
- add round-trip, default, migration, and rebuild tests
2026-07-06 22:46:49 -07:00
Lloyd ff43d85b85 Merge pull request #333 from mdspann/issue305
Add timezone to MQTT packet data
2026-07-04 22:19:49 +01:00
Rightup 229d71459f fix: improve upsert_client_sync to prevent over write. 2026-07-04 18:41:55 +01:00
mike 047ad19fd3 Add timezone to MQTT packet data 2026-07-03 15:07:26 -07:00
Rightup 34747d2610 feat: add packet retrieval by ID endpoint and corresponding database methods 2026-07-02 16:18:54 +01:00
theshaun 172c8cc016 Initialise MQTT handler only when at least one broker is configured to save some lil cpu and logging 2026-06-28 18:01:49 +10:00
yellowcooln af88459f29 merge: sync with openHop refactor base 2026-06-24 20:35:06 -04:00
Lloyd 2b67dea96b refactor:rename-project-to-openhop 2026-06-24 23:27:49 +01:00
yellowcooln 44a2d2f72f feat: add system info to hardware stats sensor 2026-06-24 16:23:50 -04:00
yellowcooln b4b44831b0 feat: integrate openHop Glass inform policy sync 2026-06-24 15:17:41 -04:00
Lloyd c761c425d1 Merge branch 'pr-294' into dev 2026-06-23 08:56:33 +01:00
agessaman cd763e2cac feat: implement threaded stats broadcasting in StorageCollector
- Introduced a dedicated thread for broadcasting aggregate statistics to WebSocket clients, improving performance by offloading heavy queries from the packet write path.
- Updated the _publish_packet_sync method to ensure only fast, per-packet operations are executed, while the stats are handled separately.
- Added methods for broadcasting stats at intervals and managing the lifecycle of the stats thread.
- Enhanced unit tests to validate the new stats broadcasting behavior and ensure correct functionality under various conditions.
2026-06-18 21:38:20 -07:00
agessaman 2435757197 feat: enhance SQLiteHandler and StorageCollector for improved caching and async performance
- Added caching for packet type stats and cumulative counts in SQLiteHandler to reduce database load.
- Implemented a dedicated writer thread in StorageCollector to handle blocking storage operations, preventing asyncio event loop stalls.
- Updated record_packet method to utilize the new writer thread for efficient packet processing.
2026-06-18 21:25:19 -07:00
Yellowcooln 5d3733a960 fix: satisfy pre-commit for modem HTTP telemetry 2026-06-14 20:08:29 -04:00
Yellowcooln 642c019076 fix: preserve modem GPS satellites in view 2026-06-14 19:32:41 -04:00
Yellowcooln dde487bce3 fix: add generic PyMC modem stats sensor 2026-06-14 18:00:15 -04:00
Lloyd 00682e8086 Merge pull request #282 from agessaman/companion/advanced-settings 2026-06-06 18:09:00 +01:00
agessaman dac60443f0 feat(companion): implement contact trimming and retention policies
- Introduced `enforce_companion_contact_capacity` to manage contact limits during companion loading, with an option to trim non-favourite contacts when exceeding capacity.
- Updated `SQLiteHandler` to support message retention limits, allowing for automatic trimming of older messages based on `offline_queue_size`.
- Enhanced API endpoints to handle contact trimming on overflow, providing feedback on trimmed contacts during updates.
- Added utility functions for selecting and trimming contacts while preserving favourites.
- Improved logging for contact management actions and errors related to capacity.
2026-06-05 21:39:11 -07:00
Rightup 14b4804c26 feat: Enhance logging system and introduce policy management endpoints
- Updated LogBuffer to support log entry IDs, enhanced log entry structure with additional metadata, and implemented subscriber management for real-time log streaming.
- Added OpenAPI specifications for new endpoints related to policy management, including retrieval, updating, validation, and group management for network policies.
- Implemented comprehensive tests for new policy endpoints, ensuring correct behavior for creating, updating, validating, and deleting policy groups and entries.
- Introduced policy evaluation tests to validate the functionality of the PolicyEngine, including various scenarios for action decisions based on defined rules.
- Enhanced packet routing tests to ensure proper handling of policy decisions in packet processing.
2026-06-04 15:53:17 +01:00
agessaman 499f871262 Merge upstream/dev into companion/advanced-settings
Integrate latest dev while preserving per-companion bridge settings
and contact capacity validation. Resolve import conflicts in main.py
and api_endpoints.py.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 15:34:52 -07:00
Adam Gessaman 7d57b34a04 feat(companion): enhance contact capacity management and bridge settings
- Introduced `CompanionContactCapacityError` to handle cases where persisted contacts exceed configured limits.
- Added utility functions for parsing companion bridge settings and validating contact capacity.
- Updated `RepeaterDaemon` to check contact capacity during companion loading and initialization.
- Enhanced API endpoints to validate companion settings and manage contact limits effectively.
- Implemented logging for bridge limits and errors related to contact capacity.
2026-06-02 07:42:40 -07:00
Rightup 60ca184dbd refactor: enhance security comments and error handling across multiple modules 2026-05-27 22:07:34 +01:00
Lloyd 45a44eb47b Refactor test cases and base code for consistency and readability
- Updated byte representations in tests to use lowercase hex format for consistency.
- Reformatted code for better readability, including line breaks and indentation adjustments.
- Consolidated multiple lines into single lines where appropriate to enhance clarity.
- Ensured that all test cases maintain consistent formatting and style across the test suite.
2026-05-27 20:15:10 +01:00
Lloyd 62f35c4b45 fix: update transport key generation to use 16-byte length and add corresponding test 2026-05-27 14:27:59 +01:00
Zindello a1c66100f5 fix: remove datetime.UTC from mqtt_handler and add 3.10 compat test
Replace the try/except UTC shim in mqtt_handler.py with timezone.utc
directly, consistent with the api_endpoints.py fix. Add a static AST
scan that fails if datetime.UTC is reintroduced anywhere in the codebase,
guarding against future regressions on Python 3.10 (LuckFox Pico Ultra).

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 11:57:45 +10:00
Lloyd 31edaa9c76 fix: update installation scripts to use the correct branch name 2026-05-26 15:42:59 +01:00
Lloyd 37ee0e892a Add more unit tests for handler helpers, identity manager, CLI, key generation, and main functionality
- Introduced tests for TraceHelper and DiscoveryHelper to validate packet forwarding and discovery request handling.
- Implemented tests for LoginHelper to ensure identity registration and login packet processing.
- Added tests for IdentityManager to cover identity registration, lookup, and filtering.
- Created tests for MeshCLI to verify command handling, configuration setting, and error paths.
2026-05-26 13:01:38 +01:00