feat(truapi): export createWebSocketProvider for browser clients - #438
Merged
Conversation
Both native host READMEs tell products to pass the bridge URL to `@parity/truapi`'s `createWebSocketProvider`, which did not exist. The same provider was hand-written four times instead: as a Swift string literal on iOS, again in Kotlin on Android, as the CLI's own JS provider, and once more downstream in a product that needed to reach `signing-host --frame-listen`. Add `createWebSocketProvider(url)` next to the MessagePort provider, and `connectWebSocketHost(url)` on the sandbox path so a plain browser tab pointed at such a host is detected as hosted and shares the cached client. The CLI's provider now delegates its TCP branch and keeps only the Unix-socket half.
This was referenced Aug 18, 2026
filvecchiato
approved these changes
Aug 19, 2026
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.
Closes #435
What
@parity/truapigainscreateWebSocketProvider(url), the WebSocketWireProviderthat both native host READMEs already tell products to import, plusconnectWebSocketHost(url)on the sandbox path so a plain browser tab is detected as hosted andgetClientSync()returns a client.Why
truapi-host signing-host --frame-listenis documented as the browser-facing option, but the browser had no transport that could reach it. The sandbox builds only the iframe handover and aMessagePortparked onwindow.__HOST_API_PORT__. Products worked around that by writing the bridge themselves, which is why the same provider now exists four times:ios/.../TrUAPIHost.swift:192as a Swift string literal,android/.../TrUAPIHost.kt:421again in Kotlin,rust/crates/truapi-host-cli/js/ws-provider.tsas this repo's own provider, andapps/web/src/dev/cliHost.tsin the DIM2 SPA, written only because there was nothing to import.With this, running a real host next to a dev server needs no product-specific glue:
That gives any web product a desk-local host with a real signer, real product accounts and the real statement store, and it covers two things the simulated host cannot: AutoSigning grants and authorized statement proofs. We have been developing the DIM2 SPA against
signing-hostthis way for two weeks, so the shape is proven, just not shareable until now.How
Mostly a move.
nativeWsProviderinrust/crates/truapi-host-cli/js/ws-provider.tswas already correct and used nothing Node-specific, so the implementation moves next tocreateMessagePortProviderintransport.ts, reuses the sharedcreateBaseProviderbookkeeping, and the CLI's provider delegates its TCP branch to it while keeping the Unix-socket half that only the CLI speaks. The frame protocol is one binary WebSocket message per SCALE frame, byte-identical to what the MessagePort transport carries, so the bridge is a pipe with no reframing.connectWebSocketHoststores the endpoint, which makesisCorrectEnvironment()true and letsgetClientSync()build and cache the client, so product code that already runs inside a webview or an iframe needs no changes. It throws if a client for another transport already exists, since that client is cached and cannot be redirected.The native hosts are left alone on purpose. Collapsing their Swift and Kotlin copies onto this export is an obvious follow-up, but it touches release artifacts and is better sequenced by their owners.
Notes
postMessageafter close now throws, matching every other provider in the package, where the old TCP branch returned silently.#436covers the other half of a one-command dev setup, a headless serve mode so the host does not need its own terminal.