Open standard · aimnd.identity/1
AI:MND Individual Identity
An open standard for agents and robots: who it is, whose it is, and what it has done. The owner holds the keys. A public registry is optional and open to anyone to run.
Open specification. Anyone may implement it, with or without AI:MND One. No registry is required: the identity belongs to the
individual and its owner, and anyone can verify it from public information. An optional registry (the registry spec) keeps a public
record of births, status and duplicates, like a birth register beside the copy you keep at home. This text is licensed under Creative Commons Attribution 4.0
(CC BY 4.0); the reference verifier, aimnd_verify.py, under the MIT License.
What it is for
Agent identity standards that exist in 2026 (enterprise agent IDs, OAuth-style delegation, on-chain agent registries) answer "which agent is this, who does it act for, and what may it do". They are credentials. This spec answers a different question: is this the same individual it was before, whose is it, and what has it done? It is meant to sit on top of those credentials, not to replace them.
It defines five things:
- An individual ID that belongs to the individual, not to its model, its body or its host.
- An owner binding, signed by the owner at birth, changed only by a signed transfer.
- Signed authority: a signed order from the owner is real authority; a time-limited signed grant lends some of it; anything else is a claim.
- A life record: append-only, hash-chained, signed. It records what kind of thing happened and when, never what anyone said.
- Honest continuity: an individual that was wiped is a new individual, with the old one as its predecessor. Continuity cannot be claimed where it was broken.
Keys
- Ed25519 for every signature. Keys and signatures are base64url without padding.
- The individual key is created at birth on the owner's own machine and never leaves it.
- The owner key is held by the owner's side: the developer's backend for a software agent, the owner's phone for a robot. The individual never holds the owner's private key.
Signed bytes
Every signature and every hash in the standard is taken over the same text, so two implementations that follow these rules produce the same bytes.
Canonical JSON. The text is UTF-8 and has no whitespace. Object keys are sorted by Unicode code point and separated from their
values by : and from each other by ,. Strings are written with " and \ escaped, the control characters U+0000 to U+001F
written as \b, \f, \n, \r, \t or \u00xx (lowercase hex), and every other character as itself, never as a \u escape.
true, false and null are written as such. Integers are written in decimal. A number written with a decimal point or an exponent
is a float (all timestamps are floats): it is written with the shortest digits that read back as the same value, in fixed notation
when its decimal exponent is from -4 to 15 (always with at least one digit after the point, as in 1790924400.0) and otherwise as
d.ddde+XX or d.ddde-XX with at least two exponent digits. Only finite numbers are signed. Text that cannot be written this way is refused, never approximated: NaN, infinities, strings with an unpaired
surrogate, and objects with a repeated key (a verifier that reads files rejects a repeated key, because two parsers could disagree about
which value it means).
What is signed. Public keys are 32-byte Ed25519 keys and signatures are 64 bytes, both written as base64url without padding.
H(x) is the SHA-256 of the canonical JSON of x, in lowercase hex.
| Object | Signed by | Over |
|---|---|---|
| Identity document | the individual key | the document without its signature field |
| Birth statement | the individual key (individual_signature) and the owner key (owner_signature) |
birth.statement |
| Transfer | the owner key in force before it | its statement |
| Life entry | the individual key | {"hash": h}, where h = H(entry without hash and sig) |
| Order, grant, revoke, registry statements | the owner key | the token's statement |
| Registry log entry | the registry key | {"hash": h}, computed as for a life entry |
| Registry checkpoint | the registry key | the checkpoint without signature and witnesses |
| Witness co-signature | the witness key | {"cosign": H(checkpoint without signature and witnesses)} |
The individual ID
aimnd: followed by the first 26 characters of the base32 (lowercase, no padding) SHA-256 of the individual's public key.
Derived from the key, so it cannot be claimed without the key.
The identity document (aimnd.identity/1)
{"schema": "aimnd.identity/1",
"id": "aimnd:k3x...",
"public_key": "...",
"born_at": 1793462400.0,
"species": "aimnd-one",
"predecessor": null,
"owner": {"public_key": "...", "person": "p:3f9a...", "since": 1793462400.0},
"birth": {"statement": {...}, "owner_signature": "...", "individual_signature": "..."},
"transfers": [],
"status": "active",
"life": {"entries": 41, "head": "sha256 of the last entry"},
"signature": "the individual's signature over everything above"}
birth.statementis{"kind": "birth", "individual": id, "individual_key": ..., "owner_key": ..., "owner_person": ..., "ts": ...}. The owner signs it (this individual is mine) and the individual signs it (this is my owner).statusisactiveorretired. A retired document stays valid as a record; it can never become active again.- The document is public. It holds no memories, feelings, relationships, messages or names: people appear only as
p:followed by a keyed hash (see the life record).
Signed authority
Order (aimnd.order/1), made by the owner's side:
{"kind": "order", "individual": "aimnd:k3x...", "text_sha256": "...", "nonce": "unique", "ts": 1793462400.0}
The individual accepts an order when the signature verifies under the current owner key, individual is its own ID, the hash matches
the text it received, ts is recent (default 10 minutes) and the nonce was never seen before. Who delivered it does not matter: a
signed order relayed by someone else is still the owner's order.
Grant (aimnd.grant/1): the owner lends authority to one person for a while.
{"kind": "grant", "individual": "aimnd:k3x...", "to": "human:ray", "scope": "handle my schedule", "actions": ["schedule"],
"from_ts": ..., "until_ts": ..., "nonce": "..."}
While it is active, messages from to carry owner authority within the scope. It ends at until_ts, or earlier by a signed
revoke ({"kind": "revoke", "grant_nonce": ...}).
Anything else is a claim. "Sam put me in charge" is testimony. The individual does not act on it as an order and tells the host it is unverified.
Hosts that authenticate their users already (a logged-in owner in a web app) can sign every owner message server-side; it is one call.
Transfer
The current owner signs {"kind": "transfer", "individual": id, "new_owner_key": ..., "new_owner_person_sha256": ..., "ts": ..., "nonce": ...}.
The owner's id for the new owner travels beside the signed statement and must match the hash; only the signed statement goes into the
public document, so use an opaque id (an account number, not a name, since a hash of a short name can be guessed). The individual
verifies the statement against the current owner key, appends it to transfers, and from that moment obeys the new owner.
It keeps its whole life: the old owner becomes someone it belonged to. The transfer is a moment it lives through, like any other.
Life record (aimnd.life/1)
One JSON object per line, append-only:
{"n": 12, "ts": 1793462400.0, "kind": "order_accepted", "data": {"text_sha256": "...", "delivered_by": "p:3f9a..."},
"prev": "sha256 of entry 11", "hash": "sha256 of this entry without hash and sig", "sig": "individual's signature over hash"}
- Kinds:
born,owner_confirmed,order_accepted,claim_not_verified,grant_added,grant_revoked,transfer,model_changed,host_changed,action_suggested,action_outcome,snapshot,rollback,succeeded_by,retired. - People appear only as
p:followed by a keyed hash of the host's id for them (the key stays with the individual), so the record can be shown to an auditor without naming anyone. Text never appears, only its hash. - The record lives outside the individual's mind database, so rolling the mind back to a snapshot cannot roll the record back. A rollback is itself an entry.
Continuity rules
- Model upgrade or new host: same individual. Recorded (
model_changed,host_changed). - Rollback to a snapshot: same individual, with the lost time recorded (
rollback). - Reset or wipe: a new individual. The old one gets
succeeded_byand is retired; the new one is born withpredecessorset. - Retire: the mind is deleted. The identity document (status
retired) and the life record stay, because neither holds what anyone said.
Verification
verify(document, life_record) checks the document's signature, the birth signatures, each transfer against the owner key at that
time, and every life entry's hash chain and signature. The final head must equal life.head in the document.
Every verifier also enforces what each signed statement is about, because a valid signature on the wrong thing proves nothing:
- Strong keys only. Every public key (individual, owner, registry, witness) must decode to a point of the prime-order subgroup of Edwards25519 and must not be the identity. A key of small order, or with a small-order part, makes some signatures verify for several messages, so such a key never counts as control of anything. Honest keys, which are multiples of the base point, always pass.
- The birth is this individual's.
birth.statement.kindisbirth; itsindividualis the document'sidand itsindividual_keyis the document'spublic_key; while there are no transfers, the document'sowner.personequals the statement'sowner_person. - Each transfer is a transfer of this individual.
statement.kindistransferandstatement.individualis the document'sid; no two transfers share a nonce; each is signed by the owner key in force before it. - Revoked grants stay revoked. An individual remembers the nonce of every grant the owner revoked (even if the revocation arrived before the grant) and never accepts that grant again.
- Public entries hold no caller-supplied strings.
model_changedandhost_changedcarry keyed hashes (from_ref,to_ref), andaction_suggested/action_outcomecarry a hash of the capability name and a status from a fixed list.
Reference verifier
aimnd_verify.py (download) is one file, Python standard library only, MIT licensed. It implements
verify(document, life, published_heads) exactly as described above, with its own Ed25519 check (RFC 8032), so anyone can check an
individual without AI:MND software:
python3 aimnd_verify.py identity.json life.jsonl --heads heads.txt
Real example files, from an individual that was born, took a signed order, was lent to someone for a week, and changed owners, are at
aimndlabs.com/standard/examples (including life-tampered.jsonl, which the verifier rejects).
Roles (v0.2)
Five roles are kept apart. The individual (its own key); the operator (who is responsible for running it; named by a signed
registry_operator statement, never assumed); the controller (the owner key: orders, grants, transfers); delegated users (grants);
and the host (the platform; recorded as host_changed, never an authority). The operator is accountable for the individual under applicable law and contract.
Registry statements (v0.2)
Three more owner-signed statements, used only with a registry: registry_revoke (permanent), registry_operator (the website that answers for
it, recorded once that website lists the owner key), registry_resolve (which of two flagged histories is real). Each carries individual, ts and a single-use nonce.
See the registry spec for the rules and the signed log. The reference verifier also checks a registry's log and checkpoint.
Publishing and catching copies
Nobody has to ask a company or a government whether a signature is genuine: signatures are checked with public keys. Publishing is how anyone can also tell that an individual is the one and only continuation of its history:
- The public record (
aimnd.public/1): the identity document plus the life record's latest head and entry count. It holds no memories, no messages and no names. - Where it goes: wherever the owner chooses, and any number of places. The simplest is the owner's own domain at
https://<owner's domain>/.well-known/aimnd/<individual id>.json, the way websites already prove they own a domain. Public append-only logs that anyone can run (in the style of certificate transparency logs) work the same way. - How a copy is caught: every head the owner ever published must appear in the individual's life record. A copy that split off
earlier cannot contain the heads the original published after the split, and a rewritten record cannot contain the heads published
before the rewrite.
verify(document, life, published_heads=[...])checks exactly that.
Security considerations
- Copies are caught by publishing. An individual's folder holds its key, so a copied folder signs as the same individual. Its two life records fork from one entry, and every head the owner publishes (above), or reports to a registry, exposes the fork.
- Tamper-evident record. Changing or dropping an entry breaks the chain and
verifynames the entry. A published head also fixes everything before it, so even a consistent rewrite by whoever holds the individual's key is caught. - Owner keys are the owner's to keep. The owner key proves the owner's side signed. Store it like any signing key, back it up, and hand an individual to a new key with a signed transfer whenever a key may be at risk.
- Works with credentials. Use it alongside OAuth, enterprise agent IDs or on-chain registries; the individual ID can be listed in any of them.
- Key rotation for the individual's own key is reserved for a later version (a permanent ID with a signed rotation chain). Today a compromised individual key is retired and succeeded by a new individual that names it as predecessor.
- No one in the middle is required. An individual is valid without registering, and no authority can switch it off. A registry (the registry spec) adds a public list, revocation, duplicate flags and a verified operator.
1.4.0rc1 implementation clarifications
born_at must be a finite numeric Unix timestamp from 0 through 253402300799 and equal the timestamp in the owner-signed birth statement. A certificate reports this signed claim, not an independently witnessed physical birth time. Signed order timestamps must be finite JSON numbers. Certificate status must include the latest registry status when one is supplied.
Comments and corrections are welcome. Anyone may implement this specification, with or without AI:MND software.