Skip to content

Persist + rehydrate the master session across daemon restart #220

@hanwencheng

Description

@hanwencheng

Context

A daemon restart (dev.sh rebuilds it on any Rust source change via build_if_needed, or Ctrl-C+rerun) drops the master's in-memory session, so the web memory/config pages 502 with config not ready: real chain: master device not registered on chain yet … and no --master-device-key-hash fallback set. Nothing durable is actually lost — only the daemon's in-memory cache of it — but the daemon doesn't rehydrate, forcing a re-onboarding ceremony on every restart. This is the implementation gap behind arch.md's phone-first / no-always-on master-plane design (§"Master control-plane host model": the master plane is event-driven + biometric-gated, push-woken, never always-on; the broker is the only always-on component).

Problem (current behavior)

  • agentkeys-daemon ui-bridge holds onboarding_session + registered_master in RwLock<Option<…>> (ui_bridge.rs:730/740) and persists/rehydrates neither.
  • resolve_session_coords (ui_bridge.rs:2311) resolves the master device_key_hash from only the in-memory registered_master, never querying the chain — so after a restart it errors even though the device IS registered on chain (the daemon log shows tx=(already-registered)).
  • The error string ("master device not registered on chain yet") is misleading: it means "not in daemon memory," not "not on chain."

The durable anchors already exist (restore needs no re-onboarding)

  • K11 WebAuthn passkey, Secure-Enclave-sealed, TouchID-gated — survives everything (arch.md K-inventory).
  • SidecarRegistry on chain = source of truth for the device binding; master device_key_hash = keccak(operator_omni) (§9 stage 4 / harness/scripts/_lib.sh) → deterministically derivable, no cache needed.
  • K10 in the OS keychain (TouchID-backed).
  • J1 is long-lived + re-mintable via passkey-as-identity re-auth (arch.md §10.2, "Standard (re-auth case)").

Scope

  • Persist the master session coordinates (operator_omni, device_key_hash, the J1 while valid) to ~/.agentkeys/daemon-<wallet>/session.json (mode 0600 — the agent daemon already uses this pattern per arch.md §11.2; the master ui-bridge doesn't).
  • Rehydrate on startup: valid J1 → zero-prompt restore; expired/absent J1 → one TouchID passkey re-auth (NOT a full re-onboarding).
  • Resolve device_key_hash by deriving keccak(operator_omni) or a SidecarRegistry.getDevice lookup, instead of caching only the K11-finish result.
  • Fix the misleading error to distinguish "no local session" from "actually not on chain."
  • The WASM-in-browser port (same lib/client contract) inherits this with IndexedDB in place of the file.

Acceptance

  • Restart the daemon (or a dev.sh rebuild) with a still-valid J1 → the web memory/config pages work with zero prompts (no re-onboarding, no --master-device-key-hash).
  • With an expired J1 → exactly one TouchID passkey re-auth restores the session.
  • No --master-device-key-hash fallback flag is needed for the normal dev/web loop.
  • cred-wire-demo.sh / the web-parity demo stay green.

Out of scope

  • The WASM / native-lib ports themselves (this lands the daemon behavior + the shared lib/client shape they reuse).
  • The browser passkey-account register UserOp wiring (arch.md E7 frontend item).

Effort

~L (session persistence + startup rehydrate + passkey re-auth-on-expiry + chain/derive device-hash resolution + the error-message fix).

References

  • arch.md §"Master control-plane host model (phone-first)" · §10.2 (passkey-as-identity re-auth) · §11.2 (agent session.json pattern) · K-inventory (K10/K11 durability)
  • crates/agentkeys-daemon/src/ui_bridge.rs (resolve_session_coords :2311, onboarding_session/registered_master :730/740, finish_chain_register :1074)
  • Surfaced while testing the web app (Path A) per docs/operator-runbook-wire.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/daemonagentkeys-daemon (sidecar) work

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions