fix(pairing-cli): send the JSON envelope the apps decode, and stop panicking on wss:// - #6848
fix(pairing-cli): send the JSON envelope the apps decode, and stop panicking on wss://#6848lodar wants to merge 1 commit into
Conversation
…nicking on wss://
Two defects that together made it impossible to pair a handset with the Buzz
mobile app against an HTTPS relay. Both measured against a live relay.
1. THE ENVELOPE
`resolve_payload` returned a bare bech32 nsec as `PayloadType::Nsec` in both
arms. Mobile `_processPayload` begins with
`jsonDecode(payload) as Map<String, dynamic>` and nothing in `mobile/lib`
branches on `payload_type`, so a bare nsec dies on the leading `n` with
`FormatException: Unexpected character (at character 1)`. That is verbatim what
a real store build produced on a real handset.
`--envelope-relay <https url>` now emits the same shape the desktop client
sends (`desktop/src-tauri/src/commands/pairing.rs:145-148`,
`PayloadType::Custom` at :215): `{"relayUrl","pubkey","nsec"}`. `pubkey` is
DERIVED from the transferred nsec so the two cannot drift. Without the flag the
payload is unchanged, so CLI-to-CLI interop testing keeps working exactly as
before.
2. wss:// PANICKED BEFORE ANY PAIRING COULD START
thread 'main' panicked at rustls-0.23.42/src/crypto/mod.rs:249:14
Could not automatically determine the process-level CryptoProvider
Both `ring` and `aws-lc-rs` are reachable in the workspace, so rustls refuses to
choose. Plain `ws://` never reaches that code path — which is why interop
testing done entirely against a plaintext relay was structurally blind to it:
the defect exists only past the TLS an HTTPS deployment adds. Pin the `ring`
provider and install it at the top of `main()`.
`Cargo.lock` records the new direct `rustls` dependency; without that hunk the
tree does not build under `--locked`.
Signed-off-by: lodar <markounik@gmail.com>
Chessing234
left a comment
There was a problem hiding this comment.
both diagnoses are convincing, and the rustls one especially — "both ring and aws-lc-rs are in the workspace tree, so rustls cannot select a process-level provider" with the note that ws:// never reaches that path, which is why plaintext interop testing couldn't have found it, is the kind of explanation that stops the fix being reverted later. deriving pubkey from the transferred nsec so the two can't drift is right.
the default is still the broken payload. --envelope-relay is opt-in, so buzz-pair source --relay wss://… with no extra flag continues to emit a bare nsec that the mobile app cannot decode — the exact failure the pr documents (FormatException: Unexpected character (at character 1)). the stated reason is preserving CLI-to-CLI interop testing, which is a real need, but it leaves the tool's default aimed at the case that doesn't work and the flag aimed at the case that does. inverting it — envelope by default, --bare-nsec for the CLI-to-CLI path — puts the correct behaviour on the path someone reaches for first, and the interop tests are the caller that can afford to be explicit.
if it stays opt-in, the doc comment should say what to expect on a real handset without the flag, because right now # Payload shape explains the failure and then presents the fix as an option rather than as the thing you almost certainly want.
is --envelope-relay validated? the value_name = "HTTPS_URL" and the help text both promise an https url, but i don't see anything rejecting a wss:// value, a bare hostname, or a typo. this is the one field whose wrongness surfaces on the handset after a successful pairing — the QR scans, the key transfers, and then the device joins nothing — which is the most expensive place to discover a typo. parsing it and requiring https (or http for local) at argument time would move that failure to the terminal where the user can fix it in a second.
smaller: --relay and --envelope-relay differ in meaning (ephemeral pairing relay vs the buzz relay to join) but not in name, and the doc comment carries the whole distinction. after this lands they'll both appear in --help next to each other; a name like --join-relay or --target-relay would carry it without the reader needing the module docs.
and one thing i'd flag as a question rather than a defect: the envelope shape is being reproduced from desktop/src-tauri/src/commands/pairing.rs:145-148, i.e. two implementations of one wire format with nothing binding them together. if that struct is serializable from a shared crate, using it here would make a future field addition a compile error instead of a second FormatException on a handset.
Summary
Two defects that together made it impossible to pair a handset with the mobile app against an HTTPS relay. Both were measured against a live relay.
1. The payload the CLI sends is not the payload the apps decode.
resolve_payloadreturned a bare bech32 nsec asPayloadType::Nsecin both arms. Mobile_processPayloadbegins withjsonDecode(payload) as Map<String, dynamic>, and nothing inmobile/libbranches onpayload_type, so a bare nsec dies on the leadingnwithFormatException: Unexpected character (at character 1)— verbatim what a real store build produced on a real handset.--envelope-relay <https url>now emits the same shape the desktop client sends (desktop/src-tauri/src/commands/pairing.rs:145-148,PayloadType::Customat :215):{"relayUrl","pubkey","nsec"}.pubkeyis derived from the transferred nsec so the two cannot drift. Without the flag the payload is unchanged, so CLI-to-CLI interop testing keeps working exactly as before.2.
wss://panicked before any pairing could start.Both
ringandaws-lc-rsare reachable in the workspace, so rustls refuses to choose. Plainws://never reaches that code path — which is why interop testing done entirely against a plaintext relay was structurally blind to it: the defect exists only past the TLS an HTTPS deployment adds. This pins theringprovider and installs it at the top ofmain().Cargo.lockrecords the new directrustlsdependency; without that one hunk the tree does not build under--locked.Related issue
No issue opened for this. There are open PRs for each half, and I would rather point at them than pretend otherwise:
fix(pairing-cli): install rustls crypto provider, same crate, same three files), plus the wider reports buzz CLI: panics with rustls CryptoProvider error #2666, buzz agents draft-create panics: rustls CryptoProvider not installed #2552 and [Bug] buzz agents draft-create panics on secure relay path due to missing rustls CryptoProvider selection #2457.fix(pairing): CLI payload format mismatch + mobile ws:// scheme rejection), which also editsmobile/lib/.../pairing_provider.dart.This PR differs in fixing both halves inside the pairing CLI only — no mobile-side change — with the envelope behind an opt-in flag so existing CLI-to-CLI flows are untouched, the pubkey derived rather than passed in, and unit coverage for the decode gate. If a maintainer would rather land #4153/#2839 plus #4757, close this — the important thing is that a handset can pair over HTTPS.
Testing
crates/buzz-pairing-cli/src/main.rs: that a bare nsec fails the apps' firstjsonDecodegate and the envelope clears it, that the envelope pubkey is derived from the transferred nsec, that a generated key stays internally consistent, that the absent flag leaves current behaviour unchanged, that an invalid nsec is rejected before an envelope is built, and that odd relay URLs stay well-formed JSON.cargo test -p buzz-pairing-cli --locked— 7/7 green on the pinned 1.95.0 toolchain. Worth flagging for the maintainers:just test-unitenumerates packages by hand and does not includebuzz-pairing-cli, so these tests are compiled byclippy --all-targetsbut never run by any job. That is a gap in this repo's CI, not in this PR, and I am happy to open a separate one adding the package to the recipe.rustfmt --checkclean on the changed files; lint, unit tests, the Windows build and the cross-compile matrix ran green on this exact tree in my own CI before opening.No UI surface is touched.
The branch is based on
f88cda9eb, which is behindmain. Neither pairing-cli file has been touched upstream since, and theCargo.lockhunk is a single line inside an otherwise unchangedbuzz-pairing-cliblock, so it applies cleanly. Happy to rebase on request.