28 Commits
Author SHA1 Message Date
Adam Gessaman 4b858e2274 fix(web_viewer): replace the Carto dark basemap with OpenFreeMap
Carto now requires an API key for basemaps.cartocdn.com and is retiring
raster tiles, so the mesh map's dark theme rendered an "API KEY REQUIRED"
watermark. OSM hosts no dark tiles of its own -- its Standard layer is
light only -- so switch the dark basemap to OpenFreeMap's vector 'dark'
style, rendered through MapLibre GL via maplibre-gl-leaflet. Leaflet and
the light OSM raster layer are unchanged, and the bridge renders into
tilePane so marker and overlay z-order is untouched.

Fall back to inverted OSM raster tiles when WebGL 2 or the bridge is
unavailable, so a failure degrades to a filtered map rather than a blank
one. The filter targets .leaflet-tile rather than .leaflet-tile-container:
the container is a 0x0 element that its absolutely-positioned tiles
overflow, so a filter there has an empty reference box and paints nothing.

CSP: drop cartocdn, allow tiles.openfreemap.org, and permit blob: workers.
MapLibre spawns its renderer in a worker from a blob: URL; without
worker-src it falls back to default-src 'self' and never starts.
2026-08-28 22:57:40 -07:00
agessaman 1fb0dbb72b chore(web_viewer): drop debug console logging from templates
Removes 26 console.log calls, including contacts.html dumping whole data
structures and sample contact records on every render. Variables and
callback parameters that existed only to feed those logs go with them
(deviceTypes, anyNewDevices, an availableEdges debug block, and a
realtime status handler whose body was nothing but a log), so no new
no-unused-vars warnings are introduced — the count drops 60 to 59.

console.warn and console.error are kept. One console.log survives in
realtime.html: the decoder key-count line is a one-time startup message
with real diagnostic value, and removing it would mean deleting two
counters and their increments inside a loop.

