# ERC-8004 & Proof-of-Human

Self Agent ID is a reference implementation of [ERC-8004](https://eips.ethereum.org/EIPS/eip-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.

```solidity
/// @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](/docs/agent-id/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

```solidity
/// @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

```solidity
/// @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](/docs/self-pass/contracts/basic-integration/).

## 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)`:

| Score | SDK label  |
| ----- | ---------- |
| ≥ 100 | `passport` |
| ≥ 80  | `kyc`      |
| ≥ 60  | `govt_id`  |
| ≥ 40  | `liveness` |
| < 40  | `unknown`  |

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](/docs/agent-id/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](/docs/agent-id/agent-registration-json/).

## 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.

| Contract               | Celo Mainnet (42220)                                                                                                   |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| SelfAgentRegistry      | [`0xaC3DF9ABf80d0F5c020C06B04Cced27763355944`](https://celoscan.io/address/0xaC3DF9ABf80d0F5c020C06B04Cced27763355944) |
| SelfReputationRegistry | [`0x69Da18CF4Ac27121FD99cEB06e38c3DC78F363f4`](https://celoscan.io/address/0x69Da18CF4Ac27121FD99cEB06e38c3DC78F363f4) |
| SelfValidationRegistry | [`0x71a025e0e338EAbcB45154F8b8CA50b41e7A0577`](https://celoscan.io/address/0x71a025e0e338EAbcB45154F8b8CA50b41e7A0577) |

Celo Sepolia testnet addresses are listed in [Smart Contracts](/docs/agent-id/smart-contracts/#celo-sepolia-11142220). Source: [github.com/selfxyz/self-agent-id](https://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

- [Smart Contracts](/docs/agent-id/smart-contracts/) — deployed addresses and the full on-chain API
- [Agent Registration JSON & Agent Cards](/docs/agent-id/agent-registration-json/) — the identity document format
- [Verification Patterns](/docs/agent-id/verification-patterns/) — how services and contracts consume these checks
