feat: direct Tauri app-call path — drop the loopback app-websocket (Phase 5)#137
feat: direct Tauri app-call path — drop the loopback app-websocket (Phase 5)#137zippy wants to merge 6 commits into
Conversation
Phase 5 on top of the unified plugin: route @holochain/client App API calls through Tauri IPC into the in-process conductor instead of a loopback app-websocket. Detection + transport live in a new module in holochain-client-js; the plugin gains an app_request command and window-scoped app binding. Records the design, the AppInterfaceApi / subscribe_to_app_signals findings, the security model, and phasing.
Add Runtime::handle_app_request, the App-API twin of the private
req_admin_api: it routes an AppRequest for a given installed app
through holochain's AppInterfaceApi and returns the AppResponse with no
loopback websocket. This is the dispatch primitive the unified Tauri
plugin will call to serve @holochain/client requests directly.
App-level failures come back as AppResponse::Error (not Err), matching
the websocket interface, so the JS client's error handling is unchanged.
Tests dispatch AppRequest::AppInfo and a signed AppRequest::CallZome
against the forum fixture, decoding the zome result to an empty
Vec<Link>. The call-zome test uses the cell's real coordinator zome
name ("posts", not the "forum" role name) and an encoded unit payload.
Add the Tauri-IPC counterpart to the runtime's handle_app_request: an app_request command that serves the full App API in-process, so a webview can use @holochain/client without a loopback app websocket. - HolochainPlugin gains a window-label -> app binding (bind_window) and app_request_bytes(window_label, request): it decodes the msgpack App API request with holochain's SerializedBytes codec (identical to the websocket wire format), dispatches via Runtime::handle_app_request scoped to the app the window is bound to, and returns the encoded response. The target app is taken from the window, never the request, replacing the per-app websocket auth token. - commands::app_request resolves the calling window's label and delegates. Registered in the invoke handler, build.rs, and default permissions. - Error gains WindowNotBound and Serialization variants. Integration test: a window bound to the forum app fetches AppInfo and makes a signed CallZome (get_all_posts -> empty) entirely over app_request_bytes, with no app interface attached; an unbound window is refused. Existing boot test refactored onto shared helpers. main_window_builder still injects the websocket env; switching it to inject the Tauri-direct env is a later step.
Add Runtime::subscribe_to_app_signals(app_id) -> broadcast::Receiver<Signal>, so a Tauri plugin can forward conductor signals to a webview without an app websocket. The conductor's signal broadcast is keyed by app, but its only public (non-test) accessor — get_signal_tx — is keyed by cell, so this first resolves one of the app's provisioned cells. Test: subscribe, then create_post against the forum fixture; the posts zome's post_commit hook emits a signal, which the receiver observes as a Signal::App. Adds tokio as a direct dependency to name broadcast::Receiver in the signature.
main_window_builder now defaults to direct Tauri IPC instead of a loopback app websocket: it binds the window to the app, spawns a task that forwards the app's conductor signals to the window as holochain://signal events (msgpack-encoded, via Runtime::subscribe_to_app_signals), and injects __HC_TAURI_HOLOCHAIN__ so @holochain/client routes the App API through the app_request command. WindowOptions::use_app_websocket falls back to the legacy __HC_LAUNCHER_ENV__ wiring. The injected guest-js gains injectHolochainTauriEnv, which sets the direct-mode env and bridges holochain://signal events to the transport's subscribeSignals callback using @tauri-apps/api; the shared zome-call signer is factored out and parameterized by plugin name. dist-js bundle rebuilt. Adds tokio as a direct dependency for the signal-forwarding task.
Update the holochain-runtime-example UI to demonstrate (and verify) the
plugin's now-default direct mode: report the injected __HC_TAURI_HOLOCHAIN__
env, connect @holochain/client over Tauri IPC, make a read (get_all_posts) and
a write (create_post) zome call, and confirm the create_post signal arrives via
client.on("signal"). Adds create_post + signal panels to index.html.
Point the example UI at the sibling ../holochain-client-js (file: dep), which
carries the unreleased Tauri transport; bump to the published version once it
ships.
Verified by running the app (DISPLAY, dev shell): env=direct, connect + appInfo,
get_all_posts, create_post, and a type=app zome=posts signal all succeed over
IPC with no app websocket attached.
|
✔️ 2a883c5...0b706f8 - Conventional commits check succeeded. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
In the in-process unified plugin, the webview's
@holochain/clientnow reaches the conductor through Tauri IPC instead of a loopback app-websocket. The conductor is in the same process, so the TCP socket + injected__HC_LAUNCHER_ENV__(port/token) were pure overhead. The websocket path stays intact for the separated client + service deployment and any non-Tauri consumer.Stacked on #136 (Phase 4). Ships lockstep with the JS side, holochain/holochain-client-js#425 — together they form the direct app-call path.
What's in it
handle_app_request— wrapsAppInterfaceApi::new(conductor).handle_request(app_id, req), the App-API twin of the existing admin dispatch.app_requestIPC command — moves the same msgpack{ type, value }bytes the websocket carries; resolves the app id from the calling window's label (a managed label→app-id map populated bymain_window_builder), never from a JS argument — this replaces the per-app websocket auth token.subscribe_to_app_signals+ plugin signal forwarding — each conductorSignalis forwarded to the bound window in the shape@holochain/clientdecodes as aRawSignal.__HC_TAURI_HOLOCHAIN__instead of__HC_LAUNCHER_ENV__; noattach_app_interface, no loopback port. Zome calls are still signed by the in-process keystore via the existingsign_zome_call.Trust model
The app id is derived from the window label on the Rust side, and Tauri's capability/permission system gates
plugin:holochain|app_requestto the intended windows. No signing key is exposed to the webview.Known limitations / follow-ups
@holochain/clientatfile:../../../../holochain-client-js(the unreleased transport from #425); this should move to a published/git version once #425 merges.Test plan