8004 on Solana

Technical Documentation for AI Agent Identity & Reputation Registry

agent-registry-8004 (Devnet) 8oo48pya1SZD23ZhzoNMhxR2UGb8BRa41Su4qP9EuaWm
atom-engine (Devnet) AToM1iKaniUCuWfHd5WQy5aLgJYWMiKq78NtNJmtzSXJ

2 programs | Metaplex Core | ATOM Engine (HLL + Ring Buffer) | 35+ security tests | Asset-based IDs

Changelog

New: ATOM Engine integration for advanced on-chain reputation analytics with Sybil resistance.

New Program: atom-engine

CPI Integration

New Account: AtomStats (460 bytes)

FieldTypeDescription
collectionPubkeyCollection filter
assetPubkeyAgent identifier
feedback_countu32Total feedbacks
quality_scorei32Weighted score (EMA)
hll_packed[u8; 128]HyperLogLog registers
hll_saltu64Per-agent salt
recent_callers[u64; 24]Ring buffer fingerprints
eviction_cursoru8Round robin pointer

Enriched Events

NewFeedback now includes:

Overview

8004 is a standard for on-chain AI agent registries. This Solana implementation features SEAL v1 integrity, ATOM Engine reputation, and single-collection architecture:

ID

Identity Module

NFT-based agent registration with Metaplex Core. Each agent's Core asset address is its unique identifier.

Reputation + ATOM

Feedback system with CPI to ATOM Engine. HyperLogLog for Sybil resistance, trust tiers, and real-time scoring.

🔒

SEAL v1

Solana Event Authenticity Layer. On-chain hash computation ensures feedback integrity — clients cannot lie about what they submitted.

0.0058
SOL per Agent Registration
~0.0046
SOL per Feedback + ATOM
460
Bytes AtomStats/Agent
5
Trust Tiers

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐ │ agent-registry-8004 (Devnet) │ │ 8oo48pya1SZD23ZhzoNMhxR2UGb8BRa41Su4qP9EuaWm │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┐ ┌──────────────────┐ ┌────────────────────────────┐ │ │ ┌────────────────────┐ ┌──────────────────┐ ┌────────────────────────────┐ │ │ │ Identity Module │ │ Reputation Module│ │ SEAL v1 │ │ │ ├────────────────────┤ ├──────────────────┤ ├────────────────────────────┤ │ │ │ • register() │ │ • give_feedback()│ │ • On-chain seal_hash │ │ │ │ • set_metadata_pda │ │ └─→ CPI ATOM │ │ • Hash-chain digests │ │ │ │ • delete_meta_pda │ │ • revoke_feedback│ │ • Feedback/revoke/response │ │ │ │ • set_agent_uri() │ │ └─→ CPI ATOM │ │ integrity verification │ │ │ │ • transfer_agent() │ │ • append_response│ │ │ │ │ └────────────────────┘ └────────┬─────────┘ └────────────────────────────┘ │ ├──────────────────────────────────┼──────────────────────────────────────────┤ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ atom-engine (Devnet) │ │ │ │ AToM1iKaniUCuWfHd5WQy5aLgJYWMiKq78NtNJmtzSXJ │ │ │ ├──────────────────────────────────────────────────────────────────────┤ │ │ │ • update_stats() — HLL + Ring Buffer + EMA + Trust Tier │ │ │ │ • revoke_stats() — Remove from ring buffer, adjust scores │ │ │ │ • AtomStats PDA — 460 bytes per agent │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────────────┤ │ METAPLEX CORE │ │ • Collection created on initialize() as Core Collection │ │ • Agent NFTs are Core Assets verified against collection │ └─────────────────────────────────────────────────────────────────────────────┘

ATOM Engine

Agent Trust On-chain Model - Advanced reputation analytics with Sybil resistance.

ATOM provides real-time trust scoring via HyperLogLog (unique clients), ring buffer (burst detection), and EMA (quality weighting).

Trust Tiers

TierValueThreshold (Upgrade)Hysteresis (Downgrade)
Unknown0--
New11 feedback0
Established210 feedbacks + 60 quality50 quality
Trusted350 feedbacks + 75 quality65 quality
Legendary4200 feedbacks + 90 quality80 quality

Key Algorithms

HyperLogLog (Unique Client Estimation)

256 registers, 4-bit packed (128 bytes). Estimates unique clients with ~6.5% standard error.

// Per-agent salt prevents grinding attacks
salted_hash = keccak256(client_hash || hll_salt)
register_idx = salted_hash[0..8] % 256
leading_zeros = count_leading_zeros(salted_hash[8..])
registers[idx] = max(registers[idx], leading_zeros + 1)

Ring Buffer (Burst Detection & Revoke)

24 slots with 56-bit fingerprints. Round-robin eviction prevents manipulation.

// Fingerprint = truncated keccak hash
fingerprint = keccak256(client_pubkey)[0..7]

// Round-robin eviction (cursor increments each write)
slot = eviction_cursor % 24
recent_callers[slot] = fingerprint
eviction_cursor += 1

EMA Quality Score

Exponential Moving Average with α=0.1 (10% weight to new scores).

// Centered score: 0-100 → -50 to +50
centered = (score as i32) - 50

// EMA update (scaled by 1000 for precision)
quality_score = (quality_score * 900 + centered * 100) / 1000

CPI Flow

┌─────────────────────────────────────────────────────────────────────────┐ │ give_feedback() │ │ agent-registry-8004 │ ├─────────────────────────────────────────────────────────────────────────┤ │ 1. Validate feedback parameters │ │ 2. Compute client_hash = keccak256(client.key) │ │ 3. CPI → atom_engine::update_stats(client_hash, score) │ │ └─→ Returns: UpdateResult { trust_tier, quality_score, ... } │ │ 4. Emit NewFeedback event with ATOM metrics │ └─────────────────────────────────────────────────────────────────────────┘

Account Structure: AtomStats

pub struct AtomStats {           // 460 bytes total
    pub collection: Pubkey,      // 32 - Collection filter
    pub asset: Pubkey,           // 32 - Agent identifier
    pub feedback_count: u32,     // 4  - Total feedbacks
    pub positive_count: u32,     // 4  - Score > 50
    pub negative_count: u32,     // 4  - Score < 50
    pub quality_score: i32,      // 4  - EMA score (scaled)
    pub last_feedback_slot: u64, // 8  - Slot of last feedback
    pub hll_packed: [u8; 128],   // 128 - HLL registers (256 × 4-bit)
    pub hll_salt: u64,           // 8  - Per-agent salt
    pub recent_callers: [u64; 24], // 192 - Ring buffer
    pub eviction_cursor: u8,     // 1  - Round robin pointer
    // Output cache (computed on update)
    pub trust_tier: u8,          // 1
    pub confidence: u8,          // 1
    pub risk_score: u8,          // 1
    pub diversity_ratio: u8,     // 1
    pub bump: u8,                // 1
}
PDA: seeds = ["atom_stats", asset.key()]

Tools & Ecosystem

SDK

8004 Solana TypeScript SDK

Official TypeScript SDK for interacting with 8004 programs. Includes PDA derivation, Borsh schemas, and program wrappers.

Available

GitHub Repository →

🔍

Agent Explorer

Browse and explore all registered 8004 agents on Solana. View agent metadata, reputation scores, and feedback history.

Available

x402synthex.xyz/agents →

UI

Agent Creation Tool

Web interface for creating and managing 8004 agents. No-code solution for registering AI agents on Solana.

Launching This Week

x402synthex.xyz/create-agent →

IDX

8004 Indexer

Off-chain indexer for 8004 events and account data. Enables fast queries for agent reputation and feedback history.

In Development Expected: Q1 2026

Identity Module

Manages agent registration with NFT-based identity. Each agent is represented by a unique Metaplex Core asset.

Key Design: Permissionless Registration

Anyone can register agents without requiring deployer signature. The program's Collection Authority PDA signs for Metaplex collection verification via invoke_signed().

Instructions

initialize()

Initialize the registry and create the Metaplex Core Collection.

What it does:

  • Creates Config PDA with global state
  • Creates Metaplex Core Collection
  • Config PDA becomes collection update_authority (enables permissionless registration)

Accounts:

AccountTypeDescription
configinit mutPDA: ["config"]
collectioninitCore Collection asset
authoritysignerRegistry deployer

register(agent_uri: String) / register_empty()

Register a new agent with optional URI.

Parameters:

ParamTypeConstraint
agent_uriStringMax 200 bytes (or empty for register_empty)

Flow:

  1. Create Metaplex Core asset with collection plugin
  2. Add to collection via Config PDA signature
  3. Initialize AgentAccount PDA keyed by Core asset address
  4. Asset address = unique identifier (no sequential counter)
