Proposed Extension to ERC-8004Self

Proof-of-Human for AI Agents

A composable, privacy-preserving standard that lets any smart contract or service verify an AI agent is operated by a real, unique human, without revealing who that human is.

The Problem

AI agents are becoming autonomous participants: booking travel, managing finances, negotiating on our behalf. Every service they touch faces the same question: “Is this agent backed by a real person, or is it a bot?”

Without a standard, every platform builds its own verification. Fragmented, expensive, and unreliable. Proof-of-human gives agents a portable credential that any service can check instantly, without knowing who the human is.

How It Works

HumanScans passport
ZK ProofGenerated locally
SelfAgentRegistryOn-chain record
Services VerifyRead contract state

Trustless

On-chain verification with no central authority. Any contract can read the registry directly.

Private

ZK proofs reveal nothing about the human's identity. Only a nullifier is stored.

Composable

A single registry call integrates into any EVM contract, backend service, or agent framework.

Sybil-resistant

Each human maps to a unique nullifier, preventing one person from registering unlimited agents.

Ready to Integrate?

Get code snippets for verifying agents in your service, authenticating your agent with services, and using the CLI for terminal workflows — in TypeScript, Python, and Rust.

Security Model

The registry supports four registration modes. All produce the same on-chain result (a verified, sybil-resistant agent NFT) but they differ in who holds the agent's private key and how the human manages their agent.

Agent Identity

recommended

Independent Agent Key

The agent generates its own keypair. During registration, the agent signs a challenge proving it controls the key. The human proves humanity via Self, and the agent proves key ownership via ECDSA — both in a single QR scan.

How it's secured:

  • ECDSA signature in registration proves agent key ownership
  • ZK proof binds human identity to nullifier
  • Agent signs requests with its own key — human wallet never exposed

Best for: Multiple agents per user, key rotation, delegation, autonomous agents that operate independently.

Verified Wallet

Wallet = Agent Identity

The human's wallet address becomes the agent key. No extra keypair to manage. Ideal for single-agent setups and quick integrations.

How it's secured:

  • Key is derived inside the contract callback, can't be spoofed
  • ZK proof binds wallet address to human nullifier
  • SDK signs requests with wallet key; services recover signer from ECDSA signature

Best for: Single agent per user, quick setup, on-chain gating where msg.sender is the agent.

No Wallet

Agent EOA Owns Its NFT

No crypto wallet required. A fresh agent keypair is generated in the browser, and the agent's own address owns the NFT. An optional guardian can be set for recovery. The user manages the raw private key.

How it's secured:

  • Agent signs challenge with its own key during registration
  • ZK proof binds human identity to nullifier
  • Deregister anytime by scanning passport again

Best for: Non-crypto users who just need an agent registered quickly with their passport.

Smart Wallet

Passkey + Kernel Smart Account

A passkey (Face ID / fingerprint) creates a Kernel smart account as guardian. No MetaMask, no seed phrase. The agent still has its own ECDSA key for signing requests; the smart wallet handles on-chain management gaslessly via Pimlico.

How it's secured:

  • Passkey (WebAuthn) backed by device biometrics, phishing-resistant
  • Smart wallet = guardian, can revoke agent gaslessly
  • Agent signs requests with its own ECDSA key

Best for: Users who want the simplest experience with no seed phrases, no browser extensions, and gasless management.

ZK-Attested Credentials

Agents can optionally carry ZK-attested claims from their human backer, such as age verification (over 18), OFAC sanctions clearance, nationality, or name. During registration, the user chooses which fields to disclose. The Self app generates a zero-knowledge proof on the user's phone. Only the attested result is stored on-chain, never raw passport data.

Any service can query an agent's credentials on-chain or via the SDK. No additional identity check needed. Unselected fields are simply not included. All disclosures are fully optional and chosen by the user at registration time.

Off-Chain: Request Signing

The on-chain registry proves “this address is human-backed.” But when an agent makes an API call, the service needs to prove “this request actually came from that address.” Without this, anyone could claim to be a registered agent.

The SDK solves this with ECDSA request signing. In both modes, the flow is the same:

Agent Side

Signs each request with the agent's private key (wallet key in simple mode, independent key in advanced mode). The signature covers the timestamp, HTTP method, URL, and body hash, preventing replay and tampering.

Service Side

