Skip to content

feat: direct Tauri app-call path — drop the loopback app-websocket (Phase 5)#137

Open
zippy wants to merge 6 commits into
feat/holochain-0.6-and-unified-pluginfrom
feat/tauri-direct-app-calls
Open

feat: direct Tauri app-call path — drop the loopback app-websocket (Phase 5)#137
zippy wants to merge 6 commits into
feat/holochain-0.6-and-unified-pluginfrom
feat/tauri-direct-app-calls

Conversation

@zippy

@zippy zippy commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary

In the in-process unified plugin, the webview's @holochain/client now 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

  • runtime: handle_app_request — wraps AppInterfaceApi::new(conductor).handle_request(app_id, req), the App-API twin of the existing admin dispatch.
  • plugin: app_request IPC 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 by main_window_builder), never from a JS argument — this replaces the per-app websocket auth token.
  • runtime: subscribe_to_app_signals + plugin signal forwarding — each conductor Signal is forwarded to the bound window in the shape @holochain/client decodes as a RawSignal.
  • direct window mode (default) — injects __HC_TAURI_HOLOCHAIN__ instead of __HC_LAUNCHER_ENV__; no attach_app_interface, no loopback port. Zome calls are still signed by the in-process keystore via the existing sign_zome_call.
  • e2e: the desktop example exercises connect + appInfo + zome call + a live signal over IPC with no app interface attached.

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_request to the intended windows. No signing key is exposed to the webview.

Known limitations / follow-ups

  • The signal-forwarder task currently lives until the conductor channel closes, not per-window-close.
  • The example app pins @holochain/client at file:../../../../holochain-client-js (the unreleased transport from #425); this should move to a published/git version once #425 merges.
  • Plugin README does not yet document direct mode / the trust model (the client-js README does).

Test plan

  • Desktop example runs end to end over IPC (verified on a real display; the CI sandbox lacks GL to render the webview).
  • Existing websocket path and tests unaffected.

zippy added 6 commits May 26, 2026 09:03
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.
@cocogitto-bot

cocogitto-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

✔️ 2a883c5...0b706f8 - Conventional commits check succeeded.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b31710d9-0e7f-4e93-932a-65d7aa14fb11

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tauri-direct-app-calls

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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