Skip to content

Replace dev_key_service with TEE worker for omni-anchored EVM keypair derivation #74

@hanwencheng

Description

@hanwencheng

Background

Per Stage 7 design (#64) and aligned with the Wildmeta omni_account + TEE-signer pattern, the daemon's auth flow should converge on:

  • One user-facing identity (email / OAuth2) → omni_account anchor
  • Server-derived EVM wallet per omni_account (deterministic, never leaves the trust boundary)
  • No local key management by the operator — recovery via re-authenticating any linked identity in IdentityLinkStore

The pieces in place today:

The missing piece: a custodial signer that derives a keypair from omni_account and signs SIWE challenges on the user's behalf so the daemon never needs a local secp256k1 key.

Step 1 (this issue's first deliverable) — dev_key_service in mock-server

Ship a development-only signer module in crates/agentkeys-mock-server with the exact wire shape the eventual TEE worker will use, gated by env so production builds reject it.

Module

  • crates/agentkeys-mock-server/src/dev_key_service.rs
    • Loads master secret from \$DEV_KEY_SERVICE_MASTER_SECRET (32-byte hex). Refuse to enable if absent.
    • derive_keypair(omni_account) -> (secret_key, eth_address) via HKDF-SHA256 with info = "agentkeys-evm-wallet-v1:" || omni_account.
    • sign_eip191(omni_account, message) -> Signature.
    • Strong "DEV ONLY — replace with TEE" warnings in the file header + every public method.

Endpoints (env-gated, return 503 if DEV_KEY_SERVICE_MASTER_SECRET unset)

  • POST /dev/derive-address — body { omni_account }{ address }
  • POST /dev/sign-message — body { omni_account, message_hex }{ signature }

Daemon integration

Update crates/agentkeys-daemon/src/main.rs to use the new flow:

  1. Operator authenticates via email/OAuth2 → broker mints session JWT for omni_email
  2. Daemon calls backend /dev/derive-address with omni_email → derived EVM address
  3. Daemon calls broker /v1/wallet/link (auth: omni_email session JWT) to register the derived address
  4. Per-mint: daemon calls broker /v1/auth/wallet/start(derived_addr) → SIWE message
  5. Daemon calls backend /dev/sign-message(omni_email, siwe_message) → signature
  6. Daemon calls broker /v1/auth/wallet/verify → session JWT for omni_evm
  7. Daemon calls broker /v1/mint-oidc-jwt → OIDC JWT → AssumeRoleWithWebIdentity → AWS temp creds

The signer call (step 5) is direct daemon → backend, mirroring the eventual daemon → TEE attested channel.

Removes

  • agentkeys init --mock-token legacy bootstrap (replaced by email/OAuth2 + auto-derive)
  • /v1/auth/exchange legacy bearer shim (no caller after daemon migrates)
  • Broker → backend /session/validate round-trip (no caller after exchange shim deletes)

Step 2 (follow-up issue) — Replace dev_key_service with TEE worker

Same wire surface (/dev/* endpoints become /tee/* or stay), real TEE backing:

  • Master secret generated inside the enclave at first boot, sealed-data persisted
  • Remote attestation so daemon can verify the worker is genuine before sending omni → key derivation requests
  • Logs every signing operation with omni_account + message hash, no secret material

When TEE lands: dev_key_service.rs deletes; routing flips to TEE worker URL via env var; zero changes to daemon or broker.

Out of scope

  • Threshold signing for high-value omni_accounts
  • Master-secret rotation policy
  • Multi-region TEE replication
  • Production gating of DEV_KEY_SERVICE_MASTER_SECRET (compile-time cfg(not(production)) could come later)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions