From 7bcd0ef46b2e3facc833baf9ba46927ced886632 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sun, 19 Jul 2026 22:00:53 +0200 Subject: [PATCH] docs: cover the Path Analyzer feature - user-guide.md: new Path Analyzer section (opening, filters, the Messages/Repeaters/Map views, phone layout notes, local-view caveat); TOC entry; per-item placement action count 12 -> 13 with Path Analyzer added to the enumeration - architecture.md: /api/path-analyzer/messages added to the Messages endpoint table plus a Path Analyzer design section (batched echo fetch, client-side filtering rationale, SNR attribution, hash -> contact resolution, legacy-row degradation) - whatsnew.md: three user-facing bullets under Unreleased (the tool, the combined filters, phone support) No deploy notes needed: no new dependencies, no schema changes. Co-Authored-By: Claude Fable 5 --- docs/architecture.md | 11 ++++++++ docs/user-guide.md | 64 ++++++++++++++++++++++++++++++++++++++++++-- docs/whatsnew.md | 3 +++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index b3f4b55..ae5322b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -105,6 +105,16 @@ The `/repeaters` (list) and `/repeaters/manage` (per-repeater tools) panels are - **Role gating** — the firmware silently drops text CLI from non-admin logins (which would surface as a timeout), so the CLI/Settings/Actions endpoints reject guest sessions with 403 up front instead - **Actions whitelist** — `advert.zerohop`, `advert` (flood), `clock sync`, `reboot`. `reboot` never replies (the firmware restarts immediately without building one), so a clean send followed by silence is reported as success. Text `erase` is firmware-gated to the USB serial console (`sender_timestamp == 0`), hence no erase in the UI +### Path Analyzer + +The `/path-analyzer` panel (standalone iframe page, `path-analyzer.js`) is a read-only analysis view over data the app already collects — no new tables, no background work: + +- **Data source** — `GET /api/path-analyzer/messages?days=N` joins `channel_messages` with `echoes` (path hex + SNR + per-echo `hash_size`, keyed by `pkt_payload`). Echoes are fetched with `db.get_echoes_for_payloads()` — chunked `IN` queries (≤500 params, under SQLite's host-parameter limit) via `idx_echoes_pkt` — deliberately avoiding the per-message echo query the older `/api/messages` path still does. The pkt_payload reconstruction (raw_json text → channel-secret AES/HMAC compute) is shared with `/api/messages` via the `_get_row_pkt_payload()` helper +- **All filtering/stats/map logic is client-side** over the bulk payload (hundreds of KB for 7 days — fine on a LAN): filters operate on per-hop tokens split with each echo's own `hash_size` (mixed 1/2/3-byte networks are real), so SQL-side token filtering was rejected. Stats and map always reflect the active filters for free +- **SNR attribution** — echo SNR is measured at our receiver, so stats credit it to the *final* hop only; intermediate hops get relay counts but never SNR +- **Hash→contact resolution** — pubkey-prefix match against `/api/contacts/cached?format=full` (memoized per token). 1-byte hashes collide by design; the map renders unresolved hops as amber candidate markers with manual pick, and the repeater-name filter is intentionally inclusive over candidates +- Legacy rows whose `pkt_payload` cannot be recomputed (missing channel secret) are returned with `packet_hash: null` and no echoes rather than dropped + --- ## Project Structure @@ -197,6 +207,7 @@ The channels API reads from the `channels` DB table rather than iterating device | GET | `/api/messages//meta` | Get message metadata (echoes, paths) | | POST | `/api/messages//resend` | Re-broadcast an own channel message verbatim via `CMD_SEND_RAW_PACKET` (same packet hash, so unreached repeaters pick it up). 400 for not-own / missing `raw_packet` snapshot / disconnected / firmware < 1.16, 404 for unknown id | | GET | `/api/messages/search` | Full-text search (`?q=`, `?channel_idx=`, `?limit=`) | +| GET | `/api/path-analyzer/messages` | Bulk channel messages across **all** channels with batched echo data (`?days=1..30`, default 3); powers the Path Analyzer panel (`GET /path-analyzer`) | ### Contacts diff --git a/docs/user-guide.md b/docs/user-guide.md index d7ca1ec..235f3dc 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -17,6 +17,7 @@ This guide covers all features and functionality of mc-webui. For installation i - [DM Path Management](#dm-path-management) - [Interactive Console](#interactive-console) - [My Repeaters (Repeater Administration)](#my-repeaters-repeater-administration) +- [Path Analyzer](#path-analyzer) - [Device Dashboard](#device-dashboard) - [Quick-Access FAB Buttons](#quick-access-fab-buttons) - [Settings](#settings) @@ -613,6 +614,65 @@ Erasing the repeater's file system is **not** available over the mesh — the fi --- +## Path Analyzer + +A full-screen tool for analyzing how channel messages travel through the mesh: which repeaters relayed them, how strong the signal was, and what the routes look like on a map. + +To open: +1. Tap the hamburger menu (☰) +2. Select **Path Analyzer** from the menu + +Pick a time range at the top (last 1, 3, 5, or 7 days) — the tool loads every channel message from **all** your channels in that window, together with every copy (echo) of each message your node overheard. Everything below works on that data set; no extra loading. + +### Filters + +The filter bar applies to all three views at once and updates as you type: + +- **Hops** - Only messages that arrived over exactly that many hops (0 = heard directly, 4+ = long routes). A message matches when *any* of its echoes has that hop count +- **HB (path hash size)** - Only messages whose route was recorded with 1-, 2-, or 3-byte repeater hashes +- **Repeater** - Type a repeater hash (e.g. `3B`) or part of a repeater's **name** (e.g. `wegrzce`); matches messages whose route passed through it. Note that with short 1-byte hashes two repeaters can share a hash, so a name search includes routes where the named repeater *might* be one of the candidates +- **Sender** - Part of the sender's name +- **Message text** - Part of the message content + +A counter shows how much of the data set matches ("38 of 412 messages"), and **Clear** resets everything. + +### Messages view + +A table of messages: time, channel, sender, text, packet hash (click to copy — the same hash analyzer services use), hop count, hash size, and echo count. Click a row to expand its routes: + +- Every echo is shown as a chain of repeater hashes (`5A → F0 → 90`) with its SNR and receive time +- Click a single hash chip to copy that hash; click the rest of the line to copy the whole route +- The small map icon at the end of each route jumps straight to the **Map** view with that route drawn +- Echoes with an empty route show as "Direct (flood, 0 hops)" + +On phones the Hash, HB, and Echoes columns fold away to keep the table readable — the packet hash and hash size appear at the top of the expanded row instead, and long routes wrap across multiple lines. + +### Repeaters view + +Per-repeater statistics computed from the currently filtered messages: + +- **Relayed** - How many echoes passed through this repeater hash (anywhere in the route) +- **Messages** - How many distinct messages that was +- **As last hop** - How often this repeater was the final hop, i.e. the one your node heard directly +- **Avg SNR (last hop)** - Average signal quality, counted *only* when the repeater was the final hop. Your node can only measure the radio link it actually receives on, so SNR is never attributed to repeaters in the middle of a route — that's why some rows show "—" + +Columns are sortable, and the **Contact** column matches each hash to your contact list (showing "ambiguous (n)" when several contacts share a short hash). Click any row to jump back to the Messages view filtered to that repeater. + +### Map view + +Repeaters from your contact list that have a position are plotted as dots. Pick a message in the side list (it shows sender, time, and the message text), then pick one of its routes — the path is drawn hop by hop: + +- Hops that resolve to exactly one known repeater become solid points connected by a line +- When a short hash matches several contacts, all candidates are marked in amber and the legend lists them — tap the right one and the path redraws with your choice +- Unknown hops (no matching contact, or no position) are listed in the legend and the line is drawn dashed across the gap, so you can see which parts of the route are certain +- If the sender is in your contacts with a position, it is added as a green origin point + +The eraser button clears the drawn path. On phones the message list moves above the map. + +Everything in the Path Analyzer is based on what **your node** overheard — it's a local view of the mesh, not a global one. A route you don't see here may still exist; it just never reached your radio. + +--- + ## Device Dashboard Access device information and statistics: @@ -665,7 +725,7 @@ Tap the toggle button (short click, no drag) to hide or show the rest of the FAB Open Settings → **Appearance** tab to adjust: - **Hide Quick Access** - Master switch: hides the FAB cluster entirely and moves all actions to the Main Menu -- **Per-item placement** - Choose whether each of the 12 actions appears in the FAB or in the Main Menu. Changes take effect immediately +- **Per-item placement** - Choose whether each of the 13 actions appears in the FAB or in the Main Menu. Changes take effect immediately - **Button size** - 28 to 72 pixels (default: 56) - **Spacing** - 2 to 24 pixels between buttons (default: 12) - **Reset position** - Reset both main chat and DM FAB positions to their defaults @@ -751,7 +811,7 @@ Controls small notification toasts shown after actions (e.g. "Advert Sent", erro **Quick Access Buttons:** - **Hide Quick Access** - Master switch that hides the entire floating FAB cluster and moves all items to the Main Menu (slide-out) -- **Per-item placement** - A table of all 12 configurable actions (Filter messages, Search messages, Direct Messages, Contact Management, Settings, Send Advert, Flood Advert, Map, Console, My Repeaters, Device Info, System Log). Each row has two radio buttons: **Quick Access** (shows in FAB) and **Main Menu** (shows in the slide-out). Changes take effect immediately +- **Per-item placement** - A table of all 13 configurable actions (Filter messages, Search messages, Direct Messages, Contact Management, Settings, Send Advert, Flood Advert, Map, Console, My Repeaters, Path Analyzer, Device Info, System Log). Each row has two radio buttons: **Quick Access** (shows in FAB) and **Main Menu** (shows in the slide-out). Changes take effect immediately - **Button size (px)** - Adjust the size of FAB buttons (default: 56) - **Spacing (px)** - Space between FAB buttons (default: 12) - **Position** - Reset FAB position to default (top-right) diff --git a/docs/whatsnew.md b/docs/whatsnew.md index cc807aa..e16dfb5 100644 --- a/docs/whatsnew.md +++ b/docs/whatsnew.md @@ -15,6 +15,9 @@ For deep technical notes, see [architecture.md](architecture.md). For the full g - **Remote CLI, Settings, and Actions (admin logins).** **CLI** is a real terminal to the repeater — quick-command chips, per-repeater history, round-trip times. **Settings** edits the repeater configuration in collapsible sections (Basic, Radio, Location, Features, Network health, Advertisement, Operator info, Advanced): values load live from the repeater, only the fields you change are sent, and every field reports back individually — including a "reboot required" badge for radio parameters and the firmware's own error text for rejected values. **Actions** covers zero-hop advert, flood advert (marked "not recommended" — high network load), clock sync, and a confirmation-guarded reboot in a Danger zone. Erasing the file system stays USB-serial-only by firmware design, and the panel says so. - **Observer mode — feed packet analyzers straight from mc-webui.** The new **Settings → Observer** tab turns your node into a MeshCore observer: every packet the device overhears is published to one or more MQTT brokers in the standard `meshcore-packet-capture` format, so analyzer services (a self-hosted Corescope, letsmesh-style maps) see your local mesh traffic without a separate capture script or a dedicated second node. Configure brokers with host/port, optional username/password and TLS; each row shows a live connected/error badge, and the tab counts packets captured vs published in real time. Capture is completely passive — chat and direct messages are unaffected — and all changes apply immediately, no restart needed. (LetsMesh token-authenticated brokers are not supported yet.) - **Scheduled flood adverts.** The Observer tab includes an optional advert interval in hours: the app sends a flood advert on that schedule so your observer stays visible on analyzer maps. The timer survives restarts, so a redeploy won't send an extra advert early. Set it to 0 to keep adverts fully manual. +- **Path Analyzer — see how your messages travel the mesh.** A new full-screen tool (main menu → **Path Analyzer**) loads every channel message from all your channels for a chosen window (1–7 days) together with every copy your node overheard, and lets you dig in three ways. **Messages**: expand any message to see all its routes hop by hop with SNR — copy a repeater hash, copy the whole route, or jump straight to the map. **Repeaters**: per-repeater statistics (how many packets each hash relayed, over how many messages, and average SNR when it was the hop you actually heard), sortable and clickable to filter. **Map**: pick a message, pick a route, and it's drawn on the map point by point — when a short 1-byte hash matches several repeaters, the candidates are shown in amber and you pick the right one instead of the app guessing, and uncertain segments are drawn dashed so you always know which part of a route is confirmed. +- **Path Analyzer filters.** Everything is filterable live and in combination: hop count, path-hash size (1/2/3-byte), repeater — by hash **or by name** — sender, and message text. All three views (including the statistics and the map's message list) follow the active filters. +- **Path Analyzer works on phones.** On narrow screens the tables shed their secondary columns (packet hash and hash size move into the expanded row), long routes wrap instead of scrolling sideways, and the map's message list stacks above the map. ### Reliability & polish