Part of #44 (ADR 0001). Phase 2. The policy + trust layer that makes the Deckard-native bridge (PRD-04) safe: scope each connected origin (accounts × chains × methods), ship a curated registry + anti-phishing blocklist, treat origin as attacker-controllable. Pairs with PRD-04; depends on PRD-02.
Why
Once a dapp can connect (PRD-04), the wallet must answer "what may this origin do?" not "what may any caller do?". Standards (research §37): EIP-2255 (wallet_requestPermissions/getPermissions, {invoker, parentCapability, caveats}) and CAIP-25 scoping (accounts × chains × methods). Origin is self-asserted and spoofable (research §13, 29) → scope + attestation-as-hint + shipped blocklist, and the human always approves on decoded effects, never a claimed name.
Goals
- Per-origin permission model: a connected origin gets a scoped, persisted, revocable session (accounts/chains/methods/caps/expiry), enforced by the daemon, distinct from the global
Policy.
- Curated dapp registry (the allowlist-first posture for generic connectivity): known-good origins + metadata + ERC-7730 descriptor refs. Offline-first and signed if updatable out-of-band.
- Anti-phishing blocklist (known-malicious domains; eth-phishing-detect style), shipped + updatable.
wallet_addEthereumChain/switch guards (research §36).
Non-goals
Transport (PRD-04); signing decode (PRD-02); building/curating ERC-7730 upstream (we reference/consume). Open access as default — default is curated-allowlist; unknown origins only via an explicit friction-ful "unverified origin" confirm (never silent).
Design
Per-origin policy (deckard-contract + deckard-signerd): OriginGrant { origin, accounts, chains, methods, caps?, expiry }. The global Policy stays the outer fence — a grant can only ever be narrower (a grant never widens global caps/allowlist). Daemon evaluates both; stricter wins. Pure evaluate_origin(&proposal, &OriginGrant, &Policy) next to the existing decision fns (parity charter). Grants created at connect-time (human approves the requested scope, EIP-2255/CAIP-25 style), revocable from settings governance (next to "Pause all agents").
Origin attestation (hint only, research §29): origin string from the untrusted connector is shown unverified unless corroborated by a registry match or first-party attestation (PRD-04). No third-party relay attestation (WalletConnect/Verify shelved with WC). Decoded effects remain ground truth; never gate solely on a green check.
Registry + blocklist (offline-first, signed): in-repo signed JSON of curated origins → metadata + descriptor refs; ships with the build; out-of-band updates require a maintainer signature (tampered/unsigned → refused loudly, mirror policy_store.rs). Blocklist match = hard, loud block (red) even if the user tries to proceed — the one place we override user intent (a known drainer domain). Registry references which ERC-7730 descriptors apply so PRD-02's card renders human-readable intent; uncovered → PRD-02 blind-sign fallback.
Acceptance tests
origin_grant_cannot_widen_global; out_of_scope_method_denied; blocklist_hard_blocks; unsigned_registry_refused (falls back to safe built-in = nothing auto-trusted); unverified_origin_requires_friction; addchain_uses_user_chainid; evaluate_origin mock/daemon parity.
DoD
Shared DoD (#44) plus: revoke-origin + list-connected-origins are ⌘K commands + in settings governance; registry/blocklist trust model in SECURITY.md; "origin-untrusted, effects-are-ground-truth" in THREAT-MODEL.md; UI matches DESIGN.md (no green-check-as-safety).
Sources: research §13, 29, 36–37; EIP-2255, EIP-3085/3326; CAIP-25; eth-phishing-detect; existing policy.rs, policy_store.rs.
Part of #44 (ADR 0001). Phase 2. The policy + trust layer that makes the Deckard-native bridge (PRD-04) safe: scope each connected origin (accounts × chains × methods), ship a curated registry + anti-phishing blocklist, treat origin as attacker-controllable. Pairs with PRD-04; depends on PRD-02.
Why
Once a dapp can connect (PRD-04), the wallet must answer "what may this origin do?" not "what may any caller do?". Standards (research §37): EIP-2255 (
wallet_requestPermissions/getPermissions,{invoker, parentCapability, caveats}) and CAIP-25 scoping (accounts × chains × methods). Origin is self-asserted and spoofable (research §13, 29) → scope + attestation-as-hint + shipped blocklist, and the human always approves on decoded effects, never a claimed name.Goals
Policy.wallet_addEthereumChain/switchguards (research §36).Non-goals
Transport (PRD-04); signing decode (PRD-02); building/curating ERC-7730 upstream (we reference/consume). Open access as default — default is curated-allowlist; unknown origins only via an explicit friction-ful "unverified origin" confirm (never silent).
Design
Per-origin policy (
deckard-contract+deckard-signerd):OriginGrant { origin, accounts, chains, methods, caps?, expiry }. The globalPolicystays the outer fence — a grant can only ever be narrower (a grant never widens global caps/allowlist). Daemon evaluates both; stricter wins. Pureevaluate_origin(&proposal, &OriginGrant, &Policy)next to the existing decision fns (parity charter). Grants created at connect-time (human approves the requested scope, EIP-2255/CAIP-25 style), revocable from settings governance (next to "Pause all agents").Origin attestation (hint only, research §29): origin string from the untrusted connector is shown unverified unless corroborated by a registry match or first-party attestation (PRD-04). No third-party relay attestation (WalletConnect/Verify shelved with WC). Decoded effects remain ground truth; never gate solely on a green check.
Registry + blocklist (offline-first, signed): in-repo signed JSON of curated origins → metadata + descriptor refs; ships with the build; out-of-band updates require a maintainer signature (tampered/unsigned → refused loudly, mirror
policy_store.rs). Blocklist match = hard, loud block (red) even if the user tries to proceed — the one place we override user intent (a known drainer domain). Registry references which ERC-7730 descriptors apply so PRD-02's card renders human-readable intent; uncovered → PRD-02 blind-sign fallback.Acceptance tests
origin_grant_cannot_widen_global;out_of_scope_method_denied;blocklist_hard_blocks;unsigned_registry_refused(falls back to safe built-in = nothing auto-trusted);unverified_origin_requires_friction;addchain_uses_user_chainid;evaluate_originmock/daemon parity.DoD
Shared DoD (#44) plus: revoke-origin + list-connected-origins are ⌘K commands + in settings governance; registry/blocklist trust model in
SECURITY.md; "origin-untrusted, effects-are-ground-truth" inTHREAT-MODEL.md; UI matchesDESIGN.md(no green-check-as-safety).Sources: research §13, 29, 36–37; EIP-2255, EIP-3085/3326; CAIP-25; eth-phishing-detect; existing
policy.rs,policy_store.rs.