Skip to content

feat: Holochain 0.6.1 + in-process FFI-free tauri-plugin-holochain (Phase 4)#136

Open
zippy wants to merge 8 commits into
mainfrom
feat/holochain-0.6-and-unified-plugin
Open

feat: Holochain 0.6.1 + in-process FFI-free tauri-plugin-holochain (Phase 4)#136
zippy wants to merge 8 commits into
mainfrom
feat/holochain-0.6-and-unified-plugin

Conversation

@zippy

@zippy zippy commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary

Migrates the runtime to Holochain 0.6.1 and introduces a unified, FFI-free in-process tauri-plugin-holochain, replacing the darksoil tauri-plugin-holochain + FFI crates. The plugin runs the conductor in the same process as the Tauri app and exposes it to the webview, verified end to end with a desktop example.

This is the base of a stacked set of PRs for the 0.6 / unified-plugin / direct-call work; the App-call-over-IPC, admin, and auth/restart layers build on top of this branch in follow-up PRs.

What's in it

  • build: migrate to Holochain 0.6.1 — Cargo deps, fixtures, Kotlin parcelers, and the AppStatus enum changes (dropped Paused/Running); own the nix dev shell and drop the darksoil tauri-plugin flake.
  • feat: in-process tauri-plugin-holochain (Phase 4) — the unified plugin + a verified desktop example app.
  • test: verify the 0.6.1 migration + new features, and a top-level runner for the plugin and desktop example.
  • build: dev-shell fixes — pin webkitgtk to 2.42.5 (holonix's 2.52.x aborts EGL on non-NixOS) and allow opting into GPU compositing.

Relationship to #135

Supersedes the approach in #135 ("Updates hc 0.6"): that branch targets hc 0.6.0-rc.0, stays on pnpm, and keeps the FFI crates + darksoil plugin. This branch goes to 0.6.1 and replaces FFI with the in-process unified plugin. #135 is left open pending your decision on which to land.

Test plan

  • Desktop example runs end to end against the in-process conductor (see the top-level test runner).
  • 0.6.1 migration verification suite added in this branch.

zippy added 8 commits May 24, 2026 12:21
Phase 0 of the 0.6 + unified-plugin plan. Replace the darksoil-studio
tauri-plugin-holochain dev-shell input (its only use in this repo) with a
dev shell composed from first principles in flake.nix:

- holochain toolchain (holochain, hc, hc-scaffold, lair-keystore, ...) from
  holonix packages
- Rust from rust-overlay via rust-toolchain.toml (single source of truth for
  channel/components/targets)
- Android SDK/NDK (compileSdk/buildTools 34, NDK 26.1.10909125) via nixpkgs
  androidenv, exporting ANDROID_HOME/ANDROID_NDK(_ROOT/_HOME)/NDK_HOME
- cargo-ndk, nodejs, pnpm, jdk17, binaryen, and Tauri v2 desktop libs

Extend rust-toolchain.toml with the Android (arm64/armv7/x86/x86_64) and
wasm32 targets the build scripts and hApp builds require.

flake.lock now references only holonix (+ followed nixpkgs/flake-parts/
rust-overlay); the darksoil and webkitnixpkgs nodes are gone. Verified
nix develop yields the full toolchain and cargo-ndk cross-compiles a cdylib
to valid arm64-v8a and x86_64 Android .so files.
Phase 1 of the 0.6 + unified-plugin plan: bring the Holochain
version-advancing work over from the volla-cloud-services fork, excluding
all Volla-specific branding, Nexus registry, and CI changes. Targets the
released 0.6.1 directly (folding in the plan's Phase 2) since 0.6.1 is out
and holonix main-0.6 ships it.

Dependencies / toolchain:
- holochain/conductor_api/types 0.5.6 -> 0.6.1, lair_keystore_api 0.6.2 ->
  0.6.3; example app hdi 0.6.5 -> 0.7.0, hdk 0.5.5 -> 0.6.0
- flake holonix main-0.5 -> main-0.6 (holochain 0.6.1 binary, hc-scaffold
  0.601.1); rust-toolchain channel 1.81.0 -> 1.88.0
- runtime: move uuid to [dependencies] (used by import_key_seed);
  runtime-types-ffi: add bytes
- pin RustCrypto RC family in Cargo.lock (pkcs8 0.11.0-rc.11,
  ed25519 3.0.0-rc.4, signature 3.0.0-rc.10) -- the final releases are
  incompatible with ed25519-dalek 3.0.0-pre.1 still required via iroh

API migration (runtime, runtime-ffi, runtime-types-ffi + regenerated
Kotlin/AIDL):
- AppInfoStatus -> AppStatus (Running -> Enabled, tuple Disabled; drop
  Paused/PausedAppReason and DeletingAgentKey)
- AdminResponse::AppEnabled(app); AppBundleSource::Bytes(_.into()); drop
  allow_throwaway_random_agent_key; add danger_bind_addr; drop
  RoleSettingsFfi::UseExisting
- example zomes: GetLinksInputBuilder -> LinkQuery + GetStrategy,
  delete_link with GetOptions; manifests to the 0.6 format

New features carried over:
- import_key_seed (Lair seed import) + RuntimeError::InvalidArguments
- optional agent_key on install; relay_url (iroh) plumbed through config

Defaults use Holochain public infrastructure (not *.volla.tech):
bootstrap/signal dev-test-bootstrap2.holochain.org, iroh relay
use1-1.relay.n0.iroh-canary.iroh.link.

Verified: cargo check (lib + tests) of the three migrated crates passes
against 0.6.1. Running the conductor test suite is Phase 3. Version numbers
(app/libs/crates) intentionally left unchanged; JS lockfile not regenerated
(npm-driven; not needed for `npm test`). Auto-generated dokka docs under
libraries/*/docs left to regenerate from a docs build.
Add runtime integration tests for the features carried over in the 0.6.1
migration, and record the Phase 4 consumer-app findings in the plan.

- test_import_key_seed: importing the same seed into a *fresh* keystore
  reproduces the same AgentPubKey (seed-based identity recovery), and a
  different seed yields a different key
- test_install_app_with_agent_key: installing with an explicit agent_key
  yields an app whose agent_pub_key matches the imported key
- test_new_runtime: assert relay_url is honored in the conductor network
  config (alongside the existing bootstrap/signal assertions)

Verified: cargo test -p holochain-conductor-runtime (22/22) and
-runtime-ffi (12/12) pass against Holochain 0.6.1. Kotlin client/service
tests (test:client/test:service) require an Android emulator and run in CI.

Also documents the audited Phase 4 consumer apps (kando, emergence) and the
darksoil tauri-plugin-holochain API they use, in the plan doc.
…e (Phase 4)

A third Tauri plugin that runs a Holochain conductor in-process — no UniFFI,
Kotlin, Android service, or cross-app AIDL. Built directly on
holochain-conductor-runtime. The separated client + service plugins remain
fully supported; this is the single-binary path.

API:
- crates/runtime: Runtime::new_with_network_config(passphrase, data_dir,
  NetworkConfig) so the plugin uses holochain's native NetworkConfig; the
  existing RuntimeConfig path is refactored to share one constructor.
- crates/tauri-plugin-holochain (new, desktop + mobile):
  - init(passphrase, HolochainPluginConfig { data_dir, network }) boots the
    conductor async; emits holochain://ready / holochain://setup-failed
  - HolochainExt::holochain() -> HolochainPlugin, exposing .runtime() (the full
    runtime API) and a typed main_window_builder that injects __HC_LAUNCHER_ENV__
    + a zome-call signer so @holochain/client in the webview connects to the
    in-process conductor
  - sign_zome_call command (clean serde request/response types)
  - guest-js built from source (rollup) into dist-js; native NetworkConfig and
    vec_to_locked re-exported

Verification:
- Headless integration test (tests/integration.rs): builds a Tauri app with the
  plugin, waits for ready, installs+enables the forum fixture, attaches the app
  interface. Passes.
- apps/holochain-runtime-example: a desktop app using the plugin, UI built by
  vite from the npm @holochain/client (no committed bundle; ui/dist gitignored).
  Run end-to-end: env injected, @holochain/client connects (appInfo), and a
  signed get_all_posts zome call succeeds.
holonix's nixpkgs ships webkitgtk 2.52.x, which aborts with
"Could not create default EGL display: EGL_BAD_PARAMETER" and renders a
blank Tauri webview on non-NixOS GPUs during `tauri dev`. Source the
GTK/webkit runtime stack from a pinned nixpkgs (webkitgtk 2.42.5) plus
wrapGAppsHook and the GIO/XDG/WEBKIT_DISABLE_COMPOSITING_MODE env.

Dev-shell only; production bundles link the system/CI webkit.
Add `test:tauri-plugin-holochain` (the plugin integration test that boots
a conductor in a Tauri app), `test:example` (build the example UI +
`cargo build` it), and a combined `test:tauri-plugin`, wired into the root
`test` script. Also add `build:example` and `start:example` helpers.
The dev shell forces WEBKIT_DISABLE_COMPOSITING_MODE=1 so `tauri dev`
renders on finicky GPUs/drivers. Gate that behind ENABLE_WEBKIT_COMPOSITING:
set it before `nix develop` to keep hardware-accelerated compositing instead.
@cocogitto-bot

cocogitto-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

✔️ e2ce396...eb15d8d - Conventional commits check succeeded.

@zippy zippy had a problem deploying to Android Service Runtime Release June 3, 2026 20:19 — with GitHub Actions Failure
@zippy zippy had a problem deploying to Android Service Runtime Release June 3, 2026 20:19 — with GitHub Actions Failure
@zippy zippy had a problem deploying to Android Service Runtime Release June 3, 2026 20:19 — with GitHub Actions Failure
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR executes a comprehensive migration to Holochain 0.6.1 and introduces a unified, in-process Tauri plugin that eliminates external service dependencies. The workspace dependency versions are bumped across the board, the runtime is extended with relay URL configuration and key seed import/recovery, the FFI type system is simplified (collapsing app status enums and removing obsolete variants), and the Android service runtime gains key seed import support with refactored Context-based bindings. A new production-grade tauri-plugin-holochain crate runs the Holochain conductor in-process, exposing zome-call signing to webviews via TypeScript injection and Tauri command handlers. An example desktop application demonstrates the plugin with an interactive UI that connects to the conductor and executes zome calls. The example client app is updated to use new HDK APIs (LinkQuery instead of GetLinksInputBuilder), and the development infrastructure (Nix flake and Rust toolchain) is refreshed to support the full stack without relying on external darksoil packages.

Suggested reviewers

  • mattyg
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/holochain-0.6-and-unified-plugin

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libraries/service/src/main/java/org/holochain/androidserviceruntime/service/HolochainService.kt (1)

521-534: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistent error handling: AdminBinder.signZomeCall lacks try/catch.

AppBinder.signZomeCall (lines 674-681) was updated to wrap the runtime call in try/catch and forward exceptions via callback.onFailure(). However, AdminBinder.signZomeCall does not have the same error handling, which could result in unhandled exceptions crashing the coroutine or being silently swallowed.

Consider applying the same pattern here for consistency.

🛡️ Proposed fix
         override fun signZomeCall(
             callback: IHolochainServiceCallback,
             req: ZomeCallParamsFfiParcel,
         ) {
             Log.d(logTag, "signZomeCall")
             if (!this.isAuthorized()) {
                 callbackUnauthorized(callback)
                 return
             }

             serviceScope.launch(Dispatchers.IO) {
-                callback.signZomeCall(ZomeCallParamsSignedFfiParcel(runtime!!.signZomeCall(req.inner)))
+                try {
+                    callback.signZomeCall(ZomeCallParamsSignedFfiParcel(runtime!!.signZomeCall(req.inner)))
+                } catch (e: Exception) {
+                    callback.onFailure(e.toString())
+                }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@libraries/service/src/main/java/org/holochain/androidserviceruntime/service/HolochainService.kt`
around lines 521 - 534, The AdminBinder.signZomeCall implementation should
mirror AppBinder's error handling: wrap the runtime!!.signZomeCall(req.inner)
call inside a try/catch in the serviceScope.launch(Dispatchers.IO) block and on
exception call callback.onFailure(...) to forward the error instead of letting
it escape; keep the successful path calling
callback.signZomeCall(ZomeCallParamsSignedFfiParcel(...)) and ensure you still
check isAuthorized() and return early as currently implemented.
🧹 Nitpick comments (5)
docs/holochain-0.6-and-unified-plugin-plan.md (1)

28-34: 💤 Low value

Optional: tag the fenced ASCII diagram with a language.

markdownlint MD040 flags this block. Adding text after the opening fence silences it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/holochain-0.6-and-unified-plugin-plan.md` around lines 28 - 34, Add a
language tag to the fenced ASCII diagram block to silence markdownlint MD040;
edit the code block that currently starts with ``` and contains the
tauri-plugin-service / tauri-plugin-client / tauri-plugin-holochain ASCII
diagram so it reads ```text (i.e. add the text tag after the opening backticks).
This change targets the fenced block containing the lines with
"tauri-plugin-service (Rust shim)", "tauri-plugin-client  (Rust shim)", and
"tauri-plugin-holochain (pure Rust)" to ensure the diagram is treated as plain
text by the linter.
crates/runtime-types-ffi/Cargo.toml (1)

19-19: 💤 Low value

Align bytes with [workspace.dependencies] in crates/runtime-types-ffi/Cargo.toml.

crates/runtime-types-ffi/Cargo.toml sets bytes = "1.10.1" while the root Cargo.toml defines bytes = "1.9.0" under [workspace.dependencies] (line 19). Consider switching to bytes = { workspace = true } (or document why this crate needs a newer bytes).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/runtime-types-ffi/Cargo.toml` at line 19, The dependency version for
bytes in the runtime-types-ffi crate is pinned to "1.10.1" and should be aligned
with the workspace version; change the bytes declaration in
crates/runtime-types-ffi/Cargo.toml from bytes = "1.10.1" to bytes = { workspace
= true } so it inherits the workspace dependency (or add a brief comment in that
Cargo.toml explaining why this crate must override the workspace version if you
intentionally need 1.10.1).
crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts (1)

17-17: 💤 Low value

crypto.getRandomValues is synchronous — await and Uint8Array.from are redundant.

getRandomValues returns the same typed array it’s given, synchronously; the await and wrapping copy add no value.

♻️ Suggested simplification
-      const nonce = Uint8Array.from(await crypto.getRandomValues(new Uint8Array(32)));
+      const nonce = crypto.getRandomValues(new Uint8Array(32));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts` at line 17,
The nonce assignment is using await and Uint8Array.from unnecessarily; replace
the current expression so nonce is assigned directly from
crypto.getRandomValues(new Uint8Array(32)) (i.e., remove the await and the extra
copy). Update the variable initialization where nonce is declared in index.ts to
use crypto.getRandomValues directly.
apps/holochain-runtime-example/src-tauri/Cargo.toml (1)

18-18: 💤 Low value

Consider managing tauri-plugin-log via the workspace.

Every other dependency here uses { workspace = true }, while tauri-plugin-log = "2" is a loose caret pin. Centralizing it in the workspace keeps versions consistent across crates. Non-blocking for an example app.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/holochain-runtime-example/src-tauri/Cargo.toml` at line 18, The
tauri-plugin-log dependency is a hard version ("2") while other deps use
workspace pins; change its declaration to use the workspace member pin so
versions are centralized (i.e., replace the loose version entry for
tauri-plugin-log with a workspace reference). Update the dependency entry for
tauri-plugin-log in this crate's Cargo.toml to use { workspace = true } (or move
the version into the workspace Cargo.toml and reference it from here) so it
matches the other workspace-managed dependencies.
crates/tauri-plugin-holochain/package.json (1)

14-25: 💤 Low value

Bundled deps could be devDependencies.

@holochain/client, @msgpack/msgpack, and @tauri-apps/api are inlined into the single injection bundle via nodeResolve() and this package exposes no main/exports for consumers. They function purely as build-time inputs, so moving them to devDependencies better reflects intent. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-plugin-holochain/package.json` around lines 14 - 25, The current
package.json lists build-only packages ("`@holochain/client`", "`@msgpack/msgpack`",
"`@tauri-apps/api`") under "dependencies" but they are only inlined into the
bundle via nodeResolve() and not runtime exports; move those three entries out
of "dependencies" and add them to "devDependencies" (preserve versions), leaving
only true runtime deps in "dependencies" so the manifest accurately reflects
build vs runtime requirements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/example-client-app/ui/package.json`:
- Around line 10-22: Update the devDependency "`@sveltejs/vite-plugin-svelte`"
from v3.x to a v4+ release (e.g., "^5.0.0") so it is compatible with "vite" v6
and "svelte" v5; then regenerate the lockfile (npm/yarn/pnpm) and verify the
resolved "`@sveltejs/vite-plugin-svelte`" package's peerDependencies include
support for Vite 6 and Svelte 5 to avoid peer dependency conflicts.

In `@apps/holochain-runtime-example/src-tauri/src/lib.rs`:
- Around line 22-24: data_dir() currently returns a fixed temp path which lets
conductor state persist between runs and causes "app already installed" on
subsequent runs; change the implementation so each run uses a unique/cleanable
temp dir (for example create a tempfile::TempDir per run and return its path or
wire it into the runtime startup) or alternatively make setup_app/idempotent by
checking for APP_ID ("forum") and skipping install if already present; update
references to data_dir(), setup_app(), and APP_ID to use the new TempDir-backed
path or the pre-install check and ensure the TempDir is kept alive for the
process lifetime so the path remains valid until shutdown.

In `@crates/runtime/src/config.rs`:
- Around line 26-27: The doc comment for the config field relay_url contains a
typo ("iroh-raly"); update the documentation comment above the public field
relay_url: Url2 to read "URL of the iroh-relay server" so the comment correctly
spells "relay".

In `@crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts`:
- Around line 16-44: The signZomeCall implementation returns only {bytes,
signature} and mis-handles nonce and expiresAt and payload serialization; update
window.__HC_ZOME_CALL__.signZomeCall so it returns a full CallZomeRequestSigned
object matching `@holochain/client`@0.20.5: include provenance, cap_secret (null
or request.cap_secret), cell_id as [cellIdDnaHash, cellIdAgentPubKey],
zome_name, fn_name, payload as a serialized Uint8Array (call
encode(request.payload) exactly once and use Uint8Array.from(...) rather than
Array.from to avoid double-serialization), nonce as a Uint8Array (remove the
unnecessary await on crypto.getRandomValues and call crypto.getRandomValues(new
Uint8Array(32)) directly), expires_at in the correct epoch-second units (compute
from Date.now() / 1000 and add the desired TTL, not Date.now() * 1e3), and
signature as a Uint8Array from the plugin response; populate these fields when
returning from signZomeCall (function name: signZomeCall, locals: nonce,
expiresAt/expires_at, payload, zomeCall, response).

In `@crates/tauri-plugin-holochain/src/lib.rs`:
- Around line 106-114: The generated initialization script interpolates app_id
and token directly into a double-quoted JS string which can break or introduce
injection vulnerabilities; update the code that builds the script (the call
chain using .initialization_script(...) and the injected JS function
injectHolochainClientEnv) to JSON-serialize the string values using
serde_json::to_string(&app_id) and
serde_json::to_string(&app_auth.authentication.token) and pass the numeric port
as-is (app_auth.port) so the format string becomes
window.injectHolochainClientEnv(<serialized app_id>, <port>, <serialized
token>); ensuring proper escaping and safe JS injection.

In `@DEVELOPMENT.md`:
- Line 5: The instruction in DEVELOPMENT.md references a removed input and
misspells it; update the sentence to remove any mention of the obsolete
tauri-plugin-holochain input and its URL, and only mention holonix.url as the
source that may need changing when updating to a major Holochain version;
specifically edit the line that currently mentions
`taurie-plugin-holochain.url`/`tauri-plugin-holochain` so it only refers to
`holonix.url` (and correct the spelling) to avoid misleading contributors.

In
`@libraries/client/src/main/java/org/holochain/androidserviceruntime/client/HolochainServiceAppClient.kt`:
- Around line 110-115: The call to waitForServiceReady() currently has its
boolean result ignored, allowing setupApp(...) to run even when the conductor
never became ready; update the flow after waitForServiceReady() to check its
return value and abort if false — throw a specific exception (e.g.,
HolochainServiceNotReadyException or reuse HolochainServiceNotConnectedException
with a clear message) instead of proceeding to setupApp(installAppPayload,
enableAfterInstall). Keep the existing waitForConnectReady() guard, but ensure
waitForServiceReady() failing prevents calling setupApp.

In
`@libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelers.kt`:
- Around line 179-204: The parceler and the tests disagree on the variant name
for discriminator 2; update the AppStatusFfiParceler so it uses the same variant
as the tests by replacing AppStatusFfi.Enabled with AppStatusFfi.Running in both
the create() branch for case 2 and the write() branch that writes discriminator
2 (i.e., modify AppStatusFfiParceler to map 2 <-> AppStatusFfi.Running), or
alternatively update ParcelablesTest.kt's testAppStatusFfiParcelRunning to
expect AppStatusFfi.Enabled—pick one consistent variant and change the two
matching occurrences (the case with readInt() == 2 and the writeInt(2) branch)
so create(), write(), and tests align.

In `@package.json`:
- Line 67: The "test" npm script currently invokes "test:runtime" with npm and
the rest with pnpm and includes a leading space; update the package.json "test"
script so all sub-scripts use pnpm (replace "npm run test:runtime" with "pnpm
run test:runtime") and remove the leading space before the command string to
ensure consistent pnpm workspace behavior and correct execution of the "test"
script.

---

Outside diff comments:
In
`@libraries/service/src/main/java/org/holochain/androidserviceruntime/service/HolochainService.kt`:
- Around line 521-534: The AdminBinder.signZomeCall implementation should mirror
AppBinder's error handling: wrap the runtime!!.signZomeCall(req.inner) call
inside a try/catch in the serviceScope.launch(Dispatchers.IO) block and on
exception call callback.onFailure(...) to forward the error instead of letting
it escape; keep the successful path calling
callback.signZomeCall(ZomeCallParamsSignedFfiParcel(...)) and ensure you still
check isAuthorized() and return early as currently implemented.

---

Nitpick comments:
In `@apps/holochain-runtime-example/src-tauri/Cargo.toml`:
- Line 18: The tauri-plugin-log dependency is a hard version ("2") while other
deps use workspace pins; change its declaration to use the workspace member pin
so versions are centralized (i.e., replace the loose version entry for
tauri-plugin-log with a workspace reference). Update the dependency entry for
tauri-plugin-log in this crate's Cargo.toml to use { workspace = true } (or move
the version into the workspace Cargo.toml and reference it from here) so it
matches the other workspace-managed dependencies.

In `@crates/runtime-types-ffi/Cargo.toml`:
- Line 19: The dependency version for bytes in the runtime-types-ffi crate is
pinned to "1.10.1" and should be aligned with the workspace version; change the
bytes declaration in crates/runtime-types-ffi/Cargo.toml from bytes = "1.10.1"
to bytes = { workspace = true } so it inherits the workspace dependency (or add
a brief comment in that Cargo.toml explaining why this crate must override the
workspace version if you intentionally need 1.10.1).

In `@crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts`:
- Line 17: The nonce assignment is using await and Uint8Array.from
unnecessarily; replace the current expression so nonce is assigned directly from
crypto.getRandomValues(new Uint8Array(32)) (i.e., remove the await and the extra
copy). Update the variable initialization where nonce is declared in index.ts to
use crypto.getRandomValues directly.

In `@crates/tauri-plugin-holochain/package.json`:
- Around line 14-25: The current package.json lists build-only packages
("`@holochain/client`", "`@msgpack/msgpack`", "`@tauri-apps/api`") under
"dependencies" but they are only inlined into the bundle via nodeResolve() and
not runtime exports; move those three entries out of "dependencies" and add them
to "devDependencies" (preserve versions), leaving only true runtime deps in
"dependencies" so the manifest accurately reflects build vs runtime
requirements.

In `@docs/holochain-0.6-and-unified-plugin-plan.md`:
- Around line 28-34: Add a language tag to the fenced ASCII diagram block to
silence markdownlint MD040; edit the code block that currently starts with ```
and contains the tauri-plugin-service / tauri-plugin-client /
tauri-plugin-holochain ASCII diagram so it reads ```text (i.e. add the text tag
after the opening backticks). This change targets the fenced block containing
the lines with "tauri-plugin-service (Rust shim)", "tauri-plugin-client  (Rust
shim)", and "tauri-plugin-holochain (pure Rust)" to ensure the diagram is
treated as plain text by the linter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b48ce22c-ef8b-414c-a89c-685751f595cd

📥 Commits

Reviewing files that changed from the base of the PR and between 4514bf6 and eb15d8d.

⛔ Files ignored due to path filters (36)
  • Cargo.lock is excluded by !**/*.lock
  • apps/holochain-runtime-example/src-tauri/icons/128x128.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/128x128@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/32x32.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square107x107Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square142x142Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square150x150Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square284x284Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square30x30Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square310x310Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square44x44Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square71x71Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/Square89x89Logo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/StoreLogo.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/icon.ico is excluded by !**/*.ico
  • apps/holochain-runtime-example/src-tauri/icons/icon.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-20x20@1x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-20x20@2x-1.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-20x20@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-20x20@3x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-29x29@1x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-29x29@2x-1.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-29x29@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-29x29@3x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-40x40@1x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-40x40@2x-1.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-40x40@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-40x40@3x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-512@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-60x60@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-60x60@3x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-76x76@1x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-76x76@2x.png is excluded by !**/*.png
  • apps/holochain-runtime-example/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png is excluded by !**/*.png
  • crates/tauri-plugin-holochain/dist-js/holochain-env/index.min.js is excluded by !**/*.min.js
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (65)
  • Cargo.toml
  • DEVELOPMENT.md
  • apps/android-service-runtime/src-tauri/src/lib.rs
  • apps/example-client-app/Cargo.toml
  • apps/example-client-app/dnas/forum/workdir/dna.yaml
  • apps/example-client-app/dnas/forum/zomes/coordinator/posts/Cargo.toml
  • apps/example-client-app/dnas/forum/zomes/coordinator/posts/src/all_posts.rs
  • apps/example-client-app/dnas/forum/zomes/coordinator/posts/src/comment.rs
  • apps/example-client-app/dnas/forum/zomes/coordinator/posts/src/post.rs
  • apps/example-client-app/package.json
  • apps/example-client-app/ui/package.json
  • apps/example-client-app/workdir/happ.yaml
  • apps/example-client-app/workdir/web-happ.yaml
  • apps/holochain-runtime-example/.gitignore
  • apps/holochain-runtime-example/src-tauri/Cargo.toml
  • apps/holochain-runtime-example/src-tauri/build.rs
  • apps/holochain-runtime-example/src-tauri/capabilities/default.json
  • apps/holochain-runtime-example/src-tauri/icons/icon.icns
  • apps/holochain-runtime-example/src-tauri/src/lib.rs
  • apps/holochain-runtime-example/src-tauri/src/main.rs
  • apps/holochain-runtime-example/src-tauri/tauri.conf.json
  • apps/holochain-runtime-example/ui/index.html
  • apps/holochain-runtime-example/ui/package.json
  • apps/holochain-runtime-example/ui/src/main.js
  • apps/holochain-runtime-example/ui/vite.config.js
  • crates/runtime-ffi/fixtures/forum.happ
  • crates/runtime-ffi/src/autostart.rs
  • crates/runtime-ffi/src/runtime.rs
  • crates/runtime-types-ffi/Cargo.toml
  • crates/runtime-types-ffi/src/types.rs
  • crates/runtime/Cargo.toml
  • crates/runtime/fixtures/forum.happ
  • crates/runtime/src/config.rs
  • crates/runtime/src/error.rs
  • crates/runtime/src/runtime.rs
  • crates/tauri-plugin-client/android/src/main/java/InvokeTypes.kt
  • crates/tauri-plugin-holochain/.gitignore
  • crates/tauri-plugin-holochain/Cargo.toml
  • crates/tauri-plugin-holochain/build.rs
  • crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts
  • crates/tauri-plugin-holochain/package.json
  • crates/tauri-plugin-holochain/permissions/default.toml
  • crates/tauri-plugin-holochain/rollup.config.js
  • crates/tauri-plugin-holochain/src/commands.rs
  • crates/tauri-plugin-holochain/src/error.rs
  • crates/tauri-plugin-holochain/src/lib.rs
  • crates/tauri-plugin-holochain/tests/integration.rs
  • crates/tauri-plugin-holochain/tsconfig.json
  • crates/tauri-plugin-service/android/src/main/java/com/plugin/holochain_service/InvokeTypes.kt
  • docs/holochain-0.6-and-unified-plugin-plan.md
  • flake.nix
  • libraries/client/src/androidTest/kotlin/JsonTest.kt
  • libraries/client/src/androidTest/kotlin/ParcelablesTest.kt
  • libraries/client/src/main/aidl/org/holochain/androidserviceruntime/client/IHolochainServiceAdmin.aidl
  • libraries/client/src/main/aidl/org/holochain/androidserviceruntime/client/IHolochainServiceApp.aidl
  • libraries/client/src/main/aidl/org/holochain/androidserviceruntime/client/IHolochainServiceCallback.aidl
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/HolochainServiceAdminClient.kt
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/HolochainServiceAppClient.kt
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/IHolochainServiceCallbackStub.kt
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Json.kt
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelables.kt
  • libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelers.kt
  • libraries/service/src/main/java/org/holochain/androidserviceruntime/service/HolochainService.kt
  • package.json
  • rust-toolchain.toml

Comment on lines +10 to +22
"@holochain/client": "^0.20.0-dev.2",
"@msgpack/msgpack": "^3.1.2",
"uuid": "^11.1.0"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tsconfig/svelte": "^3.0.0",
"@tsconfig/svelte": "^5.0.4",
"bestzip": "^2.2.1",
"rimraf": "^5.0.10",
"svelte": "^4.0.0",
"svelte": "^5.0.0",
"tslib": "^2.8.0",
"typescript": "^5.6.3",
"vite": "^5.4.10"
"vite": "^6.2.5"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

@sveltejs/vite-plugin-svelte 5 peer dependency vite 6 svelte 5

💡 Result:

The @sveltejs/vite-plugin-svelte package introduced support for Vite 6 beginning with its major version 4 release (specifically via PRs #1020 and #1026) [1][2][3][4]. Regarding peer dependencies for @sveltejs/vite-plugin-svelte version 4 and later: - Svelte 5: Versions of the plugin supporting Vite 6 typically require Svelte 5 [2]. - Vite 6: The plugin's peer dependency for Vite was updated to include ^6.0.0-beta.10 || ^6.0.0 [2][4]. For the most up-to-date requirements, it is recommended to check the specific package version on npm or the GitHub repository, as later versions of the plugin (such as 5.x, 6.x, and 7.x) have continued to evolve their dependency ranges [1][5][6]. For example, as of June 2026, newer versions of the plugin may list different peer dependency ranges for both Vite and Svelte to accommodate newer major releases [5][6]. Always consult the official package.json for your installed version to ensure compatibility [5][6].

Citations:


Bump @sveltejs/vite-plugin-svelte to v4+ (e.g., ^5.0.0) to match Vite 6 / Svelte 5.
@sveltejs/vite-plugin-svelte major v3.1.x predates Vite 6 support; Vite 6 support starts with major v4, where peer dependency ranges were updated for Vite 6 and (typically) Svelte 5—so leaving it at ^3.1.2 will cause peer dependency conflicts with vite@^6.2.5 and svelte@^5.0.0.

🔧 Proposed change
-    "`@sveltejs/vite-plugin-svelte`": "^3.1.2",
+    "`@sveltejs/vite-plugin-svelte`": "^5.0.0",

Regenerate the lockfile and ensure the resolved plugin version’s peerDependencies include Vite 6 and Svelte 5.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/example-client-app/ui/package.json` around lines 10 - 22, Update the
devDependency "`@sveltejs/vite-plugin-svelte`" from v3.x to a v4+ release (e.g.,
"^5.0.0") so it is compatible with "vite" v6 and "svelte" v5; then regenerate
the lockfile (npm/yarn/pnpm) and verify the resolved
"`@sveltejs/vite-plugin-svelte`" package's peerDependencies include support for
Vite 6 and Svelte 5 to avoid peer dependency conflicts.

Comment on lines +22 to +24
fn data_dir() -> PathBuf {
std::env::temp_dir().join("holochain-runtime-example")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fixed temp_dir data directory persists across runs and may break re-installs.

data_dir() always returns the same temp/holochain-runtime-example path, so conductor state survives between launches. On a second run, setup_app installing APP_ID = "forum" into an already-populated conductor can fail with an "app already installed" error rather than starting clean. For a repeatable example, consider a unique/cleaned directory per run (e.g., tempfile::TempDir) or making install idempotent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/holochain-runtime-example/src-tauri/src/lib.rs` around lines 22 - 24,
data_dir() currently returns a fixed temp path which lets conductor state
persist between runs and causes "app already installed" on subsequent runs;
change the implementation so each run uses a unique/cleanable temp dir (for
example create a tempfile::TempDir per run and return its path or wire it into
the runtime startup) or alternatively make setup_app/idempotent by checking for
APP_ID ("forum") and skipping install if already present; update references to
data_dir(), setup_app(), and APP_ID to use the new TempDir-backed path or the
pre-install check and ensure the TempDir is kept alive for the process lifetime
so the path remains valid until shutdown.

Comment on lines +26 to +27
/// URL of the iroh-raly server
pub relay_url: Url2,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Typo in documentation comment.

The comment says "iroh-raly" but should be "iroh-relay".

📝 Proposed fix
-    /// URL of the iroh-raly server
+    /// URL of the iroh-relay server
     pub relay_url: Url2,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// URL of the iroh-raly server
pub relay_url: Url2,
/// URL of the iroh-relay server
pub relay_url: Url2,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/runtime/src/config.rs` around lines 26 - 27, The doc comment for the
config field relay_url contains a typo ("iroh-raly"); update the documentation
comment above the public field relay_url: Url2 to read "URL of the iroh-relay
server" so the comment correctly spells "relay".

Comment on lines +16 to +44
signZomeCall: async (request: CallZomeRequest): Promise<CallZomeRequestSigned> => {
const nonce = Uint8Array.from(await crypto.getRandomValues(new Uint8Array(32)));
const expiresAt = 1e3 * (Date.now() + 3e5);
const payload = Array.from(encode(request.payload));

const zomeCall = {
provenance: Array.from(request.provenance),
cellIdDnaHash: Array.from(request.cell_id[0]),
cellIdAgentPubKey: Array.from(request.cell_id[1]),
zomeName: request.zome_name,
fnName: request.fn_name,
capSecret: null,
payload,
nonce: Array.from(nonce),
expiresAt,
};

// The Rust `sign_zome_call` command takes a single `request` argument.
const response = await (window as any).__TAURI_INTERNALS__.invoke(
'plugin:holochain|sign_zome_call',
{ request: zomeCall }
);

return {
bytes: Uint8Array.from(response.bytes),
signature: Uint8Array.from(response.signature),
} as CallZomeRequestSigned;
},
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In @holochain/client version 0.20.5, what object shape does window.__HC_ZOME_CALL_SIGNER__.signZomeCall receive and what fields must its returned CallZomeRequestSigned contain?

💡 Result:

In @holochain/client v0.20.5, window.HC_ZOME_CALL_SIGNER.signZomeCall takes an unsigned zome call request (CallZomeRequest) and returns a signed zome call request (CallZomeRequestSigned). For CallZomeRequestSigned, the required object shape/fields are: { provenance: Uint8Array (agent public key) cap_secret: CapSecret | null cell_id: [Uint8Array, Uint8Array] (CellId: [DnaHash, AgentPubKey]) zome_name: string fn_name: string payload: Uint8Array (serialized payload) signature: Uint8Array (signature over the zome-call parameters bytes) expires_at: number (timestamp) nonce: Uint8Array } The fields above correspond to the zome call signed parameters required by Holochain: the signed call contains (at minimum) cell_id, zome_name, fn_name, payload, cap_secret, provenance, signature, nonce, and expires_at. [1][2][3] Confidence: medium (the exact TS interface for @holochain/client v0.20.5 isn’t directly surfaced in the fetched docs, but the concrete CallZomeRequestSigned field mapping matches the zome-call signing structure used by holochain client tooling and the underlying Rust ZomeCall fields).

Citations:


Fix window.__HC_ZOME_CALL_SIGNER__.signZomeCall to return the full CallZomeRequestSigned expected by @holochain/client@0.20.5

  • @holochain/client v0.20.5’s signZomeCall contract expects the returned CallZomeRequestSigned to include fields like provenance, cap_secret, cell_id, zome_name, fn_name, payload (serialized), signature, expires_at, and nonce. The current implementation returns only { bytes, signature }, which can break callZome.
  • Confirm encode(request.payload) isn’t double-serializing (the signed payload needs to be the serialized Uint8Array that the client/conductor expects).
  • Drop the unnecessary await on crypto.getRandomValues(...), and re-check the expiresAt units (the current code derives from Date.now() in ms and multiplies by 1e3).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts` around lines
16 - 44, The signZomeCall implementation returns only {bytes, signature} and
mis-handles nonce and expiresAt and payload serialization; update
window.__HC_ZOME_CALL__.signZomeCall so it returns a full CallZomeRequestSigned
object matching `@holochain/client`@0.20.5: include provenance, cap_secret (null
or request.cap_secret), cell_id as [cellIdDnaHash, cellIdAgentPubKey],
zome_name, fn_name, payload as a serialized Uint8Array (call
encode(request.payload) exactly once and use Uint8Array.from(...) rather than
Array.from to avoid double-serialization), nonce as a Uint8Array (remove the
unnecessary await on crypto.getRandomValues and call crypto.getRandomValues(new
Uint8Array(32)) directly), expires_at in the correct epoch-second units (compute
from Date.now() / 1000 and add the desired TTL, not Date.now() * 1e3), and
signature as a Uint8Array from the plugin response; populate these fields when
returning from signZomeCall (function name: signZomeCall, locals: nonce,
expiresAt/expires_at, payload, zomeCall, response).

Comment on lines +106 to +114
.initialization_script(
format!(
r#"window.injectHolochainClientEnv("{}", {}, {:?});"#,
app_id,
app_auth.port,
app_auth.authentication.token,
)
.as_str(),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the type of `token` on the app-auth/authentication struct returned by ensure_app_websocket
rg -nP -C3 '\btoken\b' --type=rust crates/runtime/src
ast-grep --pattern 'pub authentication: $_'

Repository: holochain/android-service-runtime

Length of output: 2068


🏁 Script executed:

rg -nP 'pub struct AppAuthenticationTokenIssued|pub token:' --type=rust

Repository: holochain/android-service-runtime

Length of output: 230


🏁 Script executed:

rg -nP 'AppAuthenticationTokenIssued' --type=rust crates/runtime/src/types.rs -A 5 -B 5

Repository: holochain/android-service-runtime

Length of output: 330


🏁 Script executed:

# Check if serde is available in the project
find . -name "Cargo.toml" -exec grep -l "serde_json\|serde" {} \;

Repository: holochain/android-service-runtime

Length of output: 459


🏁 Script executed:

cat crates/tauri-plugin-holochain/Cargo.toml

Repository: holochain/android-service-runtime

Length of output: 1156


🏁 Script executed:

# Check the actual code context around lines 106-114 in lib.rs
cat -n crates/tauri-plugin-holochain/src/lib.rs | head -150 | tail -60

Repository: holochain/android-service-runtime

Length of output: 2502


🏁 Script executed:

# Search for injectHolochainClientEnv in JavaScript files to understand expected format
find . -type f \( -name "*.js" -o -name "*.ts" \) | xargs grep -l "injectHolochainClientEnv" 2>/dev/null || echo "Not found in JS/TS files"

Repository: holochain/android-service-runtime

Length of output: 460


🏁 Script executed:

cat crates/tauri-plugin-holochain/guest-js/holochain-env/index.ts

Repository: holochain/android-service-runtime

Length of output: 1860


🏁 Script executed:

# Check what AppAuthenticationTokenIssued is from holochain crate
rg -nP 'struct AppAuthenticationTokenIssued|pub token' --type=rust | grep -A 3 -B 3 "AppAuthenticationTokenIssued"

Repository: holochain/android-service-runtime

Length of output: 230


🏁 Script executed:

# Check dependencies to see if serde_json is available
rg 'serde_json' --type=toml crates/tauri-plugin-holochain/

Repository: holochain/android-service-runtime

Length of output: 59


🏁 Script executed:

# Check if serde_json is imported in the file
rg 'serde_json' --type=rust crates/tauri-plugin-holochain/src/

Repository: holochain/android-service-runtime

Length of output: 59


🏁 Script executed:

# Check workspace dependencies to see if serde_json is available
cat Cargo.toml | rg -A 30 '\[workspace.dependencies\]'

Repository: holochain/android-service-runtime

Length of output: 843


🏁 Script executed:

# Verify what {:?} produces for Vec<u8>
python3 << 'PY'
# Simulate what {:?} produces for Vec<u8> in Rust
# Vec<u8> debug format is [1, 2, 3, ...]
test_token = [72, 101, 108, 108, 111]  # "Hello" in ASCII
print(f"Debug format of Vec<u8>: {test_token}")
print(f"As JS code: window.injectHolochainClientEnv(\"test\", 8000, {test_token});")
print("\nThis is valid JavaScript array syntax")
PY

Repository: holochain/android-service-runtime

Length of output: 251


🏁 Script executed:

# Check if app_id could have problematic characters
rg -n 'app_id.*String' --type=rust crates/tauri-plugin-holochain/src/lib.rs -B 2 -A 2

Repository: holochain/android-service-runtime

Length of output: 258


🏁 Script executed:

# Verify serde_json behavior with Vec<u8>
python3 << 'PY'
import json

# Test what serde_json would produce
token = [72, 101, 108, 108, 111]  # Vec<u8> representing "Hello"
app_id = "my_app"

# serde_json::to_string on Vec<u8> produces JSON array
token_json = json.dumps(token)
app_id_json = json.dumps(app_id)

print("Token as JSON:", token_json)
print("App ID as JSON:", app_id_json)

# What the final format would produce
js_code = f'window.injectHolochainClientEnv({app_id_json}, 8000, {token_json});'
print("\nFinal JS code:")
print(js_code)

# Test with problematic app_id
problematic_app_id = 'my_app";alert("xss'
escaped = json.dumps(problematic_app_id)
print("\nProblematic app_id:", problematic_app_id)
print("Escaped by serde_json:", escaped)
print("In JS:", f'window.injectHolochainClientEnv({escaped}, 8000, {token_json});')
PY

Repository: holochain/android-service-runtime

Length of output: 415


🏁 Script executed:

# Check what imports are currently in the lib.rs file
head -30 crates/tauri-plugin-holochain/src/lib.rs

Repository: holochain/android-service-runtime

Length of output: 1363


🏁 Script executed:

# Check where app_id comes from and if it could contain problematic characters
rg -n 'ensure_app_websocket' --type=rust crates/runtime/src/ -B 3 -A 10 | head -50

Repository: holochain/android-service-runtime

Length of output: 3187


🏁 Script executed:

# Check if app_id validation exists anywhere
rg -n 'app_id.*valid|validate.*app_id' --type=rust crates/

Repository: holochain/android-service-runtime

Length of output: 59


Escape app_id and use JSON serialization for proper JavaScript injection.

Interpolating app_id directly inside double quotes with {} creates a security vulnerability — if app_id contains ", \, or other special characters, it will break the generated script. Use serde_json::to_string() to properly escape both values, ensuring robust, self-escaping injection.

♻️ Suggested approach
 .initialization_script(
     format!(
-        r#"window.injectHolochainClientEnv("{}", {}, {:?});"#,
-        app_id,
+        "window.injectHolochainClientEnv({}, {}, {});",
+        serde_json::to_string(&app_id)?,
         app_auth.port,
+        serde_json::to_string(&app_auth.authentication.token)?,
     )
     .as_str(),
 )

serde_json is already available as a workspace dependency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-plugin-holochain/src/lib.rs` around lines 106 - 114, The
generated initialization script interpolates app_id and token directly into a
double-quoted JS string which can break or introduce injection vulnerabilities;
update the code that builds the script (the call chain using
.initialization_script(...) and the injected JS function
injectHolochainClientEnv) to JSON-serialize the string values using
serde_json::to_string(&app_id) and
serde_json::to_string(&app_auth.authentication.token) and pass the numeric port
as-is (app_auth.port) so the format string becomes
window.injectHolochainClientEnv(<serialized app_id>, <port>, <serialized
token>); ensuring proper escaping and safe JS injection.

Comment thread DEVELOPMENT.md
To bump the holochain version you will need to update all holochain dependencies in the top-level `Cargo.toml` file, and then publish all the components listed in the following order:
To bump the holochain version you will need to do these things:
1. update all holochain dependencies in the top-level `Cargo.toml` file
2. do a `nix flake update` before `nix develop` to bring down the latest Holochain version. Note that if you are updating to a major version you may need to change the source for `holonix.url` and `taurie-plugin-holochain.url` in the `flake.nix` file first.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale reference: flake.nix no longer has a tauri-plugin-holochain input.

This PR removes the tauri-plugin-holochain input from flake.nix (Phase 0). Instructing contributors to update taurie-plugin-holochain.url (also misspelled) is misleading — only holonix.url remains. Suggest dropping it.

📝 Proposed fix
-2. do a `nix flake update` before `nix develop` to bring down the latest Holochain version.  Note that if you are updating to a major version you may need to change the source for `holonix.url` and `taurie-plugin-holochain.url` in the `flake.nix` file first.
+2. do a `nix flake update` before `nix develop` to bring down the latest Holochain version.  Note that if you are updating to a major version you may need to change the source for `holonix.url` in the `flake.nix` file first.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
2. do a `nix flake update` before `nix develop` to bring down the latest Holochain version. Note that if you are updating to a major version you may need to change the source for `holonix.url` and `taurie-plugin-holochain.url` in the `flake.nix` file first.
2. do a `nix flake update` before `nix develop` to bring down the latest Holochain version. Note that if you are updating to a major version you may need to change the source for `holonix.url` in the `flake.nix` file first.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DEVELOPMENT.md` at line 5, The instruction in DEVELOPMENT.md references a
removed input and misspells it; update the sentence to remove any mention of the
obsolete tauri-plugin-holochain input and its URL, and only mention holonix.url
as the source that may need changing when updating to a major Holochain version;
specifically edit the line that currently mentions
`taurie-plugin-holochain.url`/`tauri-plugin-holochain` so it only refers to
`holonix.url` (and correct the spelling) to avoid misleading contributors.

Comment on lines +110 to 115
if (!this.waitForConnectReady()) {
throw HolochainServiceNotConnectedException()
}

this.waitForServiceReady()
return this.setupApp(installAppPayload, enableAfterInstall)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

waitForServiceReady() result is discarded — setup proceeds even when the runtime never became ready.

waitForConnectReady() is properly guarded, but the return value of waitForServiceReady() is ignored. If the service is bound yet the conductor doesn't finish starting within the 30s timeout, execution falls through to setupApp(...), which only checks mService != null and will issue the call against a not-ready runtime. This defeats the purpose of the readiness wait.

🛡️ Proposed fix
         if (!this.waitForConnectReady()) {
             throw HolochainServiceNotConnectedException()
         }
 
-        this.waitForServiceReady()
+        if (!this.waitForServiceReady()) {
+            throw HolochainServiceNotConnectedException()
+        }
         return this.setupApp(installAppPayload, enableAfterInstall)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!this.waitForConnectReady()) {
throw HolochainServiceNotConnectedException()
}
this.waitForServiceReady()
return this.setupApp(installAppPayload, enableAfterInstall)
if (!this.waitForConnectReady()) {
throw HolochainServiceNotConnectedException()
}
if (!this.waitForServiceReady()) {
throw HolochainServiceNotConnectedException()
}
return this.setupApp(installAppPayload, enableAfterInstall)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@libraries/client/src/main/java/org/holochain/androidserviceruntime/client/HolochainServiceAppClient.kt`
around lines 110 - 115, The call to waitForServiceReady() currently has its
boolean result ignored, allowing setupApp(...) to run even when the conductor
never became ready; update the flow after waitForServiceReady() to check its
return value and abort if false — throw a specific exception (e.g.,
HolochainServiceNotReadyException or reuse HolochainServiceNotConnectedException
with a clear message) instead of proceeding to setupApp(installAppPayload,
enableAfterInstall). Keep the existing waitForConnectReady() guard, but ensure
waitForServiceReady() failing prevents calling setupApp.

Comment on lines +179 to 204
object AppStatusFfiParceler : Parceler<AppStatusFfi> {
override fun create(parcel: Parcel): AppStatusFfi =
when (parcel.readInt()) {
1 ->
AppInfoStatusFfi.Paused(
parcelableCreator<PausedAppReasonFfiParcel>().createFromParcel(parcel).inner,
)
2 ->
AppInfoStatusFfi.Disabled(
AppStatusFfi.Disabled(
parcelableCreator<DisabledAppReasonFfiParcel>().createFromParcel(parcel).inner,
)
3 -> AppInfoStatusFfi.Running
4 -> AppInfoStatusFfi.AwaitingMemproofs
else -> throw IllegalArgumentException("Unknown AppInfoStatusFfi type")
2 -> AppStatusFfi.Enabled
3 -> AppStatusFfi.AwaitingMemproofs
else -> throw IllegalArgumentException("Unknown AppStatusFfi type")
}

override fun AppInfoStatusFfi.write(
override fun AppStatusFfi.write(
parcel: Parcel,
flags: Int,
) {
when (this) {
is AppInfoStatusFfi.Paused -> {
is AppStatusFfi.Disabled -> {
parcel.writeInt(1)
PausedAppReasonFfiParcel(reason).writeToParcel(parcel, flags)
}
is AppInfoStatusFfi.Disabled -> {
parcel.writeInt(2)
DisabledAppReasonFfiParcel(reason).writeToParcel(parcel, flags)
}
is AppInfoStatusFfi.Running -> parcel.writeInt(3)
is AppInfoStatusFfi.AwaitingMemproofs -> parcel.writeInt(4)
is AppStatusFfi.Enabled -> parcel.writeInt(2)
is AppStatusFfi.AwaitingMemproofs -> parcel.writeInt(3)
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for AppStatusFfi variant definitions in Kotlin code
rg -n 'sealed\s+(class|interface)\s+AppStatusFfi|object\s+Running|object\s+Enabled' \
  --type=kotlin -C3

# Check for all AppStatusFfi usage
rg -n 'AppStatusFfi\.(Running|Enabled|Disabled|AwaitingMemproofs)' --type=kotlin

Repository: holochain/android-service-runtime

Length of output: 2731


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find where AppStatusFfi is defined
rg -n --hidden -S --type=kotlin 'AppStatusFfi\s*(sealed|data|class|interface|enum)' .

# 2) Specifically locate any mention of "Running" within AppStatusFfi-related code
rg -n --hidden -S --type=kotlin 'AppStatusFfi\.(Running|Enabled|Disabled|AwaitingMemproofs)' libraries/client/src/main/java libraries/client/src/main/kotlin libraries/client/src/androidTest/kotlin libraries/client/src/androidTest/java  || true

# 3) Inspect the parceler file around the discriminator mappings
sed -n '150,230p' libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelers.kt

# 4) Inspect the failing/related test around line ~327
sed -n '290,390p' libraries/client/src/androidTest/kotlin/ParcelablesTest.kt

Repository: holochain/android-service-runtime

Length of output: 59


🏁 Script executed:

#!/bin/bash
set -u

# 1) Find where AppStatusFfi is defined (tolerate no matches)
rg -n --hidden -S --type=kotlin 'AppStatusFfi\s*(sealed|data|class|interface|enum)' . || true

# 2) Search for AppStatusFfi variants usage (tolerate no matches)
rg -n --hidden -S --type=kotlin 'AppStatusFfi\.(Running|Enabled|Disabled|AwaitingMemproofs)' \
  libraries/client/src/main/java libraries/client/src/main/kotlin libraries/client/src/androidTest/kotlin libraries/client/src/androidTest/java || true

# 3) Inspect the parceler file around the discriminator mappings
sed -n '150,230p' libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelers.kt

# 4) Inspect the test around where Running is used (~327)
sed -n '300,380p' libraries/client/src/androidTest/kotlin/ParcelablesTest.kt

Repository: holochain/android-service-runtime

Length of output: 8524


🏁 Script executed:

#!/bin/bash
set -u

# Locate any generated/declared AppStatusFfi type
fd -a 'AppStatusFfi*' . || true

# Find the actual definition by searching for "sealed class AppStatusFfi" etc.
rg -n --hidden --type=kotlin 'AppStatusFfi' libraries/client/src/main || true
rg -n --hidden --type=kotlin 'sealed\s+(class|interface)\s+AppStatusFfi|object\s+Running|object\s+Enabled|enum\s+class\s+AppStatusFfi' . || true

# See whether "Running" appears anywhere other than the parcelable test
rg -n --hidden --type=kotlin 'AppStatusFfi\.Running' . || true
rg -n --hidden --type=kotlin 'AppStatusFfi\.Enabled' . || true

Repository: holochain/android-service-runtime

Length of output: 2876


🏁 Script executed:

#!/bin/bash
set -u

# Find any files that mention AppStatusFfi (across repo)
rg -n --hidden --type=kotlin 'AppStatusFfi' . || true

# Find any Kotlin declarations of AppStatusFfi variants
rg -n --hidden --type=kotlin 'object\s+Running\b|object\s+Enabled\b|sealed\s+class\s+AppStatusFfi\b|sealed\s+interface\s+AppStatusFfi\b|enum\s+class\s+AppStatusFfi\b|class\s+AppStatusFfi\b' . || true

# List any potential source files related to "AppStatusFfi"
fd -a -t f 'AppStatusFfi*' . || true
fd -a -t f '*Parcel*AppStatus*' . || true

Repository: holochain/android-service-runtime

Length of output: 6177


🏁 Script executed:

#!/bin/bash
set -u

# Locate files named AppStatusFfi*
fd -a -t f 'AppStatusFfi*' libraries/client/src/main . || true

# Also locate any file defining parcel/JSON annotations for AppStatusFfi
rg -n --hidden --type=kotlin 'class\s+AppStatusFfi\b|sealed\s+(class|interface)\s+AppStatusFfi\b|object\s+AppStatusFfi\b|interface\s+AppStatusFfi\b|enum\s+class\s+AppStatusFfi\b' libraries/client/src/main . || true

# Search for plain "Running" declarations in the AppStatusFfi file(s) (if we find them by name)
for f in $(fd -a -t f 'AppStatusFfi*' libraries/client/src/main . 2>/dev/null || true); do
  echo "---- $f ----"
  rg -n --hidden --type=kotlin 'Running\b|Enabled\b|AwaitingMemproofs\b|Disabled\b' "$f" || true
done

Repository: holochain/android-service-runtime

Length of output: 59


Align AppStatusFfi variant naming between parceling and tests

  • AppStatusFfiParceler maps discriminator 2 to AppStatusFfi.Enabled and writes AppStatusFfi.Enabled as 2
  • ParcelablesTest.kt’s testAppStatusFfiParcelRunning constructs/asserts AppStatusFfi.Running for the same parcel path
  • Update either the parceler/test to use the same variant (Running vs Enabled) so the roundtrip is consistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@libraries/client/src/main/java/org/holochain/androidserviceruntime/client/Parcelers.kt`
around lines 179 - 204, The parceler and the tests disagree on the variant name
for discriminator 2; update the AppStatusFfiParceler so it uses the same variant
as the tests by replacing AppStatusFfi.Enabled with AppStatusFfi.Running in both
the create() branch for case 2 and the write() branch that writes discriminator
2 (i.e., modify AppStatusFfiParceler to map 2 <-> AppStatusFfi.Running), or
alternatively update ParcelablesTest.kt's testAppStatusFfiParcelRunning to
expect AppStatusFfi.Enabled—pick one consistent variant and change the two
matching occurrences (the case with readInt() == 2 and the writeInt(2) branch)
so create(), write(), and tests align.

Comment thread package.json
"test:service": "cd libraries/service && ./gradlew connectedDebugAndroidTest",
"test:client": "cd libraries/client && ./gradlew connectedDebugAndroidTest",
"test": " npm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:service && pnpm run test:client",
"test": " npm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:tauri-plugin && pnpm run test:service && pnpm run test:client",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inconsistent runner: test:runtime is invoked with npm, the rest with pnpm.

The aggregate test mixes npm run test:runtime with pnpm run .... In a pnpm workspace this is inconsistent and can behave differently (lifecycle, workspace resolution). Normalize to pnpm and drop the leading space.

📝 Proposed fix
-      "test": " npm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:tauri-plugin && pnpm run test:service && pnpm run test:client",
+      "test": "pnpm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:tauri-plugin && pnpm run test:service && pnpm run test:client",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"test": " npm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:tauri-plugin && pnpm run test:service && pnpm run test:client",
"test": "pnpm run test:runtime && pnpm run test:runtime-ffi && pnpm run test:tauri-plugin && pnpm run test:service && pnpm run test:client",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 67, The "test" npm script currently invokes
"test:runtime" with npm and the rest with pnpm and includes a leading space;
update the package.json "test" script so all sub-scripts use pnpm (replace "npm
run test:runtime" with "pnpm run test:runtime") and remove the leading space
before the command string to ensure consistent pnpm workspace behavior and
correct execution of the "test" script.

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