@agenttrust-sdk/mcp 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/embedded-docs/architecture.mdx +174 -0
  2. package/dist/embedded-docs/getting-started/quickstart.mdx +79 -56
  3. package/dist/embedded-docs/index.mdx +54 -37
  4. package/dist/embedded-docs/integration-guides/capability-namespaces.mdx +135 -8
  5. package/dist/embedded-docs/integration-guides/custom-attestor.mdx +169 -8
  6. package/dist/embedded-docs/integration-guides/dexter-adapter.mdx +76 -0
  7. package/dist/embedded-docs/integration-guides/facilitator-adapters.mdx +85 -41
  8. package/dist/embedded-docs/integration-guides/pay-sh-adapter.mdx +90 -54
  9. package/dist/embedded-docs/integration-guides/x402-facilitator.mdx +55 -24
  10. package/dist/embedded-docs/mcp/hosted-endpoint.mdx +197 -0
  11. package/dist/embedded-docs/mcp/index.mdx +108 -0
  12. package/dist/embedded-docs/mcp/install.mdx +183 -0
  13. package/dist/embedded-docs/mcp/prompts.mdx +90 -0
  14. package/dist/embedded-docs/mcp/resources.mdx +115 -0
  15. package/dist/embedded-docs/mcp/tools.mdx +156 -0
  16. package/dist/embedded-docs/programs/policy-vault/composer.mdx +117 -0
  17. package/dist/embedded-docs/programs/policy-vault/counterparty-tier-policy.mdx +81 -9
  18. package/dist/embedded-docs/programs/policy-vault/index.mdx +77 -47
  19. package/dist/embedded-docs/programs/policy-vault/kill-switch-policy.mdx +65 -8
  20. package/dist/embedded-docs/programs/policy-vault/require-validation-policy.mdx +76 -8
  21. package/dist/embedded-docs/programs/policy-vault/spending-policy.mdx +83 -8
  22. package/dist/embedded-docs/programs/policy-vault/velocity-policy.mdx +85 -8
  23. package/dist/embedded-docs/programs/trustgate.mdx +112 -30
  24. package/dist/embedded-docs/programs/validation-registry.mdx +139 -32
  25. package/dist/embedded-docs/reference/byte-offset-reference.mdx +102 -13
  26. package/dist/embedded-docs/reference/capability-namespaces.mdx +56 -0
  27. package/dist/embedded-docs/reference/changelog.mdx +230 -13
  28. package/dist/embedded-docs/reference/deny-reason-codes.mdx +86 -0
  29. package/dist/embedded-docs/reference/devnet-program-ids.mdx +50 -8
  30. package/dist/embedded-docs/reference/discriminator-constants.mdx +104 -10
  31. package/dist/embedded-docs/reference/mainnet-program-ids.mdx +89 -5
  32. package/dist/embedded-docs/reference/quantu-agent-registry.mdx +104 -9
  33. package/dist/embedded-docs/sdk/exports-reference.mdx +239 -0
  34. package/dist/embedded-docs/sdk/gate-payment.mdx +99 -14
  35. package/dist/embedded-docs/sdk/index.mdx +141 -40
  36. package/dist/embedded-docs/sdk/mount-trustgate.mdx +178 -8
  37. package/dist/embedded-docs/verification/adversarial-harness.mdx +88 -0
  38. package/dist/embedded-docs/verification/atomic-tx-invariant.mdx +141 -0
  39. package/dist/embedded-docs/verification/chained-validation.mdx +87 -0
  40. package/dist/embedded-docs/verification/devnet-smoke.mdx +85 -0
  41. package/dist/embedded-docs/verification/index.mdx +31 -0
  42. package/dist/embedded-docs/verification/kani-proofs.mdx +144 -0
  43. package/dist/embedded-docs/verification/live-evidence.mdx +180 -0
  44. package/dist/tools/read/get-quantu-reputation.d.ts +50 -11
  45. package/dist/tools/read/get-quantu-reputation.js +75 -26
  46. package/dist/tools/read/get-quantu-reputation.js.map +1 -1
  47. package/dist/tools/write/emit-feedback.d.ts +6 -0
  48. package/dist/tools/write/emit-feedback.js +12 -1
  49. package/dist/tools/write/emit-feedback.js.map +1 -1
  50. package/package.json +16 -15
  51. package/scripts/install-claude-desktop.sh +0 -0
@@ -1,15 +1,176 @@
1
1
  ---
2
2
  title: Custom attestor
3
- description: How third-party attestors plug into the ValidationRegistry path.
3
+ description: Register an AttestorProfile, respond to validation requests, revoke attestations. Full lifecycle with the live four-signature devnet trace.
4
4
  ---
5
5
 
6
- `In progress`
6
+ A custom attestor registers a profile, watches off-chain for validation requests for capabilities it can attest to, signs `respond_to_validation` to write the attestation PDA, and can later revoke any attestation it issued. PolicyVault then reads the resulting PDA at fixed byte offsets.
7
7
 
