Repository navigation
Description
Why
Every limit the agent operates under today is enforced by the daemon's policy gate — software. This issue makes the limits chain-enforced: the user's EOA reversibly delegates (EIP-7702) to an audited contract, and the agent's session key physically cannot exceed its cap, outlive its expiry, or touch a non-allowlisted target — even if the daemon's software fence were gone. Verified white space (research 2026-06-11): Kohaku has no 7702/session-key work, eth-infinitism ships no scoped-key contract, Walletbeat marks scoped permissions as future work; MetaMask's Agent Wallet EAP (June 8, 2026) started the clock.
Decisions already made
No bundler, ever, in this scope. Both candidate stacks support the session key sending a plain, self-gas-paid transaction (verified in source):
| MetaMask Delegation Framework | Porto / IthacaAccount | |
|---|---|---|
| 7702 delegate | EIP7702StatelessDeleGator (stateless; EOA key stays root) |
IthacaAccount (on-chain key registry) |
| Grant | off-chain EIP-712 delegation + caveat enforcers (ERC-20 amount, timestamp, allowed-targets, value-LTE, …) | on-chain authorize(Key) + setCanExecute + setSpendLimit |
| No-bundler execution | permissionless DelegationManager.redeemDelegations, caveat-checked |
ERC-7821 execute external-caller path (opData = nonce ++ sig) |
| Revoke | disableDelegation (delegator-only, on-chain) |
revoke(keyHash) (self-call by admin key) |
| Assurance | 12 firm audit reports (Cyfrin ×7, ConsenSys Diligence ×5, → 2026-05) | 2-week individual-researcher review; no firm report found |
Sources: https://github.com/MetaMask/delegation-framework · https://github.com/ithacaxyz/account
Backend chosen at implementation time against these frozen criteria, in order: (1) assurance evidence at impl time (a Porto firm audit reopens the question), (2) Sepolia + mainnet deployment availability, (3) grant-card legibility, (4) reuse for x402's ERC-7710 settlement path. Grant/revoke/execute semantics below are backend-neutral so the choice doesn't churn the wire.
v1 scope (cut 2026-06-12): SessionScope = { per_tx_cap_wei, expiry_unix, allow_to, token } — the single per-tx cap is the only spend cap. Period caps and per-method granularity are deferred (both backends support them; pure additions later). Expiry and allowlist are not caps and not cuttable — a grant without them is unbounded authority.
In scope
-
SessionGrant { scope }/SessionRevoke { key_id }indeckard-contractunder capability namesession-keys-7702(depends on Wire-contract evolution: Hello capability discovery + the five evolution rules #31; additive per its evolution rules). - Session key generated and held by the daemon (a second, lesser keystore class); local policy stays fence build(gpui): default to the git GPUI pair (Zed + gpui-component) over crates.io #1; the daemon enforces
min(local policy, session scope). - 7702 authorization signing with hard invariants: never sign
chain_id = 0(spec-valid on all chains — unconditional refusal); delegate address must be on a hardcoded, versioned allowlist of audited deployments — user-supplied delegates rejected unconditionally (>97% of early mainnet delegations were malicious sweepers, per Wintermute); self-sponsored delegation signs auth nonce +1 (the tx consumes the current nonce); un-delegation (address(0)) clears code not storage, so re-delegation across storage-incompatible implementations is refused. -
SessionGrant→NeedsApprovalalways — no policy mode auto-grants authority. Grant card renders delegate identity + version and every scope field.RevokeAll/STOP additionally queues on-chain revokes for every live grant. Grant/revoke/expiry events go to the append-only audit log. - Acceptance on the anvil Sepolia fork (chosen backend deployed from source if no canonical Sepolia deployment):
- S1 grant {cap 0.05 ETH, expiry 24 h, allowlist [A]} → NeedsApproval, card complete, approve → live
- S2 session key sends a plain EIP-1559 tx within the cap to A → lands, gas paid by session EOA, no bundler process anywhere
- S3 over-cap attempt → reverts on-chain (not a daemon Deny)
- S4 target ∉ allowlist → reverts on-chain
- S5 warp past expiry → reverts on-chain
- S6 revoke (one tx) → retry of S2 reverts; local session key zeroized
- S7 un-delegate to
address(0)→ EOA code empty, plain sends work as before - S8 grant naming an unpinned delegate → typed Deny, nothing signed
- S9 authorization request with
chain_id = 0→ refused, nothing signed - S10 transcript scan → no key bytes, no passphrase
- KB correction in
docs/research/01-landscape-2026.md: MetaMask Advanced Permissions = Early Access 2026-06-08, not shipped 2026-04-06.
Not in scope
- Period/daily caps, per-method (selector) granularity (deferred 2026-06-12).
- Walletbeat rubric contribution (cut 2026-06-12 — revisit with shipped evidence).
- Exporting a grant to an external agent runtime (the future
smart-account-autonomousmode) — semantics are shaped so it later needs no wire change, but the key does not leave the daemon in v1. - Mainnet (guardrail unchanged; canonical deployments + audit posture gate it).
Definition of done
- All in-scope checkboxes; S1–S10 runnable via the demo harness.
-
cargo fmt --all --checkclean;just checkgreen (both feature configs);cargo test --workspacegreen. - New dependencies (contract bindings etc.) listed and explicitly approved before merge, per repo rule.
Open questions
- Fund the session EOA at grant time (visible, capped) or lazily via the low-gas pre-flight?
- Do queued on-chain revokes broadcast while the vault is locked (pre-signed revoke txs), or is "revoke on next unlock" honest enough for v1?
Activity
docs(walletbeat): reconcile with ADR 0001 + issue tracking convention
hellno commentedon Jun 19, 2026
⛓ Blocked by #31 — adds SessionGrant/SessionRevoke request kinds; needs #31's evolution rules.
hellno commentedon Jun 24, 2026
Per ADR-0005 (#135): session-key grants reuse the Rules vocabulary (cap / allowlist / expiry / approval-mode) but remain a chain-enforced mechanism, distinct from #48's software-enforced origin grants. Do not unify the two enforcers — software is not "cannot exceed". The grant/attenuation model stays deferred per ADR-0002.
docs(adr): 0005 policy & authorization model (per-action Rules) (#136)