Solana ProgramsAgentRegistry

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
FrameworkAnchor 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
Sourceregent-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 bytes

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

AccountTypeNotes
configPDA [b"config"]Holds the authority and the agent counter
agentPDA [b"agent", agent_id]Initialized; rent paid by the authority
authoritySignerMust match config.authority
system_programProgram—
ArgTypeNotes
agent_id[u8; 32]SHA-256 of the agent_… id string
did_hash[u8; 32]SHA-256 of did:regent:solana:{agent_id}
responsible_partyPubkeysha256(owner account id), the owner’s UserAccount seed

Reverts if the PDA already exists.

revoke_agent

Sets revoked = true and stamps revoked_at.

AccountTypeNotes
agentMutable PDAMust exist; must not be revoked
authoritySignerMust 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.