func Authorized
ActionAuthorized 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.
Package capwallet issues narrow, bounded, revocable capabilities to agents — instead of handing an agent a wallet key...
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:
Assign on r/moul/issues"Identity is a passport. Authorization is a role. Approval is a capability: a narrow, bounded, expiring, revocable grant. This realm issues the third kind.
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")
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
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 .
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.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:

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.
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:
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.
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.
Count returns the number of capabilities issued.
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.
Grant issues a capability. The caller becomes its granter and can revoke it later. validUntil is an absolute block height (0 = no expiry).
Render shows all capabilities, or one capability's detail + usage at :<id>.
Revoke disables a capability immediately. Only the granter may revoke.
Get returns a copy of a capability.
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}Capability is a least-privilege grant from a granter to a principal.
Use records one exercise of a capability.