Problem
The parent-control web app (apps/parent-control) renders the master-side flows but does not perform the real backend calls. Audited in PR #162 (docs/plan/web-flow/wire-real-paths.md §2):
- Onboarding ceremony — only the K11 WebAuthn step is real (and only in
daemon backend mode); email-verify, wallet/SIWE→J1, and register_master_device are progress-bar narration with a mock tx hash. "Logged in" is a localStorage flag, not a session JWT.
- Memory plant/list — hits the daemon ui-bridge but stores in an in-memory map; no cap-mint → STS → memory worker → S3.
- Pairing / scope / revoke mutations — in-memory stubs; no broker pairing calls, no on-chain
registerAgentDevice / setScopeWithWebauthn.
- Under the default
empty backend (what a fresh user runs), nothing is real.
Root issue: the orchestration logic lives behind a localhost daemon (desktop-only). For the phone-first product (most operators have only a phone, no desktop), the browser/client itself must carry the master-plane logic and talk to the always-on broker.
Target
Complete the real browser-side wiring of the master plane so the web app drives the master onboarding, memory, pairing, and mutations through the real broker + chain + memory worker — the same calls harness/phase1-wire-demo.sh already makes — with no fabricated/in-memory data.
Per the plan (#162, docs/plan/web-flow/wire-real-paths.md §0.5 + §11 + §12): factor the master-plane orchestration into a portable agentkeys-core, compile it to WASM, and expose it as a CoreBackend behind the existing lib/client AgentKeysClient interface — so the browser calls the broker directly (the same core later binds to the mobile native app via UniFFI).
Scope (master plane, browser/client side only)
Gating constraint (verified — wire-real-paths.md §11)
Every master chain write is msg.sender-bound to the operator's secp256k1 key (SidecarRegistry / AgentKeysScope); K11 is an additional gate, not a substitute. A browser/WASM host cannot safely custody the secp256k1 key, so it must delegate the on-chain broadcast to a key-holding host (phone Keychain / desktop daemon) — unless the contracts move to assertion-only auth. The exact secure delegation/relayer mechanism is the open design question (under adversarial security review; see #162 §11 fork A vs B).
Acceptance criteria
- With
NEXT_PUBLIC_AGENTKEYS_BACKEND=core, a fresh operator completes onboarding (email → Touch ID → wallet/SIWE → master registered on chain) entirely from the browser, producing the same on-disk + on-chain artifacts the harness produces.
- Memory planted from the UI is readable via the real memory worker / S3 (cross-checked by the agent-side
hook memory-inject).
- A pairing claim + scope grant from the UI lands real
registerAgentDevice + setScopeWithWebauthn txs; isServiceInScope(...) == true.
- No narrated/in-memory data paths remain on the master plane; disconnected/empty states are honest.
References
- Plan: PR #162 —
docs/plan/web-flow/wire-real-paths.md (§0.5 host model, §11 gating, §12 WASM scope), data-model.md (phone-first amendment), arch.md §22c.3.
- Reference implementation of the real calls:
harness/phase1-wire-demo.sh.
- Canonical ceremonies:
docs/arch.md §9 (master bootstrap), §10.1, §10.2.
Out of scope
Problem
The parent-control web app (
apps/parent-control) renders the master-side flows but does not perform the real backend calls. Audited in PR #162 (docs/plan/web-flow/wire-real-paths.md§2):daemonbackend mode); email-verify, wallet/SIWE→J1, andregister_master_deviceare progress-bar narration with a mock tx hash. "Logged in" is alocalStorageflag, not a session JWT.registerAgentDevice/setScopeWithWebauthn.emptybackend (what a fresh user runs), nothing is real.Root issue: the orchestration logic lives behind a localhost daemon (desktop-only). For the phone-first product (most operators have only a phone, no desktop), the browser/client itself must carry the master-plane logic and talk to the always-on broker.
Target
Complete the real browser-side wiring of the master plane so the web app drives the master onboarding, memory, pairing, and mutations through the real broker + chain + memory worker — the same calls
harness/phase1-wire-demo.shalready makes — with no fabricated/in-memory data.Per the plan (#162,
docs/plan/web-flow/wire-real-paths.md§0.5 + §11 + §12): factor the master-plane orchestration into a portableagentkeys-core, compile it to WASM, and expose it as aCoreBackendbehind the existinglib/clientAgentKeysClientinterface — so the browser calls the broker directly (the same core later binds to the mobile native app via UniFFI).Scope (master plane, browser/client side only)
agentkeys-corewith a host-agnostic API (no axum/daemon deps); extend the existinginit_flowwith pairing + cap + onboarding-state.wasm-bindgenexports for: email auth (start/verify/status), wallet SIWE (start/verify), pairing (claim/pending/ack), cap-mint, onboarding-state aggregation.wasm-packbuild.CoreBackendimplementingAgentKeysClient(alongsideempty/daemon);NEXT_PUBLIC_AGENTKEYS_BACKEND=core. UI unchanged (same interface).navigator.credentials.{create,get}; the K11 enroll + assert flow back into the core.binding_nonce; real wallet/SIWE → J1 (replaces thelocalStorage.ak_onboardedflag with a real session); realregister_master_device./v1/agent/pairing/*,/v1/agent/pending-bindings) + bind/grant chain writes.Gating constraint (verified —
wire-real-paths.md§11)Every master chain write is
msg.sender-bound to the operator's secp256k1 key (SidecarRegistry/AgentKeysScope); K11 is an additional gate, not a substitute. A browser/WASM host cannot safely custody the secp256k1 key, so it must delegate the on-chain broadcast to a key-holding host (phone Keychain / desktop daemon) — unless the contracts move to assertion-only auth. The exact secure delegation/relayer mechanism is the open design question (under adversarial security review; see #162 §11 fork A vs B).Acceptance criteria
NEXT_PUBLIC_AGENTKEYS_BACKEND=core, a fresh operator completes onboarding (email → Touch ID → wallet/SIWE → master registered on chain) entirely from the browser, producing the same on-disk + on-chain artifacts the harness produces.hook memory-inject).registerAgentDevice+setScopeWithWebauthntxs;isServiceInScope(...) == true.References
docs/plan/web-flow/wire-real-paths.md(§0.5 host model, §11 gating, §12 WASM scope),data-model.md(phone-first amendment),arch.md§22c.3.harness/phase1-wire-demo.sh.docs/arch.md§9 (master bootstrap), §10.1, §10.2.Out of scope
wire/hook flow (separate concern).data-model.md).