mirror of
https://github.com/Colorado-Mesh/meshcore-bot-firmware.git
synced 2026-08-15 02:00:28 +00:00
forge: step 1 — bootstrap wrapper workflow
This commit is contained in:
261
.forge/research/SYNTHESIS.md
Normal file
261
.forge/research/SYNTHESIS.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# Research Synthesis
|
||||
|
||||
## Status
|
||||
- Files synthesized: stack.md, pitfalls.md, architecture.md, prior-art.md, codex-analysis.md, PROJECT.md
|
||||
- Files missing: none
|
||||
- Overall confidence: HIGH
|
||||
|
||||
## Executive Summary
|
||||
This project is now a firmware-only MeshCore companion bot, not a host-side bot permission layer and not a VPS coordinator replacement by emulation. The proven path is to keep upstream `meshcore-dev/MeshCore` as the firmware/build substrate, use Arduino C++/PlatformIO exactly as upstream does, and add a compact Colorado Mesh bot runtime at the companion application layer. `agessaman/meshcore-bot`, the Colorado VPS/community coordinator, and stale Codex coordinator-first notes are useful only as behavioral/prior-art references; they must not drive the runtime architecture.
|
||||
|
||||
The recommended implementation is a small embedded command responder plus passive response coordinator inside `examples/companion_radio/MyMesh`: receive DMs and allowed channel messages, classify commands, enforce channel policy, compute semantic fingerprints, schedule bounded delayed responses, cancel when a trusted known bot has already answered, and send via existing MeshCore `sendMessage()` / `sendGroupMessage()` APIs. Normal bot traffic should be limited to private DMs, `#bot`, and `#testing`; `#emergency` should use a dedicated Public forwarding path with the exact required emergency announcement semantics and strict dedup/rate limiting. Do not repurpose MeshCore `txdelay`, `direct.txdelay`, or `rxdelay` for bot election; use bot-specific role/delay/suppression settings.
|
||||
|
||||
The top risks are RAK4631 flash/RAM limits, heap fragmentation, packet-pool exhaustion, unsafe text parsing, wrong channel routing, spoofed suppression, emergency amplification, hidden nodes, and companion protocol breakage. Mitigate by designing to RAK4631 first, keeping v1 commands deterministic and single-packet, using fixed static state with no hot-path heap allocation, keeping runtime suppression state in RAM, persisting only a small versioned `BotPrefs`, and measuring PlatformIO size deltas in CI. The user's plugged-in Heltec v3 is the correct first hardware smoke target because it is available and has generous flash, but RAK4631 BLE remains the release gate.
|
||||
|
||||
## Key Decisions (resolved by research)
|
||||
|
||||
1. **Build in MeshCore's firmware stack.** Use upstream MeshCore Arduino C++/PlatformIO and current upstream dependency pins; do not introduce ESP-IDF-only, Zephyr, Rust, embedded Python/JS, MQTT firmware logic, SQLite, or a custom radio stack. Source refs: ITEM-stack-1, ITEM-stack-4, ITEM-prior-art-1.
|
||||
2. **Use a submodule plus patch/overlay workflow.** Keep `meshcore-dev/MeshCore` pinned as an upstream submodule and maintain Colorado bot code as a small patch/overlay series applied during builds. Source refs: ITEM-stack-2, ITEM-architecture-14.
|
||||
3. **Implement a firmware-resident bot, not host-side coordination.** Phase 1 should generate selected bot responses directly in firmware; `meshcore-bot` is a behavior oracle only. Source refs: ITEM-stack-6, ITEM-prior-art-3, ITEM-pitfalls-2, ITEM-architecture-8.
|
||||
4. **Place bot logic at the companion application layer.** Integrate with `MyMesh` callbacks and loop; leave `Dispatcher`, `Mesh::routeRecvPacket()`, ACKs, path returns, retransmit timing, and packet duplicate tables untouched. Source refs: ITEM-architecture-1, ITEM-stack-5.
|
||||
5. **Use strict channel policy.** Normal bot handling is DMs, `#bot`, and `#testing`; Public is ignored except for the dedicated `#emergency` forwarding path. Source refs: ITEM-architecture-3, ITEM-stack-7, ITEM-prior-art-12, ITEM-pitfalls-7.
|
||||
6. **Forward emergencies explicitly and safely.** `#emergency` should produce Public text beginning `EMERGENCY MESSAGE FROM <user>` and preserve/truncate original text with bounded one- or two-packet formatting, deduplication, loop prevention, and rate limiting. Source refs: ITEM-architecture-4, ITEM-prior-art-12, ITEM-pitfalls-8.
|
||||
7. **Coordinate with passive listen-before-answer first.** Use semantic fingerprints, bounded delayed sends, and cancellation on trusted known-bot responses. Do not add claim frames in v1. Source refs: ITEM-architecture-5, ITEM-architecture-6, ITEM-pitfalls-9, ITEM-pitfalls-10.
|
||||
8. **Trust known bot identities only.** Suppression must be keyed to known bot public keys where possible; plain group text names are spoofable hints and must not suppress emergency forwarding. Source refs: ITEM-architecture-7, ITEM-pitfalls-11.
|
||||
9. **Design storage and RAM for RAK4631.** Heltec v3 is comfortable, but the nRF52840 RAK4631 app/RAM limits define the portable v1 budget. Source refs: ITEM-stack-9, ITEM-stack-10, ITEM-stack-11, ITEM-stack-12, ITEM-architecture-10, ITEM-pitfalls-1.
|
||||
10. **Instrument and size-gate from the start.** CI should build Heltec v3 USB/BLE and RAK4631 USB/BLE, capture PlatformIO size output, and enforce bot delta thresholds after baseline. Source refs: ITEM-stack-8, ITEM-stack-13, ITEM-architecture-13, ITEM-pitfalls-1.
|
||||
11. **Start hardware smoke tests on the plugged-in Heltec v3.** Use the local Heltec v3 for first flash/build smoke only after the implementation plan is approved; expand to RAK4631 before release. Source refs: PROJECT.md, ITEM-stack-8, ITEM-stack-9.
|
||||
|
||||
## Questions for User
|
||||
|
||||
### Q-1: Which exact firmware MVP commands should ship first?
|
||||
|
||||
- **Category:** scope
|
||||
- **Why it matters:** Every command adds flash, response strings, parser cases, tests, and possible airtime. The v1 command set determines whether the feature remains firmware-feasible on RAK4631.
|
||||
- **Default recommendation:** Ship `ping`, `test`, `help/cmd`, `about`, `status` with battery/storage, simple `dice/roll`, DM replies, `#bot`/`#testing` replies, and `#emergency` forwarding. Defer weather, AQI, sports, jokes via web APIs, satellite/solar forecasts, Discord/web viewer, SQLite stats, and repeater-management workflows.
|
||||
- **Source refs:** ITEM-stack-6, ITEM-prior-art-3, ITEM-architecture-8, ITEM-pitfalls-2
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-2: Should the bot be enabled by default after flashing, or require explicit enable/configuration?
|
||||
|
||||
- **Category:** ux
|
||||
- **Why it matters:** A firmware bot that responds immediately after flashing can surprise users or create channel noise if channels/known bots/tier are not configured correctly.
|
||||
- **Default recommendation:** Default to enabled for DMs and local smoke-test commands only, but require explicit channel policy resolution and role configuration before responding on `#bot`/`#testing`; always require explicit `#emergency` source/destination channel validation before Public forwarding.
|
||||
- **Source refs:** ITEM-architecture-3, ITEM-architecture-9, ITEM-pitfalls-7, ITEM-pitfalls-16
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-3: What should the default response role/tier be for the user's plugged-in Heltec v3 bot node?
|
||||
|
||||
- **Category:** ux
|
||||
- **Why it matters:** The first hardware smoke target is also intended to become the user's bot node. Its default role affects response latency, suppression behavior, and whether it defers to fixed infrastructure.
|
||||
- **Default recommendation:** Configure the plugged-in Heltec v3 as `LOCAL` or `SUBURBAN` for smoke tests, with a conservative 300-1500 ms response window; change role later based on deployment location.
|
||||
- **Source refs:** PROJECT.md, ITEM-stack-9, ITEM-architecture-12, ITEM-pitfalls-14
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-4: What duplicate-response and latency targets are acceptable for v1?
|
||||
|
||||
- **Category:** constraints
|
||||
- **Why it matters:** Passive suppression can reduce noise but cannot guarantee exactly-once behavior in hidden-node topologies. The target determines delay windows and whether claim frames become necessary later.
|
||||
- **Default recommendation:** Accept rare duplicates in v1. Target p95 added latency under 3 seconds for normal channel commands and duplicate bot replies under 5-10% in field scenarios, with metrics before adding claim frames.
|
||||
- **Source refs:** ITEM-architecture-6, ITEM-pitfalls-9, codex-analysis
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-5: How should known bot identities be provisioned and maintained?
|
||||
|
||||
- **Category:** technical
|
||||
- **Why it matters:** Suppression should trust known bot identities only, but firmware needs a bounded, field-serviceable way to store and update public keys.
|
||||
- **Default recommendation:** Store 16 known bot full public keys plus optional labels/capability flags in versioned `BotPrefs`; expose rescue CLI commands and later companion protocol commands to add/remove/list keys.
|
||||
- **Source refs:** ITEM-architecture-7, ITEM-architecture-9, ITEM-architecture-10, ITEM-pitfalls-11
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-6: Should unauthenticated group-text responses ever suppress a local pending response?
|
||||
|
||||
- **Category:** risk
|
||||
- **Why it matters:** MeshCore group text names are spoofable. Suppressing based on text alone can silence the real bot or block emergency forwarding.
|
||||
- **Default recommendation:** For v1, allow weak group-text suppression only for low-risk normal `#bot`/`#testing` replies that match known bot labels and fingerprints; never use weak hints for `#emergency` forwarding or safety-critical behavior.
|
||||
- **Source refs:** ITEM-architecture-7, ITEM-pitfalls-11, ITEM-pitfalls-8
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-7: What exact `#emergency` behavior should occur when the original text is too long?
|
||||
|
||||
- **Category:** ux
|
||||
- **Why it matters:** MeshCore text/frame limits mean the required emergency header plus original text may not fit in one message. Truncation policy affects safety and airtime.
|
||||
- **Default recommendation:** Use a two-stage formatter when needed: first Public packet `EMERGENCY MESSAGE FROM <user>`, second Public packet containing the original text truncated to fit with an explicit truncation marker. Do not send unlimited multipart emergency messages in v1.
|
||||
- **Source refs:** ITEM-architecture-4, ITEM-pitfalls-5, ITEM-pitfalls-8, ITEM-prior-art-2
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-8: What channel names and matching rules should be canonical?
|
||||
|
||||
- **Category:** technical
|
||||
- **Why it matters:** Channel indexes are local and can change; substring matching could make the bot respond on unintended channels such as `#botnet`.
|
||||
- **Default recommendation:** Use exact normalized channel names `Public`, `#bot`, `#testing`, and `#emergency`; resolve to channel details at boot and after channel changes; refuse channel operation if names are missing, duplicated, or ambiguous.
|
||||
- **Source refs:** ITEM-architecture-3, ITEM-pitfalls-7
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-9: What persistent configuration surface is acceptable for v1?
|
||||
|
||||
- **Category:** technical
|
||||
- **Why it matters:** Appending to `NodePrefs` can break existing binary layout, but compile-time-only config is hard to field-tune.
|
||||
- **Default recommendation:** Add a separate versioned `/bot_prefs_v1` file with magic/version/length/CRC for enable flag, tier, delay/jitter, cooldowns, allowed channels, emergency mapping, known bots, command bitset, and small stats reset marker. Do not insert fields into existing `NodePrefs` offsets.
|
||||
- **Source refs:** ITEM-architecture-9, ITEM-pitfalls-15, ITEM-pitfalls-16, ITEM-prior-art-10
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-10: What are the hard size budgets for accepting v1?
|
||||
|
||||
- **Category:** constraints
|
||||
- **Why it matters:** Research estimates are feasible but unmeasured locally because PlatformIO was unavailable. The plan needs explicit thresholds to prevent feature creep.
|
||||
- **Default recommendation:** Treat RAK4631 BLE as the gate: aim for <=25 KB incremental app flash and <=2 KB static RAM initially; hard-review anything above 40-60 KB flash or 4 KB RAM. Persistent bot config should stay under 4 KB. Capture exact deltas in CI.
|
||||
- **Source refs:** ITEM-stack-10, ITEM-stack-11, ITEM-stack-12, ITEM-stack-13, ITEM-architecture-10, ITEM-architecture-11, ITEM-pitfalls-1
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-11: Should private key import/export be disabled in production bot builds?
|
||||
|
||||
- **Category:** risk
|
||||
- **Why it matters:** Known-bot trust makes bot identities valuable. If private keys remain exportable/importable, a compromised host or BLE session can clone a trusted bot identity.
|
||||
- **Default recommendation:** Disable private key export/import for production bot firmware. Use a separate provisioning build or a physical-button-gated temporary window if identity migration is required.
|
||||
- **Source refs:** ITEM-pitfalls-18
|
||||
- **Priority:** HIGH
|
||||
|
||||
### Q-12: What host/app protocol extensions are needed in v1, if any?
|
||||
|
||||
- **Category:** technical
|
||||
- **Why it matters:** New companion protocol frames can break apps if they collide with existing commands or appear unexpectedly. But field config and stats need some access path.
|
||||
- **Default recommendation:** Keep v1 usable with rescue CLI plus minimal queried stats/config frames only. Gate all new protocol commands by firmware capability/version and never change existing frame semantics.
|
||||
- **Source refs:** ITEM-architecture-9, ITEM-architecture-13, ITEM-pitfalls-13
|
||||
- **Priority:** MEDIUM
|
||||
|
||||
### Q-13: Which hardware targets define “done” for the first release candidate?
|
||||
|
||||
- **Category:** constraints
|
||||
- **Why it matters:** The project wants all companion device types eventually, but first release quality needs a focused representative set.
|
||||
- **Default recommendation:** Inner-loop builds: Heltec v3 USB, Heltec v3 BLE, RAK4631 USB, RAK4631 BLE. First hardware smoke: the plugged-in Heltec v3. Release gate: RAK4631 BLE builds and passes size/stress tests. Expand all companion builds after representative success.
|
||||
- **Source refs:** PROJECT.md, ITEM-stack-8, ITEM-stack-9, ITEM-stack-10, ITEM-pitfalls-17
|
||||
- **Priority:** MEDIUM
|
||||
|
||||
### Q-14: How much telemetry should be exposed to users versus debug tooling?
|
||||
|
||||
- **Category:** ux
|
||||
- **Why it matters:** Operators need visibility into suppression and hidden nodes, but normal users should not see noisy internal state over LoRa.
|
||||
- **Default recommendation:** Expose compact local stats through CLI/protocol (`observed`, `sent`, `suppressed`, `duplicates`, `emergency forwards`, queue failures, storage used/total). Do not post coordinator diagnostics into normal channels except explicit debug commands.
|
||||
- **Source refs:** ITEM-architecture-13, ITEM-pitfalls-12, ITEM-pitfalls-4
|
||||
- **Priority:** MEDIUM
|
||||
|
||||
### Q-15: Should future explicit claim frames be planned in the protocol now, even if disabled in v1?
|
||||
|
||||
- **Category:** technical
|
||||
- **Why it matters:** Planning the fingerprint/state model now can avoid repainting the architecture later, but implementing claims immediately adds airtime and spoofing risks.
|
||||
- **Default recommendation:** Reserve state-machine hooks and stats for `known_claim_seen`, but do not transmit claim frames in v1. If later added, claims must be compact, versioned, length-checked, fingerprint-bound, scoped, and authenticated by known bot identity.
|
||||
- **Source refs:** ITEM-architecture-6, ITEM-architecture-7, ITEM-pitfalls-9, ITEM-pitfalls-11
|
||||
- **Priority:** MEDIUM
|
||||
|
||||
## Technical Direction
|
||||
|
||||
### Stack
|
||||
- **Firmware/runtime:** C++ in upstream MeshCore companion firmware, Arduino framework, PlatformIO, upstream dependency pins preserved. Source refs: ITEM-stack-1, ITEM-stack-4.
|
||||
- **Repository shape:** wrapper repo with pinned `upstream/MeshCore` submodule, Colorado overlay files, and deterministic patch queue applied into a build worktree. Source refs: ITEM-stack-2, ITEM-architecture-14.
|
||||
- **Target baseline:** current MeshCore main pinned intentionally, not stale local-only assumptions. Source refs: ITEM-stack-3.
|
||||
- **Build matrix:** use upstream `build.sh`; inner loop builds `Heltec_v3_companion_radio_usb`, `Heltec_v3_companion_radio_ble`, `RAK_4631_companion_radio_usb`, and `RAK_4631_companion_radio_ble`; later run all companion builds. Source refs: ITEM-stack-8.
|
||||
- **Firmware coding style:** fixed-size arrays/ring buffers, compile-time flags, no heap allocation in receive/parse/schedule/send paths, bounded `char[]` parsing, terse single-packet responses. Source refs: ITEM-stack-5, ITEM-pitfalls-3, ITEM-pitfalls-5, ITEM-pitfalls-6.
|
||||
- **Host-side tooling:** Python only for tests, fixtures, worktree/patch tooling, simulations, and size-report parsing. No Python runtime dependency in deployed firmware. Source refs: ITEM-stack-14.
|
||||
|
||||
### Architecture
|
||||
The bot should be a `FirmwareBot`/`BotRuntime` owned by companion `MyMesh`. `MyMesh` adapts MeshCore callbacks into compact `BotMessage` inputs, invokes policy/classification/execution/coordination, and sends approved `BotResponse` outputs through existing MeshCore send APIs. Core modules should be:
|
||||
|
||||
1. `BotPolicy`: enabled state, exact channel policy, known bots, command bitset, emergency rules, rate limits.
|
||||
2. `CommandClassifier`: bounded normalization, prefix/verb matching, semantic fingerprint inputs.
|
||||
3. `CommandExecutor`: fixed C++ handlers for v1 commands with short response buffers.
|
||||
4. `ResponseCoordinator`: passive listen-before-answer FSM and fixed pending/recent tables.
|
||||
5. `EmergencyForwarder`: dedicated `#emergency` to Public path with dedup, loop prevention, and bounded formatting.
|
||||
6. `BotPrefs` / `KnownBotRegistry` / `BotStats`: small persisted config, explicit trusted keys, and metrics.
|
||||
|
||||
Recommended v1 data flow:
|
||||
|
||||
1. MeshCore receives/decrypts a DM or group message and invokes `MyMesh` callbacks.
|
||||
2. `BotPolicy` rejects all normal Public traffic and only allows DMs, `#bot`, and `#testing`; `#emergency` is diverted to `EmergencyForwarder`.
|
||||
3. `CommandClassifier` parses `(const char*, length)`, normalizes only safe ASCII command verbs, and derives a 64-bit semantic fingerprint from stable application fields.
|
||||
4. `CommandExecutor` creates a short candidate response without allocating a MeshCore packet.
|
||||
5. `ResponseCoordinator` schedules the response in a fixed table using role/delay/jitter/score. It does not allocate a packet until the timer wins.
|
||||
6. If a trusted known bot response for the same fingerprint is heard first, the pending response is suppressed and stats are updated.
|
||||
7. When due, `MyMesh` sends a single concise DM/group message via existing APIs.
|
||||
8. Runtime fingerprints, suppression windows, emergency recent cache, and counters remain bounded and mostly volatile.
|
||||
|
||||
### Storage / Flash / RAM Estimates
|
||||
|
||||
| Target | Hardware/app region facts | Current signal from research | Recommended v1 bot budget | Implication |
|
||||
|---|---|---:|---:|---|
|
||||
| Heltec WiFi LoRa 32 V3 | ESP32-S3, 8 MB flash, 512 KB SRAM, no PSRAM; PlatformIO default 8 MB partition gives ~3,342,336 bytes per OTA app slot and ~1,572,864 bytes SPIFFS | v1.15 release app signals around 615 KB USB / ~1.2 MB BLE non-merged; Heltec has wide flash margin | 10-25 KB flash target, hard review above 40-60 KB; 0.5-4 KB RAM; 0-4 KB persistent bot prefs | Comfortable first smoke target. Do not optimize for Heltec alone because it hides RAK constraints. Source refs: ITEM-stack-9, ITEM-stack-11, ITEM-stack-12, ITEM-pitfalls-1 |
|
||||
| RAK4631 | nRF52840, 1 MB flash, 256 KB RAM; MeshCore companion app capped at 712,704 bytes and RAM region ~237,568 bytes after SoftDevice/linker reservations | v1.15 UF2 assets ~933-949 KB container / ~467-475 KB zip proxy; exact ELF headroom requires local PlatformIO build | Aim <=25 KB additional flash and <=2 KB static RAM initially; design under 4 KB runtime RAM and under 4 KB filesystem storage; hard review above 40-60 KB flash or 4-8 KB RAM | Release gate and limiting target. No large help text, APIs, databases, logs, dynamic plugins, or persistent histories. Source refs: ITEM-stack-10, ITEM-stack-11, ITEM-stack-12, ITEM-architecture-10, ITEM-architecture-11, ITEM-pitfalls-1 |
|
||||
|
||||
Concrete compact state target: 8-12 pending coordinator entries, 16-24 recent fingerprint entries, 16 known bot keys, counters under 256 B, and one shared 160-byte response scratch buffer. Persistent storage should contain only versioned config and known bot keys; suppression history and emergency caches should be RAM-only.
|
||||
|
||||
### Prior Art to Leverage
|
||||
- **Upstream MeshCore companion firmware:** patch base, transport, storage, send APIs, callbacks, and multi-target build system. Source refs: ITEM-prior-art-1.
|
||||
- **MeshCore companion protocol:** short-message and storage-report constraints; useful for future stats/config access, not a runtime bot API. Source refs: ITEM-prior-art-2.
|
||||
- **agessaman meshcore-bot:** command names, channel policy concepts, cooldown/rate-limit behavior, and response wording. Do not port HTTP/API/SQLite/plugins. Source refs: ITEM-prior-art-3.
|
||||
- **Colorado Mesh community/VPS bot:** observable coordination semantics: DMs bypass, channel responses coordinated, local scoring, fallback delays. Preserve concepts without preserving host infrastructure. Source refs: ITEM-prior-art-4.
|
||||
- **Meshtastic firmware modules:** proof that small fixed embedded modules are the right pattern; use bounded config, static messages, rate limits, and compile-time flags. Source refs: ITEM-prior-art-5.
|
||||
- **disaster.radio console:** practical model for bounded firmware command parsing. Source refs: ITEM-prior-art-6.
|
||||
- **Cyclenerd meshcore-bot:** conservative scope lesson: DM/private/opt-in channels are safer than Public. Source refs: ITEM-prior-art-8.
|
||||
- **LoRa APRS and MESH-API/MESH-AI:** conceptual lessons for duplicate buffers, origin markers, and loop prevention, but do not reuse GPL/heavy host code in firmware. Source refs: ITEM-prior-art-7, ITEM-prior-art-9.
|
||||
|
||||
## Detailed Planning Implications
|
||||
|
||||
1. **Repository initialization:** create wrapper repo structure, add/pin MeshCore submodule, define overlay/patch application script, and document exact upstream SHA. Verify upstream builds before bot patches. Source refs: ITEM-stack-2, ITEM-stack-3, ITEM-architecture-14.
|
||||
2. **Build/tooling gate:** add `scripts/build-representative.sh` to apply patches, build Heltec v3 USB/BLE and RAK4631 USB/BLE, parse PlatformIO size output, and archive artifacts. Source refs: ITEM-stack-8, ITEM-stack-13, ITEM-pitfalls-1.
|
||||
3. **First smoke target:** after plan approval, use the plugged-in Heltec v3 for first firmware smoke because it is locally available and has generous flash. Keep flashing steps explicit and reversible. Source refs: PROJECT.md, ITEM-stack-9.
|
||||
4. **RAK4631 release gate:** do not declare firmware feasibility complete until RAK4631 BLE builds with measured app/RAM headroom and passes stress tests. Source refs: ITEM-stack-10, ITEM-pitfalls-1.
|
||||
5. **Patch boundaries:** keep patches small: bot source files, `MyMesh` hook/adapters, `BotPrefs`/DataStore integration, optional CLI/protocol constants, and CI/build flags. Source refs: ITEM-architecture-14.
|
||||
6. **Parser first:** implement bounded `(ptr,len)` parser/normalizer with unit/fuzz cases for max-length messages, missing colon, repeated colon, UTF-8, NUL, controls, empty commands, and exact channel names. Source refs: ITEM-pitfalls-6.
|
||||
7. **Policy before execution:** implement exact channel resolution and disabled/conservative defaults before adding command handlers so Public noise is structurally impossible. Source refs: ITEM-architecture-3, ITEM-pitfalls-7.
|
||||
8. **Emergency path as its own step:** implement `EmergencyForwarder` separately from normal command execution, with dedup TTL, loop prevention, formatting tests, and rate-limit counters. Source refs: ITEM-architecture-4, ITEM-pitfalls-8.
|
||||
9. **Command MVP:** add short deterministic command handlers and golden behavior fixtures from `meshcore-bot`; ensure every output fits MeshCore text constraints with node-name prefix. Source refs: ITEM-stack-6, ITEM-pitfalls-5, ITEM-prior-art-3.
|
||||
10. **Coordinator FSM:** implement fixed-size passive suppression after basic command handling works; defer any claim frames. Verify all state transitions, TTL expiry, queue-full, and cancel-on-known-response paths. Source refs: ITEM-architecture-6, ITEM-pitfalls-4, ITEM-pitfalls-9.
|
||||
11. **Known bot registry:** implement full-key storage, labels, bounded capacity, and trust decisions before allowing suppression across nodes. Source refs: ITEM-architecture-7, ITEM-pitfalls-11.
|
||||
12. **Persistence isolation:** add versioned `BotPrefs`; do not mutate existing `NodePrefs` binary layout. Default disabled/safe on missing, corrupt, or incompatible prefs. Source refs: ITEM-architecture-9, ITEM-pitfalls-16.
|
||||
13. **Instrumentation:** add `BotStats` from day one, including observed, eligible, scheduled, sent, suppressed, weak-suppressed, emergency forwarded, duplicate emergency, queue/pool failures, and storage totals. Source refs: ITEM-architecture-13.
|
||||
14. **No hot-path heap:** add review/static checks forbidding `String`, `malloc/new`, `std::vector`, maps, JSON builders, and variable heap strings in bot receive/parse/schedule/send code. Source refs: ITEM-pitfalls-3.
|
||||
15. **Protocol compatibility:** keep new companion protocol frames optional/query-only in v1; no unsolicited new frames to old apps unless version/capability-gated. Source refs: ITEM-pitfalls-13.
|
||||
16. **Security/release flags:** decide production key export/import policy before distributing trusted bot firmware. Source refs: ITEM-pitfalls-18.
|
||||
|
||||
## Risk Register
|
||||
|
||||
| Priority | Risk | Impact | Mitigation | Source refs |
|
||||
|---|---|---|---|---|
|
||||
| CRITICAL | RAK4631 BLE exceeds flash/RAM | Build failure or unstable constrained target | Treat RAK4631 BLE as gate; CI size deltas; cap v1 at compact command set | ITEM-pitfalls-1, ITEM-stack-10, ITEM-stack-12 |
|
||||
| CRITICAL | Python bot feature creep | Firmware bloat, off-grid failure, unmaintainable partial clone | Firmware MVP only; Python as behavior oracle; defer API/web/SQLite features | ITEM-pitfalls-2, ITEM-stack-6, ITEM-prior-art-3 |
|
||||
| CRITICAL | Heap fragmentation | Long-running crashes, packet allocation failures, BLE instability | Fixed arrays/rings, no hot-path heap, bounded buffers, no `String`/JSON | ITEM-pitfalls-3 |
|
||||
| CRITICAL | Packet pool/offline queue exhaustion | Dropped user traffic and failed sends | Keep pending responses separate from packets; allocate only when timer wins; small fixed pending table | ITEM-pitfalls-4 |
|
||||
| CRITICAL | Unsafe parsing | Buffer errors, command misfires, corrupted output | `(ptr,len)` parser, explicit NUL bounds, `snprintf`, fuzz/unit tests | ITEM-pitfalls-6 |
|
||||
| CRITICAL | Wrong channel routing | Bot answers on Public or misses emergency policy | Exact channel name/hash resolution, diagnostics, refuse ambiguous config | ITEM-pitfalls-7, ITEM-architecture-3 |
|
||||
| CRITICAL | Emergency forwarding loops/amplification | Public flood during emergencies | Separate emergency path, idempotency TTL, no recursive forwarding, rate limits | ITEM-pitfalls-8, ITEM-architecture-4 |
|
||||
| CRITICAL | Spoofed suppression | Malicious/mistaken node silences real bots | Known bot public keys; weak hints only for low-risk channels; no weak emergency suppression | ITEM-pitfalls-11, ITEM-architecture-7 |
|
||||
| CRITICAL | Blocking companion loop | BLE/serial/radio/display stalls | Timestamp FSM in `loop()`, no `delay()`, no busy waits | ITEM-pitfalls-12, ITEM-architecture-6 |
|
||||
| CRITICAL | NodePrefs layout breakage | Existing settings corrupted | Separate versioned `BotPrefs`; default safe on invalid prefs | ITEM-pitfalls-16, ITEM-architecture-9 |
|
||||
| CRITICAL | Target-specific APIs break portability | Works on Heltec only; fails nRF/RP2040/STM32 | Platform-neutral bot core behind `MyMesh`/DataStore abstractions; representative build matrix | ITEM-pitfalls-17 |
|
||||
| CRITICAL | Production key export/import remains enabled | Trusted bot identity can be cloned | Disable export/import in production bot firmware or gate provisioning | ITEM-pitfalls-18 |
|
||||
| MODERATE | Text/frame limits truncate replies | Lost context and high airtime from multipart chatter | Single-packet command responses; emergency two-message max when needed | ITEM-pitfalls-5, ITEM-prior-art-2 |
|
||||
| MODERATE | Passive suppression cannot solve hidden nodes | Duplicate replies still occur | Set expectations, measure, deterministic jitter, optional authenticated claims later | ITEM-pitfalls-9 |
|
||||
| MODERATE | Bad semantic fingerprint | False suppression or missed duplicates | Fingerprint stable app fields, not raw packet hash or text alone; tests | ITEM-pitfalls-10, ITEM-architecture-5 |
|
||||
| MODERATE | RSSI/SNR overused as “closest” signal | Wrong bot wins | Use SNR/RSSI only as weak zero-hop/direct hint; tier/health/path/tiebreaker dominate | ITEM-pitfalls-14 |
|
||||
| MODERATE | Flash wear/storage fill | DataStore corruption or lost contacts/channels | Persist only config; runtime caches in RAM; lazy coarse counters only | ITEM-pitfalls-15, ITEM-prior-art-10 |
|
||||
|
||||
## Conflicts & Tradeoffs
|
||||
|
||||
1. **Heltec-first smoke vs RAK-first design.** The plugged-in Heltec v3 is the right first hardware smoke target, but its 8 MB flash can hide the real release constraint. Resolution: smoke on Heltec, budget and release-gate on RAK4631 BLE. Source refs: PROJECT.md, ITEM-stack-9, ITEM-stack-10, ITEM-pitfalls-1.
|
||||
2. **Full firmware bot vs Python feature parity.** Firmware-only is the project goal, but Python bot parity is not feasible on RAK4631-class MCUs. Resolution: firmware-native MVP with deterministic local commands; host/Python remains only a behavior reference and tooling source. Source refs: ITEM-stack-6, ITEM-pitfalls-2, ITEM-prior-art-3.
|
||||
3. **Passive suppression vs exactly-once responses.** Passive suppression saves airtime and avoids new frames but cannot guarantee exactly-once under hidden nodes. Resolution: passive first with metrics; future authenticated claims only if field data demands. Source refs: ITEM-architecture-6, ITEM-pitfalls-9, codex-analysis.
|
||||
4. **Known identity security vs group-channel realities.** Strong suppression wants full public-key identity, but plain group text carries spoofable sender names. Resolution: full-key registry for authoritative trust; weak hints only for low-risk channels; no weak emergency suppression. Source refs: ITEM-architecture-7, ITEM-pitfalls-11.
|
||||
5. **Config flexibility vs storage/migration safety.** Field operators need tuning, but `NodePrefs` binary layout is fragile and storage is finite. Resolution: separate compact versioned `BotPrefs`, CLI rescue, optional protocol config later. Source refs: ITEM-architecture-9, ITEM-pitfalls-16.
|
||||
6. **Emergency reliability vs airtime conservation.** Public emergency forwarding may duplicate under hidden nodes, but suppressing too aggressively can drop critical alerts. Resolution: short idempotent forwards, longer emergency dedup TTL, no recursive forwarding, tolerate identical duplicates over missed emergencies. Source refs: ITEM-architecture-4, ITEM-pitfalls-8.
|
||||
7. **Topology tier policy vs actual RF quality.** LOCAL/SUBURBAN/HILLTOP/MOBILE is useful operator intent but not true proximity. Resolution: role is a delay/score bias, not the sole winner; combine with directness, path freshness, queue health, and deterministic tiebreaking. Source refs: ITEM-architecture-12, ITEM-pitfalls-14.
|
||||
8. **Codex supplemental analysis vs refreshed research.** Codex recommended useful general cautions, but it was coordinator-first/stale relative to the firmware-only pivot. Resolution: use Codex only where it aligns with refreshed Claude research on passive soft coordination, hidden nodes, airtime, and embedded constraints; ignore host/coordinator-first conclusions. Source refs: codex-analysis, ITEM-stack-6, ITEM-architecture-1.
|
||||
|
||||
## Confidence Assessment
|
||||
|
||||
| Dimension | Status | Confidence | Notes |
|
||||
|-----------|--------|------------|-------|
|
||||
| stack | complete | HIGH | Clear firmware stack: upstream MeshCore Arduino C++/PlatformIO, submodule/patch workflow, representative builds, storage budget estimates. |
|
||||
| pitfalls | complete | HIGH | Strong local-code-backed risks for RAK4631 limits, heap, queues, parsing, channel routing, emergency loops, protocol compatibility, and keys. |
|
||||
| architecture | complete | HIGH | Clear companion-layer module architecture, policy/classifier/executor/coordinator split, BotPrefs, known bot registry, stats, passive FSM. |
|
||||
| prior-art | complete | HIGH | Strong prior art from upstream MeshCore, Python/community bots, Meshtastic modules, disaster.radio, and host-bot examples. |
|
||||
| codex-analysis | complete | MEDIUM | Optional supplemental research only; stale coordinator-first framing must not override refreshed firmware-only research. Useful only for general embedded cautions. |
|
||||
150
.forge/research/architecture.md
Normal file
150
.forge/research/architecture.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Architecture Research: Firmware-only MeshCore Bot
|
||||
|
||||
Project date: 2026-05-14
|
||||
Mode: architecture
|
||||
Scope: firmware-only MeshCore bot integrated into MeshCore companion firmware. This replaces stale coordinator-first/host-bot conclusions with an embedded command-handling architecture using upstream MeshCore as submodule/patch base and `meshcore-bot` as behavioral reference only.
|
||||
|
||||
### ITEM-architecture-1: Put the firmware bot at the companion application layer
|
||||
|
||||
- **Recommendation:** Implement the bot as a `FirmwareBot`/`BotRuntime` owned by `examples/companion_radio/MyMesh`, invoked from `onMessageRecv()`, `onSignedMessageRecv()`, `onChannelMessageRecv()`, `onControlDataRecv()`, outbound send completion/timeout paths, and `MyMesh::loop()`. Keep `mesh::Dispatcher`, `mesh::Mesh::routeRecvPacket()`, ACKs, packet duplicate tables, path returns, and retransmit timing untouched.
|
||||
- **Rationale:** MeshCore already separates radio scheduling (`Dispatcher`), packet parsing/dedup/routing (`Mesh`), chat abstractions (`BaseChatMesh`), and companion UX/protocol (`MyMesh`). Bot commands are application behavior, not mesh forwarding behavior. Placing the bot in `MyMesh` gives it decrypted DM/channel text, channel index/name, ContactInfo for DMs, SNR/path metadata, storage access, and send APIs without changing core behavior for repeaters, room servers, sensors, or stock companion clients.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not implement bot election inside `Dispatcher` or `Mesh::routeRecvPacket()`: that would couple command policy to every packet role. Do not keep a Python/VPS runtime in the critical path: the project pivot is firmware-only and off-grid.
|
||||
|
||||
### ITEM-architecture-2: Split the bot into policy, classification, execution, and coordination components
|
||||
|
||||
- **Recommendation:** Structure the embedded bot as four small modules: `BotPolicy` (DM/channel allowlist, banned users, known bots, emergency rules), `CommandClassifier` (normalization and command/keyword match), `CommandExecutor` (fixed C++ command handlers and response formatting), and `ResponseCoordinator` (delayed send/suppression state). `MyMesh` should be only the adapter that turns MeshCore callbacks into `BotMessage` inputs and sends approved `BotResponse` outputs.
|
||||
- **Rationale:** The standard Python bot combines config, channel filtering, command lookup, external APIs, response formatting, and sending in host code. Firmware needs the same observable decisions but cannot afford a plugin framework or external data dependencies. Separate modules keep firmware testable and keep MeshCore patch hooks minimal: `MyMesh` observes messages, `BotRuntime` decides, then `MyMesh` sends by existing `sendMessage()`/`sendGroupMessage()`.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/command_manager.py`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not port Python plugins or SQLite/web/API features into firmware. Do not put channel policy inside each command handler; emergency/Public rules must be enforced centrally before execution.
|
||||
|
||||
### ITEM-architecture-3: Use strict channel policy: DMs, #bot, #testing, and emergency-only Public forwarding
|
||||
|
||||
- **Recommendation:** Default `BotPolicy` should allow normal bot command handling only for DMs, `#bot`, and `#testing`. It should ignore normal `Public` traffic entirely. Messages on `#emergency` should not run the normal command set; instead they should trigger a dedicated emergency-forward path to Public. Store allowlisted channel names as fixed strings or channel indexes resolved at boot, and re-resolve after `CMD_SET_CHANNEL`/channel load.
|
||||
- **Rationale:** The project explicitly wants normal bot traffic off Public but wants emergency announcements routed to Public. MeshCore group messages are received by channel secret/hash and mapped to `ChannelDetails` in `BaseChatMesh`; `MyMesh` already has `findChannelIdx()` and `getChannel()` to get channel name. Central channel policy prevents a command override from accidentally enabling noisy public replies.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirements — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; Local code — `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not copy the Python default `monitor_channels = general,test,emergency`; it contradicts the new scope. Do not use substring channel matching; use exact normalized channel names or resolved channel indexes to avoid `#botnet`/`#testing2` mistakes.
|
||||
|
||||
### ITEM-architecture-4: Forward #emergency to Public with a LoRa-native two-stage formatter
|
||||
|
||||
- **Recommendation:** Implement `EmergencyForwarder` as a special `BotRuntime` path: when a valid `#emergency` channel message arrives, send to Public as either one packet if it fits (`EMERGENCY MESSAGE FROM <user>: <original text>`) or two ordered Public packets when needed: `EMERGENCY MESSAGE FROM <user>` followed by the original message text truncated to MeshCore’s single-message limit with an explicit truncation marker if necessary. Deduplicate by emergency fingerprint for a short TTL so multiple bots do not all forward the same emergency.
|
||||
- **Rationale:** `BaseChatMesh::sendGroupMessage()` prepends `<node_name>: ` and clamps total group text to `MAX_TEXT_LEN` (160 bytes in the local source). MeshCore packet payload is capped at `MAX_PACKET_PAYLOAD` 184 bytes. The emergency header consumes payload budget; forcing everything into one packet can silently truncate the important text. A two-stage formatter preserves the required phrase and the original text better while staying LoRa-native. Public is preconfigured in current `MyMesh::begin()`, but saved channel config can override indexes, so Public should be found by name/secret rather than assumed to be index 0.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirements + Local code — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/packet_structure.md`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not let `#emergency` execute normal bot commands. Do not forward to Public without deduplication. Do not send unlimited multipart emergency text in v1; each extra packet increases congestion during emergencies.
|
||||
|
||||
### ITEM-architecture-5: Fingerprint requests from decrypted semantics, not packet bytes alone
|
||||
|
||||
- **Recommendation:** Define `BotMessageFingerprint` as 64 bits from SHA-256 over stable application fields: message kind (DM/channel/emergency), channel name or DM peer key prefix, sender identity where available, sender timestamp, normalized command text, and payload type. Keep RF packet hash/path/SNR as metadata only.
|
||||
- **Rationale:** MeshCore already deduplicates raw packets, but semantically identical bot requests can arrive through different RF envelopes, and independently generated bot responses will never share a packet hash. The Python bot/coordinator concepts key on logical message identity, while MeshCore packet hash is designed for route duplicate suppression. Firmware needs both: semantic fingerprint for bot suppression and raw packet/path metadata for diagnostics and scoring.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not key by text alone; two users can send `ping` simultaneously. Do not key only by `Packet::calculatePacketHash()`; the same command-level event may be represented differently at RF/path layers.
|
||||
|
||||
### ITEM-architecture-6: Coordinate with a bounded passive listen-before-answer FSM
|
||||
|
||||
- **Recommendation:** Implement `ResponseCoordinator` as fixed-size state tables and a run-to-completion FSM: `OBSERVED -> ELIGIBLE -> PENDING_SELF -> SENT_SELF | SUPPRESSED_BY_KNOWN_BOT | EXPIRED | FAILED`. Events are `message_seen`, `score_ready`, `timer_due`, `known_bot_response_seen`, `known_claim_seen` (future), `send_ok`, `send_failed`, and `ttl_expired`. Default behavior should be passive: compute a score, schedule a response in a 300-3500 ms window, and cancel if a trusted known-bot response/claim for the same fingerprint arrives first.
|
||||
- **Rationale:** The project explicitly prefers separate bot delays and passive suppression before explicit claim frames. MeshCore’s packet manager already supports scheduled outbound packets, but command execution and suppression should remain app-level so ACK/path behavior is not suppressed. A fixed FSM avoids blocking `loop()`, serial/BLE handling, radio RX, and flash writes.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirements + Local code — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not call `delay()` before responding. Do not allocate per-message heap objects. Do not start with mandatory claim/election packets; they may add more airtime than they save.
|
||||
|
||||
### ITEM-architecture-7: Trust known bot identities explicitly; do not trust arbitrary group text
|
||||
|
||||
- **Recommendation:** Add a persisted `KnownBotRegistry` keyed by full 32-byte public keys plus optional display name and capability flags. Suppression should be authoritative only when the competing bot identity can be validated: DM/control data tied to a known key, signed coordination metadata, or a future signed group-data/claim frame. For plain `GRP_TXT`, treat matching known bot names as a weak hint for low-risk `#bot`/`#testing` only, and never let unauthenticated group text suppress emergency forwarding or safety-critical behavior.
|
||||
- **Rationale:** MeshCore docs and local code identify group text as unverified: it is encrypted to the channel but the sender name is just text in the group payload. A malicious or mistaken node can spoof `KnownBot: ping`. The user requires trusting known bot identities only, which means the firmware needs explicit key-based trust. Passive suppression can still work for normal channels, but the architecture must not pretend group text alone authenticates a bot.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local docs/code — `/Users/cjvana/Documents/GitHub/MeshCore/docs/packet_structure.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not trust node names, response prefixes, or arbitrary control payloads as identities. Do not use unauthenticated suppression for `#emergency`.
|
||||
|
||||
### ITEM-architecture-8: Keep v1 commands small and deterministic
|
||||
|
||||
- **Recommendation:** Ship only firmware-suitable commands first: `ping`, `test/t`, `help`, `status`, `about`, `channels`, minimal `path`/routing summary from available packet metadata, and DM-only `advert`. Keep weather, AQI, sports, satpass, web viewer, Discord, SQLite stats, and external API commands out of firmware unless a board-specific host/network adapter is later added.
|
||||
- **Rationale:** Representative targets include RAK4631/nRF52840 and Heltec v3/ESP32-S3. External data commands in `meshcore-bot` depend on Python, HTTP APIs, local databases, or host services. A firmware-only bot should prioritize reliable off-grid responses, not feature parity. The Python bot remains the behavioral reference for command words, rate limits, channel filtering, and response style.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code/config — `/Users/cjvana/Documents/GitHub/meshcore-bot/config.ini.example`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not embed Python, JavaScript, SQLite, TLS HTTP clients, or large plugin registries. Do not claim full `meshcore-bot` command parity in firmware v1.
|
||||
|
||||
### ITEM-architecture-9: Persist bot settings in a versioned BotPrefs file and expose protocol plus rescue CLI settings
|
||||
|
||||
- **Recommendation:** Add a small versioned `BotPrefs` persisted separately from `NodePrefs`, e.g. `/bot_prefs_v1`, with: enabled/passive, tier, base/max delay, jitter, response cooldowns, allowed channel names/indexes, emergency source/destination channel names, known bot public-key slots, command enable bitset, and stats reset counter. Expose companion protocol commands such as `CMD_GET_BOT_CONFIG`, `CMD_SET_BOT_CONFIG`, `CMD_GET_BOT_STATS`, and `CMD_SET_KNOWN_BOT` in an unused/new range gated by firmware version/capability. Add rescue CLI commands for minimal field recovery: `bot enable|disable`, `bot tier`, `bot channels`, `bot known add <64hex>`, and `bot stats`.
|
||||
- **Rationale:** Existing `NodePrefs` serialization is manually offset-based; changing it directly risks compatibility mistakes. A separate versioned file isolates bot migration and can be ignored by stock clients. The companion protocol already uses binary commands, async push notifications, response codes, and version/response-length gating; CLI rescue currently supports simple field maintenance when apps are unavailable.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/NodePrefs.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not append fields to `NodePrefs` without a migration/version plan. Do not make all bot config compile-time only; Colorado Mesh deployments need field tuning. Do not require a host app to recover from a bad bot setting.
|
||||
|
||||
### ITEM-architecture-10: Design RAM/storage budgets around RAK4631 first, not ESP32 first
|
||||
|
||||
- **Recommendation:** Budget the firmware bot for the tight representative target, RAK4631: keep runtime bot RAM under 4 KB in v1 and filesystem bot storage under 4 KB. A concrete target design is: 12 pending coordinator entries at 64 bytes (768 B), 24 recent fingerprint entries at 16 bytes (384 B), 16 known bots at 40 bytes (640 B RAM if loaded; ~640 B persisted), stats/counters under 256 B, and one shared 160-byte response scratch buffer reused from bot runtime. Keep command response strings in flash/PROGMEM where supported.
|
||||
- **Rationale:** RAK4631 is nRF52840 with 1 MB flash and 256 KB RAM; local linker for RAK companion with extra FS exposes ~712,704 bytes executable flash and ~237,568 bytes RAM after SoftDevice reservation. Heltec v3 has much more flash (8 MB) and ESP32-S3 SRAM (512 KB), so RAK4631 is the limiting design point. Existing companion firmware already reserves large static tables: `MAX_CONTACTS=350`, `MAX_GROUP_CHANNELS=40`, offline queue up to 256 on BLE builds, packet pools, contacts, channels, and advert blobs. Bot state must stay compact.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Official hardware docs + local code — https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; https://documentation.espressif.com/esp32-s3_datasheet_en.pdf ; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/boards/nrf52840_s140_v6_extrafs.ld`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not size the feature using Heltec v3 headroom. Do not add persistent message history, SQLite-like logs, or heap-heavy registries on-device. Do not increase `OFFLINE_QUEUE_SIZE`/packet pool to hide bot backpressure.
|
||||
|
||||
### ITEM-architecture-11: Firmware bot flash/storage estimate is feasible if kept to a compact C++ core
|
||||
|
||||
- **Recommendation:** Treat v1 firmware bot overhead as feasible but require measurement in CI. Expected incremental resources for the compact design are approximately 20-60 KB flash/rodata, 2-4 KB RAM, and 1-4 KB filesystem storage. Compare that against representative devices: Heltec v3 has 8 MB flash and 512 KB ESP32-S3 SRAM; RAK4631 has 1 MB flash/256 KB RAM, with the local RAK companion executable limit set to 712,704 bytes. Existing v1.15 release assets show Heltec v3 merged images around 695 KB (USB) to 1.33 MB (BLE, includes bootloader/partition gaps) and RAK4631 UF2 assets around 955-971 KB (UF2 encoding, not raw app size); local RAK linker limit remains the useful compile-time constraint.
|
||||
- **Rationale:** The bot’s proposed static tables are small compared with existing contact/channel/offline queue tables. The bigger risk is flash growth from careless libraries, string-heavy commands, debug logs, or enabling ESP32 WiFi/OTA-style dependencies globally. A compact command set and no dynamic plugin system should fit RAK4631, but exact headroom cannot be asserted until PlatformIO builds produce `.text/.data/.bss` reports for Heltec v3 and RAK4631.
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** Official release/hardware docs + local config + asset HEAD check — https://github.com/meshcore-dev/MeshCore/releases/tag/companion-v1.15.0 ; https://docs.heltec.cn/en/node/esp32/wifi_lora_32/index.html ; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; `/Users/cjvana/Documents/GitHub/MeshCore/variants/heltec_v3/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not rely on GitHub asset file size as exact app flash size, especially UF2 and merged ESP32 binaries. Do not add external API clients or large command data tables until actual build reports show headroom.
|
||||
|
||||
### ITEM-architecture-12: Use scoring as a delay input, not lower-layer MeshCore timing knobs
|
||||
|
||||
- **Recommendation:** Compute a per-message bot score from command eligibility, channel priority, directness/path length, recent heard path, SNR only for direct/zero-hop observations, tier preference, queue health, and deterministic tiebreaker from known bot key/fingerprint. Convert score into a coordinator delay with bounded jitter. Do not repurpose MeshCore `txdelay`, `direct.txdelay`, or `rxdelay` as bot election controls.
|
||||
- **Rationale:** MeshCore lower-layer timing affects flood/direct retransmit and receive processing, not application-level “which bot answers.” The project already decided to use bot-specific coordinator delays and suppression windows. Keeping this separate avoids breaking path discovery, ACK behavior, and repeater fairness.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirements + local code — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Dispatcher.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/CommonCLI.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/simple_repeater/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not use topology tier as a fixed “winner.” Do not configure values outside existing MeshCore validation ranges just to influence bot replies.
|
||||
|
||||
### ITEM-architecture-13: Instrument duplicate suppression and storage headroom from day one
|
||||
|
||||
- **Recommendation:** Add `BotStats` counters and a stats protocol subtype: observed eligible messages, commands executed, emergency forwards, pending scheduled, self sent, suppressed by known response, suppressed by signed claim, suppressed by weak group hint, expired, failed sends, duplicate emergency fingerprints, outbound queue high-water mark, packet pool failures, free heap/stack where available, bot RAM config size, and DataStore used/total KB.
|
||||
- **Rationale:** The project goal is to reduce duplicate bot traffic without excessive latency. Existing companion firmware already exposes core/radio/packet stats and storage totals. Bot-specific counters are necessary to prove the firmware coordinator helps and to catch false suppression, queue exhaustion, or storage pressure on RAK4631.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/stats_binary_frames.md`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not tune delay windows by anecdote. Do not only count transmissions; false suppression and failed emergency forwarding are higher-risk metrics.
|
||||
|
||||
### ITEM-architecture-14: Preserve upstream maintainability with a submodule plus small patch series
|
||||
|
||||
- **Recommendation:** Structure the Colorado Mesh project as a wrapper repository with upstream MeshCore pinned as a git submodule (for example `vendor/MeshCore`) plus a versioned patch series (`patches/meshcore/*.patch`) and optional overlay files. CI should initialize the submodule, verify the upstream commit/tag, apply patches cleanly, build representative environments, and fail if patches no longer apply. Keep patches small and upstream-shaped: one for bot source files, one for `MyMesh` hooks/protocol constants, one for DataStore/BotPrefs, one for build flags/CI if needed.
|
||||
- **Rationale:** The user chose upstream MeshCore as a submodule/patch base. PlatformIO source filters and example paths make a pure out-of-tree plugin hard, so the maintainable compromise is a clean patch stack over upstream rather than a drifting vendored copy. This preserves a clear diff for eventual upstream discussion while allowing Colorado-specific bot policy to stay outside upstream until proven.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirements + local code — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/heltec_v3/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`; https://github.com/meshcore-dev/MeshCore/releases/tag/companion-v1.15.0
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not maintain a permanent copy of MeshCore with unstructured edits. Do not create board-specific forks. Do not make the wrapper call into files outside the MeshCore tree if PlatformIO source filters cannot reliably include them across all environments.
|
||||
|
||||
## Confidence Summary
|
||||
|
||||
| Item ID | Level | Source Type | URL/Reference |
|
||||
|---------|-------|-------------|---------------|
|
||||
| ITEM-architecture-1 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.cpp` |
|
||||
| ITEM-architecture-2 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/command_manager.py`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp` |
|
||||
| ITEM-architecture-3 | HIGH | Project requirements + local code | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp` |
|
||||
| ITEM-architecture-4 | HIGH | Project requirements + local code docs | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/packet_structure.md` |
|
||||
| ITEM-architecture-5 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py` |
|
||||
| ITEM-architecture-6 | HIGH | Project requirements + local code | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp` |
|
||||
| ITEM-architecture-7 | HIGH | Local docs/code | `/Users/cjvana/Documents/GitHub/MeshCore/docs/packet_structure.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp` |
|
||||
| ITEM-architecture-8 | HIGH | Local bot code/config | `/Users/cjvana/Documents/GitHub/meshcore-bot/config.ini.example`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands` |
|
||||
| ITEM-architecture-9 | HIGH | Local code + official docs | https://docs.meshcore.io/companion_protocol/ ; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp` |
|
||||
| ITEM-architecture-10 | HIGH | Hardware docs + local linker/config | https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; https://documentation.espressif.com/esp32-s3_datasheet_en.pdf ; `/Users/cjvana/Documents/GitHub/MeshCore/boards/nrf52840_s140_v6_extrafs.ld` |
|
||||
| ITEM-architecture-11 | MEDIUM | Release/hardware docs + local config + asset HEAD check | https://github.com/meshcore-dev/MeshCore/releases/tag/companion-v1.15.0 ; https://docs.heltec.cn/en/node/esp32/wifi_lora_32/index.html ; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ |
|
||||
| ITEM-architecture-12 | HIGH | Project requirements + local code | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Dispatcher.cpp` |
|
||||
| ITEM-architecture-13 | HIGH | Local code + official docs | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-architecture-14 | HIGH | Project requirements + local code + official release | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; https://github.com/meshcore-dev/MeshCore/releases/tag/companion-v1.15.0 |
|
||||
214
.forge/research/codex-analysis.md
Normal file
214
.forge/research/codex-analysis.md
Normal file
@@ -0,0 +1,214 @@
|
||||
warning: `--full-auto` is deprecated; use `--sandbox workspace-write` instead.
|
||||
OpenAI Codex v0.130.0
|
||||
--------
|
||||
session id: 019e2739-3393-7972-a90b-79f1a369eb12
|
||||
--------
|
||||
user
|
||||
Read .forge/PROJECT.md. Then write a concise research analysis to stdout. Do NOT review any code or plan. Do NOT search the web — analyze based on your training knowledge.
|
||||
|
||||
Cover these sections with specific, opinionated recommendations:
|
||||
1. EXISTING SOLUTIONS — what open-source and commercial products exist in this space
|
||||
2. RECOMMENDED STACK — specific libraries with versions, and what to avoid
|
||||
3. ARCHITECTURE — how to structure the system, component boundaries, data flow
|
||||
4. PITFALLS — domain-specific mistakes and how to prevent them
|
||||
5. QUESTIONS — what you would ask before building this
|
||||
|
||||
Output ONLY your analysis text. No tool call logs, no search results, just the analysis.
|
||||
2026-05-14T16:02:02.319622Z ERROR rmcp::transport::worker: worker quit with fatal: Transport channel closed, when Auth(TokenRefreshFailed("Server returned error response: invalid_grant: Token refresh failed: invalid or expired grant"))
|
||||
succeeded in 0ms:
|
||||
# Forge Project
|
||||
|
||||
## Description
|
||||
Build firmware for MeshCore bots using the standard MeshCore bot project (https://github.com/agessaman/meshcore-bot.git) and MeshCore companion firmware (https://github.com/meshcore-dev/MeshCore.git). The goal is to reduce bot adverts and evaluate/build a built-in response coordinator in firmware as a better alternative to the current VPS coordinator.
|
||||
|
||||
## Constraints
|
||||
- Use the upstream MeshCore bot project and MeshCore companion firmware as primary references.
|
||||
- Target all MeshCore companion device types where feasible.
|
||||
- Perform only read-only discovery on local GitHub projects and the hosts `cj-vps` and `cjvana@10.0.0.222` unless later authorized otherwise.
|
||||
- Deliverable type: code.
|
||||
|
||||
## Context
|
||||
- Greenfield/Brownfield: New project in the Colorado Mesh org, starting from an empty local directory.
|
||||
- Platform: MeshCore companion firmware / embedded firmware for companion device types.
|
||||
- Deliverable type: code
|
||||
- Date: 2026-05-14
|
||||
|
||||
## Initial Questions and Answers
|
||||
- What kind of deliverable should Forge produce? Code project.
|
||||
- New project or existing local project? New project in the Colorado Mesh org.
|
||||
- Which target hardware should the first firmware build support? All companion types.
|
||||
- May local projects and named hosts be inspected? Read-only discovery is OK.
|
||||
|
||||
## Candidate Coordination Approach
|
||||
The user suggested using configured transmit and receive delays to bias which bot responds, potentially approximating which bot is closest or most appropriate for the original message. Proposed topology tiers:
|
||||
|
||||
- HILLTOP: highest elevation / backbone infrastructure, typical neighbors 20+, commands `set txdelay 2`, `set direct.txdelay 2`, `set rxdelay 3`.
|
||||
- FOOTHILLS: mid elevation bridge nodes, typical neighbors 10-20, commands `set txdelay 1.5`, `set direct.txdelay 1`, `set rxdelay 3`.
|
||||
- SUBURBAN: average rooftop installs, typical neighbors 5-10, commands `set txdelay 0.8`, `set direct.txdelay 0.4`, `set rxdelay 3`.
|
||||
- LOCAL: low elevation / immediate area, typical neighbors 1-3, commands `set txdelay 0.3`, `set direct.txdelay 0.1`, `set rxdelay 3`.
|
||||
- MOBILE: variable elevation, should defer to fixed infrastructure, commands `set txdelay 3`, `set direct.txdelay 2.5`, `set rxdelay 3`.
|
||||
|
||||
Research and planning should evaluate whether this delay-tier model can reduce duplicate bot adverts/responses, whether it conflicts with MeshCore's existing TX/RX timing semantics, and whether extra coordinator messages are still needed.
|
||||
|
||||
codex
|
||||
1. EXISTING SOLUTIONS
|
||||
|
||||
Open source: MeshCore is the correct substrate; stay close to its companion firmware and bot project instead of inventing a parallel mesh layer. Meshtastic is the closest comparable mature LoRa mesh ecosystem and is useful for lessons on channel utilization, node roles, store-and-forward behavior, and UX, but its chat-first model should not be copied wholesale. Reticulum/LXMF is relevant for delay-tolerant addressing and opportunistic links. APRS/LoRa APRS is worth studying for beacon discipline, duplicate suppression, and digipeater etiquette. ChirpStack/LoRaWAN stacks solve a different centralized network-server problem and are mostly a cautionary example here.
|
||||
|
||||
Commercial: goTenna Pro, Garmin inReach, ZOLEO, Somewear, Rajant, Silvus, StreamCaster, and Doodle Labs all solve parts of resilient off-grid messaging or tactical mesh. The useful takeaway is not their architecture, which is mostly closed and heavier than MeshCore, but their policy model: role-aware nodes, conservative airtime use, explicit priority, and aggressive duplicate suppression.
|
||||
|
||||
Opinionated recommendation: build a distributed soft-coordination layer inside the bot firmware, not a firmware clone of the VPS coordinator. Delay-based response suppression is the right first mechanism because every explicit coordinator frame costs scarce airtime.
|
||||
|
||||
2. RECOMMENDED STACK
|
||||
|
||||
Use the upstream MeshCore build system and dependency pins wherever they already exist. Do not introduce a new RTOS, packet stack, or language runtime.
|
||||
|
||||
Recommended additions if the project needs new code:
|
||||
- C++17, matching upstream embedded style.
|
||||
- PlatformIO Core 6.1.x for reproducible multi-board firmware builds if upstream already uses PlatformIO.
|
||||
- RadioLib 7.1.x only if MeshCore already depends on it or exposes it cleanly; otherwise use MeshCore radio abstractions directly.
|
||||
- nanopb 0.4.9.1 only for compact structured payloads that must evolve over time.
|
||||
- ArduinoJson 7.2.x only for local config import/export or serial diagnostics, not over the air.
|
||||
- Unity 2.6.x for embedded unit tests.
|
||||
- Python 3.12, pytest 8.3.x, Hypothesis 6.112.x, and SimPy 4.1.x for host-side coordination and airtime simulations.
|
||||
|
||||
Avoid: JSON over LoRa, MQTT in firmware, full protobuf runtimes, SQLite, dynamic plugin systems, heap-heavy callback code, custom radio drivers, and any design that requires every bot to hear every other bot.
|
||||
|
||||
3. ARCHITECTURE
|
||||
|
||||
Structure the firmware as a bot coordination module sitting above MeshCore transport:
|
||||
|
||||
RX path: MeshCore packet -> bot request classifier -> request ID normalization -> eligibility engine -> response scheduler.
|
||||
|
||||
TX path: pending response intent -> suppression window -> overheard-response cancellation -> final response publish -> seen/sent ring buffer update.
|
||||
|
||||
Core components:
|
||||
- Request classifier: identifies bot-triggering messages and derives a stable idempotency key from origin, command type, normalized content, and a short time bucket.
|
||||
- Eligibility engine: decides whether this bot may answer based on role, capability, directness, recent neighbor observations, and command type.
|
||||
- Scheduler: computes `response_at = rx_time + role_delay + airtime_scaled_jitter`.
|
||||
- Suppression cache: cancels pending responses when an equivalent valid response is heard.
|
||||
- Metrics: counts eligible, deferred, canceled, transmitted, duplicate-heard, and hidden-node-suspected events.
|
||||
- Config: expose role, base delay, jitter, suppression window, and command-specific overrides through the existing MeshCore CLI/config style.
|
||||
|
||||
The proposed tier model is directionally good: LOCAL and SUBURBAN should usually answer before HILLTOP and MOBILE. Treat the tiers as policy priority, not true distance. Do not overload MeshCore's core `txdelay` semantics if those affect all radio traffic; add bot-specific coordination delays instead. The suppression window should be longer than the slowest role delay plus worst-case packet airtime plus jitter. A universal `rxdelay 3` is likely too short if MOBILE can delay 3 seconds.
|
||||
|
||||
Do not add coordinator claim frames in the first version. Measure duplicate rate first. If duplicates remain unacceptable, add a very small authenticated "claim" frame only for expensive or high-noise responses.
|
||||
|
||||
4. PITFALLS
|
||||
|
||||
Delay tiers will not solve hidden nodes. Two bots may both hear the requester but not each other. Prevent damage with idempotency keys, client-side duplicate tolerance, and metrics that reveal hidden-node cases.
|
||||
|
||||
Neighbor count is not proximity. Hilltop nodes may hear many nodes but be the wrong responder for local context. Use configured role first, observed directness second, RSSI/SNR only as a weak tie-breaker.
|
||||
|
||||
Airtime math matters. LoRa packet airtime can dominate small delay differences, especially at slow spreading factors. Jitter should scale with estimated airtime, not be a fixed tiny random number.
|
||||
|
||||
Do not run logic inside radio callbacks. Callbacks should enqueue compact events; normal firmware loop/task code should parse, schedule, and transmit.
|
||||
|
||||
Do not persist every seen request. Use RAM ring buffers for hot dedupe and write config/metrics sparingly to avoid flash wear.
|
||||
|
||||
Do not trust unauthenticated suppression. A forged response or claim could silence all bots. Reuse MeshCore identity/authentication primitives where available.
|
||||
|
||||
Do not assume all companion device types have equal RAM, flash, display, BLE, or radio behavior. Keep the coordinator core platform-neutral and put board-specific behavior behind existing MeshCore abstractions.
|
||||
|
||||
5. QUESTIONS
|
||||
|
||||
What exact traffic should be suppressed: bot adverts, command replies, telemetry, direct-message responses, or all of them?
|
||||
|
||||
What duplicate rate and response latency are acceptable in the field?
|
||||
|
||||
Should the system prefer the nearest/local bot, the most reliable fixed bot, or the least airtime-costly bot?
|
||||
|
||||
How does the current VPS coordinator decide winners, and what failures are driving the firmware replacement?
|
||||
|
||||
Are role tiers manually configured, automatically inferred, or both?
|
||||
|
||||
Which companion device is the minimum hardware baseline for RAM, flash, and radio capability?
|
||||
|
||||
Can packet formats change, or must this remain fully compatible with stock MeshCore nodes?
|
||||
|
||||
Are adversarial or misconfigured nodes in scope for the security model?
|
||||
|
||||
What RF settings, region, channel usage, and typical packet sizes should simulations target?
|
||||
|
||||
Should emergency/admin commands bypass normal delay suppression?
|
||||
22,375
|
||||
1. EXISTING SOLUTIONS
|
||||
|
||||
Open source: MeshCore is the correct substrate; stay close to its companion firmware and bot project instead of inventing a parallel mesh layer. Meshtastic is the closest comparable mature LoRa mesh ecosystem and is useful for lessons on channel utilization, node roles, store-and-forward behavior, and UX, but its chat-first model should not be copied wholesale. Reticulum/LXMF is relevant for delay-tolerant addressing and opportunistic links. APRS/LoRa APRS is worth studying for beacon discipline, duplicate suppression, and digipeater etiquette. ChirpStack/LoRaWAN stacks solve a different centralized network-server problem and are mostly a cautionary example here.
|
||||
|
||||
Commercial: goTenna Pro, Garmin inReach, ZOLEO, Somewear, Rajant, Silvus, StreamCaster, and Doodle Labs all solve parts of resilient off-grid messaging or tactical mesh. The useful takeaway is not their architecture, which is mostly closed and heavier than MeshCore, but their policy model: role-aware nodes, conservative airtime use, explicit priority, and aggressive duplicate suppression.
|
||||
|
||||
Opinionated recommendation: build a distributed soft-coordination layer inside the bot firmware, not a firmware clone of the VPS coordinator. Delay-based response suppression is the right first mechanism because every explicit coordinator frame costs scarce airtime.
|
||||
|
||||
2. RECOMMENDED STACK
|
||||
|
||||
Use the upstream MeshCore build system and dependency pins wherever they already exist. Do not introduce a new RTOS, packet stack, or language runtime.
|
||||
|
||||
Recommended additions if the project needs new code:
|
||||
- C++17, matching upstream embedded style.
|
||||
- PlatformIO Core 6.1.x for reproducible multi-board firmware builds if upstream already uses PlatformIO.
|
||||
- RadioLib 7.1.x only if MeshCore already depends on it or exposes it cleanly; otherwise use MeshCore radio abstractions directly.
|
||||
- nanopb 0.4.9.1 only for compact structured payloads that must evolve over time.
|
||||
- ArduinoJson 7.2.x only for local config import/export or serial diagnostics, not over the air.
|
||||
- Unity 2.6.x for embedded unit tests.
|
||||
- Python 3.12, pytest 8.3.x, Hypothesis 6.112.x, and SimPy 4.1.x for host-side coordination and airtime simulations.
|
||||
|
||||
Avoid: JSON over LoRa, MQTT in firmware, full protobuf runtimes, SQLite, dynamic plugin systems, heap-heavy callback code, custom radio drivers, and any design that requires every bot to hear every other bot.
|
||||
|
||||
3. ARCHITECTURE
|
||||
|
||||
Structure the firmware as a bot coordination module sitting above MeshCore transport:
|
||||
|
||||
RX path: MeshCore packet -> bot request classifier -> request ID normalization -> eligibility engine -> response scheduler.
|
||||
|
||||
TX path: pending response intent -> suppression window -> overheard-response cancellation -> final response publish -> seen/sent ring buffer update.
|
||||
|
||||
Core components:
|
||||
- Request classifier: identifies bot-triggering messages and derives a stable idempotency key from origin, command type, normalized content, and a short time bucket.
|
||||
- Eligibility engine: decides whether this bot may answer based on role, capability, directness, recent neighbor observations, and command type.
|
||||
- Scheduler: computes `response_at = rx_time + role_delay + airtime_scaled_jitter`.
|
||||
- Suppression cache: cancels pending responses when an equivalent valid response is heard.
|
||||
- Metrics: counts eligible, deferred, canceled, transmitted, duplicate-heard, and hidden-node-suspected events.
|
||||
- Config: expose role, base delay, jitter, suppression window, and command-specific overrides through the existing MeshCore CLI/config style.
|
||||
|
||||
The proposed tier model is directionally good: LOCAL and SUBURBAN should usually answer before HILLTOP and MOBILE. Treat the tiers as policy priority, not true distance. Do not overload MeshCore's core `txdelay` semantics if those affect all radio traffic; add bot-specific coordination delays instead. The suppression window should be longer than the slowest role delay plus worst-case packet airtime plus jitter. A universal `rxdelay 3` is likely too short if MOBILE can delay 3 seconds.
|
||||
|
||||
Do not add coordinator claim frames in the first version. Measure duplicate rate first. If duplicates remain unacceptable, add a very small authenticated "claim" frame only for expensive or high-noise responses.
|
||||
|
||||
4. PITFALLS
|
||||
|
||||
Delay tiers will not solve hidden nodes. Two bots may both hear the requester but not each other. Prevent damage with idempotency keys, client-side duplicate tolerance, and metrics that reveal hidden-node cases.
|
||||
|
||||
Neighbor count is not proximity. Hilltop nodes may hear many nodes but be the wrong responder for local context. Use configured role first, observed directness second, RSSI/SNR only as a weak tie-breaker.
|
||||
|
||||
Airtime math matters. LoRa packet airtime can dominate small delay differences, especially at slow spreading factors. Jitter should scale with estimated airtime, not be a fixed tiny random number.
|
||||
|
||||
Do not run logic inside radio callbacks. Callbacks should enqueue compact events; normal firmware loop/task code should parse, schedule, and transmit.
|
||||
|
||||
Do not persist every seen request. Use RAM ring buffers for hot dedupe and write config/metrics sparingly to avoid flash wear.
|
||||
|
||||
Do not trust unauthenticated suppression. A forged response or claim could silence all bots. Reuse MeshCore identity/authentication primitives where available.
|
||||
|
||||
Do not assume all companion device types have equal RAM, flash, display, BLE, or radio behavior. Keep the coordinator core platform-neutral and put board-specific behavior behind existing MeshCore abstractions.
|
||||
|
||||
5. QUESTIONS
|
||||
|
||||
What exact traffic should be suppressed: bot adverts, command replies, telemetry, direct-message responses, or all of them?
|
||||
|
||||
What duplicate rate and response latency are acceptable in the field?
|
||||
|
||||
Should the system prefer the nearest/local bot, the most reliable fixed bot, or the least airtime-costly bot?
|
||||
|
||||
How does the current VPS coordinator decide winners, and what failures are driving the firmware replacement?
|
||||
|
||||
Are role tiers manually configured, automatically inferred, or both?
|
||||
|
||||
Which companion device is the minimum hardware baseline for RAM, flash, and radio capability?
|
||||
|
||||
Can packet formats change, or must this remain fully compatible with stock MeshCore nodes?
|
||||
|
||||
Are adversarial or misconfigured nodes in scope for the security model?
|
||||
|
||||
What RF settings, region, channel usage, and typical packet sizes should simulations target?
|
||||
|
||||
Should emergency/admin commands bypass normal delay suppression?
|
||||
235
.forge/research/pitfalls.md
Normal file
235
.forge/research/pitfalls.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# Pitfalls Research: Firmware-only MeshCore Bot
|
||||
|
||||
Project: firmware-only embedded bot command handling in MeshCore companion firmware. Upstream MeshCore is the firmware base/submodule/patch base; `meshcore-bot` is a behavioral reference only. Normal bot traffic is limited to private DMs, `#bot`, and `#testing`; `#emergency` is forwarded to Public as an emergency announcement. Representative build targets: Heltec v3 and RAK4631.
|
||||
|
||||
Checked: 2026-05-14
|
||||
|
||||
Storage baseline used in this research:
|
||||
|
||||
| Target | Hardware/storage facts | Current firmware size signal | Headroom implication |
|
||||
|---|---|---|---|
|
||||
| Heltec WiFi LoRa 32 V3 | ESP32-S3FN8, 8 MB SiP flash, 512 KB SRAM, no external PSRAM, SX1262 | MeshCore v1.15.0 release assets: Heltec v3 USB app `.bin` 615 KB; BLE app `.bin` 1.2 MB; merged BLE 1.27 MB | Flash is comfortable for a small firmware bot, but RAM is still constrained and BLE/display/WiFi variants can consume much more than USB. |
|
||||
| RAK4631 | nRF52840, 1 MB flash, 256 KB RAM, BLE 5.0, SX1262; local companion env caps app at 712,704 bytes and RAM region at 237,568 bytes after SoftDevice reservation | MeshCore v1.15.0 release assets: RAK4631 USB `.uf2` 933 KB / `.zip` 467 KB; BLE `.uf2` 949 KB / `.zip` 475 KB. UF2 includes container overhead, so zip size is the best visible proxy without a local ELF/map build. | RAK4631 is the limiting target. A compact parser/coordinator is likely feasible; Python-bot parity or large lookup tables are not. |
|
||||
|
||||
Estimated firmware-only bot budget, before measurement: a compact built-in bot should target **20-60 KB additional flash**, **2-8 KB static/RAM**, **0 heap allocation after setup**, and **<1 KB persisted prefs**. A fuller command suite with many string templates, JSON/API logic, large help text, or display telemetry can easily exceed this and should be gated per target. These estimates are MEDIUM confidence because no local PlatformIO build/map was available in this repo.
|
||||
|
||||
### ITEM-pitfalls-1: Underestimating RAK4631 as the storage/RAM limiting device
|
||||
|
||||
- **What goes wrong:** The bot fits and behaves on Heltec v3 ESP32-S3, then fails to link, crashes, or becomes unstable on RAK4631 BLE because the nRF52840 target has far less usable flash/RAM once SoftDevice, BLE, display, contact tables, channel tables, offline queue, packet pools, and filesystem regions are included.
|
||||
- **Root cause:** Heltec v3 has 8 MB flash and 512 KB SRAM, while RAK4631 has 1 MB flash and 256 KB RAM. The local RAK4631 companion env further sets `board_upload.maximum_size = 712704`, and its linker script places application flash from `0x26000` to `0xD4000` with RAM from `0x20006000` to `0x20040000`. Current upstream release assets show RAK4631 companion firmware is already hundreds of KB before bot code, and the BLE variant carries additional queues/logging/display code.
|
||||
- **Prevention:** Make RAK4631 BLE the hard budget target, not Heltec v3. Require every bot feature PR to report `.text/.rodata/.data/.bss` deltas from PlatformIO map/size output for `RAK_4631_companion_radio_ble`, `RAK_4631_companion_radio_usb`, `Heltec_v3_companion_radio_ble`, and `Heltec_v3_companion_radio_usb`. Keep Phase 1 to a compact command parser, passive duplicate suppression, emergency forwarding, and a few single-packet commands. Defer weather/AQI/sports/satpass/feed/web features unless a build map proves headroom.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Phase 0 feasibility and every CI build gate.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official hardware docs + GitHub release assets — `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/boards/nrf52840_s140_v6_extrafs.ld`; https://heltec.org/project/wifi-lora-32-v3/ ; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; https://github.com/meshcore-dev/MeshCore/releases/expanded_assets/companion-v1.15.0
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-2: Treating the Python bot as portable command code instead of a behavioral oracle
|
||||
|
||||
- **What goes wrong:** Firmware grows into an unmaintainable partial Python-bot clone: many commands require internet APIs, JSON parsing, caching, date/time libraries, HTTP/TLS, Discord/web hooks, SQLite-like history, or large text responses. The result exceeds MCU flash/RAM, cannot work off-grid, and behaves differently from the host bot anyway.
|
||||
- **Root cause:** The Python bot depends on host resources and libraries (`requests`, `aiohttp`, Flask, feedparser, PyEphem, Open-Meteo clients, cryptography, `meshcore-cli`) and includes command modules for weather, AQI, satellite passes, feeds, sports, web viewer, Discord forwarding, stats databases, and contact management. Those are not firmware-native features.
|
||||
- **Prevention:** Define a firmware-only minimum viable command set: `ping`, `help` with short static text, status/battery/storage, path/contact diagnostics from existing MeshCore state, and emergency forwarding. Treat Python modules as golden behavior for command names, channel policy, cooldowns, and output style only. Anything needing IP services should stay host-side or be replaced by a cached/static firmware-safe answer.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Scope definition and feature triage.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/meshcore-bot/requirements.txt`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands/*`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-3: Heap fragmentation from `String`, dynamic containers, JSON, and variable response construction
|
||||
|
||||
- **What goes wrong:** The bot passes bench tests but reboots or fails to allocate packets after hours/days of varied commands because small MCU heaps fragment. Failures look like random packet-pool exhaustion, BLE instability, or corrupted command output.
|
||||
- **Root cause:** Firmware bot parsing tempts use of Arduino `String`, `std::vector`, maps, JSON builders, formatted heap strings, and per-message dynamic objects. Upstream MeshCore already warns contributors to avoid dynamic allocation except during setup/begin, and its packet manager uses fixed pools. The companion currently has only one explicit large heap path for signing (`malloc(MAX_SIGN_DATA_LEN)`), which should not become a model for bot logic.
|
||||
- **Prevention:** Use fixed-size structs, ring buffers, and `char[]` parsing. Allocate coordinator tables statically; no `malloc/new/String` in receive, parse, schedule, or send paths. Keep command output templates in `const`/flash-friendly storage where supported, and always bound-copy into `MAX_TEXT_LEN`/`MAX_FRAME_SIZE` buffers. Add a static-analysis/code-review rule that bot files cannot use `String`, heap containers, or heap allocation after setup.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Firmware implementation standards and review checklist.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + ecosystem reference — `/Users/cjvana/Documents/GitHub/MeshCore/README.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; https://cpp4arduino.com/2018/11/06/what-is-heap-fragmentation.html
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-4: Packet-pool and offline-queue exhaustion during delayed bot responses
|
||||
|
||||
- **What goes wrong:** A burst of commands creates many pending responses or emergency forwards. The 16-packet pool fills, outbound/inbound queues fill, MeshCore reports table-full/send failures, and normal ACK/path/adverts/routing maintenance are starved. Losing bot responses may still occupy packet slots until their timers fire.
|
||||
- **Root cause:** Companion `MyMesh` constructs `StaticPoolPacketManager(16)`, and `StaticPoolPacketManager` uses fixed send/rx queues. BLE variants may set `OFFLINE_QUEUE_SIZE=256`, which is a large RAM commitment, while USB defaults to 16. Existing code drops oldest channel messages when offline queue is full, so a firmware bot can accidentally evict user-visible messages with its own generated traffic.
|
||||
- **Prevention:** Keep pending-response state separate from allocated MeshCore packets. Do not call `createGroupDatagram()` until the response timer actually wins. Limit pending bot windows to a small fixed number, e.g. 8-16. If over capacity, drop low-priority bot replies before user messages. Emergency forwarding may preempt normal bot responses but must still respect packet-pool availability and report a dropped-forward counter.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Coordinator implementation and stress testing.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/heltec_v3/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-5: Exceeding MeshCore text/frame limits with Python-style bot replies
|
||||
|
||||
- **What goes wrong:** Help, weather, emergency, sports, stats, or path responses are truncated, rejected, split by future code into multiple packets, or silently lose context. Duplicate suppression may suppress only the first fragment while later fragments still transmit.
|
||||
- **Root cause:** MeshCore constants are small: `MAX_PACKET_PAYLOAD=184`, `MAX_FRAME_SIZE=172`, and `MAX_TEXT_LEN=160`. Companion protocol docs currently state datagram payload caps around 163 bytes and text messages around 133 characters. `BaseChatMesh::sendGroupMessage()` prefixes group messages with `"<sender>: "`, reducing available bot text. LoRa airtime makes multi-packet bot chatter expensive.
|
||||
- **Prevention:** Design every firmware command as a single-packet, LoRa-native response. Enforce command-specific output caps before send; include a test that serializes exact channel output with the node name prefix. `#emergency` forwarding must be short enough to fit or use a deterministic two-message policy with bounded truncation: announcement plus truncated original text. Do not implement multipart public bot replies in Phase 1.
|
||||
- **Severity:** MODERATE
|
||||
- **Phase relevance:** UX copy, command implementation, emergency forwarding.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/src/MeshCore.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseSerialInterface.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-6: Unsafe C++ command parsing and text normalization
|
||||
|
||||
- **What goes wrong:** A malformed frame or odd user message causes buffer over-read, unterminated strings, command misfire, or incorrect sender extraction. Inputs containing colons, empty names, control characters, UTF-8, long aliases, or embedded NULs may bypass filters or corrupt output.
|
||||
- **Root cause:** Existing companion code commonly null-terminates `cmd_frame[len]`, uses `strlen`, `strcpy`, `sprintf`, and colon parsing conventions (`sender: message`) inherited from group text format. Python `meshcore-bot` sanitizes input with helper utilities, but firmware C++ must do this manually under smaller buffers.
|
||||
- **Prevention:** Build a tiny parser that operates on `(const char*, length)` not untrusted `strlen` until after explicit NUL insertion inside a known spare byte. Strip C0 controls except newline-equivalent spaces; normalize only ASCII command prefixes in Phase 1; treat everything after the command verb as bounded opaque text. Use `snprintf`/bounded copy everywhere. Add fuzz/unit tests for maximum-length messages, no colon, leading colon, repeated colon, UTF-8, NUL, and all allowed channel names.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Command parser implementation and test harness.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/security_utils.py`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-7: Misidentifying channels by index instead of stable channel policy
|
||||
|
||||
- **What goes wrong:** The bot answers on Public during normal operation, ignores `#bot`, mishandles `#testing`, or forwards the wrong channel because channel indexes differ across devices. A user renames/reorders channels and the bot policy silently changes.
|
||||
- **Root cause:** Companion receive frames identify channel by `channel_idx`; `BaseChatMesh` stores `ChannelDetails` in an array and `findChannelIdx()` matches channel secrets. Public is auto-added first, then persisted channels load. Indexes are local configuration, not global semantic names. MeshCore channel messages are group-key based and unverified by sender identity at the spec level.
|
||||
- **Prevention:** Resolve policy by channel secret/hash plus configured display name at startup, then cache explicit channel slots for `Public`, `#bot`, `#testing`, and `#emergency`. Refuse to enable the bot if required channels are ambiguous, missing, or duplicate. Normal responses must be allowed only in DMs, `#bot`, and `#testing`; Public sends are allowed only from the emergency forwarder and self-tests. Expose a diagnostic command showing channel index/name/hash/policy.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Channel setup and command routing.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/ChannelDetails.h`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-8: Emergency forwarding loops, amplification, and spoofed urgency
|
||||
|
||||
- **What goes wrong:** A message in `#emergency` is forwarded repeatedly by every bot to Public; bots then see the Public announcement and re-forward or respond; malicious users trigger panic banners; long emergency text consumes multiple high-priority packets during congestion.
|
||||
- **Root cause:** The new rule intentionally bridges `#emergency` to Public. Without an idempotency key, TTL, and bot-origin detection, every firmware bot can act on the same emergency packet independently. Group messages identify the text sender string but are not a strong authenticated identity. Passive duplicate suppression without claim frames allows hidden bots to miss each other.
|
||||
- **Prevention:** Treat emergency forwarding as a separate high-priority, idempotent action keyed by original channel hash, timestamp, normalized original text, and sender string/public key prefix where available. Keep an emergency-forward cache longer than normal bot duplicate windows, e.g. 10-30 minutes. Never forward bot-originated `EMERGENCY MESSAGE FROM` text. Use exactly the user-requested Public format but clamp length. Prefer one winner via the same passive suppression window, but if duplicates occur they must have identical text and no recursive trigger.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Emergency feature design before public testing.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Project requirement + local code — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-9: Passive duplicate suppression without claim frames cannot guarantee single response
|
||||
|
||||
- **What goes wrong:** Two bots that cannot hear each other both answer after their local delay window. Bench tests with colocated radios look clean, but field deployments still produce duplicates across terrain, hidden nodes, and asymmetric paths.
|
||||
- **Root cause:** Phase 1 intentionally defers explicit on-air claim frames. LoRa listen-before-talk/CAD reduces collisions but does not solve hidden-node consensus. MeshCore’s duplicate tables suppress identical packets, not independently generated bot responses. A final response can only suppress peers that hear it before their timer fires.
|
||||
- **Prevention:** Set expectations: Phase 1 reduces noise, not guarantees exactly once. Use deterministic scoring plus jitter, short bounded windows, and cancel-on-heard-response. Record duplicate fingerprints in telemetry. Do not add claim frames until field captures prove passive suppression is insufficient; if claims are added, authenticate them and keep them zero-hop/scoped.
|
||||
- **Severity:** MODERATE
|
||||
- **Phase relevance:** Coordination algorithm and acceptance criteria.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + ecosystem docs/search — `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.cpp`; https://meshtastic.org/docs/overview/mesh-algo/ ; https://scholarworks.gnu.ac.kr/item/ccb29254-804b-413d-adc5-853fe697241b
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-10: Suppression keyed by raw packet hash or plain text creates false positives/negatives
|
||||
|
||||
- **What goes wrong:** Bots fail to suppress duplicate responses for the same command observed through different RF envelopes, or they suppress unrelated commands because two users typed the same text. Emergency forwarding may drop a real second emergency because the text happens to match.
|
||||
- **Root cause:** MeshCore flood/direct paths, transport codes, and retry/ACK behavior can change packet bytes while the application-level command is the same. Conversely, text-only matching ignores sender, channel, timestamp, payload type, and DM vs channel semantics.
|
||||
- **Prevention:** Define a firmware `BotMessageFingerprint` over stable application fields after decryption/parsing: payload kind, channel identity or DM peer identity, sender timestamp, normalized command text, sender public-key prefix/name when available, and a small packet hash tie-in for diagnostics. Store both request fingerprint and response fingerprint. Use a separate emergency fingerprint with a longer TTL.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Coordinator state model and test vectors.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-11: Trusting spoofable bot identities, response text, or control packets
|
||||
|
||||
- **What goes wrong:** A non-bot node suppresses real bots by sending fake claim/control data or a message that looks like a bot response. In emergency mode this can block forwarding or inject false Public emergency announcements.
|
||||
- **Root cause:** MeshCore `PAYLOAD_TYPE_CONTROL` is a generic control/discovery packet path and companion currently forwards control data to the app without authentication. Group text is shared-key encrypted but the visible `name: message` prefix is not enough to prove a known bot identity. The project decision says suppression should trust known bot identities only.
|
||||
- **Prevention:** In Phase 1, suppress only on final responses that match known bot public-key prefixes or an allowlisted bot identity table; do not suppress emergency forwarding based on unauthenticated claims. If control/claim packets are introduced later, include version/length/magic/fingerprint and require signature or known-key verification before they can suppress. Parse unknown control frames as untrusted hints or ignore them.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Security design and duplicate suppression.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code/docs — `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-12: Blocking the companion superloop with bot delays or slow display/BLE/serial work
|
||||
|
||||
- **What goes wrong:** While a bot waits to see if another node responds, BLE notifications stall, serial frames time out, display/UI feels frozen, RTC ticks lag, sensors are skipped, or radio RX/TX processing is delayed. The coordinator makes the node less reliable than stock companion firmware.
|
||||
- **Root cause:** Companion firmware is a cooperative loop: `the_mesh.loop()`, serial/BLE/WiFi interface polling, sensors, display task, and RTC tick all depend on returning quickly. Protocol docs require one in-flight command and handling unsolicited notifications, while local display/UI code also updates from message paths.
|
||||
- **Prevention:** Implement response delays as scheduled timestamps in a small FSM checked from `MyMesh::loop()`. Never use `delay()` or busy waits for bot coordination. Do not render long bot logs on display; only signal a compact bot event/counter. Respect `_serial->isWriteBusy()` before pushing optional stats. Emergency forwarding can preempt normal bot work but must still be non-blocking.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Main-loop integration and UI/BLE/serial testing.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/main.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-13: Breaking BLE/serial companion protocol compatibility with bot notifications
|
||||
|
||||
- **What goes wrong:** Existing apps and `meshcore-bot` clients misparse frames, lose messages, or deadlock because firmware emits new bot frames while a command response is expected, changes existing response codes, or overflows BLE MTU assumptions.
|
||||
- **Root cause:** Companion protocol clients are instructed to send one command at a time, wait for a response, and also handle asynchronous notifications. Firmware version/capability fields vary by app target version. Current `MyMesh` has fixed command/response/push code ranges and a local `FIRMWARE_VER_CODE=8` in the read checkout.
|
||||
- **Prevention:** Do not change existing frame semantics for stock commands. In firmware-only bot mode, prefer no new host-visible frames in Phase 1 except stats/capability behind explicit query. If new frames are required, park them in a new negotiated range, gate by firmware version/capability, keep every frame <= `MAX_FRAME_SIZE`, and ensure old clients safely ignore or never see them.
|
||||
- **Severity:** MODERATE
|
||||
- **Phase relevance:** Firmware API and compatibility testing.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + official docs — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-14: Misusing SNR/RSSI/path data to choose the “closest” bot
|
||||
|
||||
- **What goes wrong:** A bot with a strong last-hop signal wins even though it is not closest to the original sender, or a bot suppresses itself because a stale contact path suggests another node is better. Mobile or asymmetric links make the scoring erratic.
|
||||
- **Root cause:** MeshCore path fields differ by route type. Flood path is historical route; direct path is routing instructions; TRACE path fields can contain SNR data. RSSI/SNR from the radio is for the last received hop, not end-to-end quality. Python bot code already carries complex RF correlation fallbacks because host-side events can be difficult to match.
|
||||
- **Prevention:** Use SNR/RSSI only as a weak tie-breaker for zero-hop/direct observations. Primary eligibility should be command support, channel policy, known bot identity, recent directness/path length, queue health, and deterministic bot-id jitter. Keep topology tier as a small bias, not a winner-takes-all delay. Include test captures for multi-hop and TRACE packets.
|
||||
- **Severity:** MODERATE
|
||||
- **Phase relevance:** Scoring model and field validation.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-15: Persisting high-churn bot state and wearing or filling filesystem storage
|
||||
|
||||
- **What goes wrong:** Duplicate windows, recent-response caches, bot logs, command history, or emergency history are written to flash repeatedly. Filesystem wear, corruption, or storage-full behavior then breaks identity, contacts, channels, and advert blobs.
|
||||
- **Root cause:** Companion `DataStore` already persists identity, prefs, contacts, channels, and advert blobs using SPIFFS/LittleFS/InternalFS/ExtraFS. nRF52 targets preallocate advert blob records and may migrate contacts/channels to secondary FS. `CMD_GET_BATT_AND_STORAGE` reports storage but bot runtime state does not need persistence.
|
||||
- **Prevention:** Persist only bot configuration: enabled flag, tier, channel policy hashes, known bot identity prefixes, cooldown settings, and emergency-forward enabled. Keep all request fingerprints, suppression windows, emergency recent cache, and counters in RAM with bounded TTL. If counters must survive reboot, save coarse totals lazily and rarely, never per message.
|
||||
- **Severity:** MODERATE
|
||||
- **Phase relevance:** Persistence design and DataStore changes.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/NodePrefs.h`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-16: NodePrefs binary-layout migration breaks existing companion settings
|
||||
|
||||
- **What goes wrong:** Adding bot fields to `NodePrefs` changes struct layout expectations, causing old `/new_prefs` files to load wrong values for radio, BLE PIN, location policy, or buzzer settings. A field-unit mismatch could put bots on the wrong LoRa params or expose a stale BLE PIN.
|
||||
- **Root cause:** `DataStore::loadPrefsInt()` and `savePrefs()` manually read/write specific offsets into `/new_prefs`; the persisted file is not a self-describing schema. App/device compatibility also depends on existing command frames for tuning and other params.
|
||||
- **Prevention:** Do not insert fields into the middle of `NodePrefs` or change existing offsets. Add a separate bot prefs file with magic/version/length/CRC, or append only with explicit backwards-compatible load defaults. On first boot after update, validate radio params, channel policy, and bot enable flag; default bot disabled if config is invalid.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Configuration persistence implementation.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/NodePrefs.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-17: All-companion portability breaks through target-specific assumptions
|
||||
|
||||
- **What goes wrong:** Firmware bot code compiles for Heltec v3 but fails on nRF52, RP2040, STM32, USB-only, BLE, WiFi, or no-display companion variants. A command uses ESP32-only APIs, assumes SPIFFS paths, assumes BLE exists, or relies on display/UI classes not present on every target.
|
||||
- **Root cause:** MeshCore supports many PlatformIO environments with variant overlays and different source filters. Companion `main.cpp` selects filesystem and serial interfaces by platform macros. Heltec v3 has USB/BLE/WiFi variants; RAK4631 has USB/BLE, display, sensors, ExtraFS, and nRF52 SoftDevice constraints.
|
||||
- **Prevention:** Keep bot logic in platform-neutral companion C++ with no direct ESP32/nRF calls. Put target-specific storage, display, BLE, and WiFi behavior behind existing `MyMesh`, `DataStore`, and serial-interface abstractions. CI must build at least Heltec v3 USB/BLE and RAK4631 USB/BLE before any feature is considered portable; later expand to all companion suffixes.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Build system and portability implementation.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/main.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/heltec_v3/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-pitfalls-18: Private key export/import remains enabled in bot firmware builds
|
||||
|
||||
- **What goes wrong:** A deployed bot node exposes identity private-key export/import over the companion protocol, increasing damage from a compromised host, BLE session, or physical access. Known-bot trust and suppression can then be subverted by cloned identities.
|
||||
- **Root cause:** The upstream base build flags in the local checkout define `ENABLE_PRIVATE_KEY_IMPORT=1` and `ENABLE_PRIVATE_KEY_EXPORT=1` with a comment warning to disable them for more secure firmware. Bot identity becomes more valuable once other nodes trust known bot public keys for suppression and emergency behavior.
|
||||
- **Prevention:** Disable private key export/import in production bot firmware unless a deliberate provisioning workflow requires it. If identity migration is needed, provide a separate provisioning build or physical-button-gated window. Known-bot identity lists should assume keys are long-lived and protected.
|
||||
- **Severity:** CRITICAL
|
||||
- **Phase relevance:** Release build flags and provisioning.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
## Confidence Summary
|
||||
|
||||
| Item ID | Level | Source Type | URL/Reference |
|
||||
|---------|-------|-------------|---------------|
|
||||
| ITEM-pitfalls-1 | HIGH | Local code + official hardware docs + release assets | `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/boards/nrf52840_s140_v6_extrafs.ld`; https://heltec.org/project/wifi-lora-32-v3/ ; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; https://github.com/meshcore-dev/MeshCore/releases/expanded_assets/companion-v1.15.0 |
|
||||
| ITEM-pitfalls-2 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/meshcore-bot/requirements.txt`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands/*` |
|
||||
| ITEM-pitfalls-3 | HIGH | Local code + WebSearch | `/Users/cjvana/Documents/GitHub/MeshCore/README.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp`; https://cpp4arduino.com/2018/11/06/what-is-heap-fragmentation.html |
|
||||
| ITEM-pitfalls-4 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/StaticPoolPacketManager.cpp` |
|
||||
| ITEM-pitfalls-5 | HIGH | Local code + official docs | `/Users/cjvana/Documents/GitHub/MeshCore/src/MeshCore.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-pitfalls-6 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/security_utils.py` |
|
||||
| ITEM-pitfalls-7 | HIGH | Local code + official docs | `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-pitfalls-8 | HIGH | Project requirement + local code | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp` |
|
||||
| ITEM-pitfalls-9 | HIGH | Local code + ecosystem docs/search | `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h`; https://meshtastic.org/docs/overview/mesh-algo/ ; https://scholarworks.gnu.ac.kr/item/ccb29254-804b-413d-adc5-853fe697241b |
|
||||
| ITEM-pitfalls-10 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/SimpleMeshTables.h` |
|
||||
| ITEM-pitfalls-11 | HIGH | Local code/docs | `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp` |
|
||||
| ITEM-pitfalls-12 | HIGH | Local code + official docs | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/main.cpp`; https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-pitfalls-13 | HIGH | Local code + official docs | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`; https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-pitfalls-14 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/src/Mesh.h`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py` |
|
||||
| ITEM-pitfalls-15 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/NodePrefs.h` |
|
||||
| ITEM-pitfalls-16 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/NodePrefs.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp` |
|
||||
| ITEM-pitfalls-17 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/main.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/heltec_v3/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini` |
|
||||
| ITEM-pitfalls-18 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp` |
|
||||
173
.forge/research/prior-art.md
Normal file
173
.forge/research/prior-art.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Prior Art Research: Firmware-Resident MeshCore Bot
|
||||
|
||||
Checked: 2026-05-14
|
||||
|
||||
Scope: firmware-resident bots and embedded command responders in LoRa/mesh systems; existing MeshCore companion firmware, Python MeshCore bot, Colorado Mesh community bot/VPS coordinator patterns, and storage/flash/RAM feasibility for Heltec v3 and RAK4631 representative builds. This replaces stale coordinator-first conclusions: the recommended direction is a firmware-only bot with host-side projects used only as behavioral references.
|
||||
|
||||
### ITEM-prior-art-1: Upstream MeshCore companion firmware is the correct patch base
|
||||
|
||||
- **URL:** https://github.com/meshcore-dev/MeshCore
|
||||
- **What it does well:** MeshCore provides the C++ mesh library and firmware examples for Companion, Repeater, Room Server, Sensor, and related roles. The local `examples/companion_radio` implementation already receives DMs and channel messages, queues offline messages, sends contact/channel text, exposes battery/storage reporting, stores contacts/channels/prefs, and has hooks (`onMessageRecv`, `onChannelMessageRecv`) exactly where a firmware bot can process commands before/alongside the serial companion interface. The latest fetched release was Companion Firmware v1.15.0, published 2026-04-19.
|
||||
- **What it lacks:** No upstream firmware-resident command bot or multi-bot response election layer is visible. Companion firmware is still designed around an external app/host, so bot logic must be added without breaking phone/serial clients or changing the node role into a repeater.
|
||||
- **What we can learn:** Use upstream MeshCore as a submodule and add Colorado Mesh firmware-bot code as a narrow companion-radio extension. Put command parsing in the receive hooks and response sending through existing MeshCore `sendMessage` / `sendGroupMessage` paths rather than inventing a parallel radio stack.
|
||||
- **License:** MIT
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch + local code — https://github.com/meshcore-dev/MeshCore ; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-2: MeshCore companion protocol supports the needed behavior but imposes short-message constraints
|
||||
|
||||
- **URL:** https://docs.meshcore.io/companion_protocol/
|
||||
- **What it does well:** The companion protocol documents channel text send (`0x03`), channel binary datagrams (`0x3E`), queued-message polling (`0x0A`), async message-waiting notices, and battery/storage reporting (`0x14` response includes battery millivolts plus used/total storage KB). It states a text-message limit of 133 characters and a datagram payload limit of 163 bytes.
|
||||
- **What it lacks:** It exposes used/total storage but no general-purpose arbitrary config/storage API for a bot. It is an app-to-firmware protocol, not an internal bot API, and BLE framing/MTU constraints make verbose responses inappropriate.
|
||||
- **What we can learn:** Firmware bot responses must be terse and deterministic. Treat 133 characters as the safe response budget. Avoid large JSON/config payloads over chat. For bot configuration, prefer compile-time defaults plus a small firmware-side config structure saved with `DataStore`, not a full Python-style config file.
|
||||
- **License:** Documentation / N/A
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch — https://docs.meshcore.io/companion_protocol/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-3: agessaman meshcore-bot is the behavior oracle, not the runtime model
|
||||
|
||||
- **URL:** https://github.com/agessaman/meshcore-bot
|
||||
- **What it does well:** The Python bot supports serial/BLE/TCP companion connections, configurable keywords, plugin commands, rate limiting, user bans, scheduled messages, DMs, logging, Discord/webhook integrations, weather/AQI/solar/sports/satellite feeds, stats, path diagnostics, repeater management, and web viewer features. Local command modules show a rich command set and a practical channel policy surface (`monitor_channels`, `respond_to_dms`).
|
||||
- **What it lacks:** It assumes Python, asyncio, SQLite, HTTP/TLS clients, API keys, geocoding, dynamic plugins, and often internet reachability. Those features are unrealistic in first-pass firmware, especially on RAK4631-class nRF52840 targets.
|
||||
- **What we can learn:** Port semantics in tiers. Firmware MVP should include: `ping`, `help/cmd`, `test`, `hello`, `dice`, `roll`, short `path/status` diagnostics from in-memory packet metadata, DM response support, #bot/#testing channel handling, #emergency-to-Public forwarding, per-command cooldown/rate limiting, passive listen-before-answer suppression, and known-bot trust. Defer: weather, AQI, sports, jokes via HTTP, satellite passes, solar forecast, Discord/web viewer, SQLite stats, dynamic plugins, repeater-management workflows, and feed parsing.
|
||||
- **License:** MIT
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local repo — `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md`, `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands/*`, `/Users/cjvana/Documents/GitHub/meshcore-bot/requirements.txt`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-4: Colorado Mesh community bot/VPS coordinator proves the desired coordination semantics
|
||||
|
||||
- **URL:** N/A
|
||||
- **What it does well:** The local and read-only remote community bot patches host-side `send_response` / `send_channel_message` so DMs bypass coordination, channel responses are coordinated exactly once, and coordinator outages fall back to score-based delay. The community scoring model uses hop score, infrastructure/fan-in proxy, exact path familiarity, and freshness. The LAN host runtime config currently uses `monitor_channels = #bot,#emergency` and `emergency_broadcast_channel = Public`, matching the pivoted channel policy.
|
||||
- **What it lacks:** The VPS coordinator requires IP reachability, registration/auth, PostgreSQL, Python containers, and host-side companion connectivity. It is the opposite of the off-grid firmware-only goal.
|
||||
- **What we can learn:** Preserve the observable behavior, not the deployment. Firmware should key pending responses by a stable original-message hash, compute a compact local delivery score, schedule a delay, listen for a known bot's response to the same request, and cancel if another trusted bot answers first. DMs should remain immediate because only the addressed bot received them.
|
||||
- **License:** Private / N/A
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local repo + read-only SSH — `/Users/cjvana/Documents/GitHub/meshcore-community-bot/docs/COMMUNITY_DESIGN.md`, `/Users/cjvana/Documents/GitHub/meshcore-community-bot/community/message_interceptor.py`, `cj-vps:~/meshcore-community-bot`, `cjvana@10.0.0.222:~/meshcore-community-bot/config.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-5: Meshtastic firmware modules prove small embedded bot-like features are the norm
|
||||
|
||||
- **URL:** https://meshtastic.org/docs/configuration/module/canned-message/
|
||||
- **What it does well:** Meshtastic ships firmware modules that generate mesh messages from device-side logic. Canned Message sends predefined messages without a phone and limits the combined preset list to 200 bytes. Detection Sensor monitors one GPIO and sends rate-limited mesh alerts. Remote Hardware performs addressed GPIO read/write/watch operations through firmware-controlled request/response behavior.
|
||||
- **What it lacks:** These are narrow firmware modules, not a general text command bot. Remote Hardware is GPIO-specific and newer firmware may require custom builds; Canned Message needs an input peripheral; Detection Sensor is a one-pin alert module.
|
||||
- **What we can learn:** The successful embedded pattern is deliberately small: fixed config, bounded text, simple state machines, rate limits, and compile-time/module flags. A MeshCore firmware bot should follow that model instead of embedding a general plugin runtime.
|
||||
- **License:** Project/documentation / N/A
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch — https://meshtastic.org/docs/configuration/module/canned-message/ ; https://meshtastic.org/docs/configuration/module/detection-sensor/ ; https://meshtastic.org/docs/configuration/module/remote-hardware/
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-6: disaster.radio shows firmware-resident slash commands are practical on LoRa mesh devices
|
||||
|
||||
- **URL:** https://github.com/sudomesh/disaster-radio/blob/master/firmware/src/middleware/Console.cpp
|
||||
- **What it does well:** The firmware console parses line-based slash commands such as `/help`, `/join`, `/nick`, `/raw`, `/lora`, `/get`, `/set`, and `/restart`. Commands update settings, broadcast info datagrams, restart the device, and use simple tokenization and bounded buffers.
|
||||
- **What it lacks:** It is not MeshCore, is not a multi-bot responder election system, and has no direct compatibility with MeshCore companion channels or DMs.
|
||||
- **What we can learn:** Keep the firmware command parser C-style and bounded: copy/terminate the input line, tokenize first word, dispatch to a fixed command table, validate all lengths, and use short replies. This is the right implementation style for `ping/help/test/dice/roll/status` in companion firmware.
|
||||
- **License:** Repository license not verified here / N/A
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch — https://github.com/sudomesh/disaster-radio/blob/master/firmware/src/middleware/Console.cpp
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-7: LoRa_APRS_iGate proves embedded automation can be broad, but its heavy features are not MVP guidance
|
||||
|
||||
- **URL:** https://github.com/richonguzman/LoRa_APRS_iGate
|
||||
- **What it does well:** This ESP32/LoRa APRS firmware performs iGate/digipeater automation, beacon scheduling, failover, packet filtering/blacklisting, telemetry, APRS queries, web configuration, OTA, MQTT, and sensor integrations. It targets many LoRa boards, including Heltec variants and RAK4631-class hardware.
|
||||
- **What it lacks:** It is APRS-oriented, not MeshCore. Many features depend on WiFi/APRS-IS/MQTT/web subsystems and its GPL-3.0 license is not suitable for direct code reuse in an MIT MeshCore patch without a deliberate license strategy.
|
||||
- **What we can learn:** Embedded LoRa firmware can do meaningful autonomous automation, but the MVP should not copy heavyweight web, MQTT, and internet-service layers. Copy the bounded scheduler/rate-limit/filtering concepts only.
|
||||
- **License:** GPL-3.0
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch — https://github.com/richonguzman/LoRa_APRS_iGate
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-8: Cyclenerd meshcore-bot shows the safe minimal host-bot baseline
|
||||
|
||||
- **URL:** https://github.com/Cyclenerd/meshcore-bot
|
||||
- **What it does well:** A small Node.js MeshCore bot over USB serial that responds only in private channels to avoid public-channel spam. It implements simple `.ping` and `.date` commands and can query/log repeater status.
|
||||
- **What it lacks:** It is host-side, private-channel-only, and does not solve coordinated channel replies. It has no firmware component.
|
||||
- **What we can learn:** If channel coordination is not ready, the safe fallback is DM-only operation plus explicit #bot/#testing opt-in. The firmware MVP can still be useful if it starts with DMs and restricted channels rather than Public.
|
||||
- **License:** Apache-2.0
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** WebFetch — https://github.com/Cyclenerd/meshcore-bot
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-9: MESH-API/MESH-AI host bots are useful behavior references but too heavy for firmware
|
||||
|
||||
- **URL:** https://github.com/mr-tbot/mesh-api
|
||||
- **What it does well:** These host-side tools bridge Meshtastic/MeshCore, MQTT, Discord, AI/API providers, custom commands, emergency commands, origin tags, duplicate buffers, and randomized command aliases. They explicitly account for loop prevention and multi-system routing.
|
||||
- **What it lacks:** They require a host runtime, network services, API integrations, and GPL-3.0 licensing. They are not firmware-resident bots or MeshCore companion patches.
|
||||
- **What we can learn:** Adopt origin markers and duplicate buffers conceptually. Do not port AI/API routes into firmware. A useful firmware response format could include a compact bot marker and original-message hash suffix when needed for suppression/debugging.
|
||||
- **License:** GPL-3.0 for mesh-api as fetched
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** WebSearch/WebFetch — https://github.com/mr-tbot/mesh-api ; https://github.com/mr-tbot/mesh-ai
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-10: Existing MeshCore storage model has enough persistent space for small bot config, not host-bot databases
|
||||
|
||||
- **URL:** https://github.com/meshcore-dev/MeshCore
|
||||
- **What it does well:** `DataStore` already persists identity, node prefs, contacts, group channels, and advert blobs; it reports used/total storage in KB. On nRF52 with `EXTRAFS`, companion firmware creates `CustomLFS ExtraFS(0xD4000, 0x19000, 128)`, i.e. about 100 KB of secondary LittleFS-style storage. Contacts are fixed records of about 153 bytes each, channels about 68 bytes each, and advert blobs are fixed bounded records. ESP32 uses SPIFFS and reports `SPIFFS.usedBytes()` / `SPIFFS.totalBytes()`.
|
||||
- **What it lacks:** There is no existing firmware database analogous to Python SQLite stats, no large config store, and storage competes with contacts/channels/adverts. RAK4631-class nRF52 storage is especially finite.
|
||||
- **What we can learn:** Store only tiny bot state persistently: enable flag, allowed channel names/indices, a few known-bot public-key prefixes, cooldown settings, and maybe a dozen canned response strings. Keep history/suppression volatile. Do not port Python stats, feed caches, or repeater analytics DB into firmware.
|
||||
- **License:** MIT
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp`, `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp`, `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-11: Representative device space supports a firmware bot MVP, with RAK4631 as the limiting target
|
||||
|
||||
- **URL:** https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/
|
||||
- **What it does well:** Heltec WiFi LoRa 32 V3 uses an ESP32-S3N8 with 8 MB integrated flash and SX1262 radio. RAK4631 uses Nordic nRF52840 with 1 MB flash, 256 KB RAM, 64 MHz Cortex-M4, and SX1262. MeshCore `platformio.ini` caps RAK4631 companion uploads at 712,704 bytes. Release asset sizes for MeshCore Companion v1.15.0 were: Heltec v3 USB non-merged 629,424 bytes, Heltec v3 BLE non-merged 1,262,752 bytes, RAK4631 USB UF2 955,392 bytes / ZIP 478,359 bytes, and RAK4631 BLE UF2 971,264 bytes / ZIP 486,343 bytes. UF2/ZIP sizes are packaging sizes, not exact linked ELF flash usage, so exact headroom requires a local build.
|
||||
- **What it lacks:** I did not run fresh PlatformIO builds or measure patched `.elf` sections, so exact free flash/RAM after adding bot code is still missing. RAK4631 headroom cannot be safely inferred from compressed ZIP alone.
|
||||
- **What we can learn:** Design to the RAK4631 cap first. A realistic firmware-bot MVP should target roughly 15-40 KB additional flash, 2-8 KB additional RAM, and under 2 KB persistent config. That budget fits simple parsers, fixed command tables, response strings, pending-response suppression, known-bot prefixes, and channel policy. It does not fit TLS/HTTP clients, geocoders, ephemeris libraries, SQLite-style history, web UIs, or dynamic plugins. Heltec v3 has much more flash margin; RAK4631 should be the release gate.
|
||||
- **License:** Hardware docs / N/A
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** WebFetch + gh release + local code — https://docs.heltec.org/en/node/esp32/wifi_lora_32/index.html ; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ ; https://www.nordicsemi.com/products/nrf52840 ; `gh release view companion-v1.15.0 --repo meshcore-dev/MeshCore`; `/Users/cjvana/Documents/GitHub/MeshCore/variants/rak4631/platformio.ini`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
### ITEM-prior-art-12: Public-channel emergency handling should be explicit, not an accidental bridge of all bot traffic
|
||||
|
||||
- **URL:** N/A
|
||||
- **What it does well:** The existing local/remote Python/community bot configs show normal monitored channels as #bot/#emergency and an emergency broadcast target of Public. The pivoted project policy refines this: normal bot traffic belongs in private DMs, #bot, and #testing; #emergency should be routed/announced to Public with `EMERGENCY MESSAGE FROM <user>` plus the original text.
|
||||
- **What it lacks:** Current host-side code also contains Discord webhook forwarding and richer emergency formatting. Firmware cannot assume internet, Discord, or webhook reachability.
|
||||
- **What we can learn:** Implement #emergency as a special firmware path: do not answer with normal bot help/noise; forward a concise Public alert using the configured node/user identity and original text, then rate-limit to avoid loops. Treat Public as emergency output only for MVP.
|
||||
- **License:** Private config / N/A
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local + read-only SSH + project brief — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`, `cjvana@10.0.0.222:~/meshcore-community-bot/config.ini`, `/Users/cjvana/Documents/GitHub/meshcore-community-bot/community/message_interceptor.py`
|
||||
- **Checked:** 2026-05-14
|
||||
|
||||
## Firmware Feature Portability Assessment
|
||||
|
||||
| Python/community bot feature | Firmware MVP status | Reason |
|
||||
|---|---:|---|
|
||||
| DM command handling | First | Already present receive path; no multi-bot election needed. |
|
||||
| #bot/#testing command handling | First | Small channel gate plus passive suppression. |
|
||||
| #emergency to Public forwarding | First | Project requirement; simple string transform and strict rate limit. |
|
||||
| `ping`, `help`, `cmd`, `test`, `hello` | First | Fixed strings and small parser. |
|
||||
| `dice`, `roll` | First | Tiny random-number commands using existing RNG. |
|
||||
| `path` / basic heard status | First/second | Use packet path/SNR metadata and existing advert path table; keep output short. |
|
||||
| Passive listen-before-answer suppression | First | Core reason for firmware bot; bounded pending table. |
|
||||
| Known bot identity trust | First | Small prefix list or full-key list; avoids suppressing on spoofed user text. |
|
||||
| `stats` | Later, tiny version only | Volatile counters are feasible; SQLite-style history is not. |
|
||||
| `repeater` management | Later | Contact-store operations are delicate and UI-heavy. |
|
||||
| Weather/AQI/sports/jokes/dadjoke/feeds | Too heavy for firmware MVP | HTTP/TLS, parsing, API keys, caching, and internet dependency. |
|
||||
| Satellite/solar forecast/geocoding | Too heavy for firmware MVP | Large math/data/API dependencies. |
|
||||
| Discord/web viewer/MQTT/analytics | Too heavy/off-device | Requires IP services and host/web stack. |
|
||||
| Dynamic plugins/i18n translation files | Too heavy | Firmware should use compile-time command table and fixed strings. |
|
||||
|
||||
## Confidence Summary
|
||||
|
||||
| Item ID | Level | Source Type | URL/Reference |
|
||||
|---------|-------|-------------|---------------|
|
||||
| ITEM-prior-art-1 | HIGH | WebFetch + local code | https://github.com/meshcore-dev/MeshCore ; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.cpp` |
|
||||
| ITEM-prior-art-2 | HIGH | WebFetch | https://docs.meshcore.io/companion_protocol/ |
|
||||
| ITEM-prior-art-3 | HIGH | Local repo | `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md`, command modules, requirements |
|
||||
| ITEM-prior-art-4 | HIGH | Local repo + read-only SSH | community bot design/interceptor and runtime configs |
|
||||
| ITEM-prior-art-5 | HIGH | WebFetch | https://meshtastic.org/docs/configuration/module/canned-message/ ; https://meshtastic.org/docs/configuration/module/detection-sensor/ ; https://meshtastic.org/docs/configuration/module/remote-hardware/ |
|
||||
| ITEM-prior-art-6 | HIGH | WebFetch | https://github.com/sudomesh/disaster-radio/blob/master/firmware/src/middleware/Console.cpp |
|
||||
| ITEM-prior-art-7 | HIGH | WebFetch | https://github.com/richonguzman/LoRa_APRS_iGate |
|
||||
| ITEM-prior-art-8 | HIGH | WebFetch | https://github.com/Cyclenerd/meshcore-bot |
|
||||
| ITEM-prior-art-9 | MEDIUM | WebSearch/WebFetch | https://github.com/mr-tbot/mesh-api ; https://github.com/mr-tbot/mesh-ai |
|
||||
| ITEM-prior-art-10 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/DataStore.cpp` |
|
||||
| ITEM-prior-art-11 | MEDIUM | WebFetch + gh release + local code | Heltec docs, RAK docs, Nordic docs, MeshCore v1.15.0 release assets, RAK4631 platformio cap |
|
||||
| ITEM-prior-art-12 | HIGH | Local config + project brief | `.forge/PROJECT.md`, LAN community-bot config, community message interceptor |
|
||||
148
.forge/research/stack.md
Normal file
148
.forge/research/stack.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Stack Research: Firmware-only MeshCore Bot
|
||||
|
||||
Checked: 2026-05-14
|
||||
|
||||
### ITEM-stack-1: Build in upstream MeshCore's Arduino C++/PlatformIO firmware stack
|
||||
|
||||
- **Recommendation:** Build the firmware-only bot in C++ inside the MeshCore companion firmware stack, using Arduino framework and PlatformIO exactly as upstream does. Keep `meshcore-bot` as behavioral reference only.
|
||||
- **Rationale:** Current MeshCore describes itself as a compact C++ embedded LoRa/packet-radio library, and current upstream `platformio.ini` uses `framework = arduino` with architecture bases for ESP32, nRF52, RP2040, and STM32. The Python `meshcore-bot` connects over serial/BLE/TCP and implements a plugin bot outside the radio; it is useful for command semantics, but not for reducing firmware advert/response behavior on-device.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Official docs + local code — https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/README.md; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not introduce ESP-IDF-only, Zephyr, Rust, embedded Python/JS, Docker, or a VPS-side coordinator as the product stack; they either break MeshCore's cross-device firmware model or preserve the external-bot architecture being replaced.
|
||||
|
||||
### ITEM-stack-2: Keep MeshCore as an upstream submodule plus Colorado overlay/patch queue
|
||||
|
||||
- **Recommendation:** Structure the Colorado Mesh repo as a wrapper with `upstream/MeshCore` as a pinned git submodule, a `colorado/` overlay for new source files/config snippets/tests, and a deterministic `patches/` or `git-format-patch` queue applied to the submodule during build. Do not keep local edits directly inside the submodule as the only source of truth.
|
||||
- **Rationale:** The user has selected submodule/patch-base strategy. MeshCore's PlatformIO project expects builds from the MeshCore repo root (`extra_configs = variants/*/platformio.ini`, relative `build_src_filter`, `build.sh`, and variant paths), so the wrapper should stage/apply patches into a worktree and invoke upstream `build.sh` from there. This preserves an auditable upstream pin while still allowing changes across `examples/companion_radio`, `src/helpers`, `docs`, and selected variant `.ini` files.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + project decision — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/build.sh`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not vendor-copy MeshCore into the Colorado repo; upstream is moving. Do not rely on uncommitted submodule edits; they make rebuilds and upstream syncs non-reproducible. Do not place a separate PlatformIO project above MeshCore unless it merely orchestrates the upstream-root build.
|
||||
|
||||
### ITEM-stack-3: Use current MeshCore main as the baseline, then pin it
|
||||
|
||||
- **Recommendation:** Initialize the submodule from current `meshcore-dev/MeshCore` main, record the exact commit SHA in the Colorado repo, and refresh intentionally. Validate against the local checkout only as read-only reconnaissance.
|
||||
- **Rationale:** The local MeshCore checkout is usable for inspection, but current upstream has evolved: current upstream `platformio.ini` has LoRa defaults `LORA_FREQ=869.618`, `LORA_BW=62.5`, `LORA_SF=8`, adds `-D ESP32_PLATFORM`, and nRF52 extra scripts include `create-uf2.py` and `patch_bluefruit.py`, while the local checkout differs. Starting from current upstream reduces protocol/build drift.
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** Official source + local git — https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; `/Users/cjvana/Documents/GitHub/MeshCore` commit `6b52fb32`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not base implementation solely on the stale local tree; it may miss current companion protocol and build changes. Do not float the submodule without a pinned commit; firmware builds need reproducibility.
|
||||
|
||||
### ITEM-stack-4: Preserve upstream dependency/platform pins for the first bot build
|
||||
|
||||
- **Recommendation:** Keep upstream platform and library pins initially: `platformio/espressif32@6.11.0` for ESP32 Arduino builds, `nordicnrf52` with `framework-arduinoadafruitnrf52 @ 1.10700.0` for nRF52, RadioLib `^7.3.0`, Crypto `^0.4.0`, RTClib `^2.1.3`, Melopero RV3028 `^1.1.0`, CayenneLPP `1.6.1`, and `densaugeo/base64 @ ~1.4.0` in companion targets.
|
||||
- **Rationale:** The first risk is firmware behavior, not dependency modernization. `platformio/espressif32@6.11.0` with Arduino uses Arduino-ESP32 2.0.17, and RadioLib 7.3.0 is a 2025 release although newer RadioLib releases exist. Upgrading radio/platform dependencies while adding bot logic would confound failures across ESP32 and nRF52.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Official docs/search + upstream source — https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; https://github.com/platformio/platform-espressif32/blob/v6.11.0/platform.json; https://github.com/jgromes/RadioLib/releases/tag/7.3.0; https://docs.platformio.org/en/latest/librarymanager/dependencies.html
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not upgrade to Arduino-ESP32 3.x, latest RadioLib, or custom nRF framework packages until baseline bot patches build and run; dependency churn should be a separate phase.
|
||||
|
||||
### ITEM-stack-5: Add bot logic as small fixed-storage companion helpers
|
||||
|
||||
- **Recommendation:** Implement the bot as small C++ helper classes called by `examples/companion_radio/MyMesh`, with fixed-size arrays/ring buffers and compile-time feature flags such as `CMESH_BOT_ENABLED`, `CMESH_BOT_MAX_PENDING`, and `CMESH_BOT_MAX_KNOWN_BOTS`.
|
||||
- **Rationale:** Companion `MyMesh` already owns the required hooks: private DM receive (`onMessageRecv`), group channel receive (`onChannelMessageRecv`), control/raw receive, channel lookup, contact lookup, and group/direct send (`sendGroupMessage`, `sendMessage`). Upstream contribution guidance says to keep embedded code concise and avoid dynamic allocation except during setup/begin. A helper object avoids a large framework while keeping Colorado logic reviewable.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code + upstream README — `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/README.md
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not port the Python plugin loader, SQLite-backed stats, API clients, or dynamic command registry to firmware. Do not create a general embedded scripting runtime.
|
||||
|
||||
### ITEM-stack-6: Handle only firmware-feasible bot behavior in phase 1
|
||||
|
||||
- **Recommendation:** Phase 1 should implement deterministic local commands and routing policy: `ping`, `test`, compact `help`, `cmd`, simple `dice/roll`, path/channel echo where already available, passive suppression/listen-before-answer, #bot/#testing routing, private DM replies, and #emergency forwarding to Public. Defer weather, AQI, satellite, sports, jokes from web APIs, repeater database management, and web viewer features.
|
||||
- **Rationale:** `meshcore-bot` includes many network/API/database features that assume Python, internet, filesystem logs, and async plugins. Firmware bot value is local decentralized operation and duplicate suppression. Keeping responses short also fits MeshCore `MAX_TEXT_LEN = 10*CIPHER_BLOCK_SIZE = 160` bytes and group messages include a sender-name prefix.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code/docs — `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not attempt feature parity with host-side `meshcore-bot`; API-backed commands are not off-grid and would expand flash/RAM/storage unnecessarily.
|
||||
|
||||
### ITEM-stack-7: Use MeshCore group/private message APIs for bot traffic, not new transport timing knobs
|
||||
|
||||
- **Recommendation:** Send normal bot replies through existing `sendMessage`/`sendGroupMessage` paths and implement bot-specific delay/suppression above the radio layer. Use configured channel names/hash lookup to restrict normal traffic to private DMs, #bot, and #testing, and special-case #emergency to publish an `EMERGENCY MESSAGE FROM <user>` announcement to Public.
|
||||
- **Rationale:** MeshCore already parses private text and group text into `onMessageRecv`/`onChannelMessageRecv`, and packet docs define group text as encrypted channel payload with `<sender name>: <message body>`. The project decision explicitly says not to repurpose lower-layer MeshCore `txdelay`, `direct.txdelay`, or `rxdelay` as bot election controls.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code/docs + project decision — `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not route normal bot chatter on Public. Do not use lower-layer TX/RX timing knobs as the primary coordinator; it risks affecting mesh behavior beyond bot replies.
|
||||
|
||||
### ITEM-stack-8: Build representative firmware with upstream build.sh before expanding
|
||||
|
||||
- **Recommendation:** Use upstream `build.sh` from inside the patched MeshCore worktree. During development, build only `Heltec_v3_companion_radio_usb`, `Heltec_v3_companion_radio_ble`, `RAK_4631_companion_radio_usb`, and `RAK_4631_companion_radio_ble`; after those pass, run `build.sh build-companion-firmwares` in CI.
|
||||
- **Rationale:** Upstream `build.sh build-firmware <env>` injects firmware version/build date, runs `pio run -e`, creates merged ESP32 `.bin` images, creates nRF52 `.uf2`, and writes outputs to `out/`. The representative set covers ESP32-S3 and nRF52840 plus USB/BLE companion variants without paying the cost of every supported board during inner-loop development.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local CI/build scripts — `/Users/cjvana/Documents/GitHub/MeshCore/build.sh`; `/Users/cjvana/Documents/GitHub/MeshCore/.github/workflows/build-companion-firmwares.yml`; `/Users/cjvana/Documents/GitHub/MeshCore/.github/actions/setup-build-environment/action.yml`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not hand-maintain an independent board matrix first; upstream environment suffixes and artifact logic already encode release behavior. Do not wait for all companion boards before validating the two requested representatives.
|
||||
|
||||
### ITEM-stack-9: Heltec v3 companion target is comfortably feasible
|
||||
|
||||
- **Recommendation:** Treat Heltec v3 as the easiest first target. Use current upstream `Heltec_v3_companion_radio_usb` and `Heltec_v3_companion_radio_ble` envs unchanged except for the bot feature flag/overlay patch.
|
||||
- **Rationale:** Current upstream Heltec v3 extends `Heltec_lora32_v3`, uses board `esp32-s3-devkitc-1`, ESP32-S3 Arduino, SSD1306 UI, `MAX_CONTACTS=350`, `MAX_GROUP_CHANNELS=40`, and BLE adds `OFFLINE_QUEUE_SIZE=256`. PlatformIO's `esp32-s3-devkitc-1` board uses 8 MB flash with `default_8MB.csv`; app0/app1 are 0x330000 each (3,342,336 bytes), SPIFFS is 0x180000 (1,572,864 bytes). Heltec official specs confirm ESP32-S3FN8/ESP32-S3N8 with 8 MB flash and no PSRAM.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Official source/docs — https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/heltec_v3/platformio.ini; https://raw.githubusercontent.com/platformio/platform-espressif32/master/boards/esp32-s3-devkitc-1.json; https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/partitions/default_8MB.csv; https://docs.heltec.cn/en/node/esp32/wifi_lora_32/index.html; https://resource.heltec.cn/download/WiFi_LoRa_32_V3/HTIT-WB32LA_V3.2.pdf
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not optimize for Heltec v3 storage first; its app and filesystem margins are much larger than RAK4631's. Do not depend on PSRAM; Heltec v3 does not provide it.
|
||||
|
||||
### ITEM-stack-10: RAK4631 is feasible but is the storage/RAM gatekeeper
|
||||
|
||||
- **Recommendation:** Treat RAK4631 BLE companion as the gating target. Keep the firmware bot under roughly 20 KB additional flash and 2 KB additional static RAM until measured builds prove more margin.
|
||||
- **Rationale:** Current upstream RAK4631 companion USB/BLE extend `rak4631`, use `boards/nrf52840_s140_v6_extrafs.ld`, and explicitly set `board_upload.maximum_size = 712704`. The extra-FS linker gives an app FLASH region of 712,704 bytes and RAM region of 237,568 bytes. RAK's raw nRF52840 has 1 MB flash/256 KB RAM, but SoftDevice, bootloader/settings, and extra filesystem reserve much of that; the companion app region, not raw flash, is the practical limit.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Official source/docs — https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/rak4631/platformio.ini; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/rak4631.json; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/nrf52840_s140_v6_extrafs.ld; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/; https://www.nordicsemi.com/Products/nRF52840
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not assume the full 1 MB nRF52840 flash is available to the app. Do not store large response tables or logs in RAK4631 internal flash without measuring filesystem pressure.
|
||||
|
||||
### ITEM-stack-11: Firmware bot storage budget is small enough if responses stay static and ephemeral
|
||||
|
||||
- **Recommendation:** Budget the phase-1 firmware bot at approximately 10-25 KB flash, 0.5-2 KB RAM, and 0-4 KB persistent storage. Require CI to report binary size deltas for Heltec v3 and RAK4631 on every PR.
|
||||
- **Rationale:** A minimal command parser, static command table, short static response strings, a small known-bot/suppression table, and 4-8 pending response records fit easily: example estimate is 3-8 KB `.text`, 2-10 KB `.rodata` depending on help text, 512-1536 B `.bss` for suppression state, and <512 B stack per invocation if implemented without large local buffers. MeshCore already carries large state for companion mode: approximate struct sizing from source gives `ContactInfo` ~184 B, `ChannelDetails` ~56 B, offline frame ~173 B, and `Packet` ~260 B; existing `MAX_CONTACTS=350`, `MAX_GROUP_CHANNELS=40`, and BLE `OFFLINE_QUEUE_SIZE=256` dominate RAM more than the bot should. This estimate could not be verified by compiling because `pio` is not installed in the local environment.
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** Local source analysis — `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/ContactInfo.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/ChannelDetails.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`; local command result: `pio: command not found`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not embed full help prose, translation files, jokes, channel databases, repeater databases, web/API clients, or persistent usage stats in firmware phase 1; those could turn a <25 KB feature into a storage problem.
|
||||
|
||||
### ITEM-stack-12: Storage feasibility estimate by representative target
|
||||
|
||||
- **Recommendation:** Proceed with firmware-only bot if the first implementation stays within the conservative budget below; block or trim features if measured RAK4631 BLE app flash delta exceeds 25 KB or static RAM delta exceeds 2 KB.
|
||||
- **Rationale:** Heltec v3 has about 3.34 MB per OTA app slot and 1.57 MB SPIFFS under the default 8 MB partition table, so a 10-25 KB bot is negligible (<0.8% of one app slot). RAK4631 companion has a 712,704-byte app region and 237,568-byte RAM region; the same bot is ~1.4-3.5% of the app region and ~0.2-0.9% of RAM. Persistent config should be a few hundred bytes; even a 4 KB settings/log reserve is reasonable but should be optional. The unknown is current baseline binary size, not the expected bot delta, because no local PlatformIO build artifacts exist.
|
||||
- **Confidence:** MEDIUM
|
||||
- **Source:** Official docs + local analysis — https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/partitions/default_8MB.csv; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/nrf52840_s140_v6_extrafs.ld; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/rak4631/platformio.ini; `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not declare final binary margin until PlatformIO builds run; use this as feasibility guidance, then replace with measured `.text/.data/.bss` and firmware size deltas.
|
||||
|
||||
### ITEM-stack-13: Add a size-reporting CI step before feature growth
|
||||
|
||||
- **Recommendation:** Add a wrapper command such as `scripts/build-representative.sh` that applies patches, runs the four representative envs, captures PlatformIO RAM/flash usage output, records artifact byte sizes, and fails on configurable bot-delta thresholds once a baseline is established.
|
||||
- **Rationale:** The user's explicit question is storage feasibility. PlatformIO normally reports memory usage per environment, but local `pio` is unavailable and there are no existing `.pio` artifacts. CI size reports make the feasibility estimate concrete and keep future feature creep visible, especially on RAK4631 BLE.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local build scripts + official PlatformIO behavior — `/Users/cjvana/Documents/GitHub/MeshCore/build.sh`; https://docs.platformio.org/en/latest/core/userguide/cmd_run.html
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not rely on source-only estimates after code exists. Do not run only Heltec v3; RAK4631 is the constrained representative.
|
||||
|
||||
### ITEM-stack-14: Use host-side Python only for tests and behavioral fixtures
|
||||
|
||||
- **Recommendation:** Keep Python in the repo only for development tooling: generating patch worktrees, golden command-response fixtures, fake MeshCore frames, and CI parsing of size reports. Firmware behavior should not depend on Python at runtime.
|
||||
- **Rationale:** `meshcore-bot` is rich behavioral prior art: command keywords, rate limits, channel policy, and response wording. But its async plugin loader, HTTP API clients, SQLite/database managers, web viewer, and serial/BLE/TCP connection code cannot run inside companion firmware. Host-side tests can still prevent regressions when translating selected behavior to C++.
|
||||
- **Confidence:** HIGH
|
||||
- **Source:** Local code — `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/command_manager.py`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/message_handler.py`
|
||||
- **Checked:** 2026-05-14
|
||||
- **Alternatives rejected:** Do not make the firmware call out to a local Python service for normal bot responses; that recreates the coordinator dependency.
|
||||
|
||||
## Confidence Summary
|
||||
|
||||
| Item ID | Level | Source Type | URL/Reference |
|
||||
|---------|-------|-------------|---------------|
|
||||
| ITEM-stack-1 | HIGH | Official docs + Local code | https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/README.md; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md` |
|
||||
| ITEM-stack-2 | HIGH | Local code + Project decision | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/platformio.ini`; `/Users/cjvana/Documents/GitHub/MeshCore/build.sh` |
|
||||
| ITEM-stack-3 | MEDIUM | Official source + Local git | https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; `/Users/cjvana/Documents/GitHub/MeshCore` |
|
||||
| ITEM-stack-4 | HIGH | Official docs/search + Upstream source | https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/platformio.ini; https://github.com/platformio/platform-espressif32/blob/v6.11.0/platform.json; https://github.com/jgromes/RadioLib/releases/tag/7.3.0; https://docs.platformio.org/en/latest/librarymanager/dependencies.html |
|
||||
| ITEM-stack-5 | HIGH | Local code + Upstream README | `/Users/cjvana/Documents/GitHub/MeshCore/examples/companion_radio/MyMesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/README.md |
|
||||
| ITEM-stack-6 | HIGH | Local code/docs | `/Users/cjvana/Documents/GitHub/meshcore-bot/README.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.h`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md` |
|
||||
| ITEM-stack-7 | HIGH | Local code/docs + Project decision | `/Users/cjvana/Documents/GitHub/meshcore-bot-fw/.forge/PROJECT.md`; `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/BaseChatMesh.cpp`; `/Users/cjvana/Documents/GitHub/MeshCore/docs/payloads.md` |
|
||||
| ITEM-stack-8 | HIGH | Local CI/build scripts | `/Users/cjvana/Documents/GitHub/MeshCore/build.sh`; `/Users/cjvana/Documents/GitHub/MeshCore/.github/workflows/build-companion-firmwares.yml` |
|
||||
| ITEM-stack-9 | HIGH | Official source/docs | https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/heltec_v3/platformio.ini; https://raw.githubusercontent.com/platformio/platform-espressif32/master/boards/esp32-s3-devkitc-1.json; https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/partitions/default_8MB.csv; https://docs.heltec.cn/en/node/esp32/wifi_lora_32/index.html |
|
||||
| ITEM-stack-10 | HIGH | Official source/docs | https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/rak4631/platformio.ini; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/rak4631.json; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/nrf52840_s140_v6_extrafs.ld; https://docs.rakwireless.com/product-categories/wisblock/rak4631/overview/ |
|
||||
| ITEM-stack-11 | MEDIUM | Local source analysis | `/Users/cjvana/Documents/GitHub/MeshCore/src/helpers/ContactInfo.h`; `/Users/cjvana/Documents/GitHub/MeshCore/src/Packet.h`; local `pio` availability check |
|
||||
| ITEM-stack-12 | MEDIUM | Official docs + Local analysis | https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/partitions/default_8MB.csv; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/boards/nrf52840_s140_v6_extrafs.ld; https://raw.githubusercontent.com/meshcore-dev/MeshCore/main/variants/rak4631/platformio.ini |
|
||||
| ITEM-stack-13 | HIGH | Local build scripts + Official docs | `/Users/cjvana/Documents/GitHub/MeshCore/build.sh`; https://docs.platformio.org/en/latest/core/userguide/cmd_run.html |
|
||||
| ITEM-stack-14 | HIGH | Local code | `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/commands`; `/Users/cjvana/Documents/GitHub/meshcore-bot/modules/command_manager.py` |
|
||||
Reference in New Issue
Block a user