Also normalizes the mesh page's user-visible "Neighbours" strings to
match the project's American spelling. The option value stays
"neighbors", so evidence-mode filtering is untouched.
2026-08-07 13:44:31 -07:00
agessaman ebc66992dd feat(neighbors): zero-hop neighbour discovery in packet capture
Port the observer firmware's neighbours feature into the bot's packet capture
service, by way of meshcore-packet-capture (upstream PRs #42/#43). On a long
interval the bot asks which repeaters it hears directly and records each
confirmed link with its measured SNR.

This is the strongest link evidence the bot collects: a first-party RF
measurement between two full 32-byte public keys. Path inference works from
1-3 byte prefixes with no keys, and complete_contact_tracking.hop_count
over-claims zero-hop (800 claimed vs 68 corroborated on the live database).

modules/neighbors_discovery.py keeps upstream's public names so its fixes and
tests stay portable. Two deliberate divergences:

- No command_lock plumbing. _SerializedCommands in modules/core.py already
  serialises and paces every radio command, strictly more than upstream's
  reentrant lock did.
- neighbors_collect_scopes defaults off. Upstream's zero-hop scope probe
  relies on a neighbour not being a known contact; this bot tracks contacts,
  and for a repeater with no stored path the library reaches zero-hop by
  calling change_contact_path() then reset_path() -- mutating the device's
  contact table per neighbour. Scope requests also hold the radio lock for
  their whole round trip (~25s), stalling bot replies. The default cycle is
  one command plus a passive listen window, during which the bot stays
  responsive.

Evidence lands in neighbor_links and neighbor_observations (migration 22)
rather than mesh_connections, which cannot persist provenance. The viewer
exposes it as evidence=neighbors on /api/mesh/edges and a Neighbours Only
mode on the mesh page, with populated public keys and real SNR; confirmed
neighbours also relabel edges in the combined view and count as
provenance-trusted when framing the initial map.

neighbors_enabled is the single switch. Every enabled broker publishes once
it is on (mqttN_neighbors defaults true; set false to hold one back). The
topic derives from each broker's packets topic with the last segment swapped,
so a templated broker gets meshcore/{IATA}/{PUBLIC_KEY}/neighbors -- the
topic the firmware uses -- instead of an unrelated flat one. A derived
location-routed topic is skipped with a warning when no iata is set, rather
than publishing into meshcore/XYZ/... on a shared namespace. Snapshots are
non-retained: heard_secs_ago is relative to publish time, so a retained copy
would read as current days later.

Also adds a DM-gated `neighbors` command (the 12h interval floor makes
waiting for the scheduler impractical), which acks immediately and reports in
a second message once the window closes.

Requires meshcore >= 2.3.8 for send_node_discover_req / req_regions_sync.
2026-08-04 19:24:18 -07:00
agessaman 40307bd8cb feat(mesh): enhance mesh graph performance and caching
- Implemented caching for multi-byte mesh aggregation, allowing concurrent requests to share computation and reducing CPU load.
- Updated the web viewer to coalesce live edge events and refresh the mesh graph every 30 seconds, improving responsiveness on busy networks.
- Adjusted logging behavior in the web viewer to respect the configured log level, ensuring efficient logging without excessive duplicate entries.
- Added configuration options for mesh graph caching duration, enhancing user control over performance settings.
2026-07-30 13:16:16 -07:00
agessaman 07d6737008 feat(graph): optimize mesh graph persistence and data retention
- Updated mesh graph path splitting and aggregation to run in SQLite, improving performance by avoiding Python materialization.
- Defaulted graph persistence to batched writes for new installations, reducing WAL churn and SD-card writes.
- Enhanced data retention to execute shortly after startup, independent of the nightly maintenance schedule.
- Added a table-specific index for `mesh_connections` to support window and retention queries, ensuring efficient data access.
2026-07-30 07:23:06 -07:00
agessaman 9ae9ebb327 Harden P0 security and release boundaries 2026-07-16 10:21:50 -07:00
agessaman 2324f23832 perf(web-viewer): speed up mesh graph load
Three independent wins, measured against a 1.4 GB production database
(714k observed_paths, 38.7k mesh_connections):

- Migration 0014: partial covering index on observed_paths for
  bytes_per_hop >= 2. The multibyte evidence query was a full table
  scan (2.2 s); with the index it reads only the index (77 ms). The
  optional days filter is covered too via last_seen in slot 2.

- Compress responses with flask-compress when the client supports it.
  /api/mesh/edges alone is 16.3 MB of JSON uncompressed, 3.9 MB
  gzipped. Falls back gracefully (with a warning) if the package is
  not installed.

- Fetch stats, edges, and nodes concurrently on the mesh page instead
  of three serialized awaits. Node prefixes are now computed client-side
  from public_key at the edge prefix length, which removes the
  edges-before-nodes ordering dependency. Rendering still waits for
  stats so initial framing can use the bot location.

Also call map.invalidateSize() before the initial fitBounds: if the
map was created while its container had no layout (e.g. a background
tab), Leaflet's cached zero width made fitBounds zoom to max.
2026-07-12 15:00:46 -07:00
agessaman 91b1607bf5 feat(web-viewer): frame initial mesh map on the home multi-byte component
Nodes occasionally carry wrong GPS, which stretched the initial
fit-to-all-nodes view across continents. /api/mesh/stats now exposes
the bot's configured position ([Bot] bot_latitude/bot_longitude), and
the mesh page BFSes the multi-byte-evidence subgraph from repeaters
within 30 km of the bot (nearest one as fallback), framing the initial
map view to that connected component. Falls back to the old fit when
the bot position is unset or the component is trivially small; other
mesh islands stay on the map, just outside the initial frame.
2026-07-12 11:57:28 -07:00
agessaman b2f700e15f feat(web-viewer): heat-colored repeater dots and density-aware sizing
- Hot connections mode now also colors repeater dots with viridis by
  their total observation count across touching edges (log-normalized,
  same dark-mode ramp compression as edges); node popup gains a Path
  Observations row and the heat legend a clarifying note
- Density pass: smaller dot base/cap (5..13 vs 6..16), edge widths
  capped at 5px, and both dots and lines scale down to 55% as the map
  zooms out (full size from zoom 12 in), rescaled live on zoomend
- Node degree and observation totals are now precomputed once per
  filter pass (computeNodeStats) instead of per-node scans over all
  edges, using the same endpoint resolution as the renderers
2026-07-12 11:57:28 -07:00
agessaman 1f7a1db1b0 feat(web-viewer): mesh graph dark mode, viridis heat coloring, compact header
- Multi-byte evidence is now the default mode for the mesh graph
- Mesh display theme: follows the site theme until toggled on the page,
  then remembered independently (localStorage); dark CARTO basemap,
  dark Leaflet popups/controls/legends, dark graph canvas
- Hot connections: optional viridis edge coloring by log-scaled
  observation count with a gradient legend; the near-black cold end of
  the ramp is compressed away in dark mode so cold edges stay visible
- Single-row header (title + stat chips + icon controls) replaces the
  title and stats-card rows; footer hidden on this page to maximize the
  visualization viewport
- Mobile: page scrolls naturally instead of starving the fixed-height
  flex container (which stranded the absolutely-positioned legends)
2026-07-12 11:57:28 -07:00
agessaman fb9d5f4115 fix(web-viewer): log-scale Min Observations slider with sane default
The old slider defaulted to floor(avg observations) — on heavy-tailed data
the average sits above the 75th percentile, so the first visit hid most of
the graph. Worse, the default (e.g. 98) exceeded the slider's max of 50:
the browser clamped the control while the label kept showing the unclamped
value, so the label lied about the active filter.

- slider position now maps logarithmically onto 1..max(observation_count),
  rescaled whenever edge data loads, giving fine control at the low end
  and full reach into the tail
- first-visit default is the median observation count of the loaded edges
- the persisted value is the threshold itself, so it stays meaningful when
  the scale changes (e.g. switching evidence modes)
- label shows the threshold plus live 'shown/total edges' feedback
2026-07-12 11:57:28 -07:00
agessaman f2dc37f28f feat(web-viewer): multi-byte evidence mode for the mesh graph
Single-byte repeater identity guessing has been persistently unreliable,
and mesh_connections flattens away evidence provenance (confirmed_2byte
never persists). observed_paths retains it: bytes_per_hop marks which
paths carry unambiguous multi-byte hops.

- /api/mesh/edges?evidence=multibyte derives edges purely from unique
  multi-byte path observations (consecutive hop pairs, aggregated with a
  distinct-path count); 2-byte edges coalesce into a 3-byte edge only on
  a unique prefix match, mirroring MeshGraph.add_edge
- default mode tags each edge evidence=multibyte|singlebyte by key length
- Evidence filter on the mesh page (persisted with the other filters);
  single-byte edges render dashed in map and graph views, with an
  evidence line in tooltips and a legend entry
- client-side haversine fallback for edges without a stored distance
- prefix-compatible edge/node matching so degree sizing and node details
  work when edge and node prefixes differ in resolution
- /api/mesh/stats reports multibyte_edges; shown on the Edges stat card
2026-07-12 11:57:28 -07:00
Adam Gessaman c043156fbf fix(web_viewer): sanitize contact and node names in HTML templates
- Updated `contacts.html` to escape HTML for contact usernames and advertisement data, preventing potential XSS vulnerabilities.
- Modified `mesh.html` to escape node names and prefixes in tooltips, ensuring safe rendering in the vis-network.
- Removed unnecessary parameters from the `showDeleteConfirmation` method to derive the username directly from the contact data.
2026-06-03 14:23:22 -07:00
agessaman bcb3f48f02 fix(web_viewer): restore mesh export and clean scheduler imports
Prevent exportView runtime failure by restoring the download anchor setup and move radio param imports to module scope so Ruff passes on the PR changes.

Made-with: Cursor
2026-04-25 21:01:14 -07:00
Robowarrior834 e5e84f9171 Fixed map full screen exit on ios 2026-04-21 09:45:16 -04:00
Robowarrior834 217a30f8d7 Added Fullscreen Map and Radio settings. 2026-04-20 16:24:40 -04:00
Stacy Olivas 973d1fc947 fix: BUG-028 byte_data init; BUG-LOG-1/4 SocketIO and cleanup_database handler fixes 2026-04-14 10:02:46 -07:00
agessaman f829c9e30b Update JSON placeholders in feeds.html and mesh.html for improved readability
- Changed single quotes to HTML entities in JSON placeholders within the feeds.html file to ensure proper rendering.
- Updated the assignment of PREFIX_HEX_CHARS in mesh.html to parse the value as an integer, enhancing type safety and clarity.

These changes enhance the user interface and code maintainability in the web viewer templates.
2026-04-03 11:36:33 -07:00
agessaman a2a121b4e7 Enhance API request headers across multiple templates
- Added 'X-Requested-With' header to various API requests in channel_operations.js, cache.html, config.html, contacts.html, feeds.html, greeter.html, mesh.html, radio.html, and other templates to improve request handling and prevent potential issues with cross-origin requests.
- Ensured consistent header usage across all relevant fetch calls to enhance security and compatibility.

These changes improve the robustness of API interactions within the web viewer.
2026-03-29 15:45:42 -07:00
agessaman 6c8151389b Enhance MeshGraph edge promotion logic and update BotDataViewer API
- Updated the MeshGraph class to prevent promoting a 1-byte edge to a 3-byte edge when the existing 1-byte edge lacks public keys, ensuring accurate observation attribution.
- Added a new test case to verify the behavior of edge promotion under specific conditions.
- Modified the BotDataViewer API to return the prefix length dynamically based on the edges, improving data consistency and user experience in the web viewer.
- Enhanced the mesh.html template to support displaying prefix byte counts, providing clearer information on node connections.
2026-03-09 09:43:49 -07:00
agessaman f57f480c9f Enhance MeshGraph and BotDataViewer for multi-byte support and improved edge handling
- Updated MeshGraph to support multi-resolution storage and node identity, allowing for better handling of 1-byte, 2-byte, and 3-byte edges.
- Implemented logic for merging and promoting edges based on observation counts and specificity.
- Enhanced BotDataViewer API to dynamically adjust prefix length based on configuration, improving data representation.
- Updated mesh.html template to display prefix byte counts, enhancing user experience with clearer information on node connections.
2026-03-06 16:57:57 -08:00
Ian Rifkin 36a8a67543 Add transitional support for 2-byte prefixes while keeping legacy 1-byte compatibility
- Update prefix command to accept BOTH legacy 2-char prefixes and
  configured prefix_hex_chars (e.g. 4-char) during firmware transition
- Replace strict length validation with dual-length validation (2 or N)
- Ensure prefix lookups work with either input length via LIKE matching
- Update related SQL prefix extraction to use configured prefix length
- Add fallback handling in path parsing for legacy 2-char route data

Notes:
- This is an interim compatibility change to support mixed networks
  where RF path data is still 1-byte while bot config may be 2-byte.
- Needs additional testing across real multi-hop scenarios and mixed
  bot configurations.
- Translation updates are incomplete: only English strings were updated;
  other translation files still need review.
- Behavior and UX may need refinement after real-world testing.
2026-02-27 23:48:27 -05:00
agessaman c3a196e319 Update mesh and realtime templates for clarity
- Shortened the title in the mesh graph visualization from "Mesh Graph Visualization" to "Mesh Graph" for conciseness.
- Removed the subtitle from the real-time monitoring header to streamline the display.
2026-02-16 16:20:41 -08:00
agessaman cd028a2160 Refactor NOAA period name handling in WxCommand and enhance bot configuration retrieval
- Replaced the `abbreviate_noaa` method calls with a new `_noaa_period_display_name` method to improve the display of forecast period names, including handling federal holidays.
- Updated the `BotDataViewer` class to include the bot name in the returned configuration dictionary, ensuring consistent naming across the application.
- Made minor adjustments to HTML templates for better layout and added footer links for project visibility.
2026-02-16 16:05:45 -08:00
agessaman a3dfef4901 feat: Update time filter options and improve UI for starred nodes
- Replaced the "Last 7 Days" option with "Last 48 Hours" and "Last 72 Hours" in both edge and node filters for better granularity.
- Enhanced the layout of the "Show Only Starred" checkbox for improved user experience.
- Introduced filtered node maps for efficient edge drawing, ensuring edges are only drawn between nodes within the selected timeframe.
2026-02-08 09:06:29 -08:00
agessaman 3c5057d92d feat: Enhance graph view synchronization and node rendering in web viewer
- Added functionality to synchronize the view with the URL hash, allowing direct access to the graph tab.
- Implemented event listeners for hash changes to switch between graph and map views seamlessly.
- Updated the loadData function to conditionally apply filters based on rendering options, improving performance during live updates.
- Enhanced node styling and interaction settings for better visual feedback and user experience in the graph view.
2026-01-30 20:38:36 -08:00
agessaman ab1fe6d841 feat: Enhance node highlighting and styling in web viewer
- Introduced functionality to store and restore original styles for nodes, allowing for dynamic highlighting and shrinking of non-highlighted nodes.
- Implemented smooth zooming and fade animations for markers in the map view, improving user experience during interactions.
- Refactored node styling logic to ensure connected nodes maintain their size while non-connected nodes are significantly shrunk for better visibility.
- Enhanced the clearing of highlights to restore original styles effectively, ensuring a consistent visual state after interactions.
2026-01-27 22:08:37 -08:00
agessaman bd2849655c feat: Enhance mesh graph functionality and path validation
- Added a new mesh graph feature for improved path validation, allowing for enhanced routing accuracy.
- Introduced configuration options for recency decay half-life and graph-based validation settings in config.ini.example.
- Updated the PathCommand class to utilize graph-based selection methods, combining graph and geographic scores for better repeater selection.
- Implemented new methods in MessageHandler to update the mesh graph with advertisement paths and trace packet data.
- Created a new database table for mesh connections to support graph-based path validation.
- Enhanced web viewer integration to display mesh graph updates in real-time, improving user interaction and monitoring capabilities.
2026-01-23 20:26:56 -08:00