@agenttrust-sdk/mcp 0.2.6 → 0.3.1
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-smoke.json +4 -4
- package/dist/embedded-docs/architecture.mdx +174 -0
- package/dist/embedded-docs/getting-started/quickstart.mdx +79 -56
- package/dist/embedded-docs/index.mdx +54 -37
- package/dist/embedded-docs/integration-guides/capability-namespaces.mdx +135 -8
- package/dist/embedded-docs/integration-guides/custom-attestor.mdx +169 -8
- package/dist/embedded-docs/integration-guides/dexter-adapter.mdx +76 -0
- package/dist/embedded-docs/integration-guides/facilitator-adapters.mdx +85 -41
- package/dist/embedded-docs/integration-guides/pay-sh-adapter.mdx +90 -54
- package/dist/embedded-docs/integration-guides/x402-facilitator.mdx +55 -24
- package/dist/embedded-docs/mcp/hosted-endpoint.mdx +197 -0
- package/dist/embedded-docs/mcp/index.mdx +108 -0
- package/dist/embedded-docs/mcp/install.mdx +183 -0
- package/dist/embedded-docs/mcp/prompts.mdx +90 -0
- package/dist/embedded-docs/mcp/resources.mdx +115 -0
- package/dist/embedded-docs/mcp/tools.mdx +156 -0
- package/dist/embedded-docs/programs/policy-vault/composer.mdx +117 -0
- package/dist/embedded-docs/programs/policy-vault/counterparty-tier-policy.mdx +81 -9
- package/dist/embedded-docs/programs/policy-vault/index.mdx +77 -47
- package/dist/embedded-docs/programs/policy-vault/kill-switch-policy.mdx +65 -8
- package/dist/embedded-docs/programs/policy-vault/require-validation-policy.mdx +76 -8
- package/dist/embedded-docs/programs/policy-vault/spending-policy.mdx +83 -8
- package/dist/embedded-docs/programs/policy-vault/velocity-policy.mdx +85 -8
- package/dist/embedded-docs/programs/trustgate.mdx +112 -30
- package/dist/embedded-docs/programs/validation-registry.mdx +139 -32
- package/dist/embedded-docs/reference/byte-offset-reference.mdx +102 -13
- package/dist/embedded-docs/reference/capability-namespaces.mdx +56 -0
- package/dist/embedded-docs/reference/changelog.mdx +230 -13
- package/dist/embedded-docs/reference/deny-reason-codes.mdx +86 -0
- package/dist/embedded-docs/reference/devnet-program-ids.mdx +50 -8
- package/dist/embedded-docs/reference/discriminator-constants.mdx +104 -10
- package/dist/embedded-docs/reference/mainnet-program-ids.mdx +89 -5
- package/dist/embedded-docs/reference/quantu-agent-registry.mdx +104 -9
- package/dist/embedded-docs/sdk/exports-reference.mdx +239 -0
- package/dist/embedded-docs/sdk/gate-payment.mdx +99 -14
- package/dist/embedded-docs/sdk/index.mdx +141 -40
- package/dist/embedded-docs/sdk/mount-trustgate.mdx +178 -8
- package/dist/embedded-docs/verification/adversarial-harness.mdx +88 -0
- package/dist/embedded-docs/verification/atomic-tx-invariant.mdx +141 -0
- package/dist/embedded-docs/verification/chained-validation.mdx +87 -0
- package/dist/embedded-docs/verification/devnet-smoke.mdx +85 -0
- package/dist/embedded-docs/verification/index.mdx +31 -0
- package/dist/embedded-docs/verification/kani-proofs.mdx +144 -0
- package/dist/embedded-docs/verification/live-evidence.mdx +180 -0
- package/dist/tools/write/emit-feedback.d.ts +6 -0
- package/dist/tools/write/emit-feedback.js +12 -1
- package/dist/tools/write/emit-feedback.js.map +1 -1
- package/package.json +16 -15
- package/scripts/install-claude-desktop.sh +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Install
|
|
3
|
+
description: Wire @agenttrust-sdk/mcp into Claude Desktop, Cursor, or any MCP-capable client — stdio or hosted HTTP.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Three install paths: Claude Desktop (one command), Cursor, or any generic stdio MCP client. Plus the always-on hosted HTTP endpoint at [`mcp.agenttrust.tech`](https://mcp.agenttrust.tech) for cloud agents.
|
|
7
|
+
|
|
8
|
+
## Claude Desktop (recommended)
|
|
9
|
+
|
|
10
|
+
Add to your config — `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"mcpServers": {
|
|
15
|
+
"agenttrust": {
|
|
16
|
+
"command": "npx",
|
|
17
|
+
"args": ["-y", "@agenttrust-sdk/mcp"],
|
|
18
|
+
"env": {
|
|
19
|
+
"RPC_URL": "https://api.devnet.solana.com",
|
|
20
|
+
"NETWORK": "solana-devnet"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Restart Claude Desktop. Eighteen tools are now available in chat. No clone, no local build.
|
|
28
|
+
|
|
29
|
+
### Local clone (for development)
|
|
30
|
+
|
|
31
|
+
If you want to iterate on the MCP server's source, swap the `command`/`args`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["/absolute/path/to/agenttrust/mcp/dist/index.js"]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or run the helper that wires the local path automatically:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mcp/scripts/install-claude-desktop.sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The script edits the Claude Desktop config in place. It backs up the prior config to `claude_desktop_config.json.bak.<timestamp>` so you can revert.
|
|
45
|
+
|
|
46
|
+
## Cursor
|
|
47
|
+
|
|
48
|
+
Cursor's MCP config lives at `~/.cursor/mcp.json` (or per-workspace `.cursor/mcp.json`). Same shape as Claude Desktop:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"agenttrust": {
|
|
54
|
+
"command": "npx",
|
|
55
|
+
"args": ["-y", "@agenttrust-sdk/mcp"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Generic stdio MCP client
|
|
62
|
+
|
|
63
|
+
The package ships a binary entry point. Once installed:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm add @agenttrust-sdk/mcp
|
|
67
|
+
node ./node_modules/@agenttrust-sdk/mcp/dist/index.js # stdio transport, default
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The server speaks MCP over stdin/stdout. Any compliant MCP client attaches by spawning this command.
|
|
71
|
+
|
|
72
|
+
## Hosted HTTP endpoint
|
|
73
|
+
|
|
74
|
+
The public hosted MCP HTTP endpoint is **already live**:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
https://mcp.agenttrust.tech
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Health check:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
curl https://mcp.agenttrust.tech/healthz
|
|
84
|
+
# → {"ok":true,"service":"agenttrust-mcp","version":"0.2.6","network":"solana-devnet","toolCount":18,…}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Hosted on Fly.io (Singapore region, shared-cpu-1x@256MB, always-on with auto-resume on idle). Use this URL in any MCP client that speaks `StreamableHTTPServerTransport` — no local install required. Full hosted-endpoint reference: [Hosted endpoint](/mcp/hosted-endpoint).
|
|
88
|
+
|
|
89
|
+
To run your own HTTP transport locally:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
MCP_TRANSPORT=http MCP_HTTP_PORT=8765 node ./mcp/dist/index.js
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Behind any reverse proxy (Caddy, nginx, Vercel, Fly.io) this surfaces as a public hosted endpoint.
|
|
96
|
+
|
|
97
|
+
## Write tools — adding a keypair
|
|
98
|
+
|
|
99
|
+
The five write tools (`agenttrust_init_policy`, `agenttrust_set_killswitch`, `agenttrust_request_validation`, `agenttrust_respond_to_validation`, `agenttrust_emit_feedback`) require a signing keypair. Add `KEYPAIR_B58` to the `env` block:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"agenttrust": {
|
|
105
|
+
"command": "npx",
|
|
106
|
+
"args": ["-y", "@agenttrust-sdk/mcp"],
|
|
107
|
+
"env": {
|
|
108
|
+
"RPC_URL": "https://api.devnet.solana.com",
|
|
109
|
+
"NETWORK": "solana-devnet",
|
|
110
|
+
"KEYPAIR_B58": "<base58-encoded 64-byte secret key>"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Without `KEYPAIR_B58`, write tools surface a clear `signer required` error. Read and discovery tools never need it.
|
|
118
|
+
|
|
119
|
+
Convert a Solana CLI keypair to base58:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
solana-keygen recover --output-format json -k ~/.config/solana/id.json | jq -r '.privateKey' \
|
|
123
|
+
| python3 -c "import sys, base58, json; print(base58.b58encode(bytes(json.loads(sys.stdin.read()))).decode())"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Or use the `bs58` CLI:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cat ~/.config/solana/id.json | jq -r '.[0:64]' | npx bs58 encode
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Environment variables
|
|
133
|
+
|
|
134
|
+
| Var | Default | Effect |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `RPC_URL` | devnet RPC | Solana RPC endpoint |
|
|
137
|
+
| `NETWORK` | `solana-devnet` | `solana-devnet` or `solana-mainnet`. Drives Quantu program IDs. |
|
|
138
|
+
| `KEYPAIR_B58` | unset | Base58-encoded 64-byte secret key. Required for write tools. |
|
|
139
|
+
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http` |
|
|
140
|
+
| `MCP_HTTP_PORT` | `8765` | Port for HTTP transport |
|
|
141
|
+
| `POLICY_VAULT_PROGRAM_ID` | devnet ID | Override `policy_vault` program ID |
|
|
142
|
+
| `TRUSTGATE_PROGRAM_ID` | devnet ID | Override `trustgate` program ID |
|
|
143
|
+
| `VALIDATION_REGISTRY_PROGRAM_ID` | devnet ID | Override `validation_registry` program ID |
|
|
144
|
+
| `MCP_DEFAULT_FACILITATOR` | unset | Default facilitator name in tool replies |
|
|
145
|
+
| `MCP_DOCS_DIR` | repo `docs-site/content/docs` | Override the docs corpus root (tests) |
|
|
146
|
+
| `PAY_SH_DEMO_STATE_FILE` | bundled demo state | Override the demo state file |
|
|
147
|
+
|
|
148
|
+
## Build + test from source
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git clone https://github.com/agenttrust-labs/agenttrust && cd agenttrust
|
|
152
|
+
pnpm install
|
|
153
|
+
pnpm --filter ./trustgate/sdk run build # MCP depends on the SDK build output
|
|
154
|
+
pnpm --filter ./mcp run build
|
|
155
|
+
pnpm --filter ./mcp test # 76 unit tests, no chain access
|
|
156
|
+
INTEGRATION=1 pnpm --filter ./mcp test:integration # devnet round-trip
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## IDL fetch (verify on-chain truth)
|
|
160
|
+
|
|
161
|
+
All three Anchor IDLs are published on devnet:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
anchor idl fetch 8Y6fGeNEHgmWmbt8JsRcF72jxbeBfJhomMjG6SuoJQTR --provider.cluster devnet # policy_vault
|
|
165
|
+
anchor idl fetch HF8zHfoyA7b5mhLViopTnRMprc6ZT5KActHTdkFrih2N --provider.cluster devnet # trustgate
|
|
166
|
+
anchor idl fetch Cx4RFa6ysw3qXYhugPkF8pFSWBkmKq59h2dWgF2tKhtv --provider.cluster devnet # validation_registry
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The MCP server bundles snapshots at `mcp/src/idl/*.json` as a defensive fallback (saves an RPC round-trip on cold start; keeps the server bootable in offline / air-gapped harnesses). Latest evidence snapshot: [`docs/proofs/idl-on-chain.json`](https://github.com/agenttrust-labs/agenttrust/blob/main/docs/proofs/idl-on-chain.json).
|
|
170
|
+
|
|
171
|
+
## Read next
|
|
172
|
+
|
|
173
|
+
<Cards>
|
|
174
|
+
<Card title="Tools" href="/mcp/tools">
|
|
175
|
+
Eighteen tools with input/output shape.
|
|
176
|
+
</Card>
|
|
177
|
+
<Card title="Hosted endpoint" href="/mcp/hosted-endpoint">
|
|
178
|
+
HTTP transport semantics, sessions, healthz schema.
|
|
179
|
+
</Card>
|
|
180
|
+
<Card title="Prompts" href="/mcp/prompts">
|
|
181
|
+
Three guided workflows for LLM clients.
|
|
182
|
+
</Card>
|
|
183
|
+
</Cards>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prompts
|
|
3
|
+
description: Three guided MCP workflows — audit a payment, set up an agent, explain a failure. Each composes multiple tool calls into a structured procedure.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
MCP prompts are reusable, parameterised templates that the server returns in response to `prompts/get`. The client (Claude Desktop, Cursor, OpenAI Agents SDK) presents the prompt's messages to the model, which then drives the tool calls. Prompts encode the right *order* of tool calls for common workflows.
|
|
7
|
+
|
|
8
|
+
Source: [`mcp/src/prompts/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/prompts).
|
|
9
|
+
|
|
10
|
+
## Three prompts
|
|
11
|
+
|
|
12
|
+
| Name | What it walks the user through |
|
|
13
|
+
|---|---|
|
|
14
|
+
| [`agenttrust_audit_payment`](#agenttrust_audit_payment) | Simulate a payment, read the policy, read the payee's reputation, surface the decision |
|
|
15
|
+
| [`agenttrust_setup_agent`](#agenttrust_setup_agent) | Bootstrap an agent's `PolicyAuthority` → `KillSwitchState` → first `PolicyAccount` |
|
|
16
|
+
| [`agenttrust_explain_failure`](#agenttrust_explain_failure) | Given a failed payment's reason code, explain root cause + remediation |
|
|
17
|
+
|
|
18
|
+
## `agenttrust_audit_payment`
|
|
19
|
+
|
|
20
|
+
Required args: `payer_agent`, `payee_agent`, `amount`, `mint`, `policy_id`. Optional: `caller`.
|
|
21
|
+
|
|
22
|
+
The prompt walks the model through:
|
|
23
|
+
|
|
24
|
+
1. `agenttrust_get_policy({ agent_asset: payer_agent, policy_id })` — read the spending caps, velocity threshold, counterparty tier requirement, required capability.
|
|
25
|
+
2. `agenttrust_get_quantu_reputation({ agent_asset: payee_agent })` — read the payee's tier, risk, confidence.
|
|
26
|
+
3. `agenttrust_simulate_payment({ caller, payer_agent, payee_agent, amount, mint, policy_id })` — run the gate.
|
|
27
|
+
4. Compare the simulation result to the policy thresholds + payee reputation. Surface `Allow`, `Deny(reason)`, or `RequireValidation(capability)` with the relevant numbers.
|
|
28
|
+
|
|
29
|
+
Use case: "Audit this payment before I send it." The model produces a structured explanation of the decision rather than a yes/no answer.
|
|
30
|
+
|
|
31
|
+
## `agenttrust_setup_agent`
|
|
32
|
+
|
|
33
|
+
Required args: `agent_asset`, `use_case`. The `use_case` is free-text — the prompt uses it to suggest reasonable defaults for the policy fields (per-tx cap, velocity window, counterparty tier minimum).
|
|
34
|
+
|
|
35
|
+
The prompt walks the model through:
|
|
36
|
+
|
|
37
|
+
1. `agenttrust_get_killswitch({ agent_asset })` — check whether `PolicyAuthority` and `KillSwitchState` exist.
|
|
38
|
+
2. If absent, instruct the model to call `init_authority` (off-band, since v1 doesn't expose this as an MCP write tool).
|
|
39
|
+
3. `agenttrust_init_policy({ agent_asset, policy_id: 1, enabled_kinds_bitmask: 0b11111, … })` — initialise the first policy with use-case-tailored defaults.
|
|
40
|
+
4. Confirm the result by calling `agenttrust_get_policy`.
|
|
41
|
+
|
|
42
|
+
Use case: "I just got my Quantu agent identity. Set up a basic AgentTrust policy." The prompt + the write tools land a working policy in three or four turns.
|
|
43
|
+
|
|
44
|
+
## `agenttrust_explain_failure`
|
|
45
|
+
|
|
46
|
+
Required args: `reason_code` (1..15). Optional: `payer_agent`, `payee_agent`, `policy_id`.
|
|
47
|
+
|
|
48
|
+
The prompt walks the model through:
|
|
49
|
+
|
|
50
|
+
1. `agenttrust_explain_decision({ reason_code })` — translate to canonical name + remediation hint.
|
|
51
|
+
2. If the optional context is present, run the relevant lookup (`get_quantu_reputation`, `get_velocity`, `get_killswitch`, `get_validation_attestation`) to inspect the actual on-chain state that drove the deny.
|
|
52
|
+
3. Surface the contradiction in plain language ("the policy requires `min_counterparty_tier = 3` but the payee's `tier_immediate = 1`").
|
|
53
|
+
|
|
54
|
+
Use case: "Why did my last payment fail with reason code 6?" The prompt produces an explanation grounded in real on-chain state, not training-data guesses.
|
|
55
|
+
|
|
56
|
+
## How clients invoke prompts
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// Pseudocode — the actual API depends on the MCP client.
|
|
60
|
+
const prompts = await mcp.request("prompts/list");
|
|
61
|
+
// → [{ name: "agenttrust_audit_payment", description, arguments }, …]
|
|
62
|
+
|
|
63
|
+
const audit = await mcp.request("prompts/get", {
|
|
64
|
+
name: "agenttrust_audit_payment",
|
|
65
|
+
arguments: {
|
|
66
|
+
payer_agent: "5Pfa…K8y",
|
|
67
|
+
payee_agent: "C9pY…B3dR",
|
|
68
|
+
amount: "1000000",
|
|
69
|
+
mint: "EPjF…Dt1v",
|
|
70
|
+
policy_id: 1,
|
|
71
|
+
caller: "4tSE…hRG",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
// → { messages: [{ role: "user", content: { type: "text", text: "Walk me through auditing this payment…" } }, … ] }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The model then picks tools to call, the client surfaces the tool results back to the model, and the prompt's structure produces a grounded answer.
|
|
78
|
+
|
|
79
|
+
In Claude Desktop, prompts appear as slash commands when an MCP server is connected. Type `/agenttrust_audit_payment` in chat → the UI prompts for the required arguments → the model executes the workflow.
|
|
80
|
+
|
|
81
|
+
## Validation
|
|
82
|
+
|
|
83
|
+
Phase M validated all three prompts via direct `prompts/get` calls. All three return non-empty `messages` arrays with structured user prompts referencing the right tools. Missing-argument probes return clean `-32603 missing required argument` errors. Full report: [`docs/proofs/phase-m-mcp-e2e.md`](https://github.com/agenttrust-labs/agenttrust/blob/main/docs/proofs/phase-m-mcp-e2e.md) §M1.5.
|
|
84
|
+
|
|
85
|
+
Phase P verified `agenttrust_explain_failure` end-to-end with Claude `sonnet` driving the workflow against live devnet state — the model called `agenttrust_explain_decision(6)` then `agenttrust_get_killswitch` to inspect the actual policy fields, and produced a remediation in plain language.
|
|
86
|
+
|
|
87
|
+
## Source
|
|
88
|
+
|
|
89
|
+
- Prompts: [`mcp/src/prompts/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/prompts)
|
|
90
|
+
- Server wiring: [`mcp/src/server.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/mcp/src/server.ts)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resources
|
|
3
|
+
description: Four MCP resource URIs — devnet program manifest, docs corpus mirror, demo source files. Path-traversal-safe, MIME-typed, MCP-list-friendly.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The MCP server exposes four `resources/list`-able URIs. Resources are read-only by design; writes happen through tools.
|
|
7
|
+
|
|
8
|
+
Source: [`mcp/src/resources/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/resources).
|
|
9
|
+
|
|
10
|
+
## URI manifest
|
|
11
|
+
|
|
12
|
+
| URI scheme | Mime type | Content |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `agenttrust://devnet/programs` | `application/json` | Deployed program IDs + Explorer URLs for the active cluster |
|
|
15
|
+
| `agenttrust://docs/<rel-path>` | `text/markdown` | Each MDX page in the docs corpus exposed individually |
|
|
16
|
+
| `agenttrust://examples/pay-sh-demo/<rel-path>` | `text/x-typescript` / `text/markdown` | Pay.sh demo source files |
|
|
17
|
+
| `agenttrust://examples/attestor-demo/<rel-path>` | `text/x-typescript` / `text/markdown` | Attestor demo source files |
|
|
18
|
+
|
|
19
|
+
## Devnet program manifest
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"uri": "agenttrust://devnet/programs",
|
|
24
|
+
"mimeType": "application/json"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"network": "solana-devnet",
|
|
33
|
+
"programs": {
|
|
34
|
+
"policy_vault": {
|
|
35
|
+
"id": "8Y6fGeNEHgmWmbt8JsRcF72jxbeBfJhomMjG6SuoJQTR",
|
|
36
|
+
"explorerUrl": "https://explorer.solana.com/address/8Y6f…QTR?cluster=devnet"
|
|
37
|
+
},
|
|
38
|
+
"trustgate": {
|
|
39
|
+
"id": "HF8zHfoyA7b5mhLViopTnRMprc6ZT5KActHTdkFrih2N",
|
|
40
|
+
"explorerUrl": "https://explorer.solana.com/address/HF8z…ih2N?cluster=devnet"
|
|
41
|
+
},
|
|
42
|
+
"validation_registry": {
|
|
43
|
+
"id": "Cx4RFa6ysw3qXYhugPkF8pFSWBkmKq59h2dWgF2tKhtv",
|
|
44
|
+
"explorerUrl": "https://explorer.solana.com/address/Cx4R…Khtv?cluster=devnet"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Docs corpus mirror
|
|
51
|
+
|
|
52
|
+
Every MDX page under [`docs-site/content/docs/`](https://github.com/agenttrust-labs/agenttrust/tree/main/docs-site/content/docs) is exposed as an individual resource. Example URIs:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
agenttrust://docs/index
|
|
56
|
+
agenttrust://docs/architecture
|
|
57
|
+
agenttrust://docs/programs/policy-vault/index
|
|
58
|
+
agenttrust://docs/sdk/atomic-tx-invariant
|
|
59
|
+
agenttrust://docs/verification/live-evidence
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The resource body is the rendered markdown text (frontmatter included). LLM clients use `agenttrust_docs` (a discovery tool with full-text search) to find the right resource, then fetch it via `resources/read`.
|
|
63
|
+
|
|
64
|
+
## Demo source mirrors
|
|
65
|
+
|
|
66
|
+
The Pay.sh demo and the attestor demo are bundled in the npm tarball as of 0.2.3. URIs:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
agenttrust://examples/pay-sh-demo/README.md
|
|
70
|
+
agenttrust://examples/pay-sh-demo/src/index.ts
|
|
71
|
+
agenttrust://examples/pay-sh-demo/src/middleware.ts
|
|
72
|
+
agenttrust://examples/pay-sh-demo/devnet-counterparties.json
|
|
73
|
+
|
|
74
|
+
agenttrust://examples/attestor-demo/README.md
|
|
75
|
+
agenttrust://examples/attestor-demo/scripts/devnet-chained-validation.ts
|
|
76
|
+
agenttrust://examples/attestor-demo/devnet-namespaces.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use case: an LLM asks "show me the Pay.sh demo source" and the tool returns the actual file contents rather than synthesizing a stale paraphrase from training data.
|
|
80
|
+
|
|
81
|
+
## Path-traversal safety
|
|
82
|
+
|
|
83
|
+
All resource URIs go through a path-normalization step before file reads. Probes like `agenttrust://docs/../../etc/passwd` return:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"error": {
|
|
88
|
+
"code": -32603,
|
|
89
|
+
"message": "unknown resource URI"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Same protection on the `examples/` URIs. Source: [`mcp/src/resources/docs.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/mcp/src/resources/docs.ts) — the indexer normalises every path against the corpus root before serving bytes.
|
|
95
|
+
|
|
96
|
+
Phase M validated this against a deliberate traversal probe — clean error, no escape. Full report: [`docs/proofs/phase-m-mcp-e2e.md`](https://github.com/agenttrust-labs/agenttrust/blob/main/docs/proofs/phase-m-mcp-e2e.md) §M1.4.
|
|
97
|
+
|
|
98
|
+
## How clients use resources
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// Pseudocode — actual API depends on the MCP client.
|
|
102
|
+
const list = await mcp.request("resources/list");
|
|
103
|
+
// → [{ uri: "agenttrust://devnet/programs", … }, { uri: "agenttrust://docs/index", … }, …]
|
|
104
|
+
|
|
105
|
+
const programs = await mcp.request("resources/read", { uri: "agenttrust://devnet/programs" });
|
|
106
|
+
// → { contents: [{ uri, mimeType: "application/json", text: "{ …JSON… }" }] }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Claude Desktop and Cursor surface resources in their model-context UI — the user can attach a resource to the conversation and the LLM reads it directly, no tool round-trip needed.
|
|
110
|
+
|
|
111
|
+
## Source
|
|
112
|
+
|
|
113
|
+
- Resource handlers: [`mcp/src/resources/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/resources)
|
|
114
|
+
- Server wiring: [`mcp/src/server.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/mcp/src/server.ts)
|
|
115
|
+
- Path-traversal tests: [`mcp/test/resources/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/test/resources)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tools
|
|
3
|
+
description: All 18 MCP tools — 10 read, 5 write, 3 discovery — with input/output shape and live devnet examples.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Each tool has a stable Zod schema. Input arguments are documented per tool below. Every write tool surfaces the resulting `txSignature` plus a Solana Explorer URL in its response.
|
|
7
|
+
|
|
8
|
+
Source: [`mcp/src/tools/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/tools).
|
|
9
|
+
|
|
10
|
+
## Read (10 tools — no signer required)
|
|
11
|
+
|
|
12
|
+
| Tool | Input | Returns |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| [`agenttrust_get_policy`](#agenttrust_get_policy) | `agent_asset`, `policy_id` | Decoded `PolicyAccount` PDA — every spending cap, velocity threshold, counterparty tier requirement, required capability hash |
|
|
15
|
+
| [`agenttrust_list_policies`](#agenttrust_list_policies) | `agent_asset` | Lightweight summary of all policies registered for an agent |
|
|
16
|
+
| [`agenttrust_simulate_payment`](#agenttrust_simulate_payment) | `caller`, `payer_agent`, `payee_agent`, `amount`, `mint`, `policy_id` | `Allow` / `Deny(reasonCode, reasonName)` / `RequireValidation(capabilityHash)` |
|
|
17
|
+
| [`agenttrust_get_killswitch`](#agenttrust_get_killswitch) | `agent_asset` | `KillSwitchState` + `PolicyAuthority` decoded |
|
|
18
|
+
| [`agenttrust_get_velocity`](#agenttrust_get_velocity) | `agent_asset`, `policy_id` | `VelocityLedger` — sliding-window cumulative spend |
|
|
19
|
+
| [`agenttrust_get_feedback_log`](#agenttrust_get_feedback_log) | `payment_id_hash` (32-byte hex) | `FeedbackEmissionLog` PDA |
|
|
20
|
+
| [`agenttrust_get_quantu_reputation`](#agenttrust_get_quantu_reputation) | `agent_asset` | Quantu `AtomStats` decoded — `tierImmediate`, `tierConfirmed`, `riskScore`, `confidence`, `schemaVersion` |
|
|
21
|
+
| [`agenttrust_get_validation_attestation`](#agenttrust_get_validation_attestation) | `subject_asset`, `capability_name` OR `capability_hash`, `attestor` | Every `ValidationAttestation` PDA matching the filter |
|
|
22
|
+
| [`agenttrust_list_facilitators`](#agenttrust_list_facilitators) | — | Active facilitator adapters (Pay.sh / Dexter / atxp / MCPay) + ship status |
|
|
23
|
+
| [`agenttrust_demo_state`](#agenttrust_demo_state) | — | Three pre-warmed devnet counterparties used by `examples/pay-sh-demo` |
|
|
24
|
+
|
|
25
|
+
### `agenttrust_get_policy`
|
|
26
|
+
|
|
27
|
+
Decodes the `PolicyAccount` PDA at `["policy", agent_asset, policy_id_le]`. Returns every byte: `enabled_kinds_bitmask`, `gate_mode`, all spending fields, all velocity fields, `min_counterparty_tier`, `max_risk_score`, `min_confidence`, `default_unrated_treatment`, `required_capability_hash` (hex), `accepted_attestors[]`, `scope_kind`. Non-existent policies return `exists: false`.
|
|
28
|
+
|
|
29
|
+
### `agenttrust_list_policies`
|
|
30
|
+
|
|
31
|
+
Lightweight summary across `policy_id ∈ {1..10}` for the given agent. Use `get_policy` for the full decode of a specific policy.
|
|
32
|
+
|
|
33
|
+
### `agenttrust_simulate_payment`
|
|
34
|
+
|
|
35
|
+
Read-only `gate_payment` simulation. Same semantics as the SDK's [`gatePayment()`](/sdk/gate-payment) — invokes the lazy variant (returns decision via Anchor's return-data channel), parses the response into the `GateDecision` union.
|
|
36
|
+
|
|
37
|
+
`caller` is required; pass any funded base58 pubkey. The simulation tx isn't committed, but Solana requires a fee-payer keypair. As of 0.2.1 the tool surfaces a clear actionable error if `caller` is omitted (was a cryptic `AccountNotFound` before).
|
|
38
|
+
|
|
39
|
+
### `agenttrust_get_killswitch`
|
|
40
|
+
|
|
41
|
+
Returns the `KillSwitchState` for the agent's per-agent kill switch (scope_kind = 2, scope_key = agent_asset) plus the `PolicyAuthority` (multisig members + threshold). Useful when debugging "why is this agent paused?".
|
|
42
|
+
|
|
43
|
+
### `agenttrust_get_velocity`
|
|
44
|
+
|
|
45
|
+
Decodes the `VelocityLedger` PDA at `["velocity", agent_asset, policy_id_le]`. Returns `cumulative_amount`, `last_commit_slot`, `window_start_slot`. The window-active vs window-expired check (`elapsed >= window_slots`) is a pure-fn evaluation — match the policy's `velocity_window_secs` × `tier_decay(payer_tier) × 2 slots/sec`.
|
|
46
|
+
|
|
47
|
+
### `agenttrust_get_feedback_log`
|
|
48
|
+
|
|
49
|
+
`FeedbackEmissionLog` lookup by `payment_id_hash`. The PDA is at `["feedback_log", payment_id_hash]`. Returns `score`, `is_dispute`, `emitted_at_slot` if found, or `exists: false`.
|
|
50
|
+
|
|
51
|
+
### `agenttrust_get_quantu_reputation`
|
|
52
|
+
|
|
53
|
+
Decodes Quantu's `AtomStats` at `["atom_stats", agent_asset]`. Returns:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"pda": "4z9RiK6B49QZbmqPM9yNZWgfxYD3tvQ3NETU6X89f5mv",
|
|
58
|
+
"ownerProgram": "AToMufS4QD6hEXvcvBDg9m1AHeCLpmZQsyfYa5h9MwAF",
|
|
59
|
+
"ownerMatches": true,
|
|
60
|
+
"rawByteLen": 561,
|
|
61
|
+
"reputation": {
|
|
62
|
+
"tierImmediate": 3,
|
|
63
|
+
"tierConfirmed": 2,
|
|
64
|
+
"riskScore": 42,
|
|
65
|
+
"confidence": 8500,
|
|
66
|
+
"schemaVersion": 1
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Mirrors the canonical byte offsets from [`programs/policy-vault/src/ext/atom_engine.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/policy-vault/src/ext/atom_engine.rs) verbatim — 549 / 551 / 555 / 557 / 560. Fixed in MCP 0.2.6 (Phase Q1) after the schema-version canary at byte 560 + the `tier ≤ 4` range check were added; v0.2.5 had fabricated offsets that returned junk values.
|
|
72
|
+
|
|
73
|
+
### `agenttrust_get_validation_attestation`
|
|
74
|
+
|
|
75
|
+
Returns every `ValidationAttestation` PDA matching `(subject_asset, capability_name OR capability_hash)`, optionally filtered by `attestor`. Accepts the friendly capability name (the SDK computes SHA-256 internally) or the 64-char hex hash. Added in 0.2.4 — real LLMs typically have the human-readable name; requiring the digest was a friction point.
|
|
76
|
+
|
|
77
|
+
### `agenttrust_list_facilitators`
|
|
78
|
+
|
|
79
|
+
Returns the active adapter set: Pay.sh (live), Dexter (in-flight), atxp (roadmap), MCPay (roadmap). Each entry includes the adapter's wire format hint and ship status.
|
|
80
|
+
|
|
81
|
+
### `agenttrust_demo_state`
|
|
82
|
+
|
|
83
|
+
Returns the three pre-warmed devnet counterparties used by `examples/pay-sh-demo`:
|
|
84
|
+
|
|
85
|
+
| Tier | Asset pubkey | Expected gate decision |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 0 (untrusted) | `C9pYqwnCVpwg7MwEbQa4XcmVVYsUcPwqHMYs999KB3dR` | `Deny(CounterpartyTierBelowMin)` |
|
|
88
|
+
| 1 (low-trust) | `9894Sh7F79yDzTi4Pvfm5Jy5VmLpx2XkyhS14BFwpyrd` | `Deny(CounterpartyTierBelowMin)` |
|
|
89
|
+
| 3 (Gold) | `5PfaofvEUf3adtJwMho7zzbfvgxwxbvp2V5moqhtLK8y` | `Allow` (with a tier-3 policy) |
|
|
90
|
+
|
|
91
|
+
The demo state is bundled in the tarball as of 0.2.3 — the published package doesn't need a separate state file.
|
|
92
|
+
|
|
93
|
+
## Write (5 tools — require `KEYPAIR_B58`)
|
|
94
|
+
|
|
95
|
+
| Tool | Effect |
|
|
96
|
+
|---|---|
|
|
97
|
+
| [`agenttrust_init_policy`](#agenttrust_init_policy) | Create `PolicyAccount` + `VelocityLedger` for the signer's agent |
|
|
98
|
+
| [`agenttrust_set_killswitch`](#agenttrust_set_killswitch) | Pause / unpause the agent's `KillSwitchState` (lead-only multisig in v1) |
|
|
99
|
+
| [`agenttrust_request_validation`](#agenttrust_request_validation) | Open a `ValidationRequest` PDA |
|
|
100
|
+
| [`agenttrust_respond_to_validation`](#agenttrust_respond_to_validation) | Attestor writes a `ValidationAttestation` PDA |
|
|
101
|
+
| [`agenttrust_emit_feedback`](#agenttrust_emit_feedback) | Facilitator-only `emit_feedback` CPI (signer must equal facilitator) |
|
|
102
|
+
|
|
103
|
+
### `agenttrust_init_policy`
|
|
104
|
+
|
|
105
|
+
Required args: `agent_asset`, `policy_id`, `enabled_kinds_bitmask` (e.g., `0b11111` = all five kinds). Optional: every `PolicyAccount` field — defaults are documented in [`programs/policy-vault/src/instructions/init_policy.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/policy-vault/src/instructions/init_policy.rs).
|
|
106
|
+
|
|
107
|
+
### `agenttrust_set_killswitch`
|
|
108
|
+
|
|
109
|
+
Required args: `agent_asset`, `paused`. Multisig-gated against `PolicyAuthority` per [KillSwitch policy](/programs/policy-vault/kill-switch-policy). v1 uses single-signer (lead-only) for hackathon-velocity reasons; v1.1+ exercises the full Kani-proven multi-signer path.
|
|
110
|
+
|
|
111
|
+
### `agenttrust_request_validation`
|
|
112
|
+
|
|
113
|
+
Required args: `subject_asset`, `claim_uri_hash_hex`, `deadline_slot`. The capability is implied by the active namespace context. Subject's owner OR any third party can open the request; off-chain attestors discover via the `RequestCreated` event.
|
|
114
|
+
|
|
115
|
+
### `agenttrust_respond_to_validation`
|
|
116
|
+
|
|
117
|
+
Required args: `subject_asset`, `claim_payload_hash_hex`, `claim_uri_hash_hex`, `expires_at_slot`. The signer (the keypair behind `KEYPAIR_B58`) is the attestor. v1 trust model: tx signature authenticates; v1.1+ adds Ed25519 sysvar verify.
|
|
118
|
+
|
|
119
|
+
### `agenttrust_emit_feedback`
|
|
120
|
+
|
|
121
|
+
Required args: `payment_id_hash_hex`, `payee_asset`, `base_collection`, `score`. The signer must equal the facilitator (`FacilitatorSignerMismatch` otherwise). `base_collection` is the value passed to Quantu's `register_agent` — the agent-registry-8004 collection address.
|
|
122
|
+
|
|
123
|
+
## Discovery (3 tools)
|
|
124
|
+
|
|
125
|
+
| Tool | Returns |
|
|
126
|
+
|---|---|
|
|
127
|
+
| [`agenttrust_docs`](#agenttrust_docs) | Full-text search over `docs-site/content/docs/` — ranked hits with excerpts |
|
|
128
|
+
| [`agenttrust_facilitator_walkthrough`](#agenttrust_facilitator_walkthrough) | Per-adapter integration walkthrough by name |
|
|
129
|
+
| [`agenttrust_explain_decision`](#agenttrust_explain_decision) | Translate a `DenyReason` code (1..15) into the enum name + remediation hint |
|
|
130
|
+
|
|
131
|
+
### `agenttrust_docs`
|
|
132
|
+
|
|
133
|
+
Searches the bundled docs corpus. Returns ranked hits with excerpts. Use case: an LLM asks "what's the atomic-tx invariant?" and the tool surfaces the verification page with a relevant excerpt rather than hallucinating from training data.
|
|
134
|
+
|
|
135
|
+
The corpus is bundled in the tarball as of 0.2.3. `MCP_DOCS_DIR` env var lets tests point at a different root.
|
|
136
|
+
|
|
137
|
+
### `agenttrust_facilitator_walkthrough`
|
|
138
|
+
|
|
139
|
+
Returns the canonical guide for a named facilitator (`pay-sh`, `dexter`, `atxp`, `mcpay`, or `x402`). Falls back to the generic adapters guide for unknown names. Use case: an LLM asks "walk me through adding a new facilitator" and the tool returns the contract page.
|
|
140
|
+
|
|
141
|
+
### `agenttrust_explain_decision`
|
|
142
|
+
|
|
143
|
+
Maps a `DenyReason` code (`1..15`) to the canonical name + remediation hint. Same data the [Reference → DenyReason codes](/reference/deny-reason-codes) page surfaces, but tool-shaped for LLM consumption.
|
|
144
|
+
|
|
145
|
+
## Validation status
|
|
146
|
+
|
|
147
|
+
Phase M comprehensive E2E (2026-05-07): all 18 tools present, 10/10 read tools return live devnet state with clickable Explorer URLs, 6/6 PDAs cross-validated against on-chain ground truth, 4/4 Explorer URLs return HTTP 200. Full report: [`docs/proofs/phase-m-mcp-e2e.md`](https://github.com/agenttrust-labs/agenttrust/blob/main/docs/proofs/phase-m-mcp-e2e.md).
|
|
148
|
+
|
|
149
|
+
Phase P real-LLM tool-routing (2026-05-08): 7/10 strict pass on natural-language scenarios via Claude `sonnet`. The three false negatives were context-gathering artefacts (the LLM called `agenttrust_demo_state` first to gather context, then the expected tool); a less agentic client would score 9/10. Full report: [`docs/proofs/phase-p-llm-routing.md`](https://github.com/agenttrust-labs/agenttrust/blob/main/docs/proofs/phase-p-llm-routing.md).
|
|
150
|
+
|
|
151
|
+
## Source
|
|
152
|
+
|
|
153
|
+
- Read tools: [`mcp/src/tools/read/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/tools/read)
|
|
154
|
+
- Write tools: [`mcp/src/tools/write/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/tools/write)
|
|
155
|
+
- Discovery tools: [`mcp/src/tools/discovery/`](https://github.com/agenttrust-labs/agenttrust/tree/main/mcp/src/tools/discovery)
|
|
156
|
+
- Tool aggregator: [`mcp/src/tools/index.ts`](https://github.com/agenttrust-labs/agenttrust/blob/main/mcp/src/tools/index.ts)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Composer
|
|
3
|
+
description: The pure-Rust orchestration that ties the five policy kinds together with fail-fast semantics and Allow-only state mutation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`compose_decision` is the single function that runs every policy and returns the gate's verdict. The Anchor handlers (`gate_payment`, `gate_payment_strict`) are thin wrappers around it.
|
|
7
|
+
|
|
8
|
+
Source: [`programs/policy-vault/src/policies/composer.rs`](https://github.com/agenttrust-labs/agenttrust/blob/main/programs/policy-vault/src/policies/composer.rs).
|
|
9
|
+
|
|
10
|
+
## Signature
|
|
11
|
+
|
|
12
|
+
```rust
|
|
13
|
+
pub fn compose_decision(input: ComposerInput) -> ComposerResult;
|
|
14
|
+
|
|
15
|
+
pub struct ComposerInput {
|
|
16
|
+
pub policy: PolicySnapshot,
|
|
17
|
+
pub ledger: VelocityLedgerSnapshot,
|
|
18
|
+
pub killswitch: KillSwitchSnapshot,
|
|
19
|
+
pub payer_atom: Option<AtomStatsView>,
|
|
20
|
+
pub payee_atom: Option<AtomStatsView>,
|
|
21
|
+
pub attestation: Option<ValidationAttestationView>,
|
|
22
|
+
pub amount: u64,
|
|
23
|
+
pub payee_agent_asset: Pubkey,
|
|
24
|
+
pub now_slot: u64,
|
|
25
|
+
pub unix_ts: i64,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub struct ComposerResult {
|
|
29
|
+
pub decision: GateDecision,
|
|
30
|
+
pub spending_deltas: Option<SpendingDeltas>,
|
|
31
|
+
pub velocity_deltas: Option<VelocityDeltas>,
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The Anchor wrapper feeds the composer plain-data snapshots, never on-chain account references. That separation is what makes every policy unit-testable in plain Rust and Kani-provable without an Anchor harness.
|
|
36
|
+
|
|
37
|
+
## Fail-fast order
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
KillSwitch → Spending → Velocity → CounterpartyTier → RequireValidation
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Cheapest reads first. KillSwitch is one bool. Spending is pure arithmetic. Velocity reads one local PDA. CounterpartyTier reads two foreign `AtomStats` PDAs. RequireValidation reads one foreign `ValidationAttestation` PDA. The first policy that returns `Deny` short-circuits the rest — no foreign-PDA reads beyond the deciding policy.
|
|
44
|
+
|
|
45
|
+
## State mutation rule
|
|
46
|
+
|
|
47
|
+
The composer **never** mutates state. It returns *deltas* — `SpendingDeltas`, `VelocityDeltas` — that the Anchor handler applies to `PolicyAccount` and `VelocityLedger` only on the `Allow` branch.
|
|
48
|
+
|
|
49
|
+
```rust
|
|
50
|
+
match result.decision {
|
|
51
|
+
GateDecision::Allow => {
|
|
52
|
+
if let Some(d) = result.spending_deltas {
|
|
53
|
+
spending::apply_deltas(&mut ctx.accounts.policy_account, &d);
|
|
54
|
+
}
|
|
55
|
+
if let Some(d) = result.velocity_deltas {
|
|
56
|
+
velocity::apply_deltas(&mut ctx.accounts.velocity_ledger, &d);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
GateDecision::Deny(_) | GateDecision::RequireValidation(_) => {
|
|
60
|
+
// Mutate nothing.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This rule is what `velocity_counter_le_limit` (Kani #2) is inductive over: if the pre-state ledger satisfies `cumulative_amount ≤ max_in_window` and the policy returns `Allow(deltas)`, then the post-state ledger also satisfies the bound. A fresh ledger trivially satisfies the base case.
|
|
66
|
+
|
|
67
|
+
## Strict variant
|
|
68
|
+
|
|
69
|
+
`gate_payment_strict` converts non-`Allow` into `Err`:
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
pub fn gate_payment_strict(ctx: Context<GatePaymentStrict>, …) -> Result<()> {
|
|
73
|
+
let result = compose_decision(input);
|
|
74
|
+
match result.decision {
|
|
75
|
+
GateDecision::Allow => {
|
|
76
|
+
// Apply deltas, return Ok.
|
|
77
|
+
Ok(())
|
|
78
|
+
}
|
|
79
|
+
GateDecision::Deny(reason) => Err(reason.into()),
|
|
80
|
+
GateDecision::RequireValidation(_) => Err(ErrorCode::ValidationRequired.into()),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The SDK's `composeAtomicSettleTx` calls `gate_payment_strict`. Any `Deny` on the gate fails the entire bundled transaction (`gate + transfer + emit_feedback`), which means the SPL transfer never runs and `FeedbackEmissionLog` is never created. Solana's transaction atomicity does the rest.
|
|
86
|
+
|
|
87
|
+
The Phase J5 Kani proof `gate_payment_strict_correctness` pins this contract end-to-end:
|
|
88
|
+
|
|
89
|
+
- `strict_returns_ok_iff_allow` — strict `Ok(())` ⇔ composer `Allow`.
|
|
90
|
+
- `gate_decision_is_one_of_three_disjoint_variants` — the three `GateDecision` arms are pairwise disjoint, so a fourth variant cannot silently slip past the strict dispatch.
|
|
91
|
+
|
|
92
|
+
Together: 258 sub-checks, ~0.9 s. The strict-correctness invariant is the on-chain anchor of the SDK's atomic-tx invariant.
|
|
93
|
+
|
|
94
|
+
## Why pure-Rust
|
|
95
|
+
|
|
96
|
+
Three reasons:
|
|
97
|
+
|
|
98
|
+
1. **Kani provability.** `compose_decision` is a deterministic function over plain types. Kani's bounded model checker explores every reachable state inside the bounds in seconds. An Anchor handler with account validation, CPI, and rent calculations would not be tractable.
|
|
99
|
+
2. **Unit-testability.** The same function works in `cargo test` without an Anchor program-test harness. Every policy module ships with a dense unit-test suite (Spending: 14 tests, Velocity: 14, CounterpartyTier: 13, RequireValidation: 13, KillSwitch: 2 — plus 113 module-level tests across PolicyVault).
|
|
100
|
+
3. **Wrapper churn isolation.** Account-validation, IDL changes, Anchor-version bumps live in the wrapper. Decision logic doesn't move when wrappers do.
|
|
101
|
+
|
|
102
|
+
## Read next
|
|
103
|
+
|
|
104
|
+
<Cards>
|
|
105
|
+
<Card title="KillSwitch policy" href="/programs/policy-vault/kill-switch-policy">
|
|
106
|
+
First in the composer; one bool; multisig-gated mutation.
|
|
107
|
+
</Card>
|
|
108
|
+
<Card title="Spending policy" href="/programs/policy-vault/spending-policy">
|
|
109
|
+
Per-tx, daily (UTC), and weekly (ISO Monday) limits with anchor-rollover math.
|
|
110
|
+
</Card>
|
|
111
|
+
<Card title="Velocity policy" href="/programs/policy-vault/velocity-policy">
|
|
112
|
+
Sliding-window cumulative-spend counter with payer-tier-decayed window size.
|
|
113
|
+
</Card>
|
|
114
|
+
<Card title="Atomic-tx invariant" href="/verification/atomic-tx-invariant">
|
|
115
|
+
The strict variant is the on-chain half of the three-layer atomicity guard.
|
|
116
|
+
</Card>
|
|
117
|
+
</Cards>
|