Search projects, hackathons, sponsors and tech

    ← All projects

    AgentKey

    Give your AI agent a spending key it can't misuse — limits set and enforced from your Ledger.

    Demo

    About

    AgentKey is a hardware-backed permission system for AI agents. Any AI — running on EC2, inside Claude, inside GPT — can take onchain USDC actions, bounded by hard limits that only the owner's Ledger hardware device can configure and reinstate. The kill switch is not a software flag. It is enforced inside a Chainlink CRE Nitro TEE, attested by hardware, with policy stored live on ENS and reinstatement gated to a physical button press on the Ledger device. Every agent is a real ENS subname. Its spend policy — daily cap, weekly cap, whitelist, velocity limit, and live status — lives as ENS text records on a per-agent resolver. The Chainlink CRE workflow runs inside an AWS Nitro TEE, reads that policy from ENS on every request, enforces a three-tier kill switch (GREEN / YELLOW / RED), and either writes a DON-attested report to AgentVault.onReport() to execute the transfer, or writes status="suspended" back to ENS to block the agent immediately. No single operator can bypass this — the policy is onchain, the enforcement is in hardware, and the only reinstatement path is an EIP-712 signature from the physical Ledger that created the agent, displayed as clear-signed human-readable text on the device screen. AgentVault.onReport() verifies the Chainlink DON threshold signature via KeystoneForwarder.verify() before executing any USDC transfer, and double-checks ENS status at settlement — so a suspension that fires between CRE report generation and on-chain settlement still blocks the transfer. The result is an AI agent permission system where the safety guarantee is architectural, not a policy promise.

    How it's made

    AgentKey is a Next.js frontend, a set of Solidity contracts, and a Chainlink CRE TypeScript workflow running inside an AWS Nitro TEE. *Contracts (contracts/src/):* AgentRegistrar creates each agent as a real ENS subname via ENSRegistry.setSubnodeRecord(), deploying a per-agent AgentTextResolver and AgentVault atomically. The vault becomes the ENS node owner — this is the vault-as-owner pattern that authorizes the vault to write its own policy text records without any separate role grant. AgentVault.onReport() verifies the Chainlink DON threshold signature via KeystoneForwarder.verify() before executing any USDC transfer, then double-checks ENS status at settlement. Reinstatement requires an EIP-712 signature verified against agentLedgerWallet[node] — the stored Ledger wallet address — not ENSRegistry.owner() which returns the vault. *CRE Workflow (cre/agentkey-workflow.ts):* Registered via cre.handlerInTee with { tee: 'nitro' } on an HTTP trigger. On every agent request it pulls all secrets (RPC URLs, contract addresses) from the CRE Vault DON via runtime.getSecrets() — nothing in source or env. It reads the agent's live policy from ENS, evaluates the three-tier kill switch, and either calls evmClient.writeReport() to settle the transfer or routes a suspension report through AgentVault.onReport() which triggers AgentRegistrar.suspend(). *ENS as live policy layer:* Every agent's spend limits, whitelist, velocity cap, and status are ENS text records (daily_cap, weekly_cap, whitelist, velocity_max, status, spent_today, agentkey.endpoint) on a per-agent AgentTextResolver. The CRE relayer can only ever call AgentRegistrar.suspend() — hardcoded to write status and suspension_reason only. There is no function surface for CRE to touch caps or whitelist. *Ledger integration (app/lib/ledger.ts, components/ledger/):* EIP-712 domain and type definitions for CreateAgent and Reinstate match AgentRegistrar._domainSeparator() byte-for-byte. LedgerSign renders each field as a named clear-signing screen. For the demo, a Speculos Ledger emulator runs in Docker; LiveSpeculosScreen.tsx polls the real emulator screenshot at 300ms so judges see the actual device frame during signing. *Encrypted credential keyring (app/lib/keyring.ts):* Agent JWTs are AES-256-GCM encrypted client-side before being stored as an ENS text record via AgentRegistrar.setEncryptedCredential(). The AES key is derived by signing a fixed EIP-712 KeyringDerivation message with the owner's wallet. The chain only ever sees ciphertext. Write access is gated by the same EIP-712 pattern as reinstatement, with a per-agent nonce for replay protection. The notably interesting design point: ENSRegistry.owner(node) returns the vault address, not the human owner. Reinstatement verification uses agentLedgerWallet[node] — the address stored at agent creation time — so the Ledger wallet check is always against the human, even though the ENS token is held by the contract.