Agent PDA: seeds = ["agent", asset.key()]

Events: Registered { asset, owner, agent_uri }

set_metadata_pda(key_hash, key, value, immutable)

Create or update a metadata entry as individual PDA. Only Core asset holder can call.

Parameters:

ParamTypeDescription
key_hash[u8; 8]First 8 bytes of SHA256(key)
keyStringMax 32 bytes
valueVec<u8>Max 256 bytes
immutableboolIf true, cannot modify/delete later
Metadata PDA: seeds = ["agent_meta", asset.key(), key_hash[0..8]]
Once immutable: true, the entry is permanently locked.

delete_metadata_pda(key_hash)

Delete a metadata PDA and recover rent. Only works if not immutable.

Parameters:

ParamTypeDescription
key_hash[u8; 8]First 8 bytes of SHA256(key)

Returns: ~0.0031 SOL rent to owner

Events: MetadataDeleted

set_agent_uri(new_uri: String)

Update agent URI and sync to Metaplex Core asset.

Uses UpdateV1CpiBuilder to update Core asset URI. Config PDA signs as collection update authority.

transfer_agent()

Transfer Core asset to new owner. Uses TransferV1CpiBuilder for Metaplex Core transfer.

Account Structures

RegistryConfig (78 bytes)

pub struct RegistryConfig {
    pub collection: Pubkey,             // 32 bytes (Core Collection)
    pub registry_type: RegistryType,    // 1 byte
    pub authority: Pubkey,              // 32 bytes
    pub base_index: u32,                // 4 bytes
    pub bump: u8,                       // 1 byte
}
// Removed: next_agent_id, total_agents (off-chain via indexer)
PDA: seeds = ["config"]

AgentAccount (313 bytes)

pub struct AgentAccount {
    pub owner: Pubkey,                  // 32 bytes (cached, synced on transfer)
    pub asset: Pubkey,                  // 32 bytes (Core asset = unique ID)
    pub bump: u8,                       // 1 byte
    pub agent_uri: String,              // 4 + 200 = 204 bytes
    pub nft_name: String,               // 4 + 32 = 36 bytes ("Agent #X")
    // Metadata moved to individual PDAs (v0.2.0)
}
// Removed: agent_id, created_at, nft_symbol
// Use blockTime for timestamps, Metaplex for symbol
PDA: seeds = ["agent", asset.key()]

MetadataEntryPda (~306 bytes)

pub struct MetadataEntryPda {
    pub metadata_key: String,       // 4 + 32 = 36 bytes
    pub metadata_value: Vec<u8>,    // 4 + 256 = 260 bytes
    pub immutable: bool,            // 1 byte (if true, locked forever)
    pub bump: u8,                   // 1 byte
}
// Removed: agent_id, created_at
// Delete to recover rent (~0.0029 SOL)
PDA: seeds = ["agent_meta", asset.key(), key_hash[0..8]]

Error Codes

ErrorMessageCondition
UriTooLong"URI>200"agent_uri exceeds 200 bytes
KeyTooLong"Key>32"metadata key exceeds 32 bytes
ValueTooLong"Val>256"metadata value exceeds 256 bytes
MetadataImmutable"Immutable"Cannot modify/delete immutable metadata
Unauthorized"!owner"Caller is not agent owner
Overflow"Overflow"Agent ID counter overflowed
InvalidTokenAccount"!NFT"Token account doesn't hold the NFT

Events

EventFieldsEmitted
Registeredasset, agent_uri, ownerregister()
MetadataSetasset, indexed_key, key, value, immutableset_metadata_pda()
MetadataDeletedasset, keydelete_metadata_pda()
UriUpdatedasset, new_uri, updated_byset_agent_uri()
AgentOwnerSyncedasset, old_owner, new_ownertransfer_agent()
v0.3.0: All events use asset: Pubkey instead of agent_id: u64.

Reputation Module

Manages feedback and responses for agents. Uses asset (Pubkey) as identifier.

Key Design: Off-chain Aggregation

v0.3.0: Aggregates (total_feedbacks, average_score) computed off-chain via indexer. AgentReputationMetadata only stores next_feedback_index for sequencing.

Instructions

give_feedback(score, tag1, tag2, endpoint, feedback_uri, feedback_hash, feedback_index)

Create feedback for an agent. Asset passed via account context.

Parameters:

