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.
Context
A daemon restart (
dev.shrebuilds it on any Rust source change viabuild_if_needed, or Ctrl-C+rerun) drops the master's in-memory session, so the web memory/config pages 502 withconfig 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-daemonui-bridge holdsonboarding_session+registered_masterinRwLock<Option<…>>(ui_bridge.rs:730/740) and persists/rehydrates neither.resolve_session_coords(ui_bridge.rs:2311) resolves the masterdevice_key_hashfrom only the in-memoryregistered_master, never querying the chain — so after a restart it errors even though the device IS registered on chain (the daemon log showstx=(already-registered)).The durable anchors already exist (restore needs no re-onboarding)
device_key_hash = keccak(operator_omni)(§9 stage 4 /harness/scripts/_lib.sh) → deterministically derivable, no cache needed.passkey-as-identityre-auth (arch.md §10.2, "Standard (re-auth case)").Scope
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).device_key_hashby derivingkeccak(operator_omni)or aSidecarRegistry.getDevicelookup, instead of caching only the K11-finish result.lib/clientcontract) inherits this with IndexedDB in place of the file.Acceptance
dev.shrebuild) with a still-valid J1 → the web memory/config pages work with zero prompts (no re-onboarding, no--master-device-key-hash).--master-device-key-hashfallback flag is needed for the normal dev/web loop.cred-wire-demo.sh/ the web-parity demo stay green.Out of scope
lib/clientshape they reuse).Effort
~L (session persistence + startup rehydrate + passkey re-auth-on-expiry + chain/derive device-hash resolution + the error-message fix).
References
passkey-as-identityre-auth) · §11.2 (agentsession.jsonpattern) · 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)docs/operator-runbook-wire.md.