feat(nym-proxy-ffi): observe the mixnet's three legs by capture and probe (ADR 0007) - #1323
Open
zancas wants to merge 6 commits into
Open
feat(nym-proxy-ffi): observe the mixnet's three legs by capture and probe (ADR 0007)#1323zancas wants to merge 6 commits into
zancas wants to merge 6 commits into
Conversation
The maintainer ruled on 2026-08-19 that the crate becomes mixnet-proxy and that the word shim is retired from the repository's vocabulary. The rename itself is deferred to its own pull request because it reaches every build system. This commit records the decision, the rejected alternatives, and the open questions about the wallet component's name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mixnet Mode routes a send across three legs, and the app observes none of them, because nym exposes no connection state and drops the exit's typed ConnectionError on the floor. ADR 0007 rules the missing observations into the proxy FFI: a tracing capture layer over nym's two log targets, a two-arm probe (the Sentinel round trip for legs one and two, one TLS handshake against a caller-supplied destination for all three), and a typed BootstrapEvent surface in zingo-netutils, with every race reporting, reconnects included. The whole surface compiles behind a cargo feature that only debug wallet builds enable. CONTEXT.md gains the vocabulary the ruling resolved: Leg, Exit Node, Destination (renaming correspondent), Sentinel, Capture, Probe, and Verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The diagnostics feature compiles the observation surface ADR 0007 rules: a tracing capture layer matching nym_gateway_client and nym_socks5_client_core, one streaming MixnetDiagnosticsObserver fed by a dedicated forwarder thread, a start_diagnosed constructor that carries the typed bootstrap narrative across the FFI, and the two probes. probe_sentinel classifies one Sentinel round trip into ExitProven, ExitRefused, GatewayLinkDead, or Indeterminate, reading the exit's own captured words. probe_destination completes one TLS handshake against a caller-supplied destination through the tunnel, verifying against the patched Mozilla-bundle verifier per ADR 0006, and adds HandshakeRefused, TunnelFailed, and DestinationProven. The plain constructor now rides the same start seam as the observed one, so the two cannot drift. The zingo-netutils pin moves to the nym_bootstrap_events head for BootstrapEvent and retargets to the next zingolib tag once PR 2742 merges. Release builds are byte-equivalent: the feature is off by default and no build system enables it yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The maintainer ruled against the streaming callback interface: UniFFI hands the crate a boxed trait object for every callback interface, and the only consumer is a debug screen that polls its platform bridge anyway. ADR 0007 now specifies a capped drop-oldest queue behind one drain_diagnostics export, with the overflow loss reported, and records the callback as a rejected option. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MixnetDiagnosticsObserver, its forwarder thread, and the start_diagnosed constructor are gone. Events now land in a capped drop-oldest queue, and the host polls drain_diagnostics, which surrenders the queue in order together with the count of events lost to overflow. Every type on the surface is static, no host code runs on the mixnet client's threads, and plain start carries the bootstrap narrative whenever the diagnostics feature is compiled. An overflow falsifier pins the drop-oldest contract and the loss report. Both feature graphs pass clippy and their tests (27 with diagnostics, 21 without), and the aarch64-linux-android check passes with the feature on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The maintainer ruled that BootstrapEvent::PullFailed carries the pull's whole typed NetOpFailure, and zingolib's nym_bootstrap_events branch now implements it (651bcb445). ADR 0007's netutils paragraph said the failure stays prose; this amendment records the ruling that replaced it. The FFI mapping adapts when the pin retargets to the next zingolib tag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change implements the observation surface of ADR 0007. Mixnet Mode routes a send across three legs. The app observes none of them today. This change makes the legs observable in debug wallet builds.
What the change adds
The
diagnosticscargo feature compiles four things intozingo-nym-proxy-ffi:tracingcapture layer. It matches events fromnym_gateway_clientandnym_socks5_client_core. Those are the two targets where nym drops the leg signals today.drain_diagnosticssurrenders the queue in order with the count of any events lost to overflow. The host implements no callback, so no host code runs on the mixnet client's threads.startcarries the typedBootstrapEvents from zingo-netutils into the same queue.probe_sentinelclassifies one Sentinel round trip intoExitProven,ExitRefused,GatewayLinkDead, orIndeterminate.probe_destinationcompletes one TLS handshake against a caller-supplied destination through the tunnel and addsDestinationProven,HandshakeRefused, andTunnelFailed. The handshake verifies against the compiled-in Mozilla bundle per ADR 0006, and sends no application protocol.A refusal verdict shows the exit's own captured words. Silence that no capture explains reports as indeterminate.
The two ratified ADRs ride along as their own commits: ADR 0005 (the component rename ruling) and ADR 0007 (this surface). CONTEXT.md gains the resolved vocabulary, including the rename of "correspondent" to "destination".
What the change does not do
dev. The per-variant build enablement (Dockerfile, build scripts, gradle, workbench, CI) is a follow-on pull request, the same deferral shape ADR 0005 uses for the rename.#if DEBUGbridge follows.Sequencing
The zingo-netutils pin moves to the head of zingolib PR 2742 (
nym_bootstrap_events), which definesBootstrapEvent. This pull request retargets the pin to the next zingolib tag once PR 2742 merges. It must not merge before that retarget.Verification
cargo check,cargo clippy --all-targets, andcargo nextest runpass with default features (21 tests) and with--features diagnostics(27 tests).cargo ndk --target aarch64-linux-android check --features diagnosticspasses, which covers the composed logcat-plus-capture subscriber.A note for review
The first cut used a streaming callback interface. The maintainer ruled it out because UniFFI callbacks hand the crate a boxed trait object. The drain replaced it, and every type on the new surface is static.
ProxyDeathObserveris untouched.🤖 Generated with Claude Code