ParamTypeConstraint
scoreu80-100 (validated on-chain)
tag1, tag2StringMax 32 bytes each (in event)
endpointStringMax 200 bytes (in event)
feedback_uriStringMax 200 bytes (in event)
feedback_hash[u8; 32]SHA-256 hash
feedback_indexu64Must match next_feedback_index

Global Feedback Index:

// Index verified and incremented from AgentReputationMetadata
require!(feedback_index == agent_reputation.next_feedback_index);
agent_reputation.next_feedback_index += 1;
Event-only: No Feedback PDA. Data stored in NewFeedback event + rolling hash-chain digest on AgentAccount.

revoke_feedback(feedback_index, seal_hash)

Emit revocation event and update hash-chain digest. Permissionless (indexer validates authorship).

Parameters:

ParamTypeConstraint
feedback_indexu64Must be < agent.feedback_count
seal_hash[u8; 32]SEAL hash of original feedback (for digest integrity)

State Changes:

  • agent.revoke_digest updated (hash-chain)
  • agent.revoke_count += 1
  • CPI to ATOM revoke_stats (if enabled)
Event-only: No PDA. Emits FeedbackRevoked event.

append_response(asset_key, client_address, feedback_index, response_uri, response_hash, seal_hash)

Append response to existing feedback. Authorized only (agent owner or agent wallet).

Parameters:

ParamTypeConstraint
asset_keyPubkeyAgent asset key
client_addressPubkeyOriginal feedback author
feedback_indexu64Must be < agent.feedback_count
response_uriStringMax 200 bytes (in event)
response_hash[u8; 32]SHA-256 hash (in event)
seal_hash[u8; 32]SEAL hash of original feedback (for digest integrity)

State Changes:

  • agent.response_digest updated (hash-chain)
  • agent.response_count += 1
Event-only: No Response PDA. Emits ResponseAppended event.

Reputation Data Model

Event-Only Architecture

Feedback, revocation, and response data are stored as Solana events (not PDAs) for rent optimization. Integrity is guaranteed by rolling hash-chain digests stored in AgentAccount:

// In AgentAccount (120 bytes for reputation)
pub feedback_digest: [u8; 32],   // Rolling keccak256 hash-chain
pub feedback_count: u64,         // Global feedback counter
pub response_digest: [u8; 32],   // Rolling keccak256 hash-chain
pub response_count: u64,         // Response counter
pub revoke_digest: [u8; 32],     // Rolling keccak256 hash-chain
pub revoke_count: u64,           // Revoke counter

Each operation updates its chain: new_digest = keccak256(old_digest || domain || leaf_data).

Clients verify by replaying all events and comparing final digest against on-chain value.

Error Codes

ErrorMessageCondition
InvalidScore"Score[0-100]"score > 100
UriTooLong"URI>200"file_uri exceeds 200 bytes
Unauthorized"!author"Non-owner attempts append_response
AgentNotFound"!Agent"Agent doesn't exist in Identity Registry
InvalidFeedbackIndex"!FbIdx"Index mismatch
InvalidIdentityRegistry"!IdReg"Wrong Identity Registry program

Events

EventFields
NewFeedbackasset, client_address, feedback_index, score, tag1, tag2, endpoint, feedback_uri, feedback_hash
FeedbackRevokedasset, client_address, feedback_index
ResponseAppendedasset, feedback_index, responder, response_uri, response_hash
v0.3.0: All events use asset: Pubkey. URIs/hashes stored in events only.

SEAL v1 - Solana Event Authenticity Layer

Trustless on-chain hash computation for feedback integrity. The program computes seal_hash deterministically from all instruction parameters.

On-chain truth: seal_hash is computed BY the program, not supplied by the client. Clients cannot lie about what they submitted.

How It Works

CLIENT submits instruction params (value, score, tags, uri, ...) │ ▼ PROGRAM computes: seal_hash = keccak256(canonical_encoding(params)) │ ▼ LEAF binds seal to context: keccak256(DOMAIN || asset || client || index || seal_hash || slot) │ ▼ HASH-CHAIN: digest = keccak256(prev_digest || DOMAIN || leaf) │ ▼ INDEXER stores events with seal_hash for off-chain verification

Domain Separators

ConstantValue (16 bytes)Usage
DOMAIN_SEAL_V18004_SEAL_V1____Seal hash prefix
DOMAIN_LEAF_V18004_LEAF_V1____Leaf hash prefix
DOMAIN_FEEDBACK8004_FEED_V1___Feedback chain
DOMAIN_RESPONSE8004_RESP_V1___Response chain
DOMAIN_REVOKE8004_REVK_V1___Revoke chain

