meshai/work/dashboard-frontend
malice 1a8ae710dd
MeshCore: channel provisioning, key sharing, location-based recommendations & transport-aware identity (#113)
* feat(meshcore): self-service add/remove channel provisioning from dashboard

Adds MeshCoreTransport.add_channel()/remove_channel() (meshcore_transport.py),
scanning the full 40-slot companion channel table for a free slot (no early
empty-run cutoff, unlike _enumerate_channels) and writing/clearing slots via
the only available write opcode (set_channel, 0x20) — the lib exposes no
delete-channel opcode, so removal writes the slot back to its empty state
(blank name + all-zero 16-byte secret).

POST /api/meshcore/channels and DELETE /api/meshcore/channels/{name} routes
(mesh_send_routes.py) validate name/hex-key and return the refreshed channel
list, matching the existing secrets_routes.py HTTPException idiom.

Frontend: Add-channel row (name + optional PSK hex) and a per-channel Remove
affordance inside the existing Observe MeshCore Channels block
(MeshCoreConnection.tsx), plus addMeshcoreChannel()/removeMeshcoreChannel()
API client functions (api.ts).

Built + deployed to CT 108 (docker compose build && up -d, container
healthy); self-cleaning smoke test passed — POST/DELETE of a #meshai-test
channel left the companion table exactly as found.

* MeshCore: display per-channel key (PSK) with reveal + copy

The Observe MeshCore Channels list now shows each channel's PSK hex so
operators can share it with people who want to join. Fetches the
/api/meshcore/channels/detail endpoint (name + key) instead of the
names-only list; keys are masked by default with a per-row eye reveal
toggle and a copy-to-clipboard button. Channels with no retrievable key
show a dash. Existing observe (checkbox), remove (trash), and add-row
controls are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(llm): recommend a MeshCore channel by location + flash-lite default

Add a location-aware MeshCore channel-recommendation context block to the
single-shot LLM prompt so "what channel should I join?" gets an answer with
the channel NAME and join KEY.

- router.py: new standalone helper _build_meshcore_channel_block(config,
  channel_details, position) — lists ONLY observed channels
  (meshcore_context.observe_channels), each with its PSK key
  (channel_details) and, where a region_routes cell maps to it, the covered
  region's human geography (local name, cities, centroid) built from a
  channel->regions reverse map over notifications.region_routes.cells and a
  best-effort fuzzy attach to mesh_intelligence regions. Injected inside the
  existing should_inject_mesh gate, right after the region-geography block;
  fetches channel_details() + get_node_position() off self.connector and is
  fully fail-safe (any error skips the block, never breaks the LLM path).
  Empty observe_channels => empty block (LLM cannot invent channels).
- composite_transport.py: add channel_details() passthrough mirroring
  known_channels().
- README.md: bump the example llm model to gemini-2.5-flash-lite so a fresh
  deploy is policy-compliant (flash-lite supports Google Search grounding).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(identity): transport-aware bot identity + alert-channel awareness

Fixes the LLM system prompt being transport-blind: MeshCore DMs were
being told they were on the freq51 Meshtastic mesh network and were
physical node !27780c47 (AIDA-N2), and MeshMonitor/node-health text
(Meshtastic-only) leaked into MeshCore prompts too.

- config.py: BotConfig gains mt_mesh_name/mt_node/mc_mesh_name (generic
  OSS defaults empty; name/owner defaults now generic MeshAI/Unknown).
  LLMConfig.system_prompt code default no longer hardcodes freq51/Twin
  Falls.
- router.py: generate_llm_response() computes transport once and
  branches identity: MeshCore gets an AIDA/MeshCore-radio framing with
  no MT node id; Meshtastic keeps the physical-node framing from the
  new config fields. MeshMonitor block and MT node-health/gateway/packet
  reporting (mesh_reporter Tier1/region/node detail + _MESH_AWARENESS_PROMPT
  + region geography) are now gated to transport == meshtastic. New
  _build_alert_channels_line() adds a short identity-block line on both
  transports listing region_routes-derived alert channels (MT channel
  index or MC channel name). MeshCore channel-recommendation block stays
  transport-neutral.
- README.md: dead gemini-2.5-flash-lite example updated to
  gemini-3.1-flash-lite.

Live config (meshai_data volume, not git-tracked): bot.name=AIDA,
bot.owner=K7ZVX (local.yaml identity.owner, which overrides config.yaml
per config_loader.py LOCAL_FIELDS), mt_mesh_name/mt_node set to the
freq51/AIDA-N2 values, mc_mesh_name left empty. llm.yaml system_prompt
freq51 line removed.

Verified via in-container prompt-assembly dry run through the real
generate_llm_response() path (no LLM call) for both transports, and a
live gemini-3.1-flash-lite 3-query behavior test confirming the
MeshCore channel-recommendation feature still works correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:30:57 -06:00
..
node_modules feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87) 2026-07-07 16:52:03 -06:00
public
src MeshCore: channel provisioning, key sharing, location-based recommendations & transport-aware identity (#113) 2026-07-10 23:30:57 -06:00
index.html
package-lock.json
package.json
postcss.config.js
tailwind.config.ts
tsconfig.json
tsconfig.node.json
vite.config.ts