Technical Documentation for AI Agent Identity & Reputation Registry
2 programs | Metaplex Core | ATOM Engine (HLL + Ring Buffer) | 35+ security tests | Asset-based IDs
give_feedback → CPI to atom_engine::update_statsrevoke_feedback → CPI to atom_engine::revoke_statsNewFeedback event enriched with ATOM metrics| Field | Type | Description |
|---|---|---|
| collection | Pubkey | Collection filter |
| asset | Pubkey | Agent identifier |
| feedback_count | u32 | Total feedbacks |
| quality_score | i32 | Weighted score (EMA) |
| hll_packed | [u8; 128] | HyperLogLog registers |
| hll_salt | u64 | Per-agent salt |
| recent_callers | [u64; 24] | Ring buffer fingerprints |
| eviction_cursor | u8 | Round robin pointer |
NewFeedback now includes:
new_trust_tier — Current trust tier (0-4)new_quality_score — Updated EMA scorenew_confidence — Statistical confidence (0-100)new_risk_score — Burst/outlier risk (0-100)new_diversity_ratio — Client diversity (0-100)is_unique_client — HLL changed flag8004 is a standard for on-chain AI agent registries. This Solana implementation features SEAL v1 integrity, ATOM Engine reputation, and single-collection architecture:
NFT-based agent registration with Metaplex Core. Each agent's Core asset address is its unique identifier.
Feedback system with CPI to ATOM Engine. HyperLogLog for Sybil resistance, trust tiers, and real-time scoring.
Solana Event Authenticity Layer. On-chain hash computation ensures feedback integrity — clients cannot lie about what they submitted.
Agent Trust On-chain Model - Advanced reputation analytics with Sybil resistance.
| Tier | Value | Threshold (Upgrade) | Hysteresis (Downgrade) |
|---|---|---|---|
| Unknown | 0 | - | - |
| New | 1 | 1 feedback | 0 |
| Established | 2 | 10 feedbacks + 60 quality | 50 quality |
| Trusted | 3 | 50 feedbacks + 75 quality | 65 quality |
| Legendary | 4 | 200 feedbacks + 90 quality | 80 quality |
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)
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
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
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
}
Official TypeScript SDK for interacting with 8004 programs. Includes PDA derivation, Borsh schemas, and program wrappers.
Available
Browse and explore all registered 8004 agents on Solana. View agent metadata, reputation scores, and feedback history.
Available
Web interface for creating and managing 8004 agents. No-code solution for registering AI agents on Solana.
Launching This Week
Off-chain indexer for 8004 events and account data. Enables fast queries for agent reputation and feedback history.
In Development Expected: Q1 2026
Manages agent registration with NFT-based identity. Each agent is represented by a unique Metaplex Core asset.
invoke_signed().
Initialize the registry and create the Metaplex Core Collection.
What it does:
Config PDA with global stateAccounts:
| Account | Type | Description |
|---|---|---|
| config | init mut | PDA: ["config"] |
| collection | init | Core Collection asset |
| authority | signer | Registry deployer |
Register a new agent with optional URI.
Parameters:
| Param | Type | Constraint |
|---|---|---|
| agent_uri | String | Max 200 bytes (or empty for register_empty) |
Flow:
Events: Registered { asset, owner, agent_uri }
Create or update a metadata entry as individual PDA. Only Core asset holder can call.
Parameters:
| Param | Type | Description |
|---|---|---|
| key_hash | [u8; 8] | First 8 bytes of SHA256(key) |
| key | String | Max 32 bytes |
| value | Vec<u8> | Max 256 bytes |
| immutable | bool | If true, cannot modify/delete later |
immutable: true, the entry is permanently locked.
Delete a metadata PDA and recover rent. Only works if not immutable.
Parameters:
| Param | Type | Description |
|---|---|---|
| key_hash | [u8; 8] | First 8 bytes of SHA256(key) |
Returns: ~0.0031 SOL rent to owner
Events: MetadataDeleted
Update agent URI and sync to Metaplex Core asset.
Uses UpdateV1CpiBuilder to update Core asset URI. Config PDA signs as collection update authority.
Transfer Core asset to new owner. Uses TransferV1CpiBuilder for Metaplex Core transfer.
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)
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
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)
| Error | Message | Condition |
|---|---|---|
| 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 |
| Event | Fields | Emitted |
|---|---|---|
| Registered | asset, agent_uri, owner | register() |
| MetadataSet | asset, indexed_key, key, value, immutable | set_metadata_pda() |
| MetadataDeleted | asset, key | delete_metadata_pda() |
| UriUpdated | asset, new_uri, updated_by | set_agent_uri() |
| AgentOwnerSynced | asset, old_owner, new_owner | transfer_agent() |
asset: Pubkey instead of agent_id: u64.
Manages feedback and responses for agents. Uses asset (Pubkey) as identifier.
AgentReputationMetadata only stores next_feedback_index for sequencing.
Create feedback for an agent. Asset passed via account context.
Parameters:
| Param | Type | Constraint |
|---|---|---|
| score | u8 | 0-100 (validated on-chain) |
| tag1, tag2 | String | Max 32 bytes each (in event) |
| endpoint | String | Max 200 bytes (in event) |
| feedback_uri | String | Max 200 bytes (in event) |
| feedback_hash | [u8; 32] | SHA-256 hash |
| feedback_index | u64 | Must 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;
NewFeedback event + rolling hash-chain digest on AgentAccount.
Emit revocation event and update hash-chain digest. Permissionless (indexer validates authorship).
Parameters:
| Param | Type | Constraint |
|---|---|---|
| feedback_index | u64 | Must 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 += 1revoke_stats (if enabled)FeedbackRevoked event.
Append response to existing feedback. Authorized only (agent owner or agent wallet).
Parameters:
| Param | Type | Constraint |
|---|---|---|
| asset_key | Pubkey | Agent asset key |
| client_address | Pubkey | Original feedback author |
| feedback_index | u64 | Must be < agent.feedback_count |
| response_uri | String | Max 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 += 1ResponseAppended event.
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 | Message | Condition |
|---|---|---|
| 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 |
| Event | Fields |
|---|---|
| NewFeedback | asset, client_address, feedback_index, score, tag1, tag2, endpoint, feedback_uri, feedback_hash |
| FeedbackRevoked | asset, client_address, feedback_index |
| ResponseAppended | asset, feedback_index, responder, response_uri, response_hash |
asset: Pubkey. URIs/hashes stored in events only.
Trustless on-chain hash computation for feedback integrity. The program computes seal_hash deterministically from all instruction parameters.
| Constant | Value (16 bytes) | Usage |
|---|---|---|
| DOMAIN_SEAL_V1 | 8004_SEAL_V1____ | Seal hash prefix |
| DOMAIN_LEAF_V1 | 8004_LEAF_V1____ | Leaf hash prefix |
| DOMAIN_FEEDBACK | 8004_FEED_V1___ | Feedback chain |
| DOMAIN_RESPONSE | 8004_RESP_V1___ | Response chain |
| DOMAIN_REVOKE | 8004_REVK_V1___ | Revoke chain |
Three independent rolling hash-chains stored in AgentAccount:
| Field | Updated By | Format |
|---|---|---|
| feedback_digest + feedback_count | give_feedback() | 40 bytes |
| response_digest + response_count | append_response() | 40 bytes |
| revoke_digest + revoke_count | revoke_feedback() | 40 bytes |
See SEAL.md for full binary format specification and cross-validation vectors.
Security mechanisms in the unified program architecture.
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>,
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.
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_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
| Operation | Cost (SOL) | Notes |
|---|---|---|
| Register Agent | ~0.0058 | Core asset + AgentAccount (smaller) |
| Set Metadata | ~0.0032 | MetadataEntryPda |
| Give Feedback | ~0.0014 | FeedbackAccount (optimized) |
| Append Response | ~0.0012 | ResponseAccount (minimal) |
| Initialize ATOM Stats | ~0.005 | AtomStats PDA |
| Account | v0.2.0 | v0.3.0 | Savings |
|---|---|---|---|
| AgentReputationMetadata | 50 bytes | 17 bytes | -66% |
| ResponseAccount | 73 bytes | 41 bytes | -44% |
| ResponseIndexAccount | 33 bytes | 17 bytes | -48% |
| FeedbackAccount | 99 bytes | 83 bytes | -16% |
| FeedbackTagsPda | 97 bytes | 81 bytes | -16% |
| AgentAccount | 343 bytes | 313 bytes | -9% |
| RegistryConfig | 94 bytes | 78 bytes | -17% |
Per agent (1 feedback, 1 response): significant rent savings via events-only design
Removed entirely. Archived for future upgrade.
From AgentReputationMetadata:
total_feedbacks, total_score_sum, average_score, last_updatedNow computed off-chain. Only next_feedback_index remains for sequencing.
agent_id everywhere (replaced by asset)collection from FeedbackAccountcreated_at from all accounts (use blockTime)nft_symbol from AgentAccount (read from Metaplex)response_hash, indices from ResponseAccount (in events)Accounts can be closed to recover rent:
delete_metadata_pda() - Returns ~0.0029 SOL per entryasset.key() instead of agent_id. The Core NFT address is the unique identifier.
| Account | Seeds |
|---|---|
| Config | ["config"] |
| Agent | ["agent", asset.key()] |
| Metadata Entry | ["agent_meta", asset.key(), key_hash[0..8]] |
| Account | Seeds |
|---|---|
| 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)] |
| Account | Seeds |
|---|---|
| AtomStats | ["atom_stats", asset.key()] |
| ATOM Config | ["atom_config"] |
| Account | Description |
|---|---|
| Collection | Core Collection asset (stored in config.collection_mint) |
| Asset | Core Asset for each agent = unique identifier |