AgentRegistry is the on-chain source of truth for whether an agent exists and whether it is active. Every off-chain claim about an agent can be checked against this program.
| Program ID (devnet) | 5jBmqyeo1vUAjHbEFuY59NMGTQR8cEe9Jvz2uCwCjp3L |
| Framework | Anchor 0.31.1 |
| Upgradeable? | On devnet, yes — the upgrade authority 47BBdKL1SwY7auTN4mQpnQAFX14bCjs7qie9R34FrXxc is held by Regent so fixes can ship during the pilot phase. ADR-010 commits the core programs to be non-upgradeable on mainnet |
| Source | regent-protocol/regent-solana-programs → programs/agent-registry |
Account layout
Each agent gets a Program Derived Address (PDA) keyed by a 32-byte agent reference:
#[account]
pub struct Agent {
pub agent_id: [u8; 32], // sha256(agent_id) for `agent_…` ids; legacy UUID ids are the raw 16 bytes, left-padded
pub did_hash: [u8; 32], // sha256("did:regent:solana:{agent_id}")
pub responsible_party: Pubkey, // sha256(owner account id) = the owner's UserAccount PDA seed
pub registered_at: i64, // unix seconds
pub revoked: bool,
pub revoked_at: Option<i64>,
pub bump: u8,
}
// 8-byte discriminator + 123 bytesThe PDA seed is [b"agent", agent_id], so anyone who knows the agent id can re-derive the address and read the account. There is no status enum: an agent is active while revoked == false.
Only hashes are stored — never the agent id, the DID or the owner’s identity. responsible_party is a 32-byte commitment to the owner, sha256(<owner account id>), and that value is exactly the [b"user", user_id_hash] seed of the owner’s UserAccount below, so an agent account resolves on-chain to the record holding the owner’s KYC attestation hash, country and DID hash. A value that already is a Solana public key (an owner wallet) is written as is.
Agents registered before 2026-10-04 carry the protocol authority’s key in responsible_party: owner ids are not Solana keys, and the worker fell back to the authority instead of committing to the owner. Those accounts are immutable; the binding for them is in the signed identity record and the anchored audit trail.
The owner’s record is a UserAccount (seeds = [b"user", user_id_hash]): user_id_hash, did_hash, kyc_attestation_hash, kyc_country, kyc_verified, registered_at. It is written by register_user when the owner completes identity verification (the platform emits user.did_created, the worker anchors it). The signed identity record returned by the API additionally carries a pairwise commitment sha256(agent_id:owner_id).
Instructions
register_agent
Writes a new Agent account with revoked = false.
| Account | Type | Notes |
|---|---|---|
config | PDA [b"config"] | Holds the authority and the agent counter |
agent | PDA [b"agent", agent_id] | Initialized; rent paid by the authority |
authority | Signer | Must match config.authority |
system_program | Program | — |
| Arg | Type | Notes |
|---|---|---|
agent_id | [u8; 32] | SHA-256 of the agent_… id string |
did_hash | [u8; 32] | SHA-256 of did:regent:solana:{agent_id} |
responsible_party | Pubkey | sha256(owner account id), the owner’s UserAccount seed |
Reverts if the PDA already exists.
revoke_agent
Sets revoked = true and stamps revoked_at.
| Account | Type | Notes |
|---|---|---|
agent | Mutable PDA | Must exist; must not be revoked |
authority | Signer | Must be the protocol authority |
Reverts if the agent doesn’t exist or is already revoked. The protocol’s blockchain-worker calls this when a responsible party (or automated workflow) revokes an agent off-chain.
transfer_authority
Moves config.authority to a new key. Used once on 2026-09-26 to rotate the authority of all three programs.
Trust model
Only the protocol authority can call register_agent or revoke_agent. This is intentional:
- Registration requires KYC verification, which the protocol enforces off-chain
- Revocation must follow the responsible-party authorization model
- A public, permissionless instruction would let anyone register or revoke arbitrary agents
The authority keypair is held in AWS Secrets Manager and loaded only by the blockchain-worker service. It was rotated on 2026-09-26 with transfer_authority.
Verifying an agent off-chain
import { Connection, PublicKey } from "@solana/web3.js";
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { sha256 } from "js-sha256";
const connection = new Connection("https://api.devnet.solana.com");
const programId = new PublicKey("5jBmqyeo1vUAjHbEFuY59NMGTQR8cEe9Jvz2uCwCjp3L");
// Re-derive the agent's PDA
const agentIdHash = Buffer.from(sha256("agent_b1c59d23..."), "hex");
const [agentPda] = PublicKey.findProgramAddressSync(
[Buffer.from("agent"), agentIdHash],
programId,
);
// Read it
const account = await program.account.agent.fetch(agentPda);
console.log("Revoked:", account.revoked); // false = active
console.log("Registered:", account.registeredAt);
console.log("Revoked at:", account.revokedAt);
// Resolve the owner: responsible_party is the seed of the owner's UserAccount
const [userPda] = PublicKey.findProgramAddressSync(
[Buffer.from("user"), Buffer.from(account.responsibleParty.toBytes())],
programId,
);
const owner = await program.account.userAccount.fetch(userPda);
console.log("Owner KYC verified:", owner.kycVerified, "country:", Buffer.from(owner.kycCountry).toString());If the account is missing → the agent was never anchored. If revoked is true → don’t trust any authorizations from this agent regardless of what the off-chain API says.