mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-14 14:49:47 +00:00
refactor(protocol): split mesh capability surface
This commit is contained in:
@@ -19,12 +19,12 @@ Primary root cause:
|
||||
| Meshtastic broadcast `want_response` | Shared policy in working tree | High | ESP32 and nRF now share the app-data destination/ACK/response decision; broadcast air ACK is suppressed while request response intent is preserved. |
|
||||
| Meshtastic request/reply core | Partially shared | Medium | NodeInfo/Position reply gating, TraceRoute reply gating, TraceRoute payload mutation, and TraceRoute/Position action lifecycle tracking are shared; actual reply send still lives in adapters/UI. |
|
||||
| Meshtastic duplicated policy ownership | Mostly shared | Medium | App-data send intent, NodeInfo reannounce gate, NodeInfo/Position reply gates, TraceRoute reply gate, TraceRoute payload mutation, TraceRoute/Position result lifecycle, and PKI/NO_CHANNEL resync decisions now live in shared runtime/policy. Packet construction and radio IO still live in adapters. |
|
||||
| MeshCore NodeInfo query/reply | Confirmed drift | High | ESP32 implements MeshCore NodeInfo control frames; nRF `requestNodeInfo()` ignores dest/want_response and sends advert only. |
|
||||
| MeshCore trace | Confirmed drift | Medium | ESP32 parses/forwards MeshCore `PAYLOAD_TYPE_TRACE`; nRF has no equivalent implementation. UI must not expose MC trace. |
|
||||
| MeshCore app-data ACK/capability | Confirmed drift | Medium | ESP32 claims and tracks app-data ACK; nRF can set a direct app flag but does not declare or track ACK capability. |
|
||||
| MeshCore NodeInfo query/reply | Shared runtime/effects | Medium | ESP32 and nRF now route `requestNodeInfo()` through `MeshCoreRuntime` and shared control payload codecs; platform adapters still own packet IO/projection. |
|
||||
| MeshCore trace | Shared lifecycle, platform-limited routing | Medium | ESP32 and nRF use native `PAYLOAD_TYPE_TRACE` and shared completion/timeout policy; nRF still uses a minimal one-hop hash route. |
|
||||
| MeshCore app-data ACK/capability | Shared lifecycle | Medium | ESP32 and nRF now declare ACK tracking only when runtime pending/completion handling is wired. ACK frame scheduling remains adapter IO. |
|
||||
| MeshCore direct routing/identity | Confirmed platform gap | High | ESP32 has richer peer routes, identity, secrets, and direct path behavior; nRF path is simplified. |
|
||||
| MeshCore duplicated/incomplete ownership | Confirmed architecture debt | Critical | MeshCore protocol truth is split between shared helpers, ESP32 adapter, and nRF simplified adapter. |
|
||||
| Capability granularity | Confirmed design gap | High | `MeshCapabilities` is too coarse to protect UI/actions from protocol-specific drift. |
|
||||
| Capability granularity | Fine-grained flags added | Medium | Coarse legacy fields remain for compatibility; new flags describe NodeInfo, Position, TraceRoute, app response, and ACK tracking separately. |
|
||||
|
||||
## Architecture Finding
|
||||
|
||||
@@ -211,10 +211,10 @@ Residual risk:
|
||||
|
||||
Evidence:
|
||||
|
||||
- ESP32 `MeshCoreAdapter::sendNodeInfoFrame(...)` builds NodeInfo query/info control frames.
|
||||
- ESP32 `requestNodeInfo(dest, want_response)` distinguishes broadcast query, unicast query, and info send.
|
||||
- nRF `MeshCoreRadioAdapter::requestNodeInfo(...)` ignores `dest` and `want_response`, then sends advert only.
|
||||
- nRF `MeshCapabilities` does not claim `supports_node_info`.
|
||||
- ESP32 and nRF both expose `requestNodeInfo(dest, want_response)` through
|
||||
`RequestNodeInfoIntent -> MeshCoreRuntime -> SendNodeInfoEffect`.
|
||||
- Shared MeshCore codecs build and parse NodeInfo query/info control frames.
|
||||
- ESP32 and nRF both declare the fine-grained NodeInfo capability fields they now execute.
|
||||
|
||||
Expected behavior:
|
||||
|
||||
@@ -223,14 +223,14 @@ Expected behavior:
|
||||
|
||||
Current state:
|
||||
|
||||
- Confirmed drift, partly masked by coarse `IMeshAdapter::requestNodeInfo()`.
|
||||
- ESP32 contains the richer protocol behavior; nRF does not call a shared MeshCore NodeInfo core.
|
||||
- NodeInfo query/info decision mapping is shared by `MeshCoreRuntime`.
|
||||
- NodeInfo control payload layout is shared by `meshcore_payload_helpers`.
|
||||
- ESP32 and nRF differ only in how the resulting `SendNodeInfoEffect` is transmitted and projected.
|
||||
|
||||
Next action:
|
||||
Residual risk:
|
||||
|
||||
- Either implement MeshCore NodeInfo control frames on nRF, or make unsupported behavior explicit and
|
||||
remove any UI path that assumes it works.
|
||||
- Prefer extracting MeshCore NodeInfo control-frame build/parse/reply policy into shared core first.
|
||||
- nRF currently has limited projection for received `PublishNodeInfoEffect`; the shared runtime can emit the
|
||||
effect, but platform-specific contact persistence remains thinner than ESP32.
|
||||
|
||||
### MC-002 Trace
|
||||
|
||||
@@ -238,7 +238,8 @@ Evidence:
|
||||
|
||||
- ESP32 MeshCore adapter parses and forwards direct `PAYLOAD_TYPE_TRACE`.
|
||||
- Official MeshCore source uses `Mesh::createTrace()` and `onTraceRecv()` for this behavior.
|
||||
- nRF MeshCore adapter has no trace implementation.
|
||||
- nRF MeshCore adapter uses the shared trace runtime and sends a minimal one-hop native trace when no full route
|
||||
table exists.
|
||||
|
||||
Expected behavior:
|
||||
|
||||
@@ -247,17 +248,18 @@ Expected behavior:
|
||||
|
||||
Current state:
|
||||
|
||||
- Mono node action spec now hides MC trace.
|
||||
- nRF remains unsupported.
|
||||
- Trace base payload build/decode and lifecycle policy are shared.
|
||||
- ESP32 still owns richer route scheduling and BLE `TraceData` projection.
|
||||
- nRF declares `supports_trace_route_request` for its minimal native MeshCore trace path, but not richer route
|
||||
projection.
|
||||
|
||||
### MC-003 App-data ACK
|
||||
|
||||
Evidence:
|
||||
|
||||
- ESP32 MeshCore capabilities include `supports_appdata_ack=true`.
|
||||
- ESP32 direct app-data tracks ACK state.
|
||||
- nRF MeshCore direct app payload includes a want-ack flag but capabilities do not claim ACK and there is
|
||||
no equivalent tracking/result path in the simplified adapter.
|
||||
- ESP32 and nRF direct app-data send paths register pending ACK signatures in `MeshCoreRuntime`.
|
||||
- ESP32 and nRF incoming ACK frames call shared `handleAppAck(...)`.
|
||||
- Both adapters declare `supports_protocol_ack_tracking` when this runtime path is wired.
|
||||
|
||||
Expected behavior:
|
||||
|
||||
@@ -265,11 +267,12 @@ Expected behavior:
|
||||
|
||||
Current state:
|
||||
|
||||
- Confirmed drift.
|
||||
- ACK pending/completed/timeout lifecycle is shared.
|
||||
|
||||
Next action:
|
||||
Residual risk:
|
||||
|
||||
- Decide if nRF MeshCore supports ACK tracking. If not, ensure callers do not treat `want_ack` success as delivery.
|
||||
- ACK frame response scheduling for received want-ack app-data is still platform IO; ESP32 has a fuller peer ACK
|
||||
path than nRF.
|
||||
|
||||
### MC-004 Direct Routing, Identity, And Secrets
|
||||
|
||||
@@ -285,38 +288,35 @@ Expected behavior:
|
||||
|
||||
Current state:
|
||||
|
||||
- Confirmed platform gap, not fully documented in capabilities.
|
||||
- Confirmed platform gap, now partially documented in fine-grained capabilities.
|
||||
|
||||
Next action:
|
||||
|
||||
- Split MeshCore capabilities into discovery, native NodeInfo, ACK tracking, direct route, identity/key,
|
||||
and trace support.
|
||||
- Move route/identity policy toward shared MeshCore runtime core before expanding nRF behavior.
|
||||
|
||||
## Capability Drift
|
||||
|
||||
The current `MeshCapabilities` fields are too coarse:
|
||||
Current state:
|
||||
|
||||
- `supports_unicast_appdata` does not say whether app-data ACK, app-level response, empty payload,
|
||||
direct route, or protocol-native request/reply are supported.
|
||||
- `supports_node_info` does not distinguish passive parsing, active query, direct reply, broadcast announce,
|
||||
or peer reannounce.
|
||||
- There is no capability for TraceRoute, Position request/reply, or MeshCore native trace.
|
||||
- `MeshCapabilities` now keeps the legacy coarse flags and adds fine-grained protocol flags for NodeInfo,
|
||||
Position, TraceRoute, protocol app responses, and ACK tracking.
|
||||
- ESP32/nRF Meshtastic and MeshCore adapters declare the fine-grained flags they execute.
|
||||
- Linux loopback/raw-lora adapters and the legacy ESP radio shim were updated to avoid silently omitting the
|
||||
new fields.
|
||||
|
||||
This capability gap is the structural reason UI actions drift into unsupported adapters.
|
||||
Residual risk:
|
||||
|
||||
- There are still no separate capability fields for MeshCore direct route tables, identity-key exchange, peer
|
||||
secret derivation, or rich trace route projection. Those remain documented platform differences.
|
||||
|
||||
## Immediate Recommended Fix Order
|
||||
|
||||
1. Land the shared Meshtastic app-data send policy and NodeInfo reannounce gate after build review.
|
||||
2. Add a compact adapter parity test for Meshtastic app-data encoding intent:
|
||||
NodeInfo request, Position request, TraceRoute request, broadcast request where valid.
|
||||
3. Extract shared Meshtastic reply payload construction/availability, TraceRoute, and PKI resync policy,
|
||||
replacing platform copies.
|
||||
4. Split MeshCore NodeInfo capability and stop using generic `requestNodeInfo()` as if all adapters support it.
|
||||
5. Add MeshCore capability fields for ACK tracking and trace support.
|
||||
6. Start MeshCore shared runtime extraction from NodeInfo control frames, because the ESP32 behavior already exists
|
||||
and nRF lacks it.
|
||||
7. Audit PKI unknown/resync paths after the above, because they depend on NodeInfo request semantics.
|
||||
1. Add parity tests that assert ESP32 and nRF adapters advertise the fine-grained capabilities they actually
|
||||
execute.
|
||||
2. Extract shared Meshtastic NodeInfo/Position packet construction and availability policy.
|
||||
3. Move MeshCore direct route / identity-key policy toward shared runtime before expanding nRF behavior.
|
||||
4. Add capability fields for MeshCore direct-route tables, identity/key exchange, and rich trace projection if UI
|
||||
needs to expose those actions directly.
|
||||
|
||||
## Guardrail
|
||||
|
||||
|
||||
@@ -170,8 +170,8 @@ Implementation ownership:
|
||||
|
||||
## Capability Surface Requirements
|
||||
|
||||
The current `MeshCapabilities` surface is too coarse for several protocol actions. Future parity work should
|
||||
split at least these fields out of generic app-data support:
|
||||
`MeshCapabilities` keeps the coarse legacy flags for existing UI code, but protocol-sensitive callers should
|
||||
prefer these fine-grained flags over generic app-data support:
|
||||
|
||||
- `supports_node_info_query`;
|
||||
- `supports_node_info_reply`;
|
||||
@@ -183,7 +183,7 @@ split at least these fields out of generic app-data support:
|
||||
- `supports_protocol_app_response`;
|
||||
- `supports_protocol_ack_tracking`.
|
||||
|
||||
Until those fields exist, UI must be conservative and protocol-specific specs, such as
|
||||
When a fine-grained flag is false, UI must be conservative and protocol-specific specs, such as
|
||||
`NODE_ACTION_PROTOCOL_SPEC.md`, remain authoritative.
|
||||
|
||||
## Extraction Requirement
|
||||
|
||||
@@ -25,6 +25,15 @@ struct MeshCapabilities
|
||||
bool supports_node_info = false;
|
||||
bool supports_pki = false;
|
||||
bool supports_discovery_actions = false;
|
||||
bool supports_node_info_query = false;
|
||||
bool supports_node_info_reply = false;
|
||||
bool supports_node_info_reannounce = false;
|
||||
bool supports_position_request = false;
|
||||
bool supports_position_reply = false;
|
||||
bool supports_trace_route_request = false;
|
||||
bool supports_trace_route_reply = false;
|
||||
bool supports_protocol_app_response = false;
|
||||
bool supports_protocol_ack_tracking = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -315,6 +315,13 @@ MeshCapabilities MeshCoreAdapter::getCapabilities() const
|
||||
caps.supports_node_info = true;
|
||||
caps.supports_pki = true;
|
||||
caps.supports_discovery_actions = true;
|
||||
caps.supports_node_info_query = true;
|
||||
caps.supports_node_info_reply = true;
|
||||
caps.supports_node_info_reannounce = true;
|
||||
caps.supports_trace_route_request = true;
|
||||
caps.supports_trace_route_reply = true;
|
||||
caps.supports_protocol_app_response = true;
|
||||
caps.supports_protocol_ack_tracking = true;
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,15 @@ MeshCapabilities MtAdapter::getCapabilities() const
|
||||
caps.provides_appdata_sender = true;
|
||||
caps.supports_node_info = true;
|
||||
caps.supports_pki = true;
|
||||
caps.supports_node_info_query = true;
|
||||
caps.supports_node_info_reply = true;
|
||||
caps.supports_node_info_reannounce = true;
|
||||
caps.supports_position_request = true;
|
||||
caps.supports_position_reply = true;
|
||||
caps.supports_trace_route_request = true;
|
||||
caps.supports_trace_route_reply = true;
|
||||
caps.supports_protocol_app_response = true;
|
||||
caps.supports_protocol_ack_tracking = true;
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,9 @@ chat::MeshCapabilities MeshtasticRadioAdapter::getCapabilities() const
|
||||
caps.supports_unicast_text = true;
|
||||
caps.supports_unicast_appdata = true;
|
||||
caps.supports_node_info = true;
|
||||
caps.supports_node_info_query = true;
|
||||
caps.supports_node_info_reply = true;
|
||||
caps.supports_node_info_reannounce = true;
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
@@ -389,6 +389,15 @@ class LinuxLoopbackMeshAdapter final : public ::chat::IMeshAdapter
|
||||
.supports_node_info = true,
|
||||
.supports_pki = true,
|
||||
.supports_discovery_actions = true,
|
||||
.supports_node_info_query = true,
|
||||
.supports_node_info_reply = true,
|
||||
.supports_node_info_reannounce = true,
|
||||
.supports_position_request = true,
|
||||
.supports_position_reply = true,
|
||||
.supports_trace_route_request = true,
|
||||
.supports_trace_route_reply = true,
|
||||
.supports_protocol_app_response = true,
|
||||
.supports_protocol_ack_tracking = true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,15 @@ class LoopbackMeshAdapter final : public ::chat::IMeshAdapter
|
||||
.supports_node_info = true,
|
||||
.supports_pki = true,
|
||||
.supports_discovery_actions = true,
|
||||
.supports_node_info_query = true,
|
||||
.supports_node_info_reply = true,
|
||||
.supports_node_info_reannounce = true,
|
||||
.supports_position_request = true,
|
||||
.supports_position_reply = true,
|
||||
.supports_trace_route_request = true,
|
||||
.supports_trace_route_reply = true,
|
||||
.supports_protocol_app_response = true,
|
||||
.supports_protocol_ack_tracking = true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1095,6 +1095,15 @@ bool LinuxRawLoraMeshAdapter::takePendingSendResult(::chat::MessageId& out_msg_i
|
||||
.supports_pki = pki_ready_,
|
||||
.supports_discovery_actions =
|
||||
active_protocol_ == ::chat::MeshProtocol::Meshtastic,
|
||||
.supports_node_info_query = true,
|
||||
.supports_node_info_reply = true,
|
||||
.supports_node_info_reannounce = true,
|
||||
.supports_position_request = true,
|
||||
.supports_position_reply = true,
|
||||
.supports_trace_route_request = true,
|
||||
.supports_trace_route_reply = true,
|
||||
.supports_protocol_app_response = true,
|
||||
.supports_protocol_ack_tracking = false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,14 @@ MeshCoreRadioAdapter::MeshCoreRadioAdapter(const ::chat::runtime::SelfIdentityPr
|
||||
::chat::MeshCapabilities caps{};
|
||||
caps.supports_unicast_appdata = true;
|
||||
caps.supports_broadcast_appdata = true;
|
||||
caps.supports_appdata_ack = true;
|
||||
caps.supports_node_info = true;
|
||||
caps.supports_discovery_actions = true;
|
||||
caps.supports_node_info_query = true;
|
||||
caps.supports_node_info_reply = true;
|
||||
caps.supports_node_info_reannounce = true;
|
||||
caps.supports_trace_route_request = true;
|
||||
caps.supports_protocol_ack_tracking = true;
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
@@ -486,6 +486,15 @@ MeshtasticRadioAdapter::MeshtasticRadioAdapter(const ::chat::runtime::SelfIdenti
|
||||
caps.provides_appdata_sender = true;
|
||||
caps.supports_node_info = true;
|
||||
caps.supports_pki = true;
|
||||
caps.supports_node_info_query = true;
|
||||
caps.supports_node_info_reply = true;
|
||||
caps.supports_node_info_reannounce = true;
|
||||
caps.supports_position_request = true;
|
||||
caps.supports_position_reply = true;
|
||||
caps.supports_trace_route_request = true;
|
||||
caps.supports_trace_route_reply = true;
|
||||
caps.supports_protocol_app_response = true;
|
||||
caps.supports_protocol_ack_tracking = true;
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user