feat: controllable boot, hc-auth, restart-keeping-lair, seed export (Phase 3) - #139
feat: controllable boot, hc-auth, restart-keeping-lair, seed export (Phase 3)#139zippy wants to merge 7 commits into
Conversation
Phase 3 support for the unyt-on-ASR migration, on the in-process direct-mode plugin + runtime. - Controllable boot: init_deferred registers the plugin without booting; HolochainPlugin::start / start_with_config(passphrase, config) boot the conductor late (e.g. once a user-typed lair password is collected). The runtime is held behind RwLock<Option<_>>; runtime() returns an owned clone; new holochain://lair-ready event emitted before EVENT_READY. - Lair-first boot: spawn lair in-proc (at the same path holochain uses for lair_root: None, so existing keystores are reused) and build the conductor with .with_keystore(..), so the hc-auth flow can sign a challenge and inject the auth material into the NetworkConfig before the network starts. - hc-auth (crates/runtime/src/hc_auth.rs): GET /now -> sign via lair -> PUT /authenticate flow; HcAuthConfig / HcAuthStatus; runtime accessors hc_auth_status / hc_auth_agent_key / hc_auth_raw_ed25519_b64url; HolochainPluginConfig::with_hc_auth. - Restart-keeping-lair: stop_conductor_only + restart_with_hc_auth rebuild the conductor on the still-running lair; plugin swap_runtime re-binds the per-window signal forwarders. - Agent identity + backup: device_agent_key() (the device seed's ed25519 key, a deterministic identity), export_agent_seed() (lair export_seed_by_tag + local decrypt), with_pending_import_seed (import as the device seed at boot), generate_agent_pub_key(). Adds deps: holochain_keystore, reqwest (rustls-tls), base64. Includes unit tests for the seed export/import round-trip (incl. a byte-exact ground-truth check) and device-key identity restore.
|
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 |
…eate)
Direct mode binds an app to a webview at window creation, so a consuming app must
destroy + rebuild the OS window to switch which app a window talks to -- on boot
and on every dashboard<->network switch. That breaks WebDriver-based UI tooling
(the session dies with "no such window"), drops window state, and flickers on
desktop.
Add an in-place rebind so the OS window persists:
- rebind_window(label, Option<InstalledAppId>): update the app_request routing
(the window_apps map is already read per request), swap the window's signal
forwarder, and emit EVENT_REBOUND. None unbinds the window (app-less).
- EVENT_REBOUND ("holochain://rebound"): payload is the new app id (or null); the
injected env updates __HC_TAURI_HOLOCHAIN__.INSTALLED_APP_ID in place and
dispatches a `holochain-rebound` DOM event, so @holochain/client can reconnect
to the new app without a reload.
- Track per-window signal-forwarder task handles so a rebind (or the re-bind on
swap_runtime) aborts the previous forwarder rather than leaking the old app's
signal stream.
- main_window_builder now takes Option<app_id>: None injects the IPC env (+ the
rebound listener) on an unbound window -- the dashboard -- to be bound later via
rebind_window, with no recreate.
Verified against unyt's direct-mode integration on the real Tauri/WebKitGTK app:
the persistent window injects the env, renders, and rebinds without recreation.
… gate rebind_window_reroutes_app_request_in_place binds a window to one app, rebinds to a second (app_request routing follows the binding), then unbinds (asserting WindowNotBound). wait_for_ready gated on holochain() — Ok as soon as the plugin is registered, before the conductor boots (init_deferred relies on that) — so it never waited and the two existing integration tests passed only by timing. Gate on try_runtime() instead, and correct the holochain() doc comment to match.
…er a non-local agent key Installing an app under an agent key that lives in a different lair (so the local keystore can't sign for it) fails genesis with SourceChainError "Query returned no rows" — pinning the contract behind a consumer's stale-persisted-key symptom.
…ck, destroy cleanup, ordered rebound Addresses zippy's review on PR #141: - #1 spawn-before-bind: rebind_window spawns the new forwarder before flipping routing, so a failed rebind keeps the prior binding (was: routing flipped before the fallible spawn, with no rollback). - #2 window-destroy cleanup: an on_event(RunEvent::WindowEvent::Destroyed) handler prunes the window maps and aborts the forwarder (was: never pruned). - #3 ordered rebound: EVENT_REBOUND carries a monotonic seq and the injected env applies only newer ones, so an out-of-order rebound can't strand the UI.
…t staleness 3a33f18 changed EVENT_REBOUND to ReboundEvent { seq, app_id } and updated guest-js/index.ts, but the include_str!'d dist-js/holochain-env/index.min.js was not rebuilt — so the shipped listener still read e.payload (the whole object) as the app id and had no seq gate (#3's fix wasn't in effect at runtime). Rebuilt via `npm run build`, and added an integration test asserting the bundle reads the structured payload fields, since no runtime test exercises the injected JS.
feat(tauri-plugin-holochain): rebind a window's app in place (no recreate)
|
❌ Found 6 compliant commit and 1 non-compliant commits in cc73dd9...d3fe6b2. Commit d3fe6b2 by @zippy is not conform to the conventional commit specification :
feat(tauri-plugin-holochain): rebind a window's app in place (no recreate)`
|
|
Closing: this branch's content has already landed on |
Summary
The "controllable-boot" layer for unyt-on-ASR: splits the lair keystore from the conductor so the conductor can be authenticated, restarted, and seeded under runtime control. Stacked on #138.
What's in it
Runtime::new_with_boot_config(RuntimeBootConfig{ data_root_path, network, hc_auth, pending_import_seed }). Spawns lair in-proc first, optionally runs the hc-auth flow, then builds the conductor with.with_keystore(...)on that same lair. The existingnew/new_with_network_confignow funnel through it (no API break).crates/runtime/src/hc_auth.rs) — get-or-create a persistent Ed25519 agent key in lair,GET /nowchallenge, sign via lair,PUT /authenticate→ status, build base64 auth material and inject intoNetworkConfig.base64_auth_material_{bootstrap,relay}. Auth-server failures returnOk(Failed(..))so the conductor still boots.stop_conductor_only(disables apps first) +restart_with_hc_authreturns a freshRuntimeon the same lair; pluginswap_runtimere-binds bound windows' signal forwarders to it.export_agent_seed(inverse of import, viaexport_seed_by_tag+ local box-decrypt),import_seed_into_lair,device_agent_key(the device seed's ed25519 key = deterministic identity for agent-key backup/restore).init_deferred(config)registers the plugin without booting; the host callsHolochainPlugin::start(passphrase)/start_with_config(...)once a (possibly user-typed) lair password is collected.start_lockserializes concurrent unlocks; a failed unlock is retryable. NewEVENT_LAIR_READY.Review notes
reqwest(rustls-tls, to match the workspace crypto stack) +base64;holochain_keystorepinned at0.6.1(candidate to move to a workspace dep).export_agent_seedhas a byte-exact round-trip test. The hc-auth HTTP flow andrestart_with_hc_auth/swap_runtimeare not yet covered by automated tests (would need a mock auth server).hc_auth.rsis ported from the unytcotauri-plugin-holochainfork (feat/hc-auth), adapted to this crate'sRuntimeError.Test plan
cargo test -p holochain-conductor-runtime(includes the seed round-trip test).