twzrd-x402-gate 0.1.1 → 0.5.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 (54) hide show
  1. package/README.md +279 -47
  2. package/dist/auto-gate.d.ts +48 -0
  3. package/dist/auto-gate.d.ts.map +1 -0
  4. package/dist/auto-gate.js +34 -0
  5. package/dist/auto-gate.js.map +1 -0
  6. package/dist/config.d.ts +6 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +32 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/evaluate.d.ts +77 -0
  11. package/dist/evaluate.d.ts.map +1 -0
  12. package/dist/evaluate.js +118 -0
  13. package/dist/evaluate.js.map +1 -0
  14. package/dist/gate.d.ts +13 -0
  15. package/dist/gate.d.ts.map +1 -1
  16. package/dist/gate.js +22 -0
  17. package/dist/gate.js.map +1 -1
  18. package/dist/index.d.ts +8 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +11 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/mcp-hook.d.ts.map +1 -1
  23. package/dist/mcp-hook.js +27 -18
  24. package/dist/mcp-hook.js.map +1 -1
  25. package/dist/merchant-card.d.ts +52 -0
  26. package/dist/merchant-card.d.ts.map +1 -0
  27. package/dist/merchant-card.js +69 -0
  28. package/dist/merchant-card.js.map +1 -0
  29. package/dist/payto.d.ts +6 -0
  30. package/dist/payto.d.ts.map +1 -1
  31. package/dist/payto.js +17 -0
  32. package/dist/payto.js.map +1 -1
  33. package/dist/policy.d.ts +1 -1
  34. package/dist/policy.d.ts.map +1 -1
  35. package/dist/policy.js +89 -7
  36. package/dist/policy.js.map +1 -1
  37. package/dist/quick.d.ts +48 -0
  38. package/dist/quick.d.ts.map +1 -0
  39. package/dist/quick.js +49 -0
  40. package/dist/quick.js.map +1 -0
  41. package/dist/sponsored.d.ts +51 -0
  42. package/dist/sponsored.d.ts.map +1 -0
  43. package/dist/sponsored.js +55 -0
  44. package/dist/sponsored.js.map +1 -0
  45. package/dist/types.d.ts +54 -0
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/with-guard.d.ts +23 -0
  48. package/dist/with-guard.d.ts.map +1 -0
  49. package/dist/with-guard.js +76 -0
  50. package/dist/with-guard.js.map +1 -0
  51. package/dist/wrap-fetch.d.ts.map +1 -1
  52. package/dist/wrap-fetch.js +5 -3
  53. package/dist/wrap-fetch.js.map +1 -1
  54. package/package.json +7 -11
package/README.md CHANGED
@@ -1,17 +1,43 @@
1
1
  # twzrd-x402-gate
2
2
 
