Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v0 source realm

Package capwallet issues narrow, bounded, revocable capabilities to agents — instead of handing an agent a wallet key...

Readme View source

Capability Wallet — don't give your agent a wallet, give it a capability

The default way to let an agent spend or act on-chain is to hand it a private key and hope its prompt is safe. That is a category error. A key is all your authority; a prompt is a suggestion. The moment the agent is confused, jailbroken, or just wrong, it has the full key.

Three things get conflated and shouldn't:

  • identity — "this is agent Percy"
  • authorization — "Percy may call Assign on r/moul/issues"
  • approval — "Percy may do it at most twice, for ≤ 5 GNOT each, before block H, and I can revoke it instantly"

Identity is a passport. Authorization is a role. Approval is a capability: a narrow, bounded, expiring, revocable grant. This realm issues the third kind.

The grant

 1type Capability struct {
 2	Granter       address
 3	Principal     address // the ONLY address that may exercise it
 4	TargetRealm   string  // scoped to one realm
 5	Function      string  // scoped to one function
 6	MaxCoins      int64   // per-exercise ceiling
 7	ValidUntil    int64   // absolute block height, 0 = never
 8	RemainingUses uint32  // a use counter
 9	Revoked       bool    // a kill switch
10	Uses          []Use   // an audit trail
11}

Issuing one is a single call:

1// Percy may call r/moul/issues.Assign, ≤5 GNOT, twice, no expiry.
2id := capwallet.Grant(cross(cur), percyAddr,
3	"gno.land/r/moul/issues", "Assign",
4	5_000_000, // per-use ceiling (ugnot)
5	0,         // no expiry
6	2,         // two uses
7	"issue IDs 100-200")

Two consumers, two shapes

A target realm gates an action with a read-only, side-effect-free check:

1if !capwallet.Authorized(id, caller, coins) {
2	panic("not authorized")
3}

The agent consumes a use when it acts. Every bound is enforced at once — right principal, not revoked, not expired, uses remaining, under the ceiling — and the exercise is logged:

1capwallet.Exercise(cross(cur), id, 3_000_000) // 1 use left
2capwallet.Exercise(cross(cur), id, 1_000_000) // exhausted

And the granter kills it the instant something looks wrong:

1capwallet.Revoke(cross(cur), id) // Authorized() now returns false for everyone

Why this is a real safety primitive

The blast radius of a compromised agent is exactly the union of its live capabilities — not its whole wallet. You can reason about "what is the worst this agent can do right now" by reading a table, and shrink it to zero with one transaction. That is not something a system prompt can give you.

Browse the wallet at the realm root; each capability's bounds and full exercise log live at :<id>.

1gno test .

Limitations

  • ArgsPolicy is a human-readable note, not an enforced predicate. Real argument-level constraints ("only issue IDs 100–200") need the target realm to check them, or a richer on-chain policy language. This demo scopes to realm + function + coins + uses + expiry, which is already most of the value.
  • There's no delegation graph (a capability that can mint narrower capabilities). That's a natural extension, not a starting point.

Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

Dependency graph:

gno.land/r/moul/agents/capwallet/v0 dependency graph

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.

Overview

Package capwallet issues narrow, bounded, revocable capabilities to agents — instead of handing an agent a wallet key and hoping its prompt stays safe.

The demo makes three ideas that are usually conflated stand apart:

  • identity — "this is agent Percy"
  • authorization — "Percy may call this function on this realm"
  • approval — "Percy may do it at most N times, for ≤ M coins, before block H, and I can revoke it at any moment"

A capability is the third thing: a least-privilege grant with a ceiling, an expiry, a use counter and a kill switch. Other realms consult Authorized() before honoring an agent action; the granter revokes with one call the instant something looks wrong.

Functions 7

func Authorized

Action
1func Authorized(id uint64, principal address, coins int64) bool
source

Authorized reports whether principal may exercise capability id for the given coin amount right now — the check a target realm runs before acting. It never mutates state or consumes a use.

func Exercise

crossing Action
1func Exercise(cur realm, id uint64, coins int64)
source

Exercise consumes one use of a capability. Only the principal may call it, and every bound is checked: not revoked, not expired, uses remaining, and coins within the ceiling. On success the use is recorded and the counter decremented.

func Grant

crossing Action
1func Grant(
2	cur realm,
3	principal address,
4	targetRealm, function string,
5	maxCoins, validUntil int64,
6	uses uint32,
7	argsPolicy string,
8) uint64
source

Grant issues a capability. The caller becomes its granter and can revoke it later. validUntil is an absolute block height (0 = no expiry).

func Render

1func Render(path string) string
source

Render shows all capabilities, or one capability's detail + usage at :<id>.

func Revoke

crossing Action
1func Revoke(cur realm, id uint64)
source

Revoke disables a capability immediately. Only the granter may revoke.

func Get

Action
1func Get(id uint64) Capability
source

Get returns a copy of a capability.

Types 2

type Capability

struct
 1type Capability struct {
 2	ID            uint64
 3	Granter       address
 4	Principal     address // the only address that may exercise it
 5	TargetRealm   string  // realm the capability is scoped to
 6	Function      string  // function the capability authorizes
 7	MaxCoins      int64   // per-exercise ceiling in ugnot (0 = none allowed)
 8	ValidUntil    int64   // block height after which it expires (0 = never)
 9	RemainingUses uint32  // exercises left (0 = exhausted)
10	ArgsPolicy    string  // human-readable note on allowed arguments
11	Revoked       bool
12	GrantedAt     int64
13	Uses          []Use
14}
source

Capability is a least-privilege grant from a granter to a principal.

type Use

struct
1type Use struct {
2	By     address
3	Coins  int64
4	Height int64
5}
source

Use records one exercise of a capability.

Imports 4

Source Files 4