@agenttrust-sdk/trustgate
v0.1.0
Published
Drop-in TrustGate middleware for x402 facilitators on Solana — adds gate_payment + emit_feedback to any Express app with atomic-tx invariant enforcement
Maintainers
Readme
@agenttrust-sdk/trustgate
Drop-in TrustGate middleware for x402 facilitators on Solana. Adds
gate_payment + emit_feedbackto any Express app with atomic-tx invariant enforcement.npm:
@agenttrust-sdk/trustgate· repo:mohit-1710/agenttrustComponent of the AgentTrust project (PolicyVault + TrustGate + ValidationRegistry). This SDK ships the TrustGate component.
AgentTrust completes the Solana Foundation's ERC-8004 trust stack — this package is the TypeScript surface that lets x402 facilitators (Dexter, atxp_ai, MCPay, Corbits, Latinum) integrate AgentTrust in a day.
Install
pnpm add @agenttrust-sdk/trustgate
# or
npm install @agenttrust-sdk/trustgate
# or
yarn add @agenttrust-sdk/trustgatePeer dep: express ^4.21 (only needed if you mount the middleware).
Two import surfaces
import { mountTrustGate } from "@agenttrust-sdk/trustgate/express";
import { gatePayment, settle, dispute } from "@agenttrust-sdk/trustgate/client";Plus a root namespace with the atomicity guard, PDA derivations, and shared types:
import {
AtomicityEnforced, AtomicityNotEnforcedError,
derivePolicyPda, DEFAULT_DEVNET_PROGRAM_IDS,
} from "@agenttrust-sdk/trustgate";Quick start — Express middleware
import express from "express";
import { Keypair } from "@solana/web3.js";
import { mountTrustGate } from "@agenttrust-sdk/trustgate/express";
const app = express();
app.use(express.json());
const facilitatorKeypair = Keypair.fromSecretKey(/* your facilitator key */);
await mountTrustGate(app, {
rpcUrl: "https://api.devnet.solana.com",
facilitatorKeypair,
network: "solana-devnet",
// THIS FIELD IS REQUIRED — see "Atomic-tx invariant" below.
atomicityEnforced: true,
});
app.listen(3000);You now have:
POST /verify— read-onlygate_paymentsimulation. Returns 200/Allow, 402/Deny + reason headers, or 402/RequireValidation + capability hash.GET /receipt/:paymentIdHashHex— looks up the on-chainFeedbackEmissionLogPDA. Returns{ exists: false }until the payment settles.POST /settleandPOST /dispute— atomic-tx assembly stubs (501 Not Implemented in v0.1; full transaction builders ship in v0.2 alongside the Phase 9 E2E integration).
Quick start — client helpers
import { Keypair, PublicKey } from "@solana/web3.js";
import { gatePayment } from "@agenttrust-sdk/trustgate/client";
const decision = await gatePayment({
rpcUrl: "https://api.devnet.solana.com",
caller: facilitatorKeypair,
payerAgentAsset: new PublicKey("..."),
payeeAgentAsset: new PublicKey("..."),
amount: 1_000_000n, // 1 USDC w/ 6 decimals
mint: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC devnet
policyId: 1,
});
switch (decision.kind) {
case "Allow": /* proceed with payment */ break;
case "Deny": /* show user decision.reasonName */ break;
case "RequireValidation": /* route user to validation flow with decision.capabilityHash */ break;
}Atomic-tx invariant
gate_payment + spl-token transfer + emit_feedback MUST execute in
one Solana transaction. Splitting them across two transactions opens
a real footgun on Token-2022 mints with TransferHook:
- Tx1:
gate_paymentreturns Allow →VelocityLedger.cumulative += amount - Tx2: SPL transfer →
TransferHookreverts (compliance check fails) VelocityLedgeris now corrupted: counted a payment that never happened
The SDK enforces atomicity at two layers:
- Compile-time literal type guard. The
AtomicityEnforcedmarker is{ atomicityEnforced: true }— a literaltrue, notboolean. TS rejects callers passingfalseor omitting the field. - Runtime throw. Every entry point validates
atomicityEnforced === trueand throwsAtomicityNotEnforcedErrorotherwise. Stopsas anyescape-hatches cold.
Both layers required: skipping either one re-opens the corruption vector. This is the load-bearing safety property of the SDK.
On-chain programs (devnet)
| Program | Address |
|---------|---------|
| policy_vault | 8Y6fGeNEHgmWmbt8JsRcF72jxbeBfJhomMjG6SuoJQTR |
| trustgate | HF8zHfoyA7b5mhLViopTnRMprc6ZT5KActHTdkFrih2N |
| validation_registry | Cx4RFa6ysw3qXYhugPkF8pFSWBkmKq59h2dWgF2tKhtv |
Override via the programIds config field for mainnet redeploys.
Formal verification
PolicyVault's five core safety properties are machine-checked by Kani:
paused_implies_no_allow— KillSwitch paused ⇒ never Allowvelocity_counter_le_limit— Allow preserves cumulative ≤ maxcounterparty_tier_monotone— strict pass ⇒ loose passvalidation_expiry_correct— expired attestation ⇒ never Allowmultisig_threshold_enforced— distinct signer count ≥ threshold
CI runs all 5 on every PR. See .github/workflows/kani-prove.yml in the
main repo.
License
MIT. © 2026 AgentTrust contributors.