8
- A custom attestor registers a profile, responds to validation requests, and can revoke an attestation it issued. PolicyVault then reads the resulting PDA by fixed byte offsets.
8
+ Source: [`programs/validation-registry/`](https://github.com/agenttrust-labs/agenttrust/tree/main/programs/validation-registry). Devnet smoke: [`examples/attestor-demo/`](https://github.com/agenttrust-labs/agenttrust/tree/main/examples/attestor-demo).
9
9
 
10
- | Source | Path |
11
- | --- | --- |
12
- | attestor profile | [`programs/validation-registry/src/state/attestor_profile.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/validation-registry/src/state/attestor_profile.rs) |
13
- | response instruction | [`programs/validation-registry/src/instructions/respond_to_validation.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/validation-registry/src/instructions/respond_to_validation.rs) |
14
- | revoke instruction | [`programs/validation-registry/src/instructions/revoke_validation.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/validation-registry/src/instructions/revoke_validation.rs) |
10
+ ## Lifecycle
15
11
 
12
+ ```
13
+ 1. register_attestor → create AttestorProfile PDA
14
+ 2. <off-chain> → discover ValidationRequest events for capabilities you attest to
15
+ 3. respond_to_validation → create ValidationAttestation PDA (PolicyVault now reads it)
16
+ 4. revoke_validation → set revoked = true (PolicyVault now denies)
17
+ ```
18
+
19
+ ## Step 1 — register the profile
20
+
21
+ ```ts
22
+ import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
23
+ import {
24
+ buildRegisterAttestorIx,
25
+ loadValidationRegistry,
26
+ makeProvider,
27
+ DEFAULT_DEVNET_PROGRAM_IDS,
28
+ } from "@agenttrust-sdk/trustgate";
29
+
30
+ const attestor = Keypair.fromSecretKey(/* the attestor's signing key */);
31
+ const provider = makeProvider({
32
+ rpcUrl: "https://api.devnet.solana.com",
33
+ wallet: attestor,
34
+ });
35
+
36
+ const validationRegistry = await loadValidationRegistry(
37
+ provider,
38
+ DEFAULT_DEVNET_PROGRAM_IDS.validationRegistry,
39
+ );
40
+
41
+ const ix = await buildRegisterAttestorIx({
42
+ program: validationRegistry,
43
+ attestor: attestor.publicKey,
44
+ displayNameUri: "https://my-org.example/attestor.json", // ≤ 100 bytes
45
+ });
46
+
47
+ const tx = new Transaction().add(ix);
48
+ const sig = await sendAndConfirmTransaction(provider.connection, tx, [attestor]);
49
+ ```
50
+
51
+ Constraints: `display_name_uri.len() <= 100` (`UriTooLong` on overflow). Self-registered (the signer is the attestor). PDA: `["attestor", attestor_pubkey]`. Account size includes counters for total attestations + total revoked, all initialised to zero.
52
+
53
+ ## Step 2 — discover validation requests
54
+
55
+ `request_validation` emits the `RequestCreated` event. Any off-chain process can subscribe:
56
+
57
+ ```ts
58
+ import { PublicKey } from "@solana/web3.js";
59
+
60
+ provider.connection.onLogs(
61
+ validationRegistry.programId,
62
+ (logs) => {
63
+ if (logs.err) return;
64
+ // Decode logs.logs entries — RequestCreated event has subject_asset,
65
+ // capability_hash, requester, claim_uri_hash, deadline.
66
+ },
67
+ "confirmed",
68
+ );
69
+ ```
70
+
71
+ For production attestors, [Helius webhooks](https://www.helius.dev/docs/webhooks) on the program's events stream is the standard pattern. The PDA itself is just an audit-trail record; attestors don't read it.
72
+
73
+ ## Step 3 — respond with an attestation
74
+
75
+ After the attestor verifies the off-chain claim (KYC document, audit report, model-card statement, etc.) the attestor signs `respond_to_validation` to write the on-chain attestation:
76
+
77
+ ```ts
78
+ import {
79
+ buildRespondToValidationIx,
80
+ computeCapabilityHash,
81
+ } from "@agenttrust-sdk/trustgate";
82
+
83
+ const subjectAsset = new PublicKey("…"); // payee asset
84
+ const capabilityHash = computeCapabilityHash("kyc.tier-1.v1"); // 32 bytes
85
+ const claimPayloadHash = sha256(claim_payload_bytes); // 32 bytes
86
+ const claimUriHash = sha256(Buffer.from(claim_uri_string)); // 32 bytes
87
+ const expiresAt = currentSlot + (30n * 24n * 60n * 60n * 2n); // ~30 days @ 2 slots/sec
88
+
89
+ const ix = await buildRespondToValidationIx({
90
+ program: validationRegistry,
91
+ payer: attestor.publicKey, // attestor pays rent
92
+ attestor: attestor.publicKey,
93
+ subjectAsset,
94
+ capabilityHash,
95
+ claimPayloadHash,
96
+ claimUriHash,
97
+ expiresAt: Number(expiresAt),
98
+ });
99
+
100
+ const sig = await sendAndConfirmTransaction(provider.connection, new Transaction().add(ix), [attestor]);
101
+ ```
102
+
103
+ Constraints: `expires_at == 0` (never expires) OR `expires_at > clock.slot` (`ExpiryInPast` otherwise). The `capability_namespace` PDA must already exist (`AccountNotInitialized` if you reference an unregistered capability). The `attestor_profile` PDA must exist (the attestor must have called `register_attestor` first).
104
+
105
+ PDA: `["attestation", subject_asset, capability_hash, attestor]`. Account size: 290 bytes.
106
+
107
+ The v1 trust model is "attestor signs the tx" — the Solana tx signature itself authenticates the attestor. The 64-byte `attestor_signature` field on `ValidationAttestation` is reserved for v1.1+ Ed25519 sysvar verification, which adds non-repudiation against future key compromise.
108
+
109
+ ## Step 4 — revoke when needed
110
+
111
+ ```ts
112
+ import { buildRevokeValidationIx } from "@agenttrust-sdk/trustgate";
113
+
114
+ const revocationReasonHash = sha256(Buffer.from("kyc-document-expired"));
115
+
116
+ const ix = await buildRevokeValidationIx({
117
+ program: validationRegistry,
118
+ attestor: attestor.publicKey,
119
+ subjectAsset,
120
+ capabilityHash,
121
+ revocationReasonHash,
122
+ });
123
+
124
+ await sendAndConfirmTransaction(provider.connection, new Transaction().add(ix), [attestor]);
125
+ ```
126
+
127
+ `revoke_validation` is audit-trail-preserving: it sets `revoked = true`, writes `revoked_at = clock.slot`, and stores the `revocation_reason_hash`. The PDA is not deleted. PolicyVault's `RequireValidation` policy treats `revoked == true` as `Deny(AttestationRevoked)` (DenyReason code 13).
128
+
129
+ Only the original attestor can revoke an attestation it issued (`UnauthorizedRevoker` otherwise). v1.1+ adds an external-revoke flow with attestor-profile externals counter.
130
+
131
+ ## Live four-signature trace
132
+
133
+ End-to-end devnet trace, 2026-05-06 — gate denies → request → respond → gate allows:
134
+
135
+ | Step | Tx |
136
+ |---|---|
137
+ | `gate_payment` (no attestation) → `RequireValidation` | [`3oKW7QugBLJ7…`](https://explorer.solana.com/tx/3oKW7QugBLJ7kH2QbLLWEuEn3MyNmLWCj3XovCSdDQNmq5HriwNKvPMUR9TQByZPBAPbvprDfdeYDZvh7ofntRRh?cluster=devnet) |
138
+ | `request_validation` | [`2KbXYCF67D2f…`](https://explorer.solana.com/tx/2KbXYCF67D2f2fKHk5yTzrkFBr1mV47Q3Yb1veH5e3PX4PuLa66suodAUc7uTBnr6Y44NGV1TfHHMtAZiFSnbbRF?cluster=devnet) |
139
+ | `respond_to_validation` (creates attestation) | [`67CzMS9GEt…`](https://explorer.solana.com/tx/67CzMS9GEtUBesNznKpT2UWqvjEBzhgZd7AVkhXKQ5SoqRoBotcaYf1sTF8sHxj55TNT9k847nj7FQdrwAqKussp?cluster=devnet) |
140
+ | `gate_payment` (with attestation) → `Allow` | [`dEXkCEeSn8…`](https://explorer.solana.com/tx/dEXkCEeSn8uiVAa14u7EusdFufSuUQttmcTdLHMSq5J3VSARM4KMRCfwpRSkVmYBc1yRQuyvPMCebifCf1dmrmC?cluster=devnet) |
141
+
142
+ Resulting `ValidationAttestation` PDA: [`8YKq…xt2q`](https://explorer.solana.com/address/8YKqxoBaKfQ4VcDNa6dcoMQGevxbRmcr4ENbWcyrJxt2q?cluster=devnet). Reproduce with `pnpm --filter ./examples/attestor-demo run chained` (~0.012 SOL total). Full trace: [Verification → Chained validation](/verification/chained-validation).
143
+
144
+ ## How the gate consumes your attestation
145
+
146
+ PolicyVault's `RequireValidation` policy reads the `ValidationAttestation` at fixed byte offsets:
147
+
148
+ | Offset | Width | Field |
149
+ |---:|---:|---|
150
+ | `8` | Pubkey | `subject_asset` |
151
+ | `40` | [u8; 32] | `capability_hash` |
152
+ | `72` | Pubkey | `attestor` |
153
+ | `208` | u64 LE | `expires_at` (0 = never expires) |
154
+ | `216` | bool | `revoked` |
155
+
156
+ A policy with `accepted_attestors = [your_pubkey, …]` accepts attestations only from your key. Permissionless mode (both `accepted_attestors` slots zero) accepts any attestor's signature for the capability. Full read semantics: [RequireValidation policy](/programs/policy-vault/require-validation-policy).
157
+
158
+ ## Validation against the Kani proof
159
+
160
+ The `validation_expiry_correct` proof (Kani #4, 85 sub-checks, 0.23 s) pins: an expired attestation cannot produce `Allow` from `require_validation::evaluate`. Even if all other fields match, expiry is the deciding gate.
161
+
162
+ So if you set `expires_at = currentSlot + 1000` and waited 1000 slots, your attestation goes from `Allow`-capable to `Deny(AttestationExpired)` automatically — no revocation tx required. Plan attestation lifetimes accordingly. Reference: [Verification → Kani proofs](/verification/kani-proofs).
163
+
164
+ ## Read next
165
+
166
+ <Cards>
167
+ <Card title="Capability namespaces" href="/integration-guides/capability-namespaces">
168
+ Register the capability your attestation references.
169
+ </Card>
170
+ <Card title="ValidationRegistry" href="/programs/validation-registry">
171
+ All five instructions, all four PDAs, byte-precise.
172
+ </Card>
173
+ <Card title="Chained validation" href="/verification/chained-validation">
174
+ The full live four-signature trace with reproduction commands.
175
+ </Card>
176
+ </Cards>
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Dexter adapter
3
+ description: In-flight worked example — the second AgentTrust facilitator adapter, used to prove portability of the FacilitatorAdapter contract.
4
+ ---
5
+
6
+ Dexter is the second facilitator AgentTrust supports. It exists to prove that the `FacilitatorAdapter` contract is portable: every adapter must implement the same five methods, expose the same Zod-validated wire schemas, and use the same SDK factories for on-chain validation and feedback emission. If wiring a second adapter forces route edits, policy edits, or registry-read changes, the adapter boundary failed and gets fixed before the integration ships.
7
+
8
+ Status: in flight. The current repo carries a stub at [`trustgate/server/src/facilitators/dexter/`](https://github.com/agenttrust-labs/agenttrust/tree/main/trustgate/server/src/facilitators/dexter) that satisfies the type contract; full implementation lands once Dexter publishes its x402 wire format. Current adapter status: [`agenttrust_list_facilitators`](/mcp/tools#agenttrust_list_facilitators) → `{ name: "dexter", status: "in-flight" }`.
9
+
10
+ ## What the adapter has to land
11
+
12
+ The five methods every adapter implements, with Dexter-specific notes:
13
+
14
+ | Method | Dexter-specific note |
15
+ |---|---|
16
+ | `parseRequest(req)` | Translate Dexter's body shape + headers (TBD until spec lands) into `VerifyContext`. Strict Zod schema — reject unknown root fields. |
17
+ | `formatChallenge(decision, ctx)` | Render `Allow` / `Deny` / `RequireValidation` in Dexter's response shape. Headers: `X-Agent-Trust-Decision`, `X-Capability-Required` (when applicable). |
18
+ | `formatSettlement(ctx)` | Return Dexter's settlement metadata or an unsigned transaction skeleton. Payment ID and amount/mint/recipient are bound to the verify-time context. |
19
+ | `validatePaymentProof(proof, ctx)` | Verify the proof shape; cross-check against `VerifyContext`. Same defenses as Pay.sh: replay, self-pay, amount, mint, recipient, expiry. |
20
+ | `emitFeedback(ctx, settlement)` | Call the feedback CPI idempotently via `priorEmissionLookup` + `emitFeedbackCpi`. |
21
+
22
+ Implementation pattern: read [`facilitators/pay-sh/`](https://github.com/agenttrust-labs/agenttrust/tree/main/trustgate/server/src/facilitators/pay-sh) end to end. Adapt the schemas + signature semantics to Dexter's wire format. Keep the proof-validator's defense list 1:1.
23
+
24
+ ## Why Dexter exists in the catalog
25
+
26
+ Pay.sh proves the adapter pattern handles the canonical x402 case. Dexter exists to prove the pattern handles a *different* x402 facilitator's quirks without forcing route layer changes. The same `FacilitatorRegistry` registers it; the same `/verify`, `/settle`, `/dispute` routes dispatch to it; the same SDK factories build its on-chain calls.
27
+
28
+ ```ts
29
+ import { FacilitatorRegistry, PaySh } from "./facilitators";
30
+ import { Dexter } from "./facilitators/dexter";
31
+
32
+ const registry = new FacilitatorRegistry();
33
+ registry.register(new PaySh(payShDeps));
34
+ registry.register(new Dexter(dexterDeps));
35
+ registry.setDefault("pay-sh");
36
+ ```
37
+
38
+ The route layer never branches on the facilitator name — selection happens via the `X-Facilitator` request header or the registry's default. Dexter requests use `X-Facilitator: dexter`; everything else routes to Pay.sh.
39
+
40
+ ## What lands when Dexter ships
41
+
42
+ | Deliverable | Where |
43
+ |---|---|
44
+ | `Dexter` class | `trustgate/server/src/facilitators/dexter/index.ts` |
45
+ | Zod schemas | `trustgate/server/src/facilitators/dexter/schemas.ts` |
46
+ | Proof validator | `trustgate/server/src/facilitators/dexter/proof-validator.ts` |
47
+ | Feedback helper | `trustgate/server/src/facilitators/dexter/feedback.ts` |
48
+ | Demo (optional) | `examples/dexter-demo/` |
49
+ | Adapter test suite | `trustgate/server/test/facilitators/dexter/` |
50
+
51
+ Each file mirrors the Pay.sh shape. The adapter test suite asserts the `FacilitatorAdapter` contract conformance — every method's input is type-safe and every method's output matches the documented schema.
52
+
53
+ ## Adapter contract conformance
54
+
55
+ CI runs an adapter-contract conformance workflow ([`.github/workflows/adapter-contract-conformance.yml`](https://github.com/agenttrust-labs/agenttrust/blob/main/.github/workflows/adapter-contract-conformance.yml)) on every PR. It walks every registered adapter and asserts:
56
+
57
+ - `parseRequest` accepts the verify-shape and rejects malformed inputs with `400`.
58
+ - `formatChallenge` emits the right headers per decision arm.
59
+ - `validatePaymentProof` rejects every member of the standard hostile-input matrix (replay, self-pay, mismatch, expired).
60
+ - `emitFeedback` is idempotent against the same `payment_id_hash`.
61
+
62
+ Dexter (when shipped) and any future adapter (atxp, MCPay) hook into the same conformance check. The Pay.sh adapter is the conformance reference — its 50+ test cases cover every branch of the contract.
63
+
64
+ ## Read next
65
+
66
+ <Cards>
67
+ <Card title="Pay.sh adapter" href="/integration-guides/pay-sh-adapter">
68
+ The canonical implementation. Read it end to end before writing your own.
69
+ </Card>
70
+ <Card title="Facilitator adapters" href="/integration-guides/facilitator-adapters">
71
+ The five-method contract in full.
72
+ </Card>
73
+ <Card title="Custom attestor" href="/integration-guides/custom-attestor">
74
+ If you're attesting capabilities rather than running a facilitator.
75
+ </Card>
76
+ </Cards>
@@ -1,85 +1,129 @@
1
1
  ---
2
2
  title: Facilitator adapters
3
- description: Add a new x402 facilitator without rewriting AgentTrust routes or policy.
3
+ description: Add a new x402 facilitator without rewriting AgentTrust routes or policy. Five-method contract, registry-based dispatch, conformance-tested.
4
4
  ---
5
5
 
6
- The adapter layer is the reason AgentTrust is not locked to Pay.sh. Pay.sh is live today; Dexter is the next worked path; atxp_ai and MCPay are marked roadmap through explicit stubs.
6
+ The adapter layer is what makes AgentTrust facilitator-agnostic. Pay.sh is the canonical reference; Dexter is the in-flight second worked example; atxp and MCPay are roadmap. Routes (`/verify`, `/settle`, `/dispute`), the PolicyVault gate, and the registry reads do not branch on facilitator name. Protocol quirks live in one adapter file.
7
+
8
+ The adapter implementations live in [`trustgate/server/src/facilitators/`](https://github.com/agenttrust-labs/agenttrust/tree/main/trustgate/server/src/facilitators). The server package is workspace-internal (not published to npm); copy the pattern from [`facilitators/pay-sh/`](https://github.com/agenttrust-labs/agenttrust/tree/main/trustgate/server/src/facilitators/pay-sh) into your own server, or use the published [SDK middleware](/sdk/mount-trustgate) which dispatches through the adapter layer for you.
7
9
 
8
10
  ## Mental model
9
11
 
10
- ```txt
12
+ ```
11
13
  HTTP request
12
- -> routes/{verify,settle}
13
- -> FacilitatorRegistry
14
- -> active FacilitatorAdapter
15
- -> VerifyContext
16
- -> policy decision
17
- -> settlement proof validation
18
- -> feedback emission
14
+ /verify · /settle · /dispute
15
+ → FacilitatorRegistry (selects adapter by X-Facilitator header or default)
16
+ active FacilitatorAdapter
17
+ VerifyContext
18
+ PolicyVault gate decision
19
+ settlement proof validation
20
+ feedback emission
19
21
  ```
20
22
 
21
- Routes, policy logic, and registry reads stay unchanged. Protocol quirks live in one adapter file.
23
+ The active adapter is the only code that knows about the chosen facilitator.
22
24
 
23
25
  ## The five-method contract
24
26
 
27
+ ```ts
28
+ export interface FacilitatorAdapter {
29
+ readonly protocol: FacilitatorProtocol; // identity tag for registry
30
+
31
+ parseRequest(req: Request): Promise<VerifyContext>;
32
+ formatChallenge(decision: GateDecision, ctx: VerifyContext): ChallengeResponse;
33
+ formatSettlement(ctx: VerifyContext): SettlementResponse;
34
+ validatePaymentProof(proof: unknown, ctx: VerifyContext): Promise<PaymentProofValidation>;
35
+ emitFeedback(ctx: VerifyContext, settlement: ConfirmedSettlement): Promise<FeedbackEmissionResult>;
36
+ }
37
+ ```
38
+
25
39
  | Method | Why it exists |
26
- | --- | --- |
27
- | `parseRequest(req)` | translate the facilitator's request body and headers into `VerifyContext` |
28
- | `formatChallenge(decision, ctx)` | render Allow / Deny / RequireValidation in that facilitator's wire format |
29
- | `formatSettlement(ctx)` | return protocol-specific settlement metadata or an unsigned transaction skeleton |
30
- | `validatePaymentProof(proof, ctx)` | verify proof shape and cross-check it against the verify-time context |
31
- | `emitFeedback(ctx, settlement)` | emit the AgentTrust feedback record after settlement |
40
+ |---|---|
41
+ | `parseRequest(req)` | Translate the facilitator's request body and headers into `VerifyContext` |
42
+ | `formatChallenge(decision, ctx)` | Render `Allow` / `Deny` / `RequireValidation` in that facilitator's wire format |
43
+ | `formatSettlement(ctx)` | Return protocol-specific settlement metadata or an unsigned transaction skeleton |
44
+ | `validatePaymentProof(proof, ctx)` | Verify proof shape and cross-check against the verify-time context |
45
+ | `emitFeedback(ctx, settlement)` | Emit the AgentTrust feedback record idempotently |
32
46
 
33
47
  If an adapter needs a sixth method, that is a contract change, not a one-off escape hatch.
34
48
 
35
49
  ## Status map
36
50
 
37
51
  | Facilitator | Repo status | Meaning |
38
- | --- | --- | --- |
39
- | Pay.sh | concrete adapter | ready path for the demo and production dependency factory |
40
- | Dexter | stub / in flight | worked example for proving adapter portability |
41
- | atxp_ai | stub | roadmap, not silently unsupported |
42
- | MCPay | stub | roadmap, not silently unsupported |
52
+ |---|---|---|
53
+ | Pay.sh | concrete adapter | Ready for the demo + production dependency factory; the canonical `FacilitatorAdapter` reference |
54
+ | Dexter | in-flight stub | Worked example for proving adapter portability |
55
+ | atxp | roadmap stub | Tracked, not silently unsupported |
56
+ | MCPay | roadmap stub | Tracked, not silently unsupported |
57
+
58
+ `agenttrust_list_facilitators` (an MCP tool) returns the live status set: [MCP → Tools](/mcp/tools#agenttrust_list_facilitators).
43
59
 
44
60
  ## Add a facilitator
45
61
 
46
- 1. Read the facilitator's wire format. Identify challenge headers, proof headers, body shape, recipient encoding, payment ID hints, and Allow / Deny responses.
47
- 2. Define strict Zod schemas for the request body and proof payload.
62
+ 1. Read the facilitator's wire format. Identify challenge headers, proof headers, body shape, recipient encoding, payment ID hints, Allow/Deny responses.
63
+ 2. Define strict Zod schemas for the request body and proof payload. Reject unknown root fields.
48
64
  3. Implement one `FacilitatorAdapter` class.
49
- 4. Re-export it from `trustgate/server/src/facilitators/index.ts`.
50
- 5. Register it with `FacilitatorRegistry`.
51
- 6. Add unit tests that mirror Pay.sh coverage.
65
+ 4. Re-export from [`trustgate/server/src/facilitators/index.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/index.ts).
66
+ 5. Register with `FacilitatorRegistry`.
67
+ 6. Add unit tests mirroring Pay.sh coverage (50+ cases — schema, signature, proof-validator, feedback idempotency).
52
68
  7. Add a demo under `examples/<name>-demo` if the integration needs a runnable walkthrough.
53
69
 
54
70
  ## Registry wiring
55
71
 
56
72
  ```ts
57
- import {
58
- FacilitatorRegistry,
59
- PaySh,
60
- } from "@agenttrust/trustgate-server";
73
+ import { FacilitatorRegistry, PaySh } from "./facilitators";
74
+ import { Dexter } from "./facilitators/dexter";
61
75
 
62
76
  const registry = new FacilitatorRegistry();
63
77
  registry.register(new PaySh(payShDeps));
78
+ registry.register(new Dexter(dexterDeps));
64
79
  registry.setDefault("pay-sh");
80
+
81
+ // Per-request selection via X-Facilitator header:
82
+ // curl -H "X-Facilitator: dexter" …
83
+ // Falls back to default if header is absent.
65
84
  ```
66
85
 
67
- Request-time selection can come from `X-Facilitator`, environment defaults, or a programmatic default. The active adapter is the only code that knows about the chosen facilitator.
86
+ The registry resolves the active adapter on every `/verify`, `/settle`, `/dispute` call. The dispatch site is one line; nothing in the route layer knows which adapter is active.
68
87
 
69
88
  ## Security checklist
70
89
 
71
90
  Every adapter must preserve the same proof-of-payment checks:
72
91
 
73
- - replay defense
74
- - self-pay defense
75
- - amount, mint, and recipient cross-checks
76
- - network match
77
- - expiry window
78
- - idempotent feedback emission
79
- - stable reason codes on Deny
92
+ - replay defense — reject proofs whose `paymentIdHash` matches a prior settlement
93
+ - self-pay defense — reject proofs where transfer authority equals facilitator fee payer
94
+ - amount, mint, recipient cross-checks — reject if the proof references different values than the verify-time context
95
+ - network match — reject mainnet proofs against a devnet-bound facilitator
96
+ - expiry window — reject proofs older than the SERVICE-signed challenge's `issuedAt + maxTimeoutSeconds`
97
+ - idempotent feedback emission — `priorEmissionLookup` short-circuits a second emission for the same `payment_id_hash`
98
+ - stable reason codes on `Deny` — clients consume the numeric `reasonCode`, decoupled from Borsh wire-format ordering
99
+
100
+ The Pay.sh adapter's [`proof-validator.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/proof-validator.ts) and [`feedback.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/feedback.ts) are the reference pattern.
101
+
102
+ ## Conformance test
103
+
104
+ CI runs [`.github/workflows/adapter-contract-conformance.yml`](https://github.com/agenttrust-labs/agenttrust/blob/main/.github/workflows/adapter-contract-conformance.yml) on every PR. The workflow walks every registered adapter and asserts:
105
+
106
+ - `parseRequest` accepts the verify-shape and rejects malformed inputs with HTTP `400`.
107
+ - `formatChallenge` emits the right headers per decision arm (Allow, Deny + reason, RequireValidation + capability hash).
108
+ - `validatePaymentProof` rejects every entry in the hostile-input matrix.
109
+ - `emitFeedback` is idempotent — a second call for the same `payment_id_hash` returns the prior emission's signature instead of double-emitting.
80
110
 
81
- Pay.sh's `proof-validator.ts` and `feedback.ts` are the reference pattern.
111
+ Adversarial-harness coverage: [Verification Adversarial harness](/verification/adversarial-harness).
82
112
 
83
113
  ## Target integration time
84
114
 
85
- The architecture target is under two hours for a facilitator that already has a clear x402 wire spec. If adding a facilitator forces route edits, policy edits, or registry read changes, the adapter boundary failed and should be fixed before the integration is considered done.
115
+ The architecture target is **under two hours** for a facilitator that already has a clear x402 wire spec. If adding a facilitator forces route edits, policy edits, or registry-read changes, the adapter boundary failed and gets fixed before the integration is considered done.
116
+
117
+ ## Read next
118
+
119
+ <Cards>
120
+ <Card title="Pay.sh adapter" href="/integration-guides/pay-sh-adapter">
121
+ Walk through the canonical implementation end to end.
122
+ </Card>
123
+ <Card title="Dexter adapter" href="/integration-guides/dexter-adapter">
124
+ See how the second adapter slots into the same contract.
125
+ </Card>
126
+ <Card title="Atomic-tx invariant" href="/verification/atomic-tx-invariant">
127
+ Why every adapter's settle path keeps gate + transfer + feedback in one tx.
128
+ </Card>
129
+ </Cards>
@@ -1,33 +1,52 @@
1
1
  ---
2
2
  title: Pay.sh adapter
3
- description: The live Pay.sh + AgentTrust integration path.
3
+ description: Walk the live Pay.sh + AgentTrust integration end to end — challenge, retry, settle, feedback, with hosted-demo paths and the SERVICE-signed envelope contract.
4
4
  ---
5
5
 
6
- Pay.sh is the first concrete AgentTrust facilitator adapter. It translates Pay.sh's x402 headers and `PaymentRequirements` body into the `VerifyContext` that the on-chain policy path understands.
7
-
8
- Pay.sh launch reference: [Solana Foundation launch note](https://solana.com/news/solana-foundation-launches-pay-sh-in-collaboration-with-google-cloud).
6
+ Pay.sh is the Solana Foundation's first x402 facilitator, [launched 2026-05-05 with Google Cloud](https://solana.com/news/solana-foundation-launches-pay-sh-in-collaboration-with-google-cloud). AgentTrust ships day-one Pay.sh integration as the canonical `FacilitatorAdapter` implementation. Source: [`trustgate/server/src/facilitators/pay-sh/`](https://github.com/agenttrust-labs/agenttrust/tree/main/trustgate/server/src/facilitators/pay-sh).
9
7
 
10
8
  ## What the adapter does
11
9
 
12
- | Stage | Pay.sh / x402 surface | AgentTrust action |
13
- | --- | --- | --- |
10
+ | Stage | x402 / Pay.sh wire shape | AgentTrust action |
11
+ |---|---|---|
14
12
  | Initial request | no payment proof | emit `402 Payment Required` with a base64 x402 v2 envelope |
15
- | Challenge | `paymentRequirements.extra.agentTrust` | include payer agent, payee agent, payee recipient, policy ID, `issuedAt`, and `serviceSignature` |
16
- | Retry | `PAYMENT-SIGNATURE` or `X-PAYMENT` | parse proof and rebuild `VerifyContext` |
17
- | Policy | `VerifyContext` | run the gate decision |
13
+ | Challenge | `paymentRequirements.extra.agentTrust` | include payer agent, payee agent, payee recipient, policy ID, `issuedAt`, `serviceSignature` |
14
+ | Retry | `PAYMENT-SIGNATURE` or `X-PAYMENT` header | parse proof, rebuild `VerifyContext` |
15
+ | Policy | `VerifyContext` | run `gate_payment` decision |
18
16
  | Allow | signed payment proof | validate transfer fields, emit feedback, forward resource |
19
- | Deny | gate decision | return `402` with reason code and reason name |
17
+ | Deny | gate decision | return `402` with reason code + reason name |
20
18
 
21
- ## Files to read
19
+ ## Hit the live demo
22
20
 
23
- | File | Purpose |
24
- | --- | --- |
25
- | `trustgate/server/src/facilitators/pay-sh/index.ts` | the five-method `PaySh` class |
26
- | `trustgate/server/src/facilitators/pay-sh/schemas.ts` | strict Zod wire schemas |
27
- | `trustgate/server/src/facilitators/pay-sh/sig.ts` | SERVICE challenge signature helpers |
28
- | `trustgate/server/src/facilitators/pay-sh/proof-validator.ts` | replay, self-pay, amount, mint, recipient checks |
29
- | `trustgate/server/src/facilitators/pay-sh/feedback.ts` | idempotent feedback emission |
30
- | `examples/pay-sh-demo/src/middleware.ts` | Express route bridge from Pay.sh retry to AgentTrust |
21
+ [`demo.agenttrust.tech`](https://demo.agenttrust.tech) runs the full Pay.sh + AgentTrust pipeline against deployed devnet programs. With no payment proof:
22
+
23
+ ```bash
24
+ curl -i https://demo.agenttrust.tech/protected
25
+ ```
26
+
27
+ Returns `HTTP/2 402` with a SERVICE-signed `payment-required` envelope. With Pay.sh installed, let the CLI pay and retry:
28
+
29
+ ```bash
30
+ pay --sandbox curl https://demo.agenttrust.tech/protected
31
+ ```
32
+
33
+ The demo seeds three deterministic counterparties. Drive each branch by passing the matching `X-Demo-Payer-Agent` header:
34
+
35
+ ```bash
36
+ # Allow path (tier 3)
37
+ PAYER=$(curl -s https://demo.agenttrust.tech/health | jq -r '.counterparties[2].agent')
38
+ pay --sandbox curl -H "X-Demo-Payer-Agent: $PAYER" https://demo.agenttrust.tech/protected
39
+
40
+ # Deny path (tier 0)
41
+ PAYER=$(curl -s https://demo.agenttrust.tech/health | jq -r '.counterparties[0].agent')
42
+ pay --sandbox curl -H "X-Demo-Payer-Agent: $PAYER" https://demo.agenttrust.tech/protected
43
+ ```
44
+
45
+ | Counterparty tier | Decision |
46
+ |---:|---|
47
+ | 0 | `402 Deny — CounterpartyTierBelowMin` |
48
+ | 1 | `402 Deny — CounterpartyTierBelowMin` |
49
+ | 3 | `200 Allow + X-Payment-Receipt` |
31
50
 
32
51
  ## SERVICE-signed challenge
33
52
 
@@ -44,58 +63,49 @@ The SERVICE emits a signed challenge when it returns the Pay.sh `402` response.
44
63
  - policy ID
45
64
  - payment ID hash
46
65
 
47
- `PaySh.parseRequest()` verifies that signature against the facilitator public key before it accepts the request. This closes the race window where a forged `paymentRequirements` envelope could be raced against the legitimate one.
48
-
49
- ## Run the demo
66
+ `PaySh.parseRequest()` verifies that signature against the facilitator public key before accepting the request. This closes the race window where a forged `paymentRequirements` envelope could race a legitimate one.
50
67
 
51
- ```bash
52
- pnpm --filter ./examples/pay-sh-demo dev
53
- ```
68
+ ## Files to read in repo
54
69
 
55
- ```bash
56
- pay --sandbox curl https://demo.agenttrust.tech/protected
57
- ```
58
-
59
- The demo uses three deterministic counterparties:
60
-
61
- | Counterparty tier | Expected result |
62
- | --- | --- |
63
- | 0 | `402 Deny` with `CounterpartyTierBelowMin` |
64
- | 1 | `402 Deny` with `CounterpartyTierBelowMin` |
65
- | 3 | `200 Allow` with `X-Payment-Receipt` |
66
-
67
- Get the exact payer agent keys:
68
-
69
- ```bash
70
- curl -s https://demo.agenttrust.tech/health | jq '.counterparties'
71
- ```
70
+ | File | Purpose |
71
+ |---|---|
72
+ | [`facilitators/pay-sh/index.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/index.ts) | The five-method `PaySh` class |
73
+ | [`facilitators/pay-sh/schemas.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/schemas.ts) | Strict Zod wire schemas |
74
+ | [`facilitators/pay-sh/sig.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/sig.ts) | SERVICE challenge signature helpers |
75
+ | [`facilitators/pay-sh/proof-validator.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/proof-validator.ts) | Replay, self-pay, amount, mint, recipient checks |
76
+ | [`facilitators/pay-sh/feedback.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/feedback.ts) | Idempotent feedback emission |
77
+ | [`examples/pay-sh-demo/src/middleware.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/examples/pay-sh-demo/src/middleware.ts) | Express bridge from Pay.sh retry to AgentTrust |
72
78
 
73
79
  ## Production wiring
74
80
 
75
- The demo proves the pipeline without requiring devnet RPC in CI. The server package carries the real devnet Anchor route: `trustgate/server/src/chain.ts` loads deployed program IDLs, `/verify` simulates `gate_payment`, and `/settle` delegates proof validation plus feedback emission through the active adapter.
81
+ The demo proves the pipeline without requiring devnet RPC in CI. The server package carries the real devnet path: [`trustgate/server/src/chain.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/chain.ts) loads deployed program IDLs, `/verify` simulates `gate_payment`, and `/settle` delegates proof validation plus feedback emission through the active adapter.
76
82
 
77
- Production swaps three dependency seams:
83
+ Production swaps three dependency seams the demo stubs:
78
84
 
79
85
  ```ts
86
+ import { PaySh } from "./facilitators/pay-sh";
87
+
80
88
  const adapter = new PaySh({
81
89
  signingNetwork: "solana-devnet",
82
- feePayer: facilitator.publicKey,
83
- validateOnChainTx,
84
- emitFeedbackCpi,
85
- priorEmissionLookup,
86
- replayCache,
87
- signDecision,
90
+ feePayer: facilitator.publicKey,
91
+ validateOnChainTx, // makeValidateOnChainTx({ connection, … })
92
+ emitFeedbackCpi, // makeEmitFeedbackCpi({ connection, programIds, … })
93
+ priorEmissionLookup, // makePriorEmissionLookup({ connection, programId })
94
+ replayCache, // ReplayCache (in-memory by default)
95
+ signDecision, // signs canonical decision bytes with facilitator key
88
96
  });
89
97
  ```
90
98
 
91
99
  | Dependency | Production implementation |
92
- | --- | --- |
100
+ |---|---|
93
101
  | `validateOnChainTx` | parse the confirmed SPL transfer transaction from RPC |
94
102
  | `emitFeedbackCpi` | build and send `trustgate::emit_feedback` through Anchor |
95
103
  | `priorEmissionLookup` | read the `FeedbackEmissionLog` PDA by payment hash |
96
104
  | `signDecision` | sign canonical decision bytes with the facilitator key |
97
105
 
98
- ## Failure cases to keep
106
+ The factories (`makeValidateOnChainTx`, `makeEmitFeedbackCpi`, `makePriorEmissionLookup`) are SDK exports — see [SDK → Exports reference](/sdk/exports-reference).
107
+
108
+ ## Failure cases the adapter must keep
99
109
 
100
110
  Do not remove these checks when adapting Pay.sh into a production server:
101
111
 
@@ -105,6 +115,32 @@ Do not remove these checks when adapting Pay.sh into a production server:
105
115
  - reject `payTo` / `payeeRecipient` mismatch
106
116
  - reject expired challenges
107
117
  - reject replayed proof bindings
108
- - reject transfer authority equal to facilitator fee payer
118
+ - reject transfer authority equal to facilitator fee payer (self-pay defense)
119
+
120
+ These are part of the adapter, not the route layer. The Pay.sh adapter's [`proof-validator.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/proof-validator.ts) is the reference implementation.
121
+
122
+ ## Live evidence
123
+
124
+ Real atomic-settlement trace on devnet (2026-05-06):
125
+
126
+ | Step | Tx |
127
+ |---|---|
128
+ | Signed SPL `transferChecked` | [`5iV8EYmJh9XS…`](https://explorer.solana.com/tx/5iV8EYmJh9XSXkBQrrbQ5L9kmBQabD3G3RXVPsHn9PkWceTFoeRsUV4g5aLLzZyRjeBoFvK3Woxr2cZa5xeUwhVD?cluster=devnet) |
129
+ | `emit_feedback` PDA-signed CPI | [`jMobmWJUAXuL8…`](https://explorer.solana.com/tx/jMobmWJUAXuL8FmQujfxW9NmeMbzADUoABzqjiMeuc5m3YXyeuZeUw1ZJc29JGsqyWQGDY8q3vrtBdamhKXraag?cluster=devnet) |
130
+ | `FeedbackEmissionLog` PDA | [`HB4BBi9j…`](https://explorer.solana.com/address/HB4BBi9jaD3VPcZkQQaH3DxukSqBiXfW8RejtaLa8bF3?cluster=devnet) |
131
+
132
+ Full trace + reproduction commands: [Verification → Devnet smoke](/verification/devnet-smoke).
133
+
134
+ ## Read next
109
135
 
110
- These checks are part of the adapter, not the route layer.
136
+ <Cards>
137
+ <Card title="Facilitator adapters" href="/integration-guides/facilitator-adapters">
138
+ The five-method contract every adapter satisfies. Build your own.
139
+ </Card>
140
+ <Card title="Atomic-tx invariant" href="/verification/atomic-tx-invariant">
141
+ Why the gate, transfer, and feedback must execute as one Solana tx.
142
+ </Card>
143
+ <Card title="mountTrustGate" href="/sdk/mount-trustgate">
144
+ The SDK middleware that backs the adapter dispatch.
145
+ </Card>
146
+ </Cards>