Skip to content

feat: controllable boot, hc-auth, restart-keeping-lair, seed export (Phase 3) - #139

Closed
zippy wants to merge 7 commits into
feat/tauri-direct-admin-callsfrom
feat/tauri-direct-auth-and-restart
Closed

feat: controllable boot, hc-auth, restart-keeping-lair, seed export (Phase 3)#139
zippy wants to merge 7 commits into
feat/tauri-direct-admin-callsfrom
feat/tauri-direct-auth-and-restart

Conversation

@zippy

@zippy zippy commented Jun 4, 2026

Copy link
Copy Markdown
Member

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.

Status: Phase 3 / co-design. This is the deferred, co-design slice (designed with the unyt agent). Its consumer is unyt, not the ASR example — several primitives here are intentionally unexercised by ASR itself. Reviewing it now to get eyes on the design; flag if you'd rather land #136#138 first and hold this.

What's in it

  • Controllable bootRuntime::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 existing new/new_with_network_config now funnel through it (no API break).
  • hc-auth (crates/runtime/src/hc_auth.rs) — get-or-create a persistent Ed25519 agent key in lair, GET /now challenge, sign via lair, PUT /authenticate → status, build base64 auth material and inject into NetworkConfig.base64_auth_material_{bootstrap,relay}. Auth-server failures return Ok(Failed(..)) so the conductor still boots.
  • Restart-keeping-lairstop_conductor_only (disables apps first) + restart_with_hc_auth returns a fresh Runtime on the same lair; plugin swap_runtime re-binds bound windows' signal forwarders to it.
  • Seed export / restoreexport_agent_seed (inverse of import, via export_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).
  • Deferred bootinit_deferred(config) registers the plugin without booting; the host calls HolochainPlugin::start(passphrase) / start_with_config(...) once a (possibly user-typed) lair password is collected. start_lock serializes concurrent unlocks; a failed unlock is retryable. New EVENT_LAIR_READY.

Review notes

  • New deps in the runtime crate: reqwest (rustls-tls, to match the workspace crypto stack) + base64; holochain_keystore pinned at 0.6.1 (candidate to move to a workspace dep).
  • Tests: export_agent_seed has a byte-exact round-trip test. The hc-auth HTTP flow and restart_with_hc_auth/swap_runtime are not yet covered by automated tests (would need a mock auth server).
  • hc_auth.rs is ported from the unytco tauri-plugin-holochain fork (feat/hc-auth), adapted to this crate's RuntimeError.

Test plan

  • cargo test -p holochain-conductor-runtime (includes the seed round-trip test).
  • hc-auth / restart paths to be validated against a real auth server in the unyt integration (no GL/UI needed for the runtime tests).

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.
@coderabbitai

coderabbitai Bot commented Jun 4, 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: 9dd04305-1b42-496b-901d-0e686e98e8eb

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-auth-and-restart

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.

zo-el and others added 6 commits June 23, 2026 13:41
…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)
@cocogitto-bot

cocogitto-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

❌ 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)`

  • cause:
    Missing commit type separator `:`
    
    Caused by:
         --> 1:6
          |
        1 | Merge pull request #141 from holochain/feat/tauri-window-rebind
          |      ^---
          |
          = expected scope or type_separator
    

@zippy

zippy commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Closing: this branch's content has already landed on main-0.6 (merged via the stack's merge commits, e.g. 063b62f and its predecessors, then carried into main-0.7). All commits here are ancestors of origin/main-0.6; nothing in this PR is unmerged.

@zippy zippy closed this Aug 3, 2026
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.

2 participants