@agenttrust-sdk/mcp 0.2.0 → 0.2.2
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.
- package/dist/embedded-data/devnet-attestor-trace.json +32 -0
- package/dist/embedded-data/devnet-chained-validation.json +52 -0
- package/dist/embedded-data/devnet-counterparties.json +53 -0
- package/dist/embedded-data/devnet-demo-policies.json +46 -0
- package/dist/embedded-data/devnet-namespaces.json +107 -0
- package/dist/embedded-data/devnet-smoke.json +24 -0
- package/dist/embedded-docs/getting-started/architecture-overview.mdx +85 -0
- package/dist/embedded-docs/getting-started/quickstart.mdx +100 -0
- package/dist/embedded-docs/index.mdx +64 -0
- package/dist/embedded-docs/integration-guides/capability-namespaces.mdx +15 -0
- package/dist/embedded-docs/integration-guides/custom-attestor.mdx +15 -0
- package/dist/embedded-docs/integration-guides/facilitator-adapters.mdx +85 -0
- package/dist/embedded-docs/integration-guides/pay-sh-adapter.mdx +110 -0
- package/dist/embedded-docs/integration-guides/x402-facilitator.mdx +79 -0
- package/dist/embedded-docs/programs/policy-vault/counterparty-tier-policy.mdx +15 -0
- package/dist/embedded-docs/programs/policy-vault/index.mdx +68 -0
- package/dist/embedded-docs/programs/policy-vault/kill-switch-policy.mdx +15 -0
- package/dist/embedded-docs/programs/policy-vault/require-validation-policy.mdx +15 -0
- package/dist/embedded-docs/programs/policy-vault/spending-policy.mdx +15 -0
- package/dist/embedded-docs/programs/policy-vault/velocity-policy.mdx +15 -0
- package/dist/embedded-docs/programs/trustgate.mdx +53 -0
- package/dist/embedded-docs/programs/validation-registry.mdx +49 -0
- package/dist/embedded-docs/reference/byte-offset-reference.mdx +20 -0
- package/dist/embedded-docs/reference/changelog.mdx +19 -0
- package/dist/embedded-docs/reference/devnet-program-ids.mdx +24 -0
- package/dist/embedded-docs/reference/discriminator-constants.mdx +16 -0
- package/dist/embedded-docs/reference/formal-verification.mdx +19 -0
- package/dist/embedded-docs/reference/mainnet-program-ids.mdx +16 -0
- package/dist/embedded-docs/reference/quantu-agent-registry.mdx +15 -0
- package/dist/embedded-docs/sdk/atomic-tx-invariant.mdx +37 -0
- package/dist/embedded-docs/sdk/gate-payment.mdx +22 -0
- package/dist/embedded-docs/sdk/index.mdx +73 -0
- package/dist/embedded-docs/sdk/mount-trustgate.mdx +15 -0
- package/dist/embedded-examples/attestor-demo/README.md +100 -0
- package/dist/embedded-examples/pay-sh-demo/README.md +136 -0
- package/dist/embedded-examples/pay-sh-demo/src/deps-real.ts +150 -0
- package/dist/embedded-examples/pay-sh-demo/src/deps.ts +150 -0
- package/dist/embedded-examples/pay-sh-demo/src/index.ts +471 -0
- package/dist/embedded-examples/pay-sh-demo/src/middleware.ts +198 -0
- package/dist/embedded-examples/pay-sh-demo/src/policy.ts +247 -0
- package/dist/embedded-examples/pay-sh-demo/src/x402.ts +140 -0
- package/dist/index.js +73 -18
- package/dist/index.js.map +1 -1
- package/dist/resources/docs.js +68 -46
- package/dist/resources/docs.js.map +1 -1
- package/dist/server.js +6 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/discovery/docs.js +6 -0
- package/dist/tools/discovery/docs.js.map +1 -1
- package/dist/tools/discovery/facilitator-walkthrough.js +43 -18
- package/dist/tools/discovery/facilitator-walkthrough.js.map +1 -1
- package/dist/tools/read/demo-state.js +7 -4
- package/dist/tools/read/demo-state.js.map +1 -1
- package/dist/tools/read/simulate-payment.js +13 -1
- package/dist/tools/read/simulate-payment.js.map +1 -1
- package/dist/trustgate/server/src/facilitators/README.md +241 -0
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Atomic-tx invariant
|
|
3
|
+
description: SDK and facilitator rules that keep policy checks tied to settlement.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
AgentTrust treats a policy check as useful only when the facilitator enforces the same payment context through settlement.
|
|
7
|
+
|
|
8
|
+
Production settlement must compose:
|
|
9
|
+
|
|
10
|
+
1. `gate_payment`
|
|
11
|
+
2. SPL transfer
|
|
12
|
+
3. `emit_feedback`
|
|
13
|
+
|
|
14
|
+
All three succeed, or all three revert. That is the invariant that keeps a Pay.sh payment from settling without the AgentTrust feedback record, and keeps feedback from being emitted for a payment that never moved.
|
|
15
|
+
|
|
16
|
+
## Adapter responsibilities
|
|
17
|
+
|
|
18
|
+
The adapter validates that the retry proof still matches the verify-time context:
|
|
19
|
+
|
|
20
|
+
| Check | Why it exists |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `paymentIdHash` replay binding | prevents duplicate or raced settlement calls |
|
|
23
|
+
| amount, mint, recipient cross-check | prevents paying a different asset or recipient than the policy checked |
|
|
24
|
+
| facilitator fee payer != transfer authority | prevents self-pay feedback |
|
|
25
|
+
| SERVICE-signed challenge | prevents forged `paymentRequirements` racing a legitimate one |
|
|
26
|
+
| idempotent feedback lookup | makes retries return a stable receipt instead of double-emitting |
|
|
27
|
+
|
|
28
|
+
## Demo vs production
|
|
29
|
+
|
|
30
|
+
`examples/pay-sh-demo` stubs `validateOnChainTx` and `emitFeedbackCpi` so the route can run in CI. Production fills those same dependency seams with RPC parsing and the Anchor feedback call.
|
|
31
|
+
|
|
32
|
+
| Source | Path |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| facilitator routes | [`trustgate/server/src/routes/settle.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/routes/settle.ts) |
|
|
35
|
+
| Pay.sh proof validator | [`trustgate/server/src/facilitators/pay-sh/proof-validator.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/proof-validator.ts) |
|
|
36
|
+
| Pay.sh feedback helper | [`trustgate/server/src/facilitators/pay-sh/feedback.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/feedback.ts) |
|
|
37
|
+
| Pay.sh demo deps | [`examples/pay-sh-demo/src/deps.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/examples/pay-sh-demo/src/deps.ts) |
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: gatePayment
|
|
3
|
+
description: Read-only policy decision call for facilitators.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`In progress`
|
|
7
|
+
|
|
8
|
+
`gatePayment()` simulates PolicyVault and returns `Allow`, `Deny`, or `RequireValidation`. It remains useful when a service already owns its x402 routing layer and only wants the AgentTrust decision.
|
|
9
|
+
|
|
10
|
+
For Pay.sh, the adapter path wraps this lower-level decision in:
|
|
11
|
+
|
|
12
|
+
1. x402 challenge parsing
|
|
13
|
+
2. SERVICE signature verification
|
|
14
|
+
3. proof validation
|
|
15
|
+
4. feedback emission
|
|
16
|
+
|
|
17
|
+
| Source | Path |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| SDK client | [`trustgate/sdk/src/client.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/src/client.ts) |
|
|
20
|
+
| Pay.sh adapter | [`trustgate/server/src/facilitators/pay-sh/index.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/server/src/facilitators/pay-sh/index.ts) |
|
|
21
|
+
| shared types | [`trustgate/sdk/src/types.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/src/types.ts) |
|
|
22
|
+
| package README | [`trustgate/sdk/README.md`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/README.md) |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@agenttrust-sdk/trustgate"
|
|
3
|
+
description: TypeScript client helpers, Express middleware, and atomicity guard.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The SDK is the facilitator-facing TypeScript package. The server adapter layer currently carries the concrete Pay.sh integration and the demo path; the package API remains the smaller client / Express surface for apps that call AgentTrust directly.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm add @agenttrust-sdk/trustgate
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Imports
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { mountTrustGate } from "@agenttrust-sdk/trustgate/express";
|
|
16
|
+
import { gatePayment, settle, dispute } from "@agenttrust-sdk/trustgate/client";
|
|
17
|
+
import {
|
|
18
|
+
AtomicityNotEnforcedError,
|
|
19
|
+
DEFAULT_DEVNET_PROGRAM_IDS,
|
|
20
|
+
derivePolicyPda,
|
|
21
|
+
} from "@agenttrust-sdk/trustgate";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## gatePayment
|
|
25
|
+
|
|
26
|
+
`gatePayment()` is a read-only decision call.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
type GateDecision =
|
|
30
|
+
| { kind: "Allow" }
|
|
31
|
+
| { kind: "Deny"; reasonCode: number; reasonName: string }
|
|
32
|
+
| { kind: "RequireValidation"; capabilityHash: number[] };
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The helper simulates the Anchor instruction and parses the return-data channel into the TypeScript union.
|
|
36
|
+
|
|
37
|
+
## mountTrustGate
|
|
38
|
+
|
|
39
|
+
`mountTrustGate(app, config)` adds x402 routes to an Express service in fewer than 50 lines.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
await mountTrustGate(app, {
|
|
43
|
+
rpcUrl: "https://api.devnet.solana.com",
|
|
44
|
+
facilitatorKeypair,
|
|
45
|
+
defaultPolicyId: 1,
|
|
46
|
+
network: "solana-devnet",
|
|
47
|
+
atomicityEnforced: true,
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Atomicity guard
|
|
52
|
+
|
|
53
|
+
`settle`, `dispute`, and middleware config require:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
{ atomicityEnforced: true }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The marker is literal `true`, not `boolean`, and the runtime guard throws if a caller bypasses TypeScript with a cast. The server adapter path extends this into proof validation and feedback emission.
|
|
60
|
+
|
|
61
|
+
## Current surface
|
|
62
|
+
|
|
63
|
+
| API | Status |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `gatePayment` | implemented |
|
|
66
|
+
| `mountTrustGate` `/verify` | implemented |
|
|
67
|
+
| `mountTrustGate` `/receipt` | implemented |
|
|
68
|
+
| Pay.sh adapter | implemented in `@agenttrust/trustgate-server` workspace package |
|
|
69
|
+
| Pay.sh demo | implemented in `examples/pay-sh-demo` |
|
|
70
|
+
| `settle` | guarded SDK surface; server adapter validation path is active |
|
|
71
|
+
| `dispute` | guarded SDK surface; negative feedback route remains narrower |
|
|
72
|
+
|
|
73
|
+
Source: `trustgate/sdk/src`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: mountTrustGate
|
|
3
|
+
description: Express middleware that mounts AgentTrust x402 routes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`In progress`
|
|
7
|
+
|
|
8
|
+
`mountTrustGate(app, config)` adds `/verify`, `/receipt`, `/settle`, and `/dispute` handlers to an Express service. The SDK enforces the atomicity config before binding routes.
|
|
9
|
+
|
|
10
|
+
| Source | Path |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| Express middleware | [`trustgate/sdk/src/express.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/src/express.ts) |
|
|
13
|
+
| x402 helpers | [`trustgate/sdk/src/x402.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/src/x402.ts) |
|
|
14
|
+
| atomicity tests | [`trustgate/sdk/test/atomicity.test.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/trustgate/sdk/test/atomicity.test.ts) |
|
|
15
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# attestor-demo
|
|
2
|
+
|
|
3
|
+
Devnet ValidationRegistry attestor-lifecycle demo. Captures a real on-chain
|
|
4
|
+
trace through all 5 ValidationRegistry instructions, proving the third leg
|
|
5
|
+
of the ERC-8004 trust stack works end-to-end against the deployed devnet
|
|
6
|
+
program at [`Cx4RFa6ysw3qXYhugPkF8pFSWBkmKq59h2dWgF2tKhtv`](https://explorer.solana.com/address/Cx4RFa6ysw3qXYhugPkF8pFSWBkmKq59h2dWgF2tKhtv?cluster=devnet).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm install
|
|
10
|
+
pnpm --filter ./examples/attestor-demo run smoke
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Live devnet trace (2026-05-06)
|
|
14
|
+
|
|
15
|
+
> **AgentTrust ValidationRegistry — full lifecycle, live on Solana devnet.**
|
|
16
|
+
|
|
17
|
+
Subject (Quantu agent_account): [`5PfaofvEUf3adtJwMho7zzbfvgxwxbvp2V5moqhtLK8y`](https://explorer.solana.com/address/5PfaofvEUf3adtJwMho7zzbfvgxwxbvp2V5moqhtLK8y?cluster=devnet)
|
|
18
|
+
Capability: `usdc-payment-policy.v1`
|
|
19
|
+
|
|
20
|
+
| step | tx | PDA |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **register_namespace** | [`5B3PfDGYhzhusJwj…`](https://explorer.solana.com/tx/5B3PfDGYhzhusJwjXURnhpkZ2umipdegfNREtJbcgZySR7nr976CcSJXqYSzB8eSYT14W3yrzGuks75S7pdZD3WK?cluster=devnet) | [`34gonn86FjxzXZMGd43RSvQVyH1r6PrGV9xnHXjjkEwR`](https://explorer.solana.com/address/34gonn86FjxzXZMGd43RSvQVyH1r6PrGV9xnHXjjkEwR?cluster=devnet) |
|
|
23
|
+
| **register_attestor** | [`Ct3SQ4CR9bu6oijR…`](https://explorer.solana.com/tx/Ct3SQ4CR9bu6oijRELe7pnjj8KfMRVDiQ3AkytNQtYfF2yZBsThMJNoCDADnwWp37PYcsFJSEkBjXmaLY9a9eQD?cluster=devnet) | [`GTzWJzV5htNi1Ntqwq2e2ydu9h4rArnKQwzv2sJjC9zP`](https://explorer.solana.com/address/GTzWJzV5htNi1Ntqwq2e2ydu9h4rArnKQwzv2sJjC9zP?cluster=devnet) |
|
|
24
|
+
| **request_validation** | [`qBQzSTCWfkE9Xw1E…`](https://explorer.solana.com/tx/qBQzSTCWfkE9Xw1EZ2qRwo3Hv451cbVaTRKSa32KHpnL7sfCSVBEhjGinm5qod6W6LtCgAj7xvbhydHf1wjoKq9?cluster=devnet) | [`GnbrSzWsDw1rehCrFJ4ckiM9JJJeAHdjfNDt7QQy7vhV`](https://explorer.solana.com/address/GnbrSzWsDw1rehCrFJ4ckiM9JJJeAHdjfNDt7QQy7vhV?cluster=devnet) |
|
|
25
|
+
| **respond_to_validation** | [`CCxKvvQ9ZdboukcX…`](https://explorer.solana.com/tx/CCxKvvQ9ZdboukcXPp9jj1a3o53grGR9VjZux7kS1AAWqaVnRXVqhJjphsM1QYjny5oaVP4oRGThBLUQ41DyzwC?cluster=devnet) | [`C6Yr7oKcZ6sDVibR35SWbFnGCXyfQjLeRCiPbjxYq6vY`](https://explorer.solana.com/address/C6Yr7oKcZ6sDVibR35SWbFnGCXyfQjLeRCiPbjxYq6vY?cluster=devnet) |
|
|
26
|
+
| **revoke_validation** | (gated on REVOKE=1) | (in-place — sets revoked=true on the attestation PDA above) |
|
|
27
|
+
|
|
28
|
+
The `ValidationAttestation` PDA at row 4 is the on-chain artifact the
|
|
29
|
+
PolicyVault's `RequireValidation` policy reads via the byte-offset
|
|
30
|
+
parser at `programs/policy-vault/src/ext/validation_registry.rs`. Once
|
|
31
|
+
this PDA exists for `(subject, capability, attestor) = (5PfaofvE…,
|
|
32
|
+
sha256(usdc-payment-policy.v1), GTzWJzV5…)`, a subsequent
|
|
33
|
+
`gate_payment` call that passes this attestation account through the
|
|
34
|
+
`validation_attestation` slot turns a `RequireValidation` decision
|
|
35
|
+
into `Allow`.
|
|
36
|
+
|
|
37
|
+
Full machine-readable trace at [`devnet-attestor-trace.json`](./devnet-attestor-trace.json).
|
|
38
|
+
|
|
39
|
+
## What this demonstrates
|
|
40
|
+
|
|
41
|
+
1. **All 5 ValidationRegistry instructions reachable from the SDK**
|
|
42
|
+
(`@agenttrust-sdk/trustgate`'s `validation-registry` module exposes
|
|
43
|
+
PDA derivers + ix builders for every on-chain entry point).
|
|
44
|
+
2. **The third leg of ERC-8004** is real on Solana — Quantu's
|
|
45
|
+
IdentityRegistry + ReputationRegistry sit at the foundation;
|
|
46
|
+
ValidationRegistry's capability attestation closes the trust stack.
|
|
47
|
+
3. **The complete `RequireValidation` round-trip**: policy emits
|
|
48
|
+
capabilityHash → attestor responds → policy now Allows. Each step is
|
|
49
|
+
a separate on-chain tx; AgentTrust composes the PDAs without
|
|
50
|
+
trusting any off-chain coordination.
|
|
51
|
+
|
|
52
|
+
## How it fits the demo narrative
|
|
53
|
+
|
|
54
|
+
The Pay.sh adapter (in `trustgate/server/src/facilitators/pay-sh/`)
|
|
55
|
+
already surfaces `capabilityHash` in its `formatChallenge` response when
|
|
56
|
+
the policy gate returns `RequireValidation`. A SERVICE that hits this
|
|
57
|
+
branch:
|
|
58
|
+
|
|
59
|
+
1. Sees `decision.kind = "RequireValidation"` + `capabilityHash` in the
|
|
60
|
+
x402 `/verify` response body.
|
|
61
|
+
2. Looks up the attestor service that handles that capability (typically
|
|
62
|
+
discovered out-of-band via a registry or hard-coded contract).
|
|
63
|
+
3. Submits the claim payload off chain; the attestor responds on chain
|
|
64
|
+
via `respond_to_validation` (the script in this directory does this).
|
|
65
|
+
4. Re-submits the payment to the SERVICE; the new `/verify` call
|
|
66
|
+
includes the `validation_attestation` PDA in the gate_payment
|
|
67
|
+
account list, the policy reads it, and the decision flips to
|
|
68
|
+
`Allow`.
|
|
69
|
+
|
|
70
|
+
The script proves steps 3-4 work end-to-end on devnet. Steps 1-2 are
|
|
71
|
+
business logic the SERVICE owns.
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
| env var | default | meaning |
|
|
76
|
+
|--|--|--|
|
|
77
|
+
| `RPC_URL` | `https://api.devnet.solana.com` | Solana RPC endpoint |
|
|
78
|
+
| `KEYPAIR` | `~/.config/solana/id.json` | Facilitator keypair path |
|
|
79
|
+
| `REVOKE` | (unset) | When `1`, runs the optional 5th step (revoke_validation) |
|
|
80
|
+
|
|
81
|
+
## Cost
|
|
82
|
+
|
|
83
|
+
~0.012 SOL across all 5 steps (rent for namespace + attestor +
|
|
84
|
+
request + attestation PDAs, ~0.0023 SOL each, plus tx fees).
|
|
85
|
+
|
|
86
|
+
## Idempotency
|
|
87
|
+
|
|
88
|
+
Re-running reuses:
|
|
89
|
+
- `examples/attestor-demo/attestor-keypair.json` (kept across runs so
|
|
90
|
+
the AttestorProfile PDA stays stable)
|
|
91
|
+
- The namespace PDA (skipped if `fetchCapabilityNamespace` already
|
|
92
|
+
returns a record)
|
|
93
|
+
|
|
94
|
+
It generates a fresh requester keypair each run so request_validation
|
|
95
|
+
doesn't collide on the `(subject, capability, requester)` PDA.
|
|
96
|
+
|
|
97
|
+
If you've already responded to a request once, the
|
|
98
|
+
`(subject, capability, attestor)` PDA exists and respond is skipped —
|
|
99
|
+
delete the attestor keypair file or change the `CAPABILITY_NAME` to
|
|
100
|
+
walk a fresh attestation lifecycle.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# pay-sh-demo
|
|
2
|
+
|
|
3
|
+
Pay.sh + AgentTrust TrustGate live-demo Express server. Single endpoint
|
|
4
|
+
(`/protected`), gated by a counterparty-tier policy and exercised end-to-end
|
|
5
|
+
through the AgentTrust facilitator pipeline.
|
|
6
|
+
|
|
7
|
+
## Hit it now (no clone required)
|
|
8
|
+
|
|
9
|
+
The demo runs **live** at `demo.agenttrust.tech`. Hit `/protected` with
|
|
10
|
+
no payment proof and you'll get back a real x402 v2 challenge envelope:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
curl -i https://demo.agenttrust.tech/protected
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
You'll see `HTTP/2 402` with a SERVICE-signed `payment-required` header
|
|
17
|
+
(base64 envelope). To walk the full Allow → settle → emit_feedback path,
|
|
18
|
+
clone the repo or use the `pay` CLI sandbox below.
|
|
19
|
+
|
|
20
|
+
## Run locally
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
pay --sandbox curl http://localhost:3402/protected
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Live devnet trace (2026-05-06)
|
|
27
|
+
|
|
28
|
+
> **AgentTrust + Pay.sh atomic settlement, live on Solana devnet.**
|
|
29
|
+
|
|
30
|
+
| step | tx |
|
|
31
|
+
|---|---|
|
|
32
|
+
| signed SPL transfer | [`5iV8EYmJh9XSXkBQrrbQ5L9kmBQabD3G3RXVPsHn9PkWceTFoeRsUV4g5aLLzZyRjeBoFvK3Woxr2cZa5xeUwhVD`](https://explorer.solana.com/tx/5iV8EYmJh9XSXkBQrrbQ5L9kmBQabD3G3RXVPsHn9PkWceTFoeRsUV4g5aLLzZyRjeBoFvK3Woxr2cZa5xeUwhVD?cluster=devnet) |
|
|
33
|
+
| emit_feedback CPI (PDA-signed → `agent_registry::give_feedback` → `atom_engine::update_stats`) | [`jMobmWJUAXuL8FmQujfxW9NmeMbzADUoABzqjiMeuc5m3YXyeuZeUw1ZJc29JGsqyWQGDY8q3vrtBdamhKXraag`](https://explorer.solana.com/tx/jMobmWJUAXuL8FmQujfxW9NmeMbzADUoABzqjiMeuc5m3YXyeuZeUw1ZJc29JGsqyWQGDY8q3vrtBdamhKXraag?cluster=devnet) |
|
|
34
|
+
| FeedbackEmissionLog PDA (init-only, score=100) | [`HB4BBi9jaD3VPcZkQQaH3DxukSqBiXfW8RejtaLa8bF3`](https://explorer.solana.com/address/HB4BBi9jaD3VPcZkQQaH3DxukSqBiXfW8RejtaLa8bF3?cluster=devnet) |
|
|
35
|
+
|
|
36
|
+
Captured in [`devnet-smoke.json`](./devnet-smoke.json). Reproduce with `scripts/devnet-smoke.ts`.
|
|
37
|
+
|
|
38
|
+
## What it shows
|
|
39
|
+
|
|
40
|
+
The demo proves the full Pay.sh → AgentTrust loop without depending on a real
|
|
41
|
+
Solana RPC connection:
|
|
42
|
+
|
|
43
|
+
1. The CLI hits `/protected` with no payment.
|
|
44
|
+
2. The middleware emits a `402 Payment Required` carrying the x402 v2
|
|
45
|
+
`PAYMENT-REQUIRED` envelope (base64). The envelope embeds AgentTrust's
|
|
46
|
+
policy hints in `extra.agentTrust`.
|
|
47
|
+
3. Pay.sh signs the payment locally (Surfpool sandbox) and retries with
|
|
48
|
+
`PAYMENT-SIGNATURE`.
|
|
49
|
+
4. The middleware:
|
|
50
|
+
- calls `PaySh.parseRequest` → `VerifyContext`
|
|
51
|
+
- calls `decide(ctx)` — the demo policy maps the `X-Demo-Payer-Agent`
|
|
52
|
+
header to a counterparty tier and compares against `minTier=2`
|
|
53
|
+
- on **Allow**: validates the proof shape, emits a synthetic feedback
|
|
54
|
+
CPI signature, and forwards to the resource handler
|
|
55
|
+
- on **Deny**: returns 402 with the reason code from the gate decision
|
|
56
|
+
|
|
57
|
+
## Three demo counterparties
|
|
58
|
+
|
|
59
|
+
The `defaultCounterpartyTable()` in `src/index.ts` seeds three deterministic
|
|
60
|
+
agent PDAs:
|
|
61
|
+
|
|
62
|
+
| header value (X-Demo-Payer-Agent) | tier | expected outcome |
|
|
63
|
+
|--|--|--|
|
|
64
|
+
| (output of `seed("tier0")`) | 0 | **402 Deny** — `CounterpartyTierBelowMin` |
|
|
65
|
+
| (output of `seed("tier1")`) | 1 | **402 Deny** — `CounterpartyTierBelowMin` |
|
|
66
|
+
| (output of `seed("tier3")`) | 3 | **200 Allow** |
|
|
67
|
+
|
|
68
|
+
Hit `GET /health` for the actual base58 strings. Then drive each path with
|
|
69
|
+
the matching header.
|
|
70
|
+
|
|
71
|
+
## Run
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# from repo root
|
|
75
|
+
pnpm install
|
|
76
|
+
pnpm --filter ./examples/pay-sh-demo build
|
|
77
|
+
pnpm --filter ./examples/pay-sh-demo dev
|
|
78
|
+
# server listens on :3402
|
|
79
|
+
|
|
80
|
+
# in another shell — Allow path (tier 3)
|
|
81
|
+
PAYER=$(curl -s http://localhost:3402/health | jq -r '.counterparties[2].agent')
|
|
82
|
+
pay --sandbox curl -H "X-Demo-Payer-Agent: $PAYER" http://localhost:3402/protected
|
|
83
|
+
|
|
84
|
+
# Deny path (tier 0)
|
|
85
|
+
PAYER=$(curl -s http://localhost:3402/health | jq -r '.counterparties[0].agent')
|
|
86
|
+
pay --sandbox curl -H "X-Demo-Payer-Agent: $PAYER" http://localhost:3402/protected
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If the `pay` CLI isn't installed, the integration tests in `test/` exercise
|
|
90
|
+
the same flow via supertest with synthesised x402 challenge / proof bodies.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pnpm --filter ./examples/pay-sh-demo test
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
| env var | default | meaning |
|
|
99
|
+
|--|--|--|
|
|
100
|
+
| `PORT` | `3402` | HTTP port |
|
|
101
|
+
| `NETWORK` | `solana-devnet` | network slug — must match Pay.sh's `--sandbox`/`--mainnet` flag |
|
|
102
|
+
| `MINT` | USDC mainnet | SPL mint advertised in the `extra.asset` field |
|
|
103
|
+
|
|
104
|
+
## Two modes: demo vs production
|
|
105
|
+
|
|
106
|
+
The demo ships **two boot paths**:
|
|
107
|
+
|
|
108
|
+
### `createDemoApp` (default `pnpm dev`)
|
|
109
|
+
|
|
110
|
+
Synthesises chain interactions in-process — no Solana RPC needed,
|
|
111
|
+
deterministic fixtures. Useful for CI smoke and local iteration. Three
|
|
112
|
+
counterparty tiers are seeded from a static table; payment proofs are
|
|
113
|
+
synthetic; `emit_feedback` returns deterministic signatures.
|
|
114
|
+
|
|
115
|
+
### `createRealDemoApp` (used by `demo.agenttrust.tech` in production)
|
|
116
|
+
|
|
117
|
+
Real Anchor `Program<TrustGate>` wired to live devnet. Real
|
|
118
|
+
`validateOnChainTx` parses signed VersionedTransactions via RPC. Real
|
|
119
|
+
`emitFeedbackCpi` lands `FeedbackEmissionLog` PDAs on chain. Real
|
|
120
|
+
`simulateGatePayment` calls the deployed `policy_vault` program for every
|
|
121
|
+
verify request.
|
|
122
|
+
|
|
123
|
+
The hosted demo at `https://demo.agenttrust.tech` runs `createRealDemoApp`.
|
|
124
|
+
Every `/protected` call traversing all three counterparty tiers writes a
|
|
125
|
+
real on-chain artifact. The Phase C smoke trace
|
|
126
|
+
([`devnet-smoke.json`](./devnet-smoke.json)) was captured from this exact
|
|
127
|
+
boot path against devnet.
|
|
128
|
+
|
|
129
|
+
## Adding to the demo
|
|
130
|
+
|
|
131
|
+
- New counterparty tier: append to `defaultCounterpartyTable()` in
|
|
132
|
+
`src/index.ts`.
|
|
133
|
+
- New gated route: import `paymentMiddleware` and mount it next to
|
|
134
|
+
`/protected`.
|
|
135
|
+
- Real chain wiring: build a deps factory that returns the same `PayShDeps`
|
|
136
|
+
shape, replace `makeDemoPayShDeps()` in `createDemoApp`.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production `PayShDeps` factory — wires real Anchor + RPC into the
|
|
3
|
+
* adapter. Companion to `deps.ts` (the in-memory mock) for the
|
|
4
|
+
* INTEGRATION=1 path.
|
|
5
|
+
*
|
|
6
|
+
* Real chain dependencies:
|
|
7
|
+
* - validateOnChainTx → trustgate-sdk's makeValidateOnChainTx (parses
|
|
8
|
+
* VersionedTransaction, polls connection.getSignatureStatus)
|
|
9
|
+
* - emitFeedbackCpi → trustgate-sdk's makeEmitFeedbackCpi (Anchor
|
|
10
|
+
* methods builder, signed by facilitator keypair)
|
|
11
|
+
* - priorEmissionLookup → trustgate-sdk's makePriorEmissionLookup
|
|
12
|
+
* (fetches FeedbackEmissionLog PDA + recovers signature via
|
|
13
|
+
* getSignaturesForAddress)
|
|
14
|
+
*
|
|
15
|
+
* Quantu account resolution: the demo expects `resolveQuantu(payeeAgent)`
|
|
16
|
+
* to return the bundle of Quantu accounts (asset, collection, optional
|
|
17
|
+
* ATOM) for the given payee agent. For the integration test we use a
|
|
18
|
+
* static map (the demo's payee Quantu accounts are pre-registered or
|
|
19
|
+
* mocked as cloned mainnet accounts under `anchor test`).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { AnchorProvider, Idl, Program, Wallet } from "@coral-xyz/anchor";
|
|
23
|
+
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_DEVNET_QUANTU_IDS,
|
|
27
|
+
DEFAULT_DEVNET_PROGRAM_IDS,
|
|
28
|
+
MAINNET_QUANTU_IDS,
|
|
29
|
+
QuantuFeedbackAccounts,
|
|
30
|
+
QuantuProgramIds,
|
|
31
|
+
deriveQuantuFeedbackAccounts,
|
|
32
|
+
loadTrustGate,
|
|
33
|
+
makeEmitFeedbackCpi,
|
|
34
|
+
makePriorEmissionLookup,
|
|
35
|
+
makeValidateOnChainTx,
|
|
36
|
+
} from "@agenttrust-sdk/trustgate";
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
PayShDeps,
|
|
40
|
+
ReplayCache,
|
|
41
|
+
signEnvelope,
|
|
42
|
+
} from "@agenttrust/trustgate-server";
|
|
43
|
+
|
|
44
|
+
export interface MakeRealPayShDepsArgs {
|
|
45
|
+
/** RPC endpoint (e.g. https://api.devnet.solana.com). */
|
|
46
|
+
readonly rpcUrl: string;
|
|
47
|
+
/** Network slug — gates the adapter against mismatched challenges. */
|
|
48
|
+
readonly signingNetwork: string;
|
|
49
|
+
/** Facilitator keypair — payer for emit_feedback, signer of decisions. */
|
|
50
|
+
readonly facilitator: Keypair;
|
|
51
|
+
/** Quantu account resolver. The integration test wires a static map. */
|
|
52
|
+
readonly resolveQuantu: (payeeAgent: PublicKey) => Promise<QuantuFeedbackAccounts>;
|
|
53
|
+
/** Optional override of trustgate program ID (mainnet vs devnet). */
|
|
54
|
+
readonly trustgateProgramId?: PublicKey;
|
|
55
|
+
/** Optional Quantu program IDs override. Default = devnet (matches
|
|
56
|
+
* programs/trustgate/src/constants.rs). Use MAINNET_QUANTU_IDS when
|
|
57
|
+
* running under `anchor test` (clones mainnet pubkeys). */
|
|
58
|
+
readonly quantuPrograms?: QuantuProgramIds;
|
|
59
|
+
/** Optional replay cache override (defaults to a fresh in-memory LRU). */
|
|
60
|
+
readonly replayCache?: ReplayCache;
|
|
61
|
+
/** Optional locally-loaded Anchor IDL. When set, the demo skips the
|
|
62
|
+
* on-chain IDL fetch (useful when the program is deployed but the IDL
|
|
63
|
+
* hasn't been published via `anchor idl init`). */
|
|
64
|
+
readonly trustgateIdl?: Idl;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RealPayShDepsBundle {
|
|
68
|
+
readonly deps: PayShDeps;
|
|
69
|
+
readonly connection: Connection;
|
|
70
|
+
readonly trustgate: Program;
|
|
71
|
+
readonly replayCache: ReplayCache;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build a `PayShDeps` bundle backed by real Solana RPC + Anchor.
|
|
76
|
+
*
|
|
77
|
+
* Loads the trustgate IDL from chain (the program must be deployed at
|
|
78
|
+
* `trustgateProgramId` — defaults to devnet). Quantu account resolution is
|
|
79
|
+
* caller-supplied so the demo can plug in static fixtures or a real
|
|
80
|
+
* registry-walk strategy.
|
|
81
|
+
*/
|
|
82
|
+
export async function makeRealPayShDeps(
|
|
83
|
+
args: MakeRealPayShDepsArgs,
|
|
84
|
+
): Promise<RealPayShDepsBundle> {
|
|
85
|
+
const trustgateId = args.trustgateProgramId ?? DEFAULT_DEVNET_PROGRAM_IDS.trustGate;
|
|
86
|
+
|
|
87
|
+
const connection = new Connection(args.rpcUrl, "confirmed");
|
|
88
|
+
const wallet = new Wallet(args.facilitator);
|
|
89
|
+
const provider = new AnchorProvider(connection, wallet, { commitment: "confirmed" });
|
|
90
|
+
const trustgate = args.trustgateIdl
|
|
91
|
+
? new Program(args.trustgateIdl, provider)
|
|
92
|
+
: await loadTrustGate(provider, trustgateId);
|
|
93
|
+
|
|
94
|
+
const validateOnChainTx = makeValidateOnChainTx({ connection });
|
|
95
|
+
const quantuPrograms = args.quantuPrograms ?? DEFAULT_DEVNET_QUANTU_IDS;
|
|
96
|
+
const emitFeedbackCpi = makeEmitFeedbackCpi({
|
|
97
|
+
trustgate,
|
|
98
|
+
trustgateId,
|
|
99
|
+
agentRegistryId: quantuPrograms.agentRegistry,
|
|
100
|
+
facilitator: args.facilitator,
|
|
101
|
+
resolveQuantu: args.resolveQuantu,
|
|
102
|
+
});
|
|
103
|
+
const priorEmissionLookup = makePriorEmissionLookup({
|
|
104
|
+
trustgate,
|
|
105
|
+
trustgateId,
|
|
106
|
+
connection,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const replayCache = args.replayCache ?? new ReplayCache();
|
|
110
|
+
|
|
111
|
+
const deps: PayShDeps = {
|
|
112
|
+
signingNetwork: args.signingNetwork,
|
|
113
|
+
feePayer: args.facilitator.publicKey,
|
|
114
|
+
validateOnChainTx,
|
|
115
|
+
emitFeedbackCpi,
|
|
116
|
+
priorEmissionLookup,
|
|
117
|
+
replayCache,
|
|
118
|
+
signDecision: (bytes: Uint8Array) =>
|
|
119
|
+
signEnvelope(bytes, args.facilitator.secretKey),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return { deps, connection, trustgate, replayCache };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Convenience: build a static Quantu resolver from a Map<payeeAgentB58, accounts>.
|
|
127
|
+
* Use this when the demo runs against a small fixed counterparty set.
|
|
128
|
+
*/
|
|
129
|
+
export function staticQuantuResolver(
|
|
130
|
+
table: ReadonlyMap<string, { asset: PublicKey; collection: PublicKey; atomEnabled?: boolean }>,
|
|
131
|
+
programs: QuantuProgramIds = DEFAULT_DEVNET_QUANTU_IDS,
|
|
132
|
+
): (payeeAgent: PublicKey) => Promise<QuantuFeedbackAccounts> {
|
|
133
|
+
return async (payeeAgent) => {
|
|
134
|
+
const entry = table.get(payeeAgent.toBase58());
|
|
135
|
+
if (!entry) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`staticQuantuResolver: no entry for payeeAgent=${payeeAgent.toBase58()}. ` +
|
|
138
|
+
`Add it to the demo's Quantu account table.`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return deriveQuantuFeedbackAccounts({
|
|
142
|
+
programs,
|
|
143
|
+
asset: entry.asset,
|
|
144
|
+
collection: entry.collection,
|
|
145
|
+
atomEnabled: entry.atomEnabled ?? false,
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { DEFAULT_DEVNET_QUANTU_IDS, MAINNET_QUANTU_IDS };
|