meshcore-mqtt-live-map/AGENTS.md
2026-04-17 10:57:10 -04:00

135 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Repository Guidelines
Current version: `1.8.4` (see `VERSIONS.md`).
## Project Structure & Module Organization
- `backend/app.py` wires FastAPI routes, MQTT lifecycle, and websocket broadcast flow.
- `backend/config.py` centralizes env configuration.
- `backend/state.py` holds shared in-memory state + dataclasses.
- `backend/decoder.py` contains payload parsing, multibyte MeshCore decoding, and route helpers.
- `backend/los.py` contains LOS math + elevation fetch helpers.
- `backend/history.py` handles 24h route history persistence/cleanup.
- `backend/static/index.html` is the HTML shell + template placeholders.
- `backend/static/styles.css` holds all UI styles.
- `backend/static/app.js` holds all client-side map logic.
- `backend/static/sw.js` is the PWA service worker.
- `backend/requirements.txt` and `backend/Dockerfile` define Python and Node dependencies.
- `docker-compose.yaml` runs the service as `meshmap-live`.
- `data/` stores persisted state (`state.json`), route history (`route_history.jsonl`), role overrides (`device_roles.json`), and optional neighbor overrides (`neighbor_overrides.json`).
- `.env` holds dev runtime settings; `.env.example` mirrors template defaults.
- `VERSION.txt` tracks the current version (now `1.8.4`); append changes in `VERSIONS.md`.
## Build, Test, and Development Commands
- `docker compose up -d --build` rebuilds and restarts the backend (preferred workflow).
- `docker compose logs -f meshmap-live` follows server logs for MQTT activity.
- `curl -s http://localhost:8080/snapshot` checks current device state.
- `curl -s http://localhost:8080/stats` shows ingest/route counters.
- `curl -s http://localhost:8080/debug/last` inspects recent decoded packets.
## Coding Style & Naming Conventions
- Python in `backend/*.py` uses **2-space indentation**; keep it consistent.
- The project enforces an **80-character column limit** for Python code to maintain readability.
- Formatting is handled by `yapf` using the config in `backend/.style.yapf`.
- To format code manually: `yapf --in-place --recursive --style backend/.style.yapf backend/`
- HTML/CSS/JS in `backend/static/index.html` uses 2 spaces as well.
- Use lowercase, underscore-separated names for Python variables/functions.
- Prefer small helper functions for parsing/normalization; keep logging concise.
## Testing Guidelines
- Run automated tests with `pip install -r requirements-dev.txt && pytest -q`.
- Validate runtime behavior manually with `/snapshot`, `/stats`, and `/debug/last`.
## Commit & Pull Request Guidelines
- No git history is available in this workspace, so there is no established commit convention.
- If you add git later, use short, imperative commit messages and describe behavioral changes in PRs.
## Configuration & Operations
- Most behavior is controlled by `.env` (MQTT host, TLS, topics, TTLs, map start lat/lon/zoom, MQTT online window, default map layer).
- Current dev defaults: `DEVICE_TTL_HOURS=96`, `PATH_TTL_SECONDS=172800`, `MQTT_ONLINE_SECONDS=600`, `ROUTE_TTL_SECONDS=60`, `TRAIL_LEN=0`, `DISTANCE_UNITS=mi`.
- Node size default is `NODE_MARKER_RADIUS` (pixels); users can override via the HUD slider.
- History link size default is `HISTORY_LINK_SCALE`; users can override via the History panel slider.
- Map radius filter: `MAP_RADIUS_KM=0` disables filtering; `.env.example` uses `241.4` km (150mi). Applies to nodes, trails, routes, and history edges.
- `MAP_RADIUS_SHOW=true` draws a debug circle centered on `MAP_START_LAT/LON`.
- Set `TRAIL_LEN=0` to disable trails entirely; the HUD trail hint is removed when trails are off.
- Coverage button only appears when `COVERAGE_API_URL` is set.
- `QR_CODE_BUTTON_ENABLED=true` adds a `Generate QR Code` button to node popups that opens a theme-aware MeshCore-compatible contact QR modal; default is off.
- Geographic filtering defaults to radius mode; polygon mode is optional via `MAP_BOUNDARY_MODE=polygon` and `MAP_BOUNDARY_FILE`.
- Standalone boundary builder: `tools/map-boundary-builder.html` outputs the JSON consumed by `MAP_BOUNDARY_FILE`; hosted copy: [https://yellowcooln.com/map-boundary-builder/](https://yellowcooln.com/map-boundary-builder/).
- Radar country-bounds controls:
`WEATHER_RADAR_COUNTRY_BOUNDS_ENABLED` and
`WEATHER_RADAR_COUNTRY_LOOKUP_URL`.
`WEATHER_RADAR_COUNTRY_LOOKUP_URL` defaults to
`/weather/radar/country-bounds` and is an HTTP route path, not a
filesystem directory.
- Weather wind controls:
`WEATHER_WIND_ENABLED`, `WEATHER_WIND_API_URL`,
`WEATHER_WIND_GRID_SIZE`, `WEATHER_WIND_REFRESH_SECONDS`.
- LOS curvature controls:
`LOS_CURVATURE_ENABLED` and `LOS_CURVATURE_FACTOR`.
When unset, LOS curvature defaults to `true` with factor `1.333333`.
- `DEVICE_COORDS_FILE` points to optional coordinate overrides (default `/data/device_coords.json`).
- `NEIGHBOR_OVERRIDES_FILE` can point at a JSON map/list of neighbor pairs to resolve hash collisions.
- Auto-neighbor overrides are controlled by:
`AUTO_NEIGHBOR_OVERRIDES_ENABLED`, `AUTO_NEIGHBOR_OVERRIDES_FILE`,
`AUTO_NEIGHBOR_ACTIVE_DAYS`, `AUTO_NEIGHBOR_MIN_EDGE_COUNT`, and
`AUTO_NEIGHBOR_REFRESH_SECONDS`.
- Optional custom HUD link appears when `CUSTOM_LINK_URL` is set.
- Update banner uses `GIT_CHECK_ENABLED` (compare local vs upstream) with `GIT_CHECK_PATH` pointing at a git repo.
- `GIT_CHECK_FETCH` controls whether the server fetches before comparing; `GIT_CHECK_INTERVAL_SECONDS` sets the recheck interval.
- Route history modes default to `path` via `ROUTE_HISTORY_ALLOWED_MODES`.
- `ROUTE_PATH_MAX_LEN` caps oversized path-hash lists (prevents bogus long routes).
- `ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK=true` restores pre-`v1.7.0` closest/time-based fallback for colliding 1-byte route prefixes; default is conservative `false`.
- Persisted state in `data/state.json` is loaded on startup; edit with care.
- After editing `backend/*.py` or `backend/static/*`, rebuild with `docker compose up -d --build`.
- History tool visibility is not persisted; it always loads off unless `history=on` is in the URL.
## Feature Notes
- MQTT supports WSS/TLS or TCP; the official `@michaelhart/meshcore-decoder` runs via a Node helper for advert/location parsing, 1/2/3-byte path decoding, and conservative MQTT role extraction.
- Routes are rendered as trace/message/advert lines with TTL cleanup; 0,0 coords (including stringy zeros) are filtered from trails/routes.
- Route IDs are observer-aware (`message_hash:receiver_id`) so multi-observer receptions do not overwrite each other.
- Dev route debug: in non-prod mode (`PROD_MODE=false`), clicking a route line logs hop-by-hop details to the browser console (distance, hashes, origin/receiver, timestamps).
- Turnstile protection only activates when `PROD_MODE=true` and requires
`TURNSTILE_ENABLED`, `TURNSTILE_SITE_KEY`, and `TURNSTILE_SECRET_KEY`.
- Turnstile browser auth (`meshmap_auth`/`?auth=`) is used for map + WebSocket sessions; protected API routes still require `PROD_TOKEN`.
- Discord/social embeds can be allowlisted under Turnstile via
`TURNSTILE_BOT_BYPASS` and `TURNSTILE_BOT_ALLOWLIST`.
- Route hash collisions prefer known neighbors (and optional overrides); long path lists are skipped via `ROUTE_PATH_MAX_LEN`.
- Route collisions fall back to closest-hop selection and drop hops beyond `ROUTE_MAX_HOP_DISTANCE`.
- `ROUTE_INFRA_ONLY` restricts route lines to repeaters/rooms (companions still show as markers).
- Heatmap shows recent traffic points (TTL controlled).
- LOS uses `/los/elevations` for client-side realtime updates (with `/los` fallback).
- LOS uses Earth-curvature-aware obstruction checks by default in both the frontend realtime path and the backend `/los` fallback.
- LOS UI includes peak markers, a relay suggestion marker, elevation profile hover, and map-line hover sync.
- LOS legend items (clear/blocked/peaks/relay) are hidden until the LOS tool is active.
- Mobile LOS supports long-press on nodes (Shift+click on desktop); endpoints can be dragged or click-selected and moved via map click.
- MQTT online status is derived from `/status` + `/internal` TTL windows; `/packets` is tracked as feed activity.
- Devices that remain MQTT-online keep their last known coordinates on the map until MQTT presence expires, even if fresh location packets stop.
- `MQTT_ONLINE_FORCE_NAMES` (comma-separated device names) forces selected nodes to always appear MQTT online.
- Service worker fetches navigations with `no-store` to avoid stale UI/env toggles (e.g., radius debug ring).
- Node search + labels toggle (persisted in localStorage) and a GitHub link in the HUD.
- Hide-nodes toggle hides markers, trails, heat, routes, and history layers.
- Heat toggle hides the heatmap; it defaults on and the button turns green when heat is off.
- History line weight was reduced for a lighter map overlay.
- HUD logo uses `SITE_ICON`; if missing/invalid it falls back to a small "Map" badge to keep the toggle usable.
- Route styling now keys off payload type: 2/5 = Message (blue), 8/9 = Trace (orange), 4 = Advert (green).
- 24h route history persists to `data/route_history.jsonl`, renders as a volume heatline, and defaults off (History tool panel).
- History tool opens a right-side panel with a 5-step heat filter slider: All, Blue, Yellow, Yellow+Red, Red; legend swatch hides unless active.
- History panel can be dismissed with the X button while leaving history lines visible (History tool brings it back).
- History records route modes from `ROUTE_HISTORY_ALLOWED_MODES` (default: `path`).
- Propagation render stays visible until a new render; origin changes only mark it dirty.
- Propagation now has an adjustable TX antenna gain (dBi) control, and Rx AGL defaults to 1m.
- Preview image endpoint renders in-bounds device dots for shared links.
- Peers tool opens a right-side panel showing incoming/outgoing neighbors (counts + %) based on rolling peer-history buckets; selecting a node draws peer lines on the map.
- Peer-history buckets are also updated from route `point_ids` when a hop cannot be drawn as a visible segment, so peer counts still reflect real adjacency for non-drawable hops.
- Peers tool ignores nodes listed in `MQTT_ONLINE_FORCE_NAMES` (used for observer listeners).
- Units toggle (km/mi) is stored in localStorage and defaults to `DISTANCE_UNITS`.
- PWA support is enabled via `/manifest.webmanifest` + `/sw.js` so mobile browsers can install the app.
- Clicking the logo toggles the left HUD panel while LOS/Propagation panels remain open.
- Node popups do not auto-pan; dragging the map wont snap back to keep a popup in view.
- Node popups let users click the short key under the node name to copy the full public key, and can optionally show a MeshCore-compatible contact QR modal for that node with the node name plus a clickable truncated key that still copies the full public key.
- MQTT disconnect handler tolerates extra Paho args so the loop doesnt crash; reconnects resume ingest.
- Share button copies a URL with `lat`, `lon`, `zoom`, `layer`, `history`, `heat`, `coverage`, `weather`, `labels`, `nodes`, `legend`, `menu`, `units`, and `history_filter` params.
- Weather state is not persisted in localStorage; it defaults off unless `weather=on` is in the URL.
- URL params override localStorage on load (`history=on` is the only way to load History open).
- Node size slider persists in localStorage (`meshmapNodeRadius`) and can be reset by clearing site data.
- MeshMapper coverage viewport sync reuses cached rectangles instead of rebuilding all visible squares on every pan/zoom, which keeps the Coverage layer responsive on busy maps.