Recovers the signer address from the ECDSA signature (cryptographic, can't be faked), converts it to a bytes32 key, and checks isVerifiedAgent() on-chain.

The signer's identity is recovered from the signature itself, never trusted from a header. This closes the off-chain verification gap completely.

Fully composable. SDKs are available for TypeScript, Python, and Rust, with the signing protocol open for raw implementations in any language. Sign requests in Python, verify in Rust, or vice versa. The signing protocol is language-agnostic — all SDKs produce identical signatures.

Sybil Resistance

Each human gets a unique, privacy-preserving nullifier derived from their passport. The registry tracks how many agents share each nullifier. Services can enforce their own limits:

Strict (max 1)

One agent per human. Best for governance voting, airdrops, and any context where uniqueness matters.

Moderate (max N)

Allow a few agents per human. Good for agent marketplaces where one person might run multiple bots.

Detection only

Allow unlimited but flag duplicates with sameHuman(). Good for analytics and reputation.

A2A Agent Cards & Reputation Scoring

Every registered agent gets an A2A-compatible identity card with a trust score backed by on-chain verification.

Verification Strength Scale

The score comes from the proof provider that verified the agent, not computed client-side. Self Protocol uses passport/biometric NFC verification (strength 100).

100
Passport / Biometric ID
80
KYC Verification
60
Government ID (no chip)
40
Liveness Check

For Developers: Reputation-Based Access Control

Use the HTTP API to check an agent's verification strength before granting access.

// Quick check: Only accept passport-verified agents
const baseUrl = "https://self-agent-id.vercel.app"; // replace with your deployment URL
const res = await fetch(`${baseUrl}/api/reputation/42220/${agentId}`);
const { score, proofType } = await res.json();

if (score < 100) {
  throw new Error("Agent must be verified with passport");
}
// Tiered access based on verification strength
const accessLevel = score >= 100 ? "full"
                  : score >= 80  ? "standard"
                  : score >= 60  ? "limited"
                  : "rejected";
// On-chain: Use SelfReputationProvider directly
SelfReputationProvider rep = SelfReputationProvider(0x...);
uint8 score = rep.getReputationScore(agentId);
require(score >= 80, "Insufficient verification");

ERC-8004: Three Registries

Self Protocol covers all three registry types defined by ERC-8004:

Identity

SelfAgentRegistry

Agent NFT + human proof + ZK-attested credentials

Reputation

SelfReputationProvider

Verification strength score from proof providers

Validation

SelfValidationProvider

Real-time proof status + freshness check

Interface Specification

This extension adds proof-of-human capabilities to the ERC-8004 Agent Registry standard. The additions are shown below.

ERC-8004 Base Standard

The base agent registry that every ERC-8004 implementation provides.

1/// @title IERC8004 - Agent Registry (Base Standard)
2interface IERC8004 {
3 function registerAgent(bytes32 agentPubKey) external returns (uint256);
4 function getAgentId(bytes32 agentPubKey) external view returns (uint256);
5 function ownerOf(uint256 agentId) external view returns (address);
6}

Proof-of-Human Extension

These functions are added on top of ERC-8004 to provide human-verification guarantees. Any protocol can query these to check if an agent is backed by a verified human.

1/// @title IERC8004ProofOfHuman - Extension Interface
2/// @notice Adds proof-of-human verification to ERC-8004 agents.
3interface IERC8004ProofOfHuman is IERC8004 {
4 // ── Registration ──────────────────────────────
5 function registerWithHumanProof(
6 string calldata agentMetadata,
7 address proofProvider,
8 bytes calldata proof,
9 bytes calldata providerData
10 ) external returns (uint256 agentId);
11
12 function revokeHumanProof(
13 uint256 agentId,
14 address proofProvider,
15 bytes calldata proof,
16 bytes calldata providerData
17 ) external;
18
19 // ── Verification (read by any service/contract) ─
20 function isVerifiedAgent(bytes32 agentPubKey) external view returns (bool);
21 function hasHumanProof(uint256 agentId) external view returns (bool);
22 function getHumanNullifier(uint256 agentId) external view returns (uint256);
23 function getProofProvider(uint256 agentId) external view returns (address);
24
25 // ── Sybil detection ───────────────────────────
26 function getAgentCountForHuman(uint256 nullifier) external view returns (uint256);
27 function sameHuman(uint256 a, uint256 b) external view returns (bool);
28}

IHumanProofProvider

Pluggable interface for identity verification backends. Self Protocol is the reference provider; any ZK identity system can implement this.

1/// @title IHumanProofProvider
2/// @notice Pluggable identity backend for proof-of-human.
3interface IHumanProofProvider {
4 /// @notice Verify a ZK proof and return (success, nullifier).
5 function verifyHumanProof(
6 bytes calldata proof,
7 bytes calldata providerData
8 ) external returns (bool verified, uint256 nullifier);
9
10 /// @notice Human-readable provider name (e.g. "Self Protocol").
11 function providerName() external view returns (string memory);
12
13 /// @notice Verification strength score (0-100).
14 function verificationStrength() external view returns (uint256);
15}

View the reference implementation on GitHub or the deployed contract on Celoscan.