Hash-Chain Digests

Three independent rolling hash-chains stored in AgentAccount:

FieldUpdated ByFormat
feedback_digest + feedback_countgive_feedback()40 bytes
response_digest + response_countappend_response()40 bytes
revoke_digest + revoke_countrevoke_feedback()40 bytes

See SEAL.md for full binary format specification and cross-validation vectors.

Security

Security mechanisms in the unified program architecture.

Agent Existence Verification

Reputation module verifies agent existence by checking the AgentAccount PDA exists before processing.

// Agent PDA must exist (asset = unique identifier)
#[account(
    seeds = [b"agent", asset.key().as_ref()],
    bump = agent_account.bump,
)]
pub agent_account: Account<'info, AgentAccount>,

Ownership Verification (Metaplex Core)

For metadata/URI updates, ownership verified via Core asset directly:

// Core asset stores owner in its data
constraint = asset.owner == owner.key()

Why: Metaplex Core assets store owner directly, simplifying ownership verification.

Events-Only Reputation

Feedbacks, responses, and revocations are events-only (no per-feedback PDAs). Integrity ensured via SEAL v1 hash-chains:

// Hash-chain rolling digests in AgentAccount
agent.feedback_digest = chain_hash(prev_digest, DOMAIN_FEEDBACK, leaf);
agent.feedback_count += 1;

SEAL v1 Integrity

seal_hash computed on-chain from all feedback parameters. Indexers verify by replaying events against on-chain digests.

let seal_hash = compute_seal_hash_v1(value, decimals, score, ...);
// seal_hash emitted in event, bound into hash-chain leaf

Cost Analysis (v0.3.0)

Operation Costs (Estimated)

OperationCost (SOL)Notes
Register Agent~0.0058Core asset + AgentAccount (smaller)
Set Metadata~0.0032MetadataEntryPda
Give Feedback~0.0014FeedbackAccount (optimized)
Append Response~0.0012ResponseAccount (minimal)
Initialize ATOM Stats~0.005AtomStats PDA

v0.3.0 Account Size Optimizations

Accountv0.2.0v0.3.0Savings
AgentReputationMetadata50 bytes17 bytes-66%
ResponseAccount73 bytes41 bytes-44%
ResponseIndexAccount33 bytes17 bytes-48%
FeedbackAccount99 bytes83 bytes-16%
FeedbackTagsPda97 bytes81 bytes-16%
AgentAccount343 bytes313 bytes-9%
RegistryConfig94 bytes78 bytes-17%

Per agent (1 feedback, 1 response): significant rent savings via events-only design

What Was Removed

1. Validation Module

Removed entirely. Archived for future upgrade.

2. On-chain Aggregates

From AgentReputationMetadata:

  • total_feedbacks, total_score_sum, average_score, last_updated

Now computed off-chain. Only next_feedback_index remains for sequencing.

3. Redundant Fields

  • agent_id everywhere (replaced by asset)
  • collection from FeedbackAccount
  • created_at from all accounts (use blockTime)
  • nft_symbol from AgentAccount (read from Metaplex)
  • response_hash, indices from ResponseAccount (in events)

4. Rent Recovery

Accounts can be closed to recover rent:

  • delete_metadata_pda() - Returns ~0.0029 SOL per entry

PDA Reference (v0.3.0)

v0.3.0: All PDAs now use asset.key() instead of agent_id. The Core NFT address is the unique identifier.

Identity Module PDAs

AccountSeeds
Config["config"]
Agent["agent", asset.key()]
Metadata Entry["agent_meta", asset.key(), key_hash[0..8]]

Reputation Module PDAs

AccountSeeds
Agent Reputation["agent_reputation", asset.key()]
Feedback["feedback", asset.key(), feedback_index (LE)]
Feedback Tags["feedback_tags", asset.key(), feedback_index (LE)]
Response["response", asset.key(), feedback_index (LE), response_index (LE)]
Response Index["response_index", asset.key(), feedback_index (LE)]

ATOM Engine PDAs

AccountSeeds
AtomStats["atom_stats", asset.key()]
ATOM Config["atom_config"]

Metaplex Core (External)

AccountDescription
CollectionCore Collection asset (stored in config.collection_mint)
AssetCore Asset for each agent = unique identifier