Skip to content
Skip the search — install the Self skill Let your AI agent integrate Self for you.

Start typing to search the documentation.

ERC-8004 & Proof-of-Human

Self Agent ID is a reference implementation of ERC-8004 plus a proposed optional Proof-of-Human extension. This page explains the standard, the extension interfaces, and how Self maps onto all three ERC-8004 registry types.

What is ERC-8004?

ERC-8004 is a proposed standard for on-chain AI agent registries. It defines a minimal interface for registering agents, assigning them unique IDs (as NFTs), and looking up agent ownership. Think of it as a universal, composable identity layer for agents.

The base standard is intentionally minimal. It does not specify how agents are verified or who operates them. That is where optional extensions come in, similar to how ERC-721 defines optional Metadata and Enumerable extensions within the same EIP.

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

Self’s SelfAgentRegistry implements the fuller base surface that the EIP draft describes: register() overloads, setAgentURI, key-value metadata, and agent-wallet binding. See Smart Contracts for the deployed interface.

The problem the extension solves

ERC-8004 registers agents, but it does not answer the critical question: is this agent operated by a real, unique human?

Without proof-of-human, anyone can register unlimited agents, enabling sybil attacks, bot farms, and impersonation. Protocols that gate access to “verified agents” have no standard way to check humanity, so every project builds its own solution and the ecosystem fragments.

Design principles

The proposed IERC8004ProofOfHuman extension is additive: it inherits from IERC8004 and adds only proof-of-human registration, revocation, and query functions. Three principles guide it:

  • Provider-agnostic. Any ZK identity system (Self Protocol, World ID, Humanity Protocol) can implement IHumanProofProvider. The registry does not care how humanity is proven.
  • Sybil-resistant. Each human produces a unique, scoped nullifier. The registry tracks agent counts per nullifier, and services choose their own limits (1, N, or unlimited).
  • Privacy-preserving. Only a nullifier is stored on-chain. No name, no passport number, no biometrics. ZK proofs verify humanity without revealing identity.

The extension interface

/// @title IERC8004ProofOfHuman
/// @notice Optional extension to ERC-8004 adding proof-of-human verification
interface IERC8004ProofOfHuman is IERC8004 {
    // ── Registration ──────────────────────────────
    function registerWithHumanProof(
        string calldata agentURI,
        address proofProvider,
        bytes calldata proof,
        bytes calldata providerData
    ) external returns (uint256 agentId);

    function revokeHumanProof(
        uint256 agentId,
        address proofProvider,
        bytes calldata proof,
        bytes calldata providerData
    ) external;

    // ── Verification (read by any service/contract) ─
    function hasHumanProof(uint256 agentId) external view returns (bool);
    function proofExpiresAt(uint256 agentId) external view returns (uint256);
    function isProofFresh(uint256 agentId) external view returns (bool);
    function getHumanNullifier(uint256 agentId) external view returns (uint256);
    function getProofProvider(uint256 agentId) external view returns (address);
    function isApprovedProvider(address provider) external view returns (bool);

    // ── Sybil detection ───────────────────────────
    function getAgentCountForHuman(uint256 nullifier) external view returns (uint256);
    function sameHuman(uint256 a, uint256 b) external view returns (bool);
}

The pluggable provider interface

/// @title IHumanProofProvider
/// @notice Pluggable identity backend for proof-of-human
interface IHumanProofProvider {
    /// @notice Verify a ZK proof and return (success, nullifier).
    /// @dev The nullifier is deterministic: same human + same scope
    ///      always produces the same nullifier.
    function verifyHumanProof(
        bytes calldata proof,
        bytes calldata data
    ) external returns (bool verified, uint256 nullifier);

    /// @notice Human-readable provider name (e.g. "Self Protocol").
    function providerName() external view returns (string memory);

    /// @notice Verification strength score (0-100).
    function verificationStrength() external view returns (uint8);
}

Self’s SelfHumanProofProvider implements this, wrapping Self Protocol’s Identity Verification Hub V2.

Key concepts

Nullifier — a scoped, opaque identifier unique per (human, service) pair, derived by the proof provider from the document. Two agents with the same nullifier belong to the same human. The nullifier is stored on-chain; the underlying document data is not. Nullifiers across different services are unlinkable.

Proof provider — an approved contract that verifies proofs and reports a nullifier and strength. The registry keeps an allowlist (isApprovedProvider). Verifiers should require Self’s provider unless they intentionally accept others.

maxAgentsPerHuman — a registry-level cap on how many agents one human can hold at once (default 1). Prevents a single human from evading reputation decay by rotating agents.

proofExpiresAt — a first-class expiry timestamp, min(document expiry, registration time + maxProofAge) (maxProofAge defaults to ~1 year). No oracle is needed: the expiry is set at registration and checked on-chain by isProofFresh().

hasHumanProof() returns true for any agent that ever proved humanity, including expired ones. isProofFresh() distinguishes “still valid” from “needs renewal,” so callers can give different UX for each.

Verification strength

verificationStrength() returns a 0-100 score describing how strongly the proof binds a human identity. The SDK maps the score to a label with getProviderLabel(strength):

ScoreSDK label
≥ 100passport
≥ 80kyc
≥ 60govt_id
≥ 40liveness
< 40unknown

Self Protocol’s provider currently reports 100 (passport), reflecting NFC passport / ID verification with biometric match. The /api/reputation and /api/verify-status endpoints return this score and label. This is distinct from the Reputation Registry’s document-type weights (see below).

All three ERC-8004 registry types

ERC-8004 defines three registry roles. Self Agent ID implements all three on-chain:

  • Identity — SelfAgentRegistry. Agent NFT minting, human proof storage, ZK-attested credentials, and metadata (A2A Agent Cards as agentURI).
  • Reputation — SelfReputationRegistry. Aggregated feedback with document-type weighted signals (E-Passport/EU ID Card 100, Aadhaar 80, KYC 50).
  • Validation — SelfValidationRegistry. On-demand validation requests and responses from third parties, with a configurable freshness threshold.

See Smart Contracts for the feedback, validation, and gating APIs.

Agent documents are also A2A Agent Cards

The ERC-8004 registration document served at agentURI is designed to be optionally valid as an A2A Agent Card. A document that also contains url, version, provider, capabilities, and securitySchemes is simultaneously a valid A2A Agent Card, with no separate registration step. See Agent Registration JSON & Agent Cards.

Sybil resistance properties

  1. Registration reverts when a human proof is required (default) and none is supplied.
  2. A nullifier can control at most maxAgentsPerHuman agents at once.
  3. Revoking an agent frees the nullifier slot, so the human can re-register.
  4. Expired proofs cause isProofFresh() to return false without any transaction.

Reference implementation

The reference implementation uses the UUPS upgradeable proxy pattern, but implementers can choose any deployment strategy. The interfaces are proxy-agnostic.

ContractCelo Mainnet (42220)
SelfAgentRegistry0xaC3DF9ABf80d0F5c020C06B04Cced27763355944
SelfReputationRegistry0x69Da18CF4Ac27121FD99cEB06e38c3DC78F363f4
SelfValidationRegistry0x71a025e0e338EAbcB45154F8b8CA50b41e7A0577

Celo Sepolia testnet addresses are listed in Smart Contracts. Source: github.com/selfxyz/self-agent-id.

EIP proposal status

The Self Protocol team is preparing a PR to the ERC-8004 EIP proposing proof-of-human as an optional extension section, with the full interface specification, rationale, security considerations, and a link to this reference implementation. The draft targets ethereum-magicians discussion with deployed contract addresses before formal submission.

Next steps

Was this page helpful?