SATP V3 SDK - @brainai/satp-client
Solana Agent Token Protocol - JavaScript/TypeScript SDK for interacting with the SATP V3 devnet programs.
Current stable npm package: @brainai/satp-client@2.0.1 | reviewed rc artifact: @brainai/satp-client@2.0.2-rc.0 | Programs: 6
Installation
Choose stable, rc, or Git based on what the consumer needs to prove:
| Stable npm | Default production-style consumption of the stable public package. | npm install @brainai/satp-client@2.0.1 |
| Release candidate npm | Validating the reviewed rc package before promotion or producing reproducible rc manifests. | npm install @brainai/satp-client@2.0.2-rc.0 |
| Release candidate tag | Quick rc opt-in where a moving dist-tag is acceptable. | npm install @brainai/satp-client@rc |
| Reviewed Git commit | PR coordination or source-review installs tied to an exact SATP commit. | npm install git+https://github.com/brainAI-bot/satp.git#<SATP_COMMIT> |
The npm latest tag still resolves to @brainai/satp-client@2.0.1.
Historical rc-tag readback may still show the older 0.1.0-rc.0 package until
the rc channel is promoted. The reviewed RC-S6 artifact in this source tree is
@brainai/satp-client@2.0.2-rc.0; downstream apps that need reproducible
manifests should pin that exact version after promotion instead of relying on
the moving @rc tag.
For stable consumer installs, pin the current published npm package:
npm install @brainai/satp-client@2.0.1
For exact rc validation:
npm install @brainai/satp-client@2.0.2-rc.0
For short-lived rc opt-in:
npm install @brainai/satp-client@rc
For branch-only development or PR review, pin an explicit SATP Git commit:
npm install git+https://github.com/brainAI-bot/satp.git
The old 0.0.0-extraction label was extraction-branch metadata and is not the
current consumer package. Do not treat branch-only Git installs as npm latest.
Mainnet program IDs are intentionally not enabled in this release candidate.
Constructors and helpers fail closed for network: 'mainnet' until an approved
mainnet decision packet provides production program IDs.
Runtime dependency: @solana/web3.js ^1.98.4
Quick Start
const { SATPV3SDK } = require('@brainai/satp-client');
const sdk = new SATPV3SDK({ network: 'devnet' });
const exists = await sdk.hasIdentity('brainChain');
console.log(exists);
const record = await sdk.getGenesisRecord('brainChain');
console.log(record.agentName, record.category, record.isActive);
const tx = await sdk.buildCreateIdentity(creatorPubkey, 'myAgent', {
agentName: 'My Agent',
description: 'An AI agent on Solana',
category: 'assistant',
capabilities: ['chat', 'code'],
metadataUri: 'https://example.com/meta.json',
});
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ SATP V3 SDK │
├─────────────┬──────────────┬──────────────┬─────────────────────┤
│ Identity │ Reviews │ Attestations │ Escrow │
│ (20 methods)│ (7 methods) │ (3 methods) │ (10 methods) │
├─────────────┼──────────────┼──────────────┤ │
│ Reputation │ Validation │ Migration │ │
│ (1 method) │ (1 method) │ (1 method) │ │
├─────────────┴──────────────┴──────────────┴─────────────────────┤
│ PDA Derivation │ Borsh Serialization │ RPC Helpers │
└─────────────────┴───────────────────────┴───────────────────────┘
Programs & Program IDs
identity_v3 | GTppU4E44BqXTQgbqMZ68ozFzhP1TLty3EGnzzjtNZfG | Agent identity, names, wallets, face/birth |
reviews_v3 | r9XX4frcqxxAZ6Au9V5PA3EAxs1zoNckqLLmoSRcNr4 | Peer reviews with 1-5 star ratings |
attestations_v3 | 6Xd1dAQJPvQRJ4Ntr6LtPTjDjPUZ8nfnmYLZaZ2DtrdD | Third-party attestations & proofs |
reputation_v3 | 2Lz7KzMvKdrGeAuS8WPHu7jK2yScrnKVgacpYVEuDjkJ | Weighted reputation scoring (CPI → identity) |
validation_v3 | 6rYRiCYidJYV7QvKrzKGgNu4oMh6BAvynked69R7xMbV | Validation level computation (CPI → identity) |
escrow_v3 | B1Se8SPx7GLUisa4LYeXY1tDZy5TviJrsV2yMLgqUXmg | SOL escrow for agent jobs |
API Reference
Read-only Trust Packet Helpers
buildSatpTrustPacket(opts) creates a deterministic, offline trust packet for
consumer preflight and release-packet review. It uses the same inputs as
prepareIdentityAttestationRequest, then includes the derived program IDs,
Genesis PDA, attestation PDA, request hash, and the unsigned request object.
The packet is intentionally read-only: flags.signingRequired,
flags.transactionRequired, flags.writesRequired, and
flags.livePaymentRequired are all false; instructions and signers
are empty; and transaction is null.
const {
buildSatpTrustPacket,
validateSatpTrustPacket,
} = require('@brainai/satp-client');
const trustPacket = buildSatpTrustPacket({
subjectWallet: '11111111111111111111111111111111',
agentId: 'brainChain',
claimType: 'identity',
metadataHash: '93d122f8879fe87c186c10a00db8fbc80a73cecd2ede44b9ffa6410be3c2b805',
network: 'devnet',
});
const validation = validateSatpTrustPacket(trustPacket);
if (!validation.ok) throw new Error(validation.errors.join('; '));
validateSatpTrustPacket(packet) returns { ok, errors }. Validation requires
packetType: 'satp-trust-packet' and
mode: 'offline-readonly-trust-packet', rejects changed read-only flags, and
re-derives the expected packet so tampered PDA, program, request, or hash fields
surface as explicit errors.
x402 Discovery Evidence Lookup Helpers
parseX402DiscoveryMetadata(input), buildX402EvidenceLookup(input, opts),
and buildRuntimePolicyActionDescriptorFromX402Discovery(input, opts) map x402
discovery metadata into SATP runtime policy evidence lookup data. The helpers are
read-only: x402 payment metadata can identify where evidence may be fetched, but
it is discovery/evidence lookup only and never authorizes SATP action execution,
spending, live payment, signing, transactions, or host policy bypass.
Run the offline example:
node packages/satp-client/examples/x402-discovery-evidence-lookup.js
The example parses discovery metadata, builds an evidence lookup descriptor, and
builds a runtime policy action descriptor. It asserts
X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION plus
paymentAuthorization: false, actionAuthorization: false,
spendAuthorized: false, and livePaymentRequired: false.
Wallet-Control Challenge Helpers
buildWalletControlChallenge(opts) creates a canonical, offline challenge that
binds an agent ID to a Solana wallet. It derives the SATP V3 Genesis PDA and
linked-wallet PDA from agentId, wallet, and network, includes a nonce and
expiry, and returns plain JSON. It does not connect to RPC, read keypairs,
create transactions, sign, send, deploy, or mutate chain state.
canonicalWalletControlChallenge(challenge) returns the exact UTF-8 message a
wallet signs. verifyWalletControlChallengeSignature(opts) verifies a 64-byte
Ed25519 signature against the challenge wallet and fails closed for mismatched
wallets, signatures, agent IDs, PDAs, domain, audience, expiry, and replayed
nonces supplied by your replay cache.
const {
buildWalletControlChallenge,
canonicalWalletControlChallenge,
verifyWalletControlChallengeSignature,
} = require('@brainai/satp-client');
const challenge = buildWalletControlChallenge({
agentId: 'brainChain',
wallet: walletPublicKey,
audience: 'my-service',
nonce: crypto.randomBytes(16).toString('hex'),
});
const message = canonicalWalletControlChallenge(challenge);
const verification = verifyWalletControlChallengeSignature({
challenge,
signature,
expectedWallet: walletPublicKey,
expectedAgentId: 'brainChain',
expectedAudience: 'my-service',
usedNonces: replayCache,
});
if (!verification.ok) throw new Error(verification.errors.join('; '));
Constructor
const sdk = new SATPV3SDK({ network, rpcUrl });
Identity Methods (20)
buildCreateIdentity(creator, agentId, meta) | Create a new agent identity (Genesis Record) |
buildBurnToBecome(authority, agentId, faceImage, faceMint, faceBurnTx) | Burn NFT to set agent's face (birth ritual) |
buildUpdateIdentity(authority, agentId, updates) | Update mutable fields (description, capabilities, metadata) |
buildProposeAuthority(authority, agentId, newAuthority) | Propose authority transfer (2-step) |
buildAcceptAuthority(newAuthority, agentId) | Accept proposed authority transfer |
buildCancelAuthorityTransfer(authority, agentId) | Cancel pending authority transfer |
buildRegisterName(authority, agentId, name) | Register a unique name for an agent |
buildReleaseName(authority, agentId, name) | Release a registered name |
buildLinkWallet(authority, agentId, wallet, chain, label) | Link an external wallet to identity |
buildUnlinkWallet(authority, agentId, wallet) | Unlink an external wallet |
buildInitMintTracker(authority, agentId) | Initialize NFT mint tracker |
buildRecordMint(authority, agentId) | Record an NFT mint event |
buildDeactivateIdentity(authority, agentId) | Deactivate an identity |
buildReactivateIdentity(authority, agentId) | Reactivate a deactivated identity |
getGenesisRecord(agentId) | Read a Genesis Record from chain |
hasIdentity(agentId) | Check if an agent has an identity |
getEscrowPDA(client, description, nonce) | Derive escrow PDA (sync) |
buildMigrateV2ToV3(v2Authority, agentId, meta) | Migrate from V2 to V3 identity |
Genesis Record Fields
const record = await sdk.getGenesisRecord('brainChain');
{
agentIdHash: string,
agentName: string,
description: string,
category: string,
capabilities: string[],
metadataUri: string,
faceImage: string,
faceMint: string,
faceBurnTx: string,
genesisRecord: number,
isBorn: boolean,
isActive: boolean,
authority: string,
pendingAuthority: string | null,
reputationScore: number,
validationLevel: number,
createdAt: number,
updatedAt: number,
}
Reviews Methods (7)
buildInitReviewCounter(payer, agentId) | Initialize review counter for an agent |
buildCreateReview(reviewer, agentId, rating, text, metadata, opts) | Create a 1-5 star review |
buildCreateReviewWithSelfCheck(reviewer, agentId, rating, text, metadata) | Create review with self-review prevention |
buildUpdateReview(reviewer, reviewPDA, updates) | Update an existing review |
buildDeleteReview(reviewer, reviewPDA) | Soft-delete a review |
getReview(agentId, reviewer) | Read a review from chain |
getReviewCount(agentId) | Get total review count for an agent |
const tx = await sdk.buildCreateReview(
reviewerPubkey,
'brainChain',
5,
'Excellent Solana dev',
'metadata',
{ category: 'development' }
);
Attestations Methods (3)
buildCreateAttestation(issuer, agentId, type, proofData, expiresAt) | Issue an attestation |
buildVerifyAttestation(issuer, attestationPDA) | Mark attestation as verified |
buildRevokeAttestation(issuer, attestationPDA) | Revoke an attestation |
const tx = await sdk.buildCreateAttestation(
issuerPubkey,
'brainChain',
'kyc',
'proof-hash-here',
Math.floor(Date.now()/1000) + 86400 * 365
);
Reputation & Validation Methods (2)
buildRecomputeReputation(caller, agentId, reviewAccounts) | Recompute reputation score from reviews (CPI → identity) |
buildRecomputeLevel(caller, agentId, attestationAccounts) | Recompute validation level from attestations (CPI → identity) |
These use Cross-Program Invocation to update fields directly on the Genesis Record.
Escrow Methods (10)
App-agnostic escrow builders for downstream applications that need unsigned
SATP escrow transactions. Product-specific marketplace records, fees, job
workflow, moderation, and display copy stay in the consuming application.
buildCreateEscrow(client, agentWallet, agentId, amount, description, deadline, nonce, opts) | Create SOL escrow for a job |
buildSubmitWork(agent, escrowPDA, workProof) | Agent submits work proof |
buildEscrowRelease(client, agent, escrowPDA) | Client releases full payment |
buildPartialRelease(client, agent, escrowPDA, amount) | Client releases partial payment |
buildCancelEscrow(client, escrowPDA) | Cancel escrow (refund client) |
buildRaiseDispute(signer, escrowPDA, reason) | Raise a dispute |
buildResolveDispute(arbiter, agent, client, escrowPDA, agentAmt, clientAmt) | Arbiter resolves dispute |
buildExtendDeadline(client, escrowPDA, newDeadline) | Extend job deadline |
buildCloseEscrow(client, escrowPDA) | Close completed/cancelled escrow (reclaim rent) |
getEscrow(escrowPDA) | Read escrow state from chain |
Escrow Lifecycle
Created → WorkSubmitted → Released (full or partial)
↓ ↓ ↓
Cancelled Disputed Closed (rent reclaimed)
↓
Resolved (split)
↓
Closed
const tx = await sdk.buildCreateEscrow(
clientPubkey,
agentWallet,
'brainChain',
0.5 * 1e9,
'Complete agreed work',
Math.floor(Date.now()/1000) + 86400 * 7,
0,
{ arbiter: arbiterPubkey }
);
PDA Helpers (exported from v3-pda.js)
const {
hashAgentId,
hashName,
getGenesisPDA,
getNameRegistryPDA,
getLinkedWalletPDA,
getV3MintTrackerPDA,
getV3ReviewPDA,
getV3ReviewCounterPDA,
getV3AttestationPDA,
getV3ReputationAuthorityPDA,
getV3ValidationAuthorityPDA,
getV3EscrowPDA,
getV3ProgramIds,
} = require('@brainai/satp-client/src/v3-pda');
Escrow SDK Utilities (exported from v3-sdk.js)
const {
deriveEscrowPda,
descriptionHash,
EscrowStatus,
escrowStatusLabel,
escrowRemaining,
isEscrowExpired,
} = require('@brainai/satp-client/src/v3-sdk');
Transaction Pattern
All build* methods return an unsigned Transaction object. Your application is responsible for:
- Setting
recentBlockhash and feePayer
- Signing with the appropriate wallet
- Sending to the network
const tx = await sdk.buildCreateIdentity(wallet.publicKey, 'myAgent', { ... });
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.feePayer = wallet.publicKey;
tx.sign(wallet);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig);
Consumer APIs
SATP core does not define or host an HTTP API. Downstream applications may wrap
the SDK with their own read APIs, but those routes are consumer-owned adapters
and must not be treated as SATP protocol authority.
Testing
node test-v3.js
node test-v3-devnet.js
cd .. && node tests/devnet-cpi-integration.js
node test-release-safety.js
Network Configuration
const sdk = new SATPV3SDK();
const sdk = new SATPV3SDK({ network: 'devnet' });
const sdk = new SATPV3SDK({ rpcUrl: 'https://my-rpc.example.com' });
assert.throws(() => new SATPV3SDK({ network: 'mainnet' }));
Borsh Deserialization Helpers (v3.6.0)
Zero-dependency Borsh deserialization for all 8 SATP V3 account types. Decode raw on-chain data without the borsh library.
Supported Account Types
| GenesisRecord | Identity V3 | deserializeGenesisRecord(data) |
| LinkedWallet | Identity V3 | deserializeLinkedWallet(data) |
| MintTracker | Identity V3 | deserializeMintTracker(data) |
| NameRegistry | Identity V3 | deserializeNameRegistry(data) |
| Review | Reviews V3 | deserializeReview(data) |
| ReviewCounter | Reviews V3 | deserializeReviewCounter(data) |
| Attestation | Attestations V3 | deserializeAttestation(data) |
| EscrowV3 | Escrow V3 | deserializeEscrowV3(data) |
Usage: Typed Deserialization
const { deserializeGenesisRecord, deserializeAttestation } = require('@brainai/satp-client');
const { Connection, PublicKey } = require('@solana/web3.js');
const conn = new Connection('https://api.devnet.solana.com');
const acct = await conn.getAccountInfo(new PublicKey('...'));
const genesis = deserializeGenesisRecord(acct.data);
console.log(genesis.agentName, genesis.reputationScore, genesis.isBorn);
Usage: Auto-detect Account Type
const { deserializeAccount } = require('@brainai/satp-client');
const { type, data } = deserializeAccount(acct.data);
console.log(type);
console.log(data);
Usage: Batch Deserialization (getProgramAccounts)
const { deserializeBatch, DISCRIMINATORS } = require('@brainai/satp-client');
const accounts = await conn.getProgramAccounts(REVIEWS_PROGRAM_ID);
const reviews = deserializeBatch(accounts, 'Review');
Usage: BorshReader (Custom Deserialization)
const { BorshReader } = require('@brainai/satp-client');
const r = new BorshReader(acct.data);
r.skipDiscriminator();
const hash = r.readFixedBytes32();
const name = r.readString();
const items = r.readVecString();
const pk = r.readPubkeyBase58();
const opt = r.readOptionI64();
Discriminator Utilities
const { isAccountType, getAccountDiscriminator, DISCRIMINATORS } = require('@brainai/satp-client');
if (isAccountType(acct.data, 'EscrowV3')) {
const escrow = deserializeEscrowV3(acct.data);
}
const disc = getAccountDiscriminator('Attestation');
Security
- All transactions are returned unsigned — the SDK never holds private keys
- PDA derivation is deterministic and verified against on-chain seeds
- CPI boundaries enforce program-level authorization
- Escrow funds are held by PDA-owned accounts (no custodial risk)
License
MIT — brainAI 2026