x/agent Real (skeleton + hot path)
AI agents as first-class economic accounts. Built on the Cosmos SDK x/accounts smart-account substrate. Operator + hotkey two-key model, rolling-24h spend policies, recursive child agents capped at depth 3.
keeper/authenticate.go hot path is
not stubbed: rolling-24h SpendPolicy enforcement is
real today because the agent spec treats it as security-critical.
Two-key model
- Operator (coldkey) - controls revocation and policy changes. Cold storage; rarely signs.
- Hotkey - rotatable signing key for routine transactions. Hot wallet.
Each agent has its own wallet, can stake, can earn, and can hire other agents (depth 3 cap in genesis params).
SpendPolicy: rolling 24h, not UTC midnight
Same logic as x/messaging: a strict UTC-midnight reset is replay-attackable. The keeper tracks day_window_start per agent and resets when ctx.BlockTime - day_window_start >= 86400. The check lives in keeper/authenticate.go::Authenticate and runs on every agent-signed transaction.
Recursion: parent budget debited at spawn, refunded on revoke
MsgSpawnChild requires the child's policy to be <= the parent's remaining policy. The child's allocation is subtracted from the parent's budget at spawn; on revoke, the unused portion is refunded. See keeper/recursion.go.
Recovery: split between operator and RecoveryGuard
| Operation | Who can call | Timelock |
|---|---|---|
MsgRevokeAgent | operator OR RecoveryGuard quorum | none (instant) |
MsgRotateHotkey | operator + RecoveryGuard sig | none |
MsgUpdatePolicy (tightening) | operator | none |
MsgUpdatePolicy (broadening) | operator | 24h timelock |
A multisig RecoveryGuard of N-of-M operator-designated recovery addresses overrides the operator on sensitive operations: a stolen operator key cannot widen a hijacked agent's authority before recovery completes.
Unclaimed-rewards-on-revoke hook
x/staking assumes EOA delegators. If an agent self-stakes and is later revoked, unclaimed rewards must route to the operator instead of being orphaned. keeper/recovery.go::HandleRevokeWithdrawal performs this on revoke.
Fan-out gas
x/accounts storage is per-account but tx gas is not. A single agent message that fans out to thousands of children would otherwise be asymmetric. keeper/recursion.go::ChargeFanoutGas charges per-child-traversal gas and bounds fan-out at params.max_batch_fanout.
Messages (7)
MsgCreateAgent MsgRevokeAgent MsgRotateHotkey MsgUpdatePolicy MsgSpawnChild MsgUpdateRecoveryGuard MsgUpdateParams // gov-only
Queries
ParamsAgentAccount(agent_id)AgentsByOperator(operator_addr, pagination)AgentChildren(parent_agent_id, pagination)SpendingHistory(agent_id, pagination)
What's wired today
Real Module wiring, params, KV layout + iterators, the rolling-24h Authenticate hot path, SubtractFromParent / RefundParentOnRevoke, CheckRecursionDepth / ChargeFanoutGas, HandleRevokeWithdrawal skeleton (real bank + dist call), all read queries.
Phase 1 MsgCreateAgent, MsgRevokeAgent, MsgRotateHotkey, MsgUpdatePolicy, MsgSpawnChild, MsgUpdateRecoveryGuard, x/accounts substrate wiring, RecoveryGuard signature verification, EndBlocker for queued broadening updates, x/messaging cross-hook.
See also
- x/messaging for the agent-message permission surface
- x/staking_tiers - agents can register tiers like any account
- Source: x/agent/