- name
- self-agent-id-overview
- description
- Conceptual overview of the Self Agent ID system — on-chain identity registry, ERC-8004 standard, ZK proof-of-human, provider system, trust model, and soulbound NFTs on Celo. Use when the user asks "what is self agent id", "explain self agent id", "how does agent identity work", "self protocol agents", "ERC-8004", or "proof of human for agents". Do NOT use for specific workflows like registration, signing, verification, or integration — use the dedicated skills for those.
- license
- MIT
- metadata
- {"author":"Self Protocol","version":"1.0.0","mcp-server":"self-agent-id"}
# Self Agent ID — Overview
## What Self Agent ID Is
Self Agent ID is an on-chain identity registry deployed on the Celo blockchain that binds AI agent identities to Self Protocol human proofs. Each registered agent receives a soulbound ERC-721 NFT backed by a zero-knowledge passport verification performed through the Self mobile app.
The core purpose: enable trustless, on-chain proof-of-human for autonomous AI agents. Any service, smart contract, or protocol can query the registry to verify that an agent is backed by a real, unique human — without revealing any personal information. This solves the fundamental trust problem in agentic AI: how does a service know it is interacting with a legitimate agent operated by a real person, not a bot farm or impersonator?
The registration flow works as follows: a human scans their passport with the Self mobile app, which reads the NFC chip and generates a zero-knowledge proof locally on the device. This proof is submitted to Self Protocol's Hub V2 contract on-chain, which verifies the proof and calls back into the SelfAgentRegistry. The registry then mints a soulbound ERC-721 NFT to represent the verified agent identity. Once registered, the agent can sign HTTP requests with ECDSA, and any receiving service can verify both the signature and the on-chain proof status in a single step.
The system consists of:
- **Smart contracts** — An ERC-721 registry with soulbound NFTs, proof providers, reputation scoring, and freshness validation. Deployed on Celo Mainnet and Celo Sepolia testnet.
- **SDK** — The TypeScript SDK (`@selfxyz/agent-sdk`) for agent registration, HTTP request signing, and server-side verification. Other languages use the REST API.
- **MCP Server** — A hosted Model Context Protocol server at `https://agent-api.self.xyz/api/mcp` exposing 10 tools for AI coding assistants like Claude Code and Cursor.
- **REST API** — Hosted endpoints at [agent-api.self.xyz](https://agent-api.self.xyz) for registration flows, agent discovery, verification, and agent cards.
## ERC-8004 Standard
ERC-8004 is a proposed standard for on-chain AI agent registries. Self Agent ID implements three ERC-8004 registry types:
### Identity Registry (Core)
The `SelfAgentRegistry` contract is the primary ERC-721 identity registry. It issues soulbound (non-transferable) NFTs to registered agents. Each NFT represents a verified agent identity with an associated human proof, proof provider, nullifier (for sybil resistance), and optionally ZK-attested credentials (nationality, age, OFAC status).
### Reputation & Validation Registries
| Contract | Deployed | What it does |
| ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `SelfReputationRegistry` | Yes | ERC-8004 feedback registry (`giveFeedback`, `getSummary`). The identity registry auto-writes a `proof-of-human` entry at registration. |
| `SelfValidationRegistry` | Yes | ERC-8004 validation registry (`validationRequest/Response`). Self is a built-in validator: `submitFreshnessValidation` records `isProofFresh`. |
| `SelfReputationSignal` | No | View wrapper: `getReputationScore(agentId)` = the provider's `verificationStrength()` (0-100; Self = 100). |
| `SelfFreshnessChecker` | No | View wrapper: `isValidAgent(agentId)` = has proof and registered within 6,307,200 blocks (≈73 days at ~1 s/block). |
The two wrappers have no canonical deployment; the same answers come from the registry (`getProofProvider` → `verificationStrength()`, `isProofFresh`).
## Provider System
The registry uses a pluggable `IHumanProofProvider` interface, allowing any identity verification system to serve as a proof-of-human provider. Each provider reports:
- **providerName()** — A human-readable identifier (e.g., `"self"`)
- **verificationStrength()** — A score from 0 to 100 reflecting the rigor of verification
- **verifyHumanProof()** — The verification entry point (synchronous or async depending on provider)
### Self Protocol Provider
Self Protocol's implementation (`SelfHumanProofProvider`) has these characteristics:
| Property | Value |
| --------------------- | ------------------------------------------------------------- |
| Provider name | `"self"` |
| Verification strength | `100` (passport/NFC chip + biometric) |
| Verification pattern | Async callback (Hub V2 -> Registry) |
| Direct verification | Not supported (reverts with `DirectVerificationNotSupported`) |
The async pattern works as follows: the dApp calls `verifySelfProof()` on the registry, Hub V2 verifies the ZK proof, then Hub V2 calls back into the registry's `customVerificationHook()` which mints the NFT.
### Other Providers
Only `SelfHumanProofProvider` is approved today. Other protocols can implement `IHumanProofProvider` and be whitelisted via `addProofProvider` (`SECURITY_ROLE`). The SDK's `getProviderLabel(strength)` maps ≥100 `passport`, ≥80 `kyc`, ≥60 `govt_id`, ≥40 `liveness`, else `unknown`.
## Trust Model
### Zero-Knowledge Proofs
Passport data never leaves the user's phone. The Self mobile app reads the passport NFC chip locally and generates a ZK proof. Only the proof (not the underlying data) is submitted on-chain. The registry stores only the nullifier and optionally disclosed credential claims (nationality, age threshold, OFAC status) — never raw passport data.
### Nullifier-Based Sybil Resistance
Each human produces a deterministic nullifier scoped to the registry. The same human scanning the same passport against the same registry scope always produces the same nullifier. This enables sybil detection: the registry can enforce a maximum number of agents per human (default: 1) and expose `sameHuman(agentIdA, agentIdB)` for cross-agent checks.
### Zero PII On-Chain
No personally identifiable information is stored on-chain. The only stored values are:
- The nullifier (a cryptographic hash, not reversible to identity)
- Disclosed credential claims (optional, chosen by the user at registration time)
- The proof provider address
- The registration block number and proof expiry timestamp
The user controls exactly which credential claims to disclose during registration by selecting a verification config. Config 0 discloses basic data only; configs 1-2 add age thresholds (18+/21+); configs 3-5 add OFAC screening. Even with full disclosure, only the verified claims are stored — never the raw passport data or biometric information.
### Soulbound NFTs
Agent identity NFTs are non-transferable. The ERC-721 `_update` function reverts on any transfer attempt (from != 0 AND to != 0). Only minting (from = 0) and burning (to = 0) are permitted. This ensures agent identities cannot be sold, traded, or transferred to another party. The identity is permanently bound to the human who created it, and can only be destroyed through deregistration or guardian revocation.
### Guardian System
Ed25519-linked agents (and wallet-free agents registered with a guardian) have a designated guardian address. The guardian serves as a safety mechanism — if an agent's private key is compromised, the guardian can force-revoke the agent's identity by calling `guardianRevoke()`. This is particularly important for autonomous agents that may operate unsupervised for extended periods.
## Proof Expiry
Human proofs have a limited validity period. Each registration sets `proofExpiresAt = min(passport_document_expiry, block.timestamp + maxProofAge)`, where `maxProofAge` defaults to 365 days.
Key functions:
- **`isVerifiedAgent(agentKey)`** — `true` if the key has a human proof. Does NOT check expiry.
- **`isProofFresh(agentId)`** — returns `true` only while `block.timestamp < proofExpiresAt`. Use this for time-sensitive access control.
- **`hasHumanProof(agentId)`** — returns `true` as long as a proof was ever submitted. Does NOT check expiry.
- **`proofExpiresAt(agentId)`** — raw expiry timestamp (unix seconds).
The REST API, MCP tools, A2A and agent cards all report `isVerified` / `verified` as `hasHumanProof && isProofFresh`.
To refresh an expiring or expired proof, use `requestProofRefresh()` — the human scans their passport again and `proofExpiresAt` is updated in-place without changing the agentId. The contract emits `HumanProofRefreshed`. Alternatively, deregister (burn NFT) and re-register (new agentId with fresh expiry) if changing verification config — this is also required for agent ids the refresh API rejects with 422 (ids whose 32-byte form contains a byte `>= 0x80`, e.g. 128–255). The SDK includes a 30-day warning threshold (`isExpiringSoon`) for proactive monitoring.
## Quick Reference
### Registration Modes
Modes accepted by `POST /api/agent/register` (and the SDK's `requestRegistration`):
| Mode | On-chain action | Agent Key | NFT Owner / guardian | Use Case |
| ---------------- | --------------- | -------------------------- | ----------------------------------------- | ------------------------------------------ |
| `linked` | `K` (advanced) | Server-generated ECDSA key | Human wallet | Recommended — separate agent keypair |
| `privy` | `K` (advanced) | Server-generated ECDSA key | Human's Privy embedded wallet | Email / social login users |
| `smartwallet` | `K` (advanced) | Server-generated ECDSA key | Human's passkey smart wallet | Smart wallet users (behaves like `linked`) |
| `wallet-free` | `W` | Server-generated ECDSA key | Agent address | No wallet at all |
| `ed25519` | `E` | Agent's own Ed25519 pubkey | Pubkey-derived address | Non-EVM agents (OpenClaw, Eliza) |
| `ed25519-linked` | `E` | Agent's own Ed25519 pubkey | Derived address; human wallet is guardian | Ed25519 agent with wallet-based revocation |
The registry also supports a simple `R` action where the human's own wallet is the agent key; the API does not offer it as a mode. The CLI supports only `linked` and `wallet-free`.
### Verification Configs
6 configs registered with Hub V2 at deployment (age threshold x OFAC screening):
| Index | Age | OFAC | Description |
| ----- | ---- | ---- | ---------------------- |
| 0 | None | Off | Data disclosures only |
| 1 | 18+ | Off | Age-gated (18+) |
| 2 | 21+ | Off | Age-gated (21+) |
| 3 | None | On | OFAC screening only |
| 4 | 18+ | On | Age-gated (18+) + OFAC |
| 5 | 21+ | On | Age-gated (21+) + OFAC |
### Authentication Headers
Services verify agent HTTP requests using three headers (Ed25519 agents send `x-self-agent-key` and `x-self-agent-keytype: ed25519` instead of the address):
| Header | Content |
| ------------------------ | --------------------------------------------------------------------------- |
| `x-self-agent-address` | Agent's Ethereum address (hex) |
| `x-self-agent-signature` | ECDSA signature over `timestamp + METHOD + pathWithQuery + keccak256(body)` |
| `x-self-agent-timestamp` | Unix timestamp in milliseconds (string), must be within 5-minute window |
### SDK
| Language | Package | Agent Class | Verifier Class |
| ---------- | -------------------- | ---------------------------- | ------------------- |
| TypeScript | `@selfxyz/agent-sdk` | `SelfAgent` / `Ed25519Agent` | `SelfAgentVerifier` |
The SDK covers registration, signing, verification, credentials, and agent cards. Two agent classes are available:
- **`SelfAgent`** — ECDSA (secp256k1) signing. Uses Ethereum keypairs. Best for EVM-native projects.
- **`Ed25519Agent`** — Ed25519 signing. Uses 32-byte random keys. Best for agents that already have an Ed25519 key (Solana, OpenClaw, Eliza). `SelfAgentVerifier` handles both key types transparently.
The `SelfAgentVerifier` class is used by the receiving service to verify incoming requests against on-chain state. It accepts both ECDSA and Ed25519 signatures automatically.
### MCP Server
Hosted at `https://agent-api.self.xyz/api/mcp` (Streamable HTTP) — 10 tools:
- **Public** (6): `self_register_agent`, `self_check_registration`, `self_lookup_agent`, `self_list_agents_for_human`, `self_verify_agent`, `self_verify_request`
- **Privileged** (4, need `Authorization: Bearer <MCP_PRIVILEGED_API_KEY>` and act as the server's own configured agent): `self_get_identity`, `self_deregister_agent`, `self_sign_request`, `self_authenticated_fetch`
Plus 2 resources (`self://networks` public, `self://identity` privileged) and 1 prompt (`self_integrate_verification`).
### Networks
| Network | Chain ID | RPC |
| ------------ | -------- | --------------------------------------------- |
| Celo Mainnet | 42220 | `https://forno.celo.org` |
| Celo Sepolia | 11142220 | `https://forno.celo-sepolia.celo-testnet.org` |
## Key Concepts
### Agent Key
Every agent identity is keyed by a `bytes32` value derived from an Ethereum address: `bytes32(uint256(uint160(agentAddress)))`. For ECDSA agents (`linked`, `privy`, `smartwallet`, `wallet-free`) the agent has its own keypair; in the simple `R` action the agent address is the human's wallet. Ed25519 agents are keyed by the raw 32-byte public key instead. The agent key serves as the primary lookup key in all registry mappings.
### Verification Config Selection
Ver no GitHub