Skip to content

refactor(signing): extract async local event signer boundary - #7600

Closed
baxen wants to merge 2 commits into
mainfrom
baxen/async-event-signer-extraction
Closed

refactor(signing): extract async local event signer boundary#7600
baxen wants to merge 2 commits into
mainfrom
baxen/async-event-signer-extraction

Conversation

@baxen

@baxen baxen commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Extract an async event-signing boundary without adding a remote signer, login flow, or policy changes.

  • Add Rust and Dart EventSigner / LocalEventSigner contracts over completed unsigned events. Construction (timestamps, tags, payload hashes), credentials, lifecycle and publication remain caller-owned.
  • Generalize shared NIP-42 WS authentication and desktop event/NIP-98/Blossom/huddle consumers; await signing throughout mobile consumers. Existing explicit-key callers remain compatible.
  • Keep local-secret capabilities separate: backup/export, pairing, encryption and agent provisioning still require actual local keys. No dummy keys, new fallback, protocol change or remote transport.
  • Preserve async scope/cancellation behavior: bind signed relay submissions to immutable session leases, fence direct status/typing consumers, and cancel video initialization after deferred auth or disposal. These are async-preservation fixes, not a behavioral no-op claim.
  • Preserve the existing media auth cache lifetime, refresh margin and best-effort error behavior. Concurrent async callers wait and recheck the cache; a failed refresh is not shared as a cached result. Add cancellation fences before voice-note source loads that now await auth.

Validation

Initial extraction foundation (59ad3ee6ce408fa71461475a2430b1b15648c44b): all listed lanes captured the same patch SHA-256 before and after execution: 64b5200b5be9c306c9ee10c792b6e544b3f530ca2b67919ac8041d61c8d26707.

Check Result
cargo test --locked -p buzz-ws-client 6 passed
Full mobile flutter test --no-pub --concurrency=2 2,129 passed
Exact desktop package Node test command 6,502 passed
Tauri desktop package tests 3,201 passed, 19 existing ignored
Tauri buzz-terminal workspace member tests 91 passed, 1 existing ignored on full rerun
Rustfmt (root + Tauri), affected Rust/Tauri all-target clippy Passed
Desktop TypeScript tsc --noEmit Passed
Dart formatting, flutter analyze --no-pub Passed
Repository differential file-size checks and policy tests Passed

The supplemental terminal-member run initially hit cooperative_child_exits_on_term_not_kill (Killed versus Terminated) under parallel load. A complete unchanged rerun passed. That member and its dependency graph do not use the signer extraction. No unrelated test or timeout was changed. Mobile's two transport-rotation tests now allow the signing future to complete before inspecting transport effects; their assertions are retained.

The first normal pre-push run additionally passed 13 repository Rust test groups and the full mobile hook, but its second ACP invocation hit the unchanged keepalive_resets_idle_past_deadline timing test (~101ms). The earlier full ACP invocation in the same hook passed. The complete normal pre-push retry passed with RUST_TEST_THREADS=4 (repository Rust unit tests, another full mobile pass, file-size checks and publication guards); no unrelated tests or thresholds were changed. Only the Tauri wrapper hook is excluded because it touches shared sidecar symlink targets; direct Tauri validation is recorded above.

Regression coverage includes exact legacy event IDs/fields and valid signatures, wrong-author rejection, delayed signing and failure propagation with no publication/local fallback, NIP-42 AUTH wire shape, cache success/failure behavior and cancellation during pending media auth.

This is not a claim that repository-wide just ci, the optional Tauri mesh-llm clippy variant, builds, or live-device workflows were exercised. Direct package commands avoid pnpm reinstalling reused dependencies and Tauri wrapper prerequisites touching read-only reused sidecar binaries. No UI feature or deployment is included.

Async-preservation follow-up (independent review)

Additive signed-off commit: d7a54a876d5ef139e6e991cb8435b688190fa23a (parent 59ad3ee6ce408fa71461475a2430b1b15648c44b). Only mobile files changed. Final additive diff SHA-256: fa4fa4a6dbed257b145b1471aa30c1174705c0848136344d736275553b7deb74, identical before/after every final validation lane and after commit.

  • Red first: a deferred A signer completed after a same-key community switch, invoked the stale callback, and sent the A EVENT through the production RelaySessionNotifier.publish onto the recording B socket. The regression now passes with zero callbacks and zero B sends.
  • Full mobile: flutter test --no-pub --concurrency=22,145 passed (16 additional regressions). Full Dart format check, flutter analyze --no-pub, differential file-size checks and all 10 size-policy tests passed. Normal commit and push passed their mobile/publication hooks; the Tauri wrapper was excluded to avoid shared read-only sidecar mutation. Unchanged Rust/JS packages were not rerun for this mobile-only follow-up; their foundation evidence remains above.
  • Production-seam tests cover same-key community rebuild, invalidation before a subsequent provider read, normal deferred signing, retained relay objects, callback-triggered scope switch, retired/foreign direct publish/raw leases, rate-limit wait, ordinary connection supersession, disposal, and status signing/acknowledgement state-cache races.
  • Video tests cover deferred auth cancellation on Android/iOS, disposal and failure during Android initialization, and unchanged normal authenticated playback/cleanup. Removing the guards reproduces Android controller creation after disposal and play after disposal. An explicit streaming-capability provider preserves the existing Platform.isAndroid production selection while allowing the Android path to be tested on the host. The new test-only platform interface dependency uses the already-locked version; no dependency upgrades.

Adoption contract: capture RelaySessionLease before signing; publish and sendRaw require lease:. Check lease.ensureCurrent() before scope-local post-await mutations. SignedEventRelay captures its lease at construction and must be recreated after session dependency rebuilds; ordinary reconnects retain the scope lease. Retired work throws RelaySessionSupersededError (a StateError). Checks surround signing completion/callback and transport gate waits; RelaySocket.send calls sink.add synchronously with no intervening await or consumer callback after the final fence. Existing timeout, rate-limit and retry policies are unchanged.

No live-device validation is claimed. Tests record actual production publish calls against a recording socket and actual video-controller calls against a fake platform, not a live relay/player. Typing was audited and migrated but has no standalone deferred-signer widget test. In-progress native initialization is cleaned up when its future settles; this patch adds no new initialization timeout.

Scope / review

The upstream base was checked as block/buzz:main at 78618804ec86a014524ad7d1fb55928e8f5c3edf; no rebase or merge was needed. This is an extraction draft with bounded async-preservation fixes, intended as the committed foundation for a separate follow-up layer. Authentication/recovery checks stay at their existing call sites; rate-limit, request timeout and retry policies are unchanged.

Signed-off-by: Bradley Axen <baxen@squareup.com>
@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 78618804ec86a014524ad7d1fb55928e8f5c3edf...d7a54a876d5ef139e6e991cb8435b688190fa23a.
A new review must complete for this exact range. When manual authorization
is required, a Block organization member must comment exactly
@buzz-security-review d7a54a876d5ef139e6e991cb8435b688190fa23a to authorize a new review.
Any previous review applies only to its recorded range.

… signing

Signed-off-by: Bradley Axen <baxen@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant