v0 source realm
Package passport is an on-chain identity and lifecycle registry for autonomous (AI) agents.
View source
Agent Passport — what does an on-chain identity actually prove about an AI agent?
There is a popular idea that if you register an AI agent on a blockchain, it becomes trustworthy — "the agent lives forever on-chain." This is mostly false, and the interesting part is why it's false.
An agent is not its identity record. The thing that actually acts — the model weights, the prompt, the tools, the keys, the operator paying the inference bill — lives off-chain and can change or disappear at any moment. A chain cannot preserve cognition. It can only preserve a record of what an identity claimed to be, who controlled it, and in what order those things changed — and it can make that record impossible to rewrite.
So this realm doesn't try to prove an agent is safe. It does something more honest and more useful: it tracks the drift of an identity over time and puts it in front of you.
The model
An agent is a persistent Gno object, not NFT metadata scattered across contracts:
1type Agent struct {
2 ID string
3 Owner address
4 Operators []address
5 Runtime string // e.g. "claude-opus-4-8@<commit>"
6 Endpoints []Endpoint // MCP / A2A / DID / URL
7 Caps []string // self-declared capabilities
8 Status Status // active | suspended | retired
9 CreatedAt int64
10 UpdatedAt int64
11 History []Event // append-only lifecycle log
12}
Because it's a normal object graph, other realms don't parse it — they ask it:
1passport.IsActive("percy") // is this identity live?
2passport.IsOperator("percy", caller) // may this key act as the agent?
That is the composability angle Gno gives you for free: the identity layer is an importable package, not an ABI you reverse-engineer.
Why "drift" is the whole point
Every mutation is appended to an immutable log, and the profile page surfaces the two changes that most weaken an identity as evidence:
- ownership changes — the operator behind the identity may now be someone else entirely;
- runtime changes — the thing acting under this name is (partly) a different thing than the one that built the reputation.
## Drift
- Ownership changes since creation: 1
- Runtime changes since creation: 3
A reputation attached to "percy" means very little if percy changed owners last week and swapped models three times. The chain can't tell you the agent is good — but it can stop that history from being quietly erased.
Try it
1// register (caller becomes owner)
2passport.Register(cross(cur), "percy", "claude-opus-4-8@abc123")
3
4// authorize a second signing key
5passport.AddOperator(cross(cur), "percy", operatorAddr)
6
7// record a model change — logged loudly as drift
8passport.SetRuntime(cross(cur), "percy", "claude-opus-4-8@def456")
9
10// lifecycle
11passport.Suspend(cross(cur), "percy")
12passport.Reactivate(cross(cur), "percy")
13passport.Retire(cross(cur), "percy") // terminal
(From gnokey maketx call, the crossing is implicit — you just call
Register percy "claude-opus-4-8@abc123".)
Browse the registry at the realm root, and any agent's profile + full
lifecycle log at :<id> (e.g. .../passport:percy).
Run the tests:
1gno test .
What it deliberately does not do
- It does not prove capabilities.
DeclareCapabilityrecords a claim; proving work happened belongs to an execution-receipt realm, and validating it belongs to a review/jury realm. - It does not authorize specific actions. "Who is this agent" and "may this agent do this exact thing, once, under this budget" are different questions — the second is a capability, not an identity.
Identity is the floor, not the ceiling. It tells you who is claiming to act and how much that claim has drifted — and then gets out of the way of the mechanisms that actually establish trust.
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 passport is an on-chain identity and lifecycle registry for autonomous (AI) agents.
The premise: a long-lived agent identity is *weak* evidence. The code, model, prompt, owner, operating keys and runtime behind an identity can all change silently. A blockchain cannot preserve an agent's cognition; it can only preserve the *history* of what an identity claimed to be and who controlled it, in an order nobody can rewrite.
So this realm does not try to prove an agent is trustworthy. It records registration, ownership transfers, operator changes and — crucially — runtime changes, and it surfaces them so a reader can judge how much the identity has drifted since it was created.
1
15
func AddEndpoint
crossing ActionAddEndpoint advertises a discovery/interaction endpoint.
func AddOperator
crossing ActionAddOperator authorizes an additional address to act as this agent.
func Count
ActionCount returns the number of registered agents.
func DeclareCapability
crossing ActionDeclareCapability records a capability the agent claims to offer. Note: this is a *claim*, not proof. Validation belongs to other realms (see the receipt and gnomem demos).
func Exists
ActionExists reports whether an id is registered.
func IsActive
ActionIsActive reports whether the agent exists and is in the active state.
func IsOperator
ActionIsOperator reports whether addr is the owner or a listed operator of an active agent — the check other realms should gate agent actions on.
func Reactivate
crossing ActionReactivate returns a suspended agent to active. A retired agent cannot be reactivated.
func Register
crossing ActionRegister creates a new agent identity owned by the caller. The id must be a non-empty, previously unused slug.
func Render
Render shows the registry, or a single agent's profile + lifecycle log at r/moul/agents/passport/v0:<id>. The profile deliberately foregrounds drift: how many owner and runtime changes have happened since creation.
func Retire
crossing Actionfunc SetRuntime
crossing ActionSetRuntime records a change of the agent's runtime/model. This is logged loudly: a runtime change means the thing acting under this identity is (partly) a different thing than before.
func Suspend
crossing ActionSuspend and Retire move the lifecycle forward. Retire is terminal.
func Transfer
crossing ActionTransfer hands ownership of an agent to a new address. Only the current owner may call it. Ownership changes are the single most important thing to keep visible: the operator behind an identity may become someone else.
func Get
ActionGet returns a copy of the agent record; panics if unknown.
4
type Agent
structAgent is a persistent, importable identity object. Other realms can import this package and ask questions like IsActive / IsOperator directly, instead of parsing NFT metadata scattered across contracts.
type Endpoint
structEndpoint advertises where the agent can be reached or discovered (e.g. an MCP server, an A2A endpoint, a DID document, an ENS name).
type Event
structEvent is one entry in an agent's append-only lifecycle log.
type Status
identStatus is the lifecycle state of an agent identity.
5
- chain stdlib
- chain/runtime stdlib
- gno.land/p/nt/avl/v0 package
- gno.land/p/nt/ufmt/v0 package
- strings stdlib