3
- Buyer-side x402 **trust gate**. Run a free [TWZRD preflight](https://intel.twzrd.xyz) (`ReadinessCard`)
4
- **before** signing USDC to any x402 merchant. Wraps `fetch` (HTTP 402) and the `@x402/mcp`
5
- `onPaymentRequested` hook. Fail-open by default so an unreachable preflight never hard-blocks a payment.
3
+ Agent-side x402 **firewall**. TWZRD sits in the path before any 402 payment leaves your
4
+ agent's wallet — free preflight + free merchant_card wash refuse before USDC moves.
6
5
 
7
- This is the independent **pre-spend** layer — it does not settle, route, or hold funds. It works where
8
- your code receives an exposed 402 (e.g. direct `@x402/fetch` or raw merchant calls).
6
+ ```typescript
7
+ import { installTwzrdAutoGate } from "twzrd-x402-gate";
8
+ import { wrapFetchWithPayment } from "@x402/svm";
9
9
 
10
- **ClawRouter / `@blockrun/clawrouter` note:** The local :8402 proxy signs internally and returns 200 (no
11
- outer 402 is visible). Use the pre-proxy hook from the `twzrd-clawrouter` skill (explicit call before the
12
- proxy) or an upstream `onBeforePayment` if ClawRouter exposes one. Same internalization applies to
13
- AgentCash's `fetch` (it handles 402 internally) — verify before assuming a wrapper sees it. The MCP
14
- `onPaymentRequested` hook covers clients that expose a payment callback.
10
+ const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
11
+ // Every 402 gated BEFORE your client signs → free preflight + free merchant_card wash check → throws on block.
12
+ const response = await payingFetch("https://api.exa.ai/search");
13
+ ```
14
+
15
+ ## Buyer flow (trustless, fail-open) — locked sequence
16
+
17
+ Canonical path for every agent that spends USDC on Solana x402:
18
+
19
+ | Step | Call | Cost | What gates pay |
20
+ |------|------|------|----------------|
21
+ | 1 | `POST /v1/intel/preflight` | free | `decision=block` → refuse (`twzrd_decision_block`). Score floor / optional `can_spend` also deny. |
22
+ | 2 | `GET /v1/intel/merchant_card/{payTo}` | free | `wash_flagged: true` → refuse by default (`twzrd_wash_flagged`). **Only tightens** step 1. |
23
+ | 3 | Optional paid trust | $0.05 / $0.001 | On `warn` or high-value: `GET /v1/intel/trust/{payTo}` or `quickCheck`. Never required for the free refuse path. |
24
+ | 4 | Pay (or refuse) | resource price | Only if steps 1–2 approved (and any opt-in paid escalate did not block). |
25
+
26
+ **Fail-open (no invent):**
27
+ - Preflight HTTP/network error → default fail-closed in gate 0.2+ (`TWZRD_FAIL_OPEN=true` restores legacy allow-on-outage).
28
+ - Merchant card unreachable / non-2xx / missing `wash_flagged` → `washFlagged=null` → **do not refuse on wash** (preflight decision stands).
29
+ - Only a successful card with `wash_flagged: true` triggers wash refuse or soft cap.
30
+
31
+ **Wash policy (exact):**
32
+ - Prior preflight deny → unchanged (wash never loosens a block).
33
+ - `refuseWashFlagged=false` or wash not true → keep preflight approval.
34
+ - `wash_flagged=true` + no cap → `approved=false`, `reason=twzrd_wash_flagged`, `verdict=block`.
35
+ - `wash_flagged=true` + `washMaxUsdc` set + `priceUsdc <= cap` → allow, `washCapped=true`, reason `twzrd_wash_capped_{price}_le_{cap}`.
36
+ - `wash_flagged=true` + price above cap (or price unknown) → refuse with `twzrd_wash_flagged_above_cap_*`.
37
+
38
+ **Order note:** `onWarnUpsell` (points at paid `/trust`) fires on preflight `warn` **before** the merchant_card wash check. A wash-flagged seller that preflighted as `warn` may still get the upsell hook, then be refused on step 2.
39
+
40
+ Dogfood (live, free only): `npm run wash-dogfood` → [`examples/wash-refuse-dogfood.ts`](./examples/wash-refuse-dogfood.ts).
15
41
 
16
42
  ## Install
17
43
 
@@ -19,34 +45,206 @@ AgentCash's `fetch` (it handles 402 internally) — verify before assuming a wra
19
45
  npm install twzrd-x402-gate
20
46
  ```
21
47
 
22
- ## Usage
48
+ ## Quickstart: `installTwzrdAutoGate` (default-on)
23
49
 
24
- ### Wrap any fetch that may receive a 402
50
+ `installTwzrdAutoGate` is the one-liner form of "guard the raw fetch, then hand it to your
51
+ x402 client." It takes a `payWrap` function — whatever composes your paying client on top of
52
+ a fetch — and returns a fetch that's already gated: a blocked seller throws before your client
53
+ ever gets a chance to sign.
25
54
 
26
- ```ts
27
- import { wrapFetchWithTwzrdGate, resolveConfig } from "twzrd-x402-gate";
55
+ ```typescript
56
+ import { installTwzrdAutoGate } from "twzrd-x402-gate";
57
+ import { wrapFetchWithPayment } from "@x402/svm";
28
58
 
29
- const gatedFetch = wrapFetchWithTwzrdGate(fetch, resolveConfig());
59
+ const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
30
60
 
31
- // On HTTP 402, the gate reads payTo from the x402 `accepts[0]`, runs preflight,
32
- // and THROWS if policy denies. On allow it returns the original 402 so your
33
- // x402 client attaches payment and retries as usual.
34
- const res = await gatedFetch("https://merchant.example/paid");
61
+ // Use payingFetch everywhere you'd call a paid resource:
62
+ const response = await payingFetch("https://api.exa.ai/search");
35
63
  ```
36
64
 
37
- ### As the @x402/mcp payment hook
65
+ `payWrap` receives the **guarded** fetch (the guard has already run by the time your client
66
+ sees a 402) — this is the only correct composition order. Building it the other way round
67
+ (guarding an already-paying fetch) is a no-op; see [Compatibility note](#compatibility-note).
68
+ Any x402 client that composes over an underlying `fetch` works the same way — swap in
69
+ whatever `payWrap` your client's API expects (agentcash, ClawRouter, PayAI, a custom
70
+ `@x402/svm` scheme, etc.).
38
71
 
39
- ```ts
40
- import { defaultGate } from "twzrd-x402-gate";
72
+ Default **ON**. Disable with `TWZRD_AUTO_GATE=0` (env, deploy-time kill switch) or
73
+ `{ disabled: true }` (per-call, e.g. in tests) — the raw fetch is handed straight to
74
+ `payWrap`, unguarded.
41
75
 
42
- const client = createX402MCPClient({
43
- onPaymentRequested: defaultGate.onPaymentRequested, // returns false to deny
76
+ What happens on every HTTP 402 the raw fetch returns:
77
+ 1. Reads the Solana-network entry from `accepts[]` (falls back to first entry) to get the seller wallet.
78
+ 2. Calls `POST /v1/intel/preflight` — free, no auth. `decision=block` (or score floor) throws — `payWrap`'s client never signs.
79
+ 3. Calls `GET /v1/intel/merchant_card/{payTo}` — free, no auth. `wash_flagged: true` refuses by default (only tightens step 2; fail-open if the card is unreachable — no invent).
80
+ 4. Otherwise returns the 402 to `payWrap`'s client, which pays normally.
81
+
82
+ Non-402 responses pass through unchanged.
83
+
84
+ ### Lower-level: `withTwzrdGuard`
85
+
86
+ `installTwzrdAutoGate` is built on `withTwzrdGuard` — the fetch wrapper itself, if you want to
87
+ manage the raw/paying composition yourself:
88
+
89
+ ```typescript
90
+ import { withTwzrdGuard } from "twzrd-x402-gate";
91
+ import { wrapFetchWithPayment } from "@x402/svm";
92
+
93
+ const raw = globalThis.fetch; // MUST still surface HTTP 402
94
+ const guarded = withTwzrdGuard(raw); // guard sits upstream
95
+ const safeFetch = wrapFetchWithPayment(guarded, buyerWallet);
96
+
97
+ const response = await safeFetch("https://api.exa.ai/search");
98
+ ```
99
+
100
+ What the guard does on HTTP 402:
101
+ 1. Reads the Solana-network entry from `accepts[]` (falls back to first entry) to get the seller wallet.
102
+ 2. Free `POST /v1/intel/preflight` — `decision=block` / score floor deny.
103
+ 3. Free `GET /v1/intel/merchant_card/{payTo}` — `wash_flagged:true` **refuses by default**
104
+ (`reason: twzrd_wash_flagged`). Fail-open if the card is unreachable (no invent).
105
+ 4. If approved: returns the original 402 for the x402 client to pay.
106
+
107
+ Opt out of wash refuse: `withTwzrdGuard(fetch, { refuseWashFlagged: false })` or
108
+ `TWZRD_REFUSE_WASH_FLAGGED=0`. Soft cap instead of hard refuse: `washMaxUsdc` /
109
+ `TWZRD_WASH_MAX_USDC`.
110
+
111
+ Non-402 responses pass through unchanged.
112
+
113
+ ### Auto-receipt on warn (revenue path)
114
+
115
+ ```typescript
116
+ const safeFetch = withTwzrdGuard(x402Fetch, {
117
+ autoReceipt: true, // on warn or allow, auto-buy the $0.05 TWZRD trust receipt
118
+ x402Fetch, // the paying fetch — TWZRD earns the fee on-chain
119
+ onReceipt: (receipt, tx) => {
120
+ // receipt is a twzrd_receipt (V6 + ERC-8004 reputation_credential)
121
+ console.log("Trust receipt captured:", tx);
122
+ },
44
123
  });
45
124
  ```
46
125
 
47
- ### Direct decision (no network wiring)
126
+ `autoReceipt` is **off by default** — it spends the **buyer's** USDC, so you opt in. When on,
127
+ every warn/allow verdict settles $0.05 USDC to TWZRD and returns a signed V6 trust credential
128
+ for the counterparty before you pay the resource.
129
+
130
+ **`x402Fetch` is yours to supply** (this package is dependency-free). Wire the proven
131
+ `@x402/svm` sponsored-feePayer client — the same one `twzrd-mcp-server` uses:
132
+
133
+ ```typescript
134
+ import { wrapFetchWithPayment } from "@x402/svm";
135
+ const x402Fetch = wrapFetchWithPayment(fetch, buyerWallet); // settles 402 challenges
136
+ ```
137
+
138
+ Gate it behind your own ROI policy (e.g. only auto-buy the receipt for payments above a
139
+ threshold). Runnable, no-spend demo: [`examples/auto-receipt.ts`](./examples/auto-receipt.ts)
140
+ (`npm run autoreceipt-demo`). A bundled/sponsored `x402Fetch` (so integrators need no wallet)
141
+ is the next step.
142
+
143
+ ### Quick tier ($0.001) — cheap paid qualify
144
+
145
+ The reputation ladder has three rungs: **free** preflight (`allow/warn/block`), **$0.001**
146
+ `quickCheck` (tier + score, no receipt), **$0.05** `autoReceipt` (full intel + signed V6
147
+ receipt). When the free preflight is inconclusive (`warn` / unknown seller) and you want a
148
+ cheap *paid* confirmation before committing — without paying 50× for the portable receipt —
149
+ use `quickCheck`:
150
+
151
+ ```typescript
152
+ import { quickCheck } from "twzrd-x402-gate";
153
+
154
+ const q = await quickCheck(sellerWallet, { x402Fetch }); // settles $0.001 to /v1/intel/quick
155
+ if (q.available && (q.tier === "Gold" || q.tier === "Platinum")) {
156
+ // tier is high enough — proceed with the larger payment
157
+ }
158
+ ```
159
+
160
+ `quickCheck` is **fail-soft** — it never throws; any gap (no `x402Fetch`, unreachable, settle
161
+ failure) returns `available: false`, so a quick-tier hiccup can't break your flow. The hard
162
+ allow/warn/block decision stays the free preflight's job.
163
+
164
+ ### Autonomous risk-escalation — `escalateOnWarn` (pay-to-confirm on warn)
165
+
166
+ The free preflight leaves an unknown/uncertain seller at `warn`, which **proceeds** by
167
+ default. `escalateOnWarn` closes the loop autonomously: on a proceeding `warn`, the guard
168
+ settles the cheap **$0.001** quick tier and **re-decides on the paid score** — below the
169
+ floor the payment is **blocked**, at/above it proceeds. The paid call fires from your
170
+ agent's own risk policy (no human), and the paid signal actually gates the spend (unlike
171
+ `autoReceipt`, which is upsell-only and never changes the decision).
48
172
 
49
173
  ```ts
174
+ const safeFetch = withTwzrdGuard(x402Fetch, {
175
+ escalateOnWarn: {
176
+ minSpendUsdc: 0.01, // don't pay $0.001 to vet a sub-cent buy
177
+ blockBelowScore: 40, // block when the paid quick score is below this (default: preflightMinScore)
178
+ },
179
+ x402Fetch, // settles the $0.001 quick charge
180
+ });
181
+ // warn + paid score < 40 -> throws "[twzrd-guard] payment blocked: twzrd_escalated_warn_block ..."
182
+ // warn + paid score >= 40 -> proceeds (result.escalated=true, result.escalatedScore set)
183
+ ```
184
+
185
+ Opt-in, **fail-soft** (if the quick tier can't answer, the base `warn` is preserved), and it
186
+ **only tightens** — a `warn` may become a block, but an `allow` or `block` is never changed.
187
+ This is the autonomous demand loop: an uncertain counterparty is vetted with real paid intel,
188
+ automatically, before your agent commits.
189
+
190
+ ### Sponsored payer — use the paid rungs with no wallet (prototype)
191
+
192
+ `createSponsoredX402Fetch` lets a **sponsor** settle the paid rungs on the agent's behalf, so
193
+ an integrator can call `quickCheck` / `autoReceipt` with **no wallet of their own**:
194
+
195
+ ```typescript
196
+ import { createSponsoredX402Fetch, quickCheck } from "twzrd-x402-gate";
197
+
198
+ // `settle` = the funded backend (your @x402/svm fetch, or a TWZRD treasury sponsor endpoint).
199
+ const x402Fetch = createSponsoredX402Fetch({ settle });
200
+ const q = await quickCheck(seller, { x402Fetch }); // sponsor pays — caller holds no wallet
201
+ ```
202
+
203
+ Two backends plug into `settle`: **gas-sponsored** (live via `@x402/svm` — agent pays USDC, the
204
+ resource server's `feePayer` covers SOL gas, the model `twzrd-mcp-server` uses) and
205
+ **full-sponsor** (a TWZRD treasury endpoint pays on the agent's behalf — the true no-wallet
206
+ path). The full-sponsor endpoint + treasury is **founder-gated** (who funds it + per-agent
207
+ budget caps); this ships the client seam + a dry-run so the wiring is ready.
208
+ No-spend demo: [`examples/sponsored-payer.ts`](./examples/sponsored-payer.ts) (`npm run sponsored-demo`).
209
+
210
+ ## `evaluate_x402_resource` — standalone preflight
211
+
212
+ Use when you already have the `paymentRequirements` object from a parsed 402 body:
213
+
214
+ ```typescript
215
+ import { evaluate_x402_resource } from "twzrd-x402-gate";
216
+
217
+ const result = await evaluate_x402_resource(
218
+ "https://api.exa.ai/search",
219
+ paymentRequirements, // X402PaymentRequirements from the 402 body
220
+ );
221
+
222
+ console.log(result.decision); // "allow" | "warn" | "block"
223
+ console.log(result.trustScore); // number | null
224
+ console.log(result.approved); // boolean
225
+ console.log(result.receiptUrl); // "https://intel.twzrd.xyz/v1/intel/trust/<payTo>"
226
+
227
+ if (!result.approved) throw new Error(`Blocked: ${result.reason}`);
228
+ ```
229
+
230
+ With `autoReceipt`:
231
+
232
+ ```typescript
233
+ const result = await evaluate_x402_resource(url, requirements, {
234
+ autoReceipt: true,
235
+ x402Fetch: myPayingFetch,
236
+ onReceipt: (receipt, tx) => storeCredential(receipt),
237
+ });
238
+ // result.receipt — twzrd_receipt (V6 + ERC-8004 reputation_credential)
239
+ // result.receiptTx — on-chain settlement tx
240
+ // result.receiptFeeCaptured — true when fee landed
241
+ ```
242
+
243
+ ## Lower-level APIs
244
+
245
+ ### Direct approval call
246
+
247
+ ```typescript
50
248
  import { createTwzrdGate } from "twzrd-x402-gate";
51
249
 
52
250
  const gate = createTwzrdGate();
@@ -58,38 +256,72 @@ const { approved, reason, card } = await gate.approvePayment({
58
256
  if (!approved) abort(reason);
59
257
  ```
60
258
 
259
+ ### `@x402/mcp` payment hook
260
+
261
+ ```typescript
262
+ import { defaultGate } from "twzrd-x402-gate";
263
+
264
+ const client = createX402MCPClient({
265
+ onPaymentRequested: defaultGate.onPaymentRequested, // returns false to deny
266
+ });
267
+ ```
268
+
269
+ ### `wrapFetchWithTwzrdGate`
270
+
271
+ ```typescript
272
+ import { wrapFetchWithTwzrdGate, resolveConfig } from "twzrd-x402-gate";
273
+
274
+ // Alternative fetch wrapper — same interception logic, no autoReceipt.
275
+ const gatedFetch = wrapFetchWithTwzrdGate(fetch, resolveConfig());
276
+ ```
277
+
278
+ `withTwzrdGuard` is preferred — it composes with `autoReceipt` and `onReceipt`.
279
+ `wrapFetchWithTwzrdGate` remains for codebases that can't migrate.
280
+
61
281
  ## Policy
62
282
 
63
- A payment is **denied** when any of these hold (mirrors `scripts/twzrd_gate_agentcash_fetch.sh`):
283
+ A payment is **blocked** when:
284
+ 1. `decision ∈ blockDecisions` (default: `["block"]`)
285
+ 2. `trust_score < preflightMinScore` (default: `40`)
286
+ 3. `can_spend === false` — **only** when `gateOnCanSpend: true` (default `false`, opt-in)
287
+
288
+ `warn` is allowed unless overridden. Preflight network failure **fails closed** by default (a preflight outage blocks the payment, so an intel hiccup never silently approves a spend); set `failOpen: true` / `TWZRD_FAIL_OPEN=true` to opt into legacy allow-on-outage.
64
289
 
65
- 1. `decision ∈ blockDecisions` (default: `block`)
66
- 2. `can_spend === false` — **only when `gateOnCanSpend` is true (default)**
67
- 3. `trust_score < preflightMinScore` (default: `40`)
290
+ > **`can_spend` note:** the free preflight returns `can_spend=false` for most sellers
291
+ > not yet in the TWZRD corpus, including legitimate ones. The default is decision-only
292
+ > gating so unknown sellers on platforms like Agentic.Market are not blocked by default.
293
+ > Set `gateOnCanSpend: true` for strict mode.
68
294
 
69
- Otherwise approved (`warn` is allowed with reason `twzrd_warn_allowed`). On preflight HTTP/network
70
- failure the gate **fails open** (approves) unless `failOpen` is disabled.
295
+ ## Config
71
296
 
72
- > **ClawRouter / free-tier note:** the free preflight returns `can_spend=false` for most sellers
73
- > (including well-known ones), so the default policy will deny most unknown ClawRouter/BlockRun
74
- > sellers. To follow the "gate only on `decision=block`" policy documented in the `twzrd-clawrouter`
75
- > skill, set `gateOnCanSpend: false` (or `TWZRD_GATE_ON_CAN_SPEND=false`).
297
+ | Option | Env | Default | Description |
298
+ |---|---|---|---|
299
+ | `intelBase` | `TWZRD_INTEL_BASE` | `https://intel.twzrd.xyz` | Preflight API base |
300
+ | `preflightMinScore` | `TWZRD_PREFLIGHT_MIN_SCORE` | `40` | Block below this score |
301
+ | `blockDecisions` | `TWZRD_BLOCK_DECISIONS` | `block` | Decisions that throw |
302
+ | `failOpen` | `TWZRD_FAIL_OPEN` | `false` | `true` opts into legacy allow-on-outage; default blocks (fail-closed) |
303
+ | `gateOnCanSpend` | `TWZRD_GATE_ON_CAN_SPEND` | `false` | Also block when `can_spend=false` |
304
+ | `autoReceipt` | — | `false` | Auto-buy $0.05 TWZRD receipt on warn/allow |
305
+ | `x402Fetch` | — | — | x402-capable fetch for `autoReceipt` |
306
+ | `onReceipt` | — | — | Callback after receipt is captured |
307
+ | `disabled` (`installTwzrdAutoGate` only) | `TWZRD_AUTO_GATE=0`/`false` | `false` | Bypass the guard entirely — `payWrap` gets the raw, unguarded fetch |
76
308
 
77
- ## Config (overrides or env)
309
+ ## Compatibility note
78
310
 
79
- | Option | Env | Default |
80
- |---|---|---|
81
- | `intelBase` | `TWZRD_INTEL_BASE` | `https://intel.twzrd.xyz` |
82
- | `preflightMinScore` | `TWZRD_PREFLIGHT_MIN_SCORE` | `40` |
83
- | `blockDecisions` | `TWZRD_BLOCK_DECISIONS` | `block` |
84
- | `failOpen` | `TWZRD_FAIL_OPEN` | `true` (`false`/`0` to disable) |
85
- | `gateOnCanSpend` | `TWZRD_GATE_ON_CAN_SPEND` | `true` (`false`/`0` = gate only on `decision`) |
86
- | `fetch` | — | global `fetch` |
311
+ **Proxied x402 clients** (AgentCash's `.fetch`, ClawRouter `:8402`): these clients handle
312
+ 402 internally and return 200. The guard never sees a 402 if it wraps the client's *output* —
313
+ it must wrap the client's *input*. `installTwzrdAutoGate` enforces this composition order by
314
+ construction: it guards the raw fetch first, then hands the guarded fetch to your `payWrap`.
315
+ If you're composing `withTwzrdGuard` manually instead, pass the raw (non-paying) fetch to
316
+ `withTwzrdGuard`, then wrap its output in your x402 client — never the reverse. Or call
317
+ `evaluate_x402_resource` explicitly before routing through the proxy.
87
318
 
88
319
  ## Why pre-spend, not post-pay
89
320
 
90
- `GET /v1/intel/trust/{wallet}` is the **paid** (0.05 USDC) deep-intel surface — it is *not* a gate.
91
- `POST /v1/intel/preflight` is the **free** `ReadinessCard` for the pre-spend decision. This package
92
- only ever calls the free preflight; you decide whether to proceed before any USDC leaves your wallet.
321
+ `GET /v1/intel/trust/{wallet}` is the **paid** ($0.05 USDC) deep-intel surface — not a gate.
322
+ `POST /v1/intel/preflight` is the **free** `ReadinessCard` for the pre-spend decision.
323
+ This package only ever calls the free preflight; you decide whether to proceed before any
324
+ USDC leaves your wallet.
93
325
 
94
326
  ## License
95
327
 
@@ -0,0 +1,48 @@
1
+ import { type TwzrdGuardOptions } from "./with-guard.js";
2
+ /**
3
+ * Takes a guarded (pre-pay-checked) fetch and returns the fetch your agent actually
4
+ * calls — i.e. your x402 client composed on top of the guard (agentcash, ClawRouter,
5
+ * @x402/svm, PayAI, etc.).
6
+ */
7
+ export type PayWrap = (guardedFetch: typeof fetch) => typeof fetch;
8
+ export type InstallAutoGateOptions = TwzrdGuardOptions & {
9
+ /**
10
+ * The RAW (non-paying) fetch to guard. This MUST be a fetch that still surfaces
11
+ * HTTP 402 — do not pass an already-paying client here (see compatibility note
12
+ * in README: proxied clients like AgentCash/.fetch resolve 402 internally and
13
+ * return 200, so the guard would never see the block condition).
14
+ * Default: globalThis.fetch.
15
+ */
16
+ rawFetch?: typeof fetch;
17
+ /**
18
+ * Force-disable the gate (payWrap gets the raw fetch, unguarded). Mirrors
19
+ * TWZRD_AUTO_GATE=0 / "false". Prefer the env var for a deploy-time kill switch;
20
+ * use this for tests.
21
+ */
22
+ disabled?: boolean;
23
+ };
24
+ /**
25
+ * Default-on composition helper: guards the RAW fetch with the free TWZRD preflight,
26
+ * THEN hands the guarded fetch to your payment wrapper. This is the one-liner form of
27
+ * the two-step pattern documented in the README:
28
+ *
29
+ * const raw = globalThis.fetch;
30
+ * const gated = withTwzrdGuard(raw);
31
+ * const paying = payWrap(gated);
32
+ *
33
+ * Composing it this way by construction rules out the common mis-wiring of guarding
34
+ * an already-paying fetch (which returns 200 and never gives the guard a 402 to act on).
35
+ *
36
+ * Default ON — a blocked seller throws before payWrap's client ever signs. Opt out with
37
+ * TWZRD_AUTO_GATE=0 (env) or { disabled: true } (per-call), e.g. for a local dev harness
38
+ * that intentionally wants the unguarded fetch.
39
+ *
40
+ * @example
41
+ * import { installTwzrdAutoGate } from "twzrd-x402-gate";
42
+ * import { wrapFetchWithPayment } from "@x402/svm";
43
+ *
44
+ * const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
45
+ * await payingFetch("https://api.exa.ai/search"); // blocked sellers throw before signing
46
+ */
47
+ export declare function installTwzrdAutoGate(payWrap: PayWrap, options?: InstallAutoGateOptions): typeof fetch;
48
+ //# sourceMappingURL=auto-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-gate.d.ts","sourceRoot":"","sources":["../src/auto-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,YAAY,EAAE,OAAO,KAAK,KAAK,OAAO,KAAK,CAAC;AAEnE,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG;IACvD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,KAAK,CAQd"}
@@ -0,0 +1,34 @@
1
+ import { withTwzrdGuard } from "./with-guard.js";
2
+ /**
3
+ * Default-on composition helper: guards the RAW fetch with the free TWZRD preflight,
4
+ * THEN hands the guarded fetch to your payment wrapper. This is the one-liner form of
5
+ * the two-step pattern documented in the README:
6
+ *
7
+ * const raw = globalThis.fetch;
8
+ * const gated = withTwzrdGuard(raw);
9
+ * const paying = payWrap(gated);
10
+ *
11
+ * Composing it this way by construction rules out the common mis-wiring of guarding
12
+ * an already-paying fetch (which returns 200 and never gives the guard a 402 to act on).
13
+ *
14
+ * Default ON — a blocked seller throws before payWrap's client ever signs. Opt out with
15
+ * TWZRD_AUTO_GATE=0 (env) or { disabled: true } (per-call), e.g. for a local dev harness
16
+ * that intentionally wants the unguarded fetch.
17
+ *
18
+ * @example
19
+ * import { installTwzrdAutoGate } from "twzrd-x402-gate";
20
+ * import { wrapFetchWithPayment } from "@x402/svm";
21
+ *
22
+ * const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
23
+ * await payingFetch("https://api.exa.ai/search"); // blocked sellers throw before signing
24
+ */
25
+ export function installTwzrdAutoGate(payWrap, options) {
26
+ const raw = options?.rawFetch ?? globalThis.fetch;
27
+ const envDisabled = process.env.TWZRD_AUTO_GATE === "0" || process.env.TWZRD_AUTO_GATE === "false";
28
+ const disabled = options?.disabled ?? envDisabled;
29
+ if (disabled)
30
+ return payWrap(raw);
31
+ const guarded = withTwzrdGuard(raw, options);
32
+ return payWrap(guarded);
33
+ }
34
+ //# sourceMappingURL=auto-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-gate.js","sourceRoot":"","sources":["../src/auto-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA0B,MAAM,iBAAiB,CAAC;AA0BzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,OAAgC;IAEhC,MAAM,GAAG,GAAG,OAAO,EAAE,QAAQ,IAAI,UAAU,CAAC,KAAK,CAAC;IAClD,MAAM,WAAW,GACf,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO,CAAC;IACjF,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,WAAW,CAAC;IAClD,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC"}
package/dist/config.d.ts CHANGED
@@ -1,11 +1,16 @@
1
- import type { TwzrdGateConfig } from "./types.js";
1
+ import type { TwzrdGateConfig, TwzrdUpsellContext } from "./types.js";
2
2
  export type ResolvedTwzrdGateConfig = {
3
3
  intelBase: string;
4
4
  preflightMinScore: number;
5
5
  blockDecisions: Set<string>;
6
6
  failOpen: boolean;
7
7
  gateOnCanSpend: boolean;
8
+ /** Default true: refuse when free merchant_card.wash_flagged */
9
+ refuseWashFlagged: boolean;
10
+ /** Soft cap USDC when wash_flagged; null = hard refuse */
11
+ washMaxUsdc: number | null;
8
12
  fetch: typeof fetch;
13
+ onWarnUpsell?: (ctx: TwzrdUpsellContext) => void | Promise<void>;
9
14
  };
10
15
  export declare function resolveConfig(overrides?: TwzrdGateConfig): ResolvedTwzrdGateConfig;
11
16
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,OAAO,KAAK,CAAC;CACrB,CAAC;AAYF,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,uBAAuB,CAuClF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE,CAAC;AAYF,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,uBAAuB,CAkElF"}
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  function parseBlockDecisions(raw) {
2
- const source = raw ?? "block";
2
+ const source = raw?.trim() || "block";
3
3
  return new Set(source
4
4
  .split(",")
5
5
  .map((s) => s.trim())
@@ -9,17 +9,40 @@ export function resolveConfig(overrides) {
9
9
  const intelBase = (overrides?.intelBase ??
10
10
  process.env.TWZRD_INTEL_BASE ??
11
11
  "https://intel.twzrd.xyz").replace(/\/+$/, "");
12
- const preflightMinScore = overrides?.preflightMinScore ??
12
+ const rawMin = overrides?.preflightMinScore ??
13
13
  Number(process.env.TWZRD_PREFLIGHT_MIN_SCORE ?? "40");
14
+ const preflightMinScore = (Number.isFinite(rawMin) && rawMin >= 0) ? rawMin : 40;
14
15
  const blockDecisions = overrides?.blockDecisions != null
15
16
  ? new Set([...overrides.blockDecisions].map((s) => s.trim()).filter(Boolean))
16
17
  : parseBlockDecisions(process.env.TWZRD_BLOCK_DECISIONS);
18
+ // Default false (fail-closed): block and log loudly on preflight outage.
19
+ // Opt in to legacy fail-open with TWZRD_FAIL_OPEN=true or TWZRD_FAIL_OPEN=1.
17
20
  const failOpen = overrides?.failOpen ??
18
- (process.env.TWZRD_FAIL_OPEN !== "false" &&
19
- process.env.TWZRD_FAIL_OPEN !== "0");
21
+ (process.env.TWZRD_FAIL_OPEN === "true" ||
22
+ process.env.TWZRD_FAIL_OPEN === "1");
23
+ // Default false (decision-only): an unknown seller (warn / can_spend=false,
24
+ // which is EVERY not-yet-seen merchant at score 45) is NOT blocked by default —
25
+ // only an explicit decision=block (a real wash/sybil flag) blocks. This matches
26
+ // the sister package @wzrd_sol/plugin-trustgate and the preflight's own
27
+ // warn-not-block intent, and keeps the gate usable for discovery. Opt in to
28
+ // strict can_spend gating with TWZRD_GATE_ON_CAN_SPEND=true or =1.
20
29
  const gateOnCanSpend = overrides?.gateOnCanSpend ??
21
- (process.env.TWZRD_GATE_ON_CAN_SPEND !== "false" &&
22
- process.env.TWZRD_GATE_ON_CAN_SPEND !== "0");
30
+ (process.env.TWZRD_GATE_ON_CAN_SPEND === "true" ||
31
+ process.env.TWZRD_GATE_ON_CAN_SPEND === "1");
32
+ // Default true: free merchant_card.wash_flagged → refuse pay (trustless step 3).
33
+ // Opt out: refuseWashFlagged:false or TWZRD_REFUSE_WASH_FLAGGED=0|false.
34
+ const refuseWashEnv = process.env.TWZRD_REFUSE_WASH_FLAGGED;
35
+ const refuseWashFlagged = overrides?.refuseWashFlagged ??
36
+ !(refuseWashEnv === "0" || refuseWashEnv === "false");
37
+ let washMaxUsdc = null;
38
+ if (overrides?.washMaxUsdc != null && Number.isFinite(overrides.washMaxUsdc)) {
39
+ washMaxUsdc = overrides.washMaxUsdc;
40
+ }
41
+ else if (process.env.TWZRD_WASH_MAX_USDC != null && process.env.TWZRD_WASH_MAX_USDC !== "") {
42
+ const n = Number(process.env.TWZRD_WASH_MAX_USDC);
43
+ if (Number.isFinite(n) && n >= 0)
44
+ washMaxUsdc = n;
45
+ }
23
46
  const fetchFn = overrides?.fetch ?? globalThis.fetch;
24
47
  if (typeof fetchFn !== "function") {
25
48
  throw new Error("[twzrd-x402-gate] fetch is not available; pass config.fetch");
@@ -30,7 +53,10 @@ export function resolveConfig(overrides) {
30
53
  blockDecisions,
31
54
  failOpen,
32
55
  gateOnCanSpend,
56
+ refuseWashFlagged,
57
+ washMaxUsdc,
33
58
  fetch: fetchFn,
59
+ onWarnUpsell: overrides?.onWarnUpsell,
34
60
  };
35
61
  }
36
62
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,SAAS,mBAAmB,CAAC,GAAuB;IAClD,MAAM,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC;IAC9B,OAAO,IAAI,GAAG,CACZ,MAAM;SACH,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CACnB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAA2B;IACvD,MAAM,SAAS,GAAG,CAChB,SAAS,EAAE,SAAS;QACpB,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC5B,yBAAyB,CAC1B,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEtB,MAAM,iBAAiB,GACrB,SAAS,EAAE,iBAAiB;QAC5B,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,IAAI,CAAC,CAAC;IAExD,MAAM,cAAc,GAClB,SAAS,EAAE,cAAc,IAAI,IAAI;QAC/B,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7E,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAE7D,MAAM,QAAQ,GACZ,SAAS,EAAE,QAAQ;QACnB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO;YACtC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC,CAAC;IAEzC,MAAM,cAAc,GAClB,SAAS,EAAE,cAAc;QACzB,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,OAAO;YAC9C,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAG,SAAS,EAAE,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IAED,OAAO;QACL,SAAS;QACT,iBAAiB;QACjB,cAAc;QACd,QAAQ;QACR,cAAc;QACd,KAAK,EAAE,OAAO;KACf,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type TwzrdTier } from "./quick.js";
2
+ import type { TwzrdDecision, TwzrdGateConfig, TwzrdReadinessCard, X402PaymentRequirements } from "./types.js";
3
+ export type EvaluateX402Options = TwzrdGateConfig & {
4
+ /**
5
+ * When true, automatically fetches the paid TWZRD trust receipt (via x402)
6
+ * after a warn/allow decision. Requires x402Fetch to be provided.
7
+ * Default: false.
8
+ */
9
+ autoReceipt?: boolean;
10
+ /**
11
+ * x402-capable fetch that can settle USDC payments. Used by autoReceipt (the
12
+ * $0.05 receipt) and by escalateOnWarn (the $0.001 quick tier). The caller wires
13
+ * in a Solana wallet + x402 payer.
14
+ */
15
+ x402Fetch?: typeof fetch;
16
+ /**
17
+ * Called immediately after a receipt is captured on-chain.
18
+ * Provides the raw twzrd_receipt object and the settlement tx hash (if present).
19
+ */
20
+ onReceipt?: (receipt: unknown, tx: string | undefined) => void;
21
+ /**
22
+ * Autonomous risk-escalation. When the free preflight is inconclusive
23
+ * (decision="warn" and otherwise proceeding), the gate autonomously settles the
24
+ * cheap $0.001 quick tier and RE-DECIDES on the paid score: below `blockBelowScore`
25
+ * (default: preflightMinScore) the payment is denied (approved=false); at/above it
26
+ * proceeds. The paid call fires from the agent's own risk policy - no human - and
27
+ * the paid signal actually gates the spend (unlike autoReceipt, which is upsell-only
28
+ * and never changes the decision). Opt-in; requires x402Fetch. Fail-soft: if the
29
+ * quick tier cannot answer, the base warn decision is preserved. Only tightens
30
+ * (warn -> maybe block); never loosens a block or allow. Short-circuits the
31
+ * autoReceipt path for the warn case (no double settle).
32
+ */
33
+ escalateOnWarn?: {
34
+ /** Skip escalation when the resource price is below this - don't pay $0.001 to vet a sub-cent buy. Default 0. */
35
+ minSpendUsdc?: number;
36
+ /** Deny when the paid quick score is below this. Default: preflightMinScore (40). */
37
+ blockBelowScore?: number;
38
+ };
39
+ };
40
+ export type EvaluateX402Result = {
41
+ decision: TwzrdDecision | "unknown";
42
+ trustScore: number | null;
43
+ approved: boolean;
44
+ reason: string;
45
+ card: TwzrdReadinessCard;
46
+ /** true when the preflight was unreachable and fail-open allowed the resource */
47
+ failOpen?: boolean;
48
+ /** URL of the paid TWZRD trust endpoint for this seller (for manual upsell) */
49
+ receiptUrl?: string;
50
+ /** Present when autoReceipt=true and the x402 trust call succeeded */
51
+ receipt?: unknown;
52
+ /** On-chain settlement tx from the receipt payment */
53
+ receiptTx?: string;
54
+ /** true when a fee was captured on-chain */
55
+ receiptFeeCaptured?: boolean;
56
+ /** true when a `warn` triggered an autonomous paid quick-tier re-decision (escalateOnWarn) */
57
+ escalated?: boolean;
58
+ /** the paid quick-tier score that drove the escalated decision; null when the quick tier could not answer */
59
+ escalatedScore?: number | null;
60
+ /** the paid quick-tier label (Bronze/Silver/Gold/Platinum) from the escalation */
61
+ escalatedTier?: TwzrdTier | null;
62
+ };
63
+ /**
64
+ * Evaluate an x402 resource before the buyer pays:
65
+ * 1. Run free TWZRD preflight on the seller (no auth, no cost).
66
+ * 2. Return decision + trust score.
67
+ * 3. If escalateOnWarn is set and decision=warn: autonomously settle the cheap
68
+ * $0.001 quick tier and re-decide on the paid score (the autonomous risk loop).
69
+ * 4. Else if autoReceipt=true and decision !== block: auto-fetch the paid TWZRD
70
+ * trust receipt via x402Fetch (TWZRD earns the receipt fee on-chain).
71
+ *
72
+ * Defaults to gateOnCanSpend=false (decision-only) — the free-tier preflight
73
+ * returns can_spend=false for most unknown sellers, which would block too eagerly
74
+ * on platforms like Agentic.Market where sellers are not yet in the corpus.
75
+ */
76
+ export declare function evaluate_x402_resource(resourceUrl: string, paymentRequirements: X402PaymentRequirements, opts?: EvaluateX402Options): Promise<EvaluateX402Result>;
77
+ //# sourceMappingURL=evaluate.d.ts.map