docs(design): wire observability and debug host - #315
Conversation
727e0cc to
cccd69d
Compare
TarikGul
left a comment
There was a problem hiding this comment.
I think generally the content of the doc is good - and the direction is nice. But I think we should reframe it more like a specification. Currently its very easy for the doc to get out of sync with the implementation PR.
I think we can really slim this down and take out some of the unnecessary sections that dont add much to the actual core design of what we are building.
|
Two points on the design. Both are about where the debugger lives. 1. Topology: the host dials the debuggerThe doc's model is a passive This is not a preference, it's forced. Only the inside can initiate:
So the debugger app is always the server, and every host dials outward: Two properties to state as invariants, neither currently in the doc:
Worth noting that the second invariant is also the extension point. If we later want mocking or What this removes from the current design: the relay service ( Two things this forces that the doc should state. The debugger URL must be configurable, not And that LAN hop is why 2. The tap belongs in
|
| Direction | Choke point |
|---|---|
| inbound (product → core) | ProductRuntime::receive_frame() — host_core.rs:422 |
| outbound (core → product) | FrameSink::emit_frame() — host_core.rs:40 |
Native reaches these through ws_bridge.rs (WsFrameSink), web through wasm.rs:851. So one tap
implementation covers both platforms, and nothing in @parity/truapi changes at all. That is the test
for whether the product is genuinely untouched, and the current design fails it.
A sink trait, not a hardcoded socket
Worth defining the flush target as a trait rather than wiring a WebSocket into the core, both to keep
the transport out of truapi-server and because wire frames are not the only thing worth observing.
dotli's own debugger also surfaces host-internal events like SSO, and those have no wire frame to hang
off. An enum event keeps room for them without reopening the design:
/// Dev-only sink for host debug events. A host that does not enable the
/// debugger leaves it unset and the tap is inert.
pub trait DebugSink: Send + Sync {
/// Hand one event to the sink. Fire-and-forget by construction: it cannot
/// block the frame path and cannot fail the operation that produced it.
fn emit(&self, event: DebugEvent);
}
/// One observable host event.
pub enum DebugEvent {
/// A SCALE wire frame crossing a product channel.
Frame {
/// Which product channel on this host.
channel_id: ChannelId,
/// Product → core, or core → product.
dir: FrameDirection,
/// Untouched `ProtocolMessage` bytes; the debugger app decodes.
bytes: Vec<u8>,
},
}333b792 to
13048f9
Compare
13048f9 to
4f5c8f7
Compare
|
Moved the wire debug tap into the Rust host (truapi-server's DebugSink) - tapping both frame choke points |
Adds a design doc for the wire observability layer of @parity/truapi: a payload-blind debug tap in
the Rust host (truapi-server), correlated by the wire requestId, streamed to a debugger app the host
dials out to. Frame bytes are opaque to the core (never decoded), so it leaks no payload or key
material and the product package is untouched.
Placed under docs/design/ (not docs/rfcs/) because it specifies the host-side tap and TS debugger
tooling over the existing wire, with no change to the Rust protocol interface.