twzrd-x402-gate 0.5.4 → 0.6.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.
- package/README.md +91 -1
- package/dist/decision-token.d.ts +80 -0
- package/dist/decision-token.d.ts.map +1 -0
- package/dist/decision-token.js +107 -0
- package/dist/decision-token.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/intent-adapters.d.ts +62 -0
- package/dist/intent-adapters.d.ts.map +1 -0
- package/dist/intent-adapters.js +66 -0
- package/dist/intent-adapters.js.map +1 -0
- package/dist/intent.d.ts +69 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +82 -0
- package/dist/intent.js.map +1 -0
- package/dist/policy-runtime.d.ts +88 -0
- package/dist/policy-runtime.d.ts.map +1 -0
- package/dist/policy-runtime.js +169 -0
- package/dist/policy-runtime.js.map +1 -0
- package/dist/x402-client-hook.d.ts +38 -0
- package/dist/x402-client-hook.d.ts.map +1 -1
- package/dist/x402-client-hook.js +68 -5
- package/dist/x402-client-hook.js.map +1 -1
- package/package.json +18 -8
package/README.md
CHANGED
|
@@ -18,7 +18,11 @@ const client = new x402Client();
|
|
|
18
18
|
client.register("solana:*", new ExactSvmScheme(svmSigner));
|
|
19
19
|
// Optional: client.register("eip155:*", new ExactEvmScheme(evmSigner));
|
|
20
20
|
|
|
21
|
-
installTwzrdX402ClientHook(client, {
|
|
21
|
+
installTwzrdX402ClientHook(client, {
|
|
22
|
+
gateOnCanSpend: false, // decision-only default (warn allowed)
|
|
23
|
+
refuseWashFlagged: true,
|
|
24
|
+
});
|
|
25
|
+
// Strict opt-in: gateOnCanSpend: true — also block when can_spend=false
|
|
22
26
|
// → onBeforePaymentCreation: scores selectedRequirements, abort if policy denies
|
|
23
27
|
|
|
24
28
|
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
|
|
@@ -89,6 +93,92 @@ Dogfood:
|
|
|
89
93
|
npm install twzrd-x402-gate
|
|
90
94
|
```
|
|
91
95
|
|
|
96
|
+
## TWZRD Payment Control (protocol-neutral authorization core)
|
|
97
|
+
|
|
98
|
+
The gate now ships a protocol-neutral policy runtime underneath the x402 surface.
|
|
99
|
+
Defining invariant: **a signer path that calls `assertIntentApproved` will not sign
|
|
100
|
+
an intent that differs from what TWZRD evaluated.** (Honest scope: TWZRD does not
|
|
101
|
+
own third-party wallets - the binding is enforceable exactly where the check runs
|
|
102
|
+
before the signer, not as a claim over arbitrary wallet internals.)
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import {
|
|
106
|
+
evaluateIntent,
|
|
107
|
+
assertIntentApproved,
|
|
108
|
+
createLocalDecisionSigner,
|
|
109
|
+
createDecisionRegistry,
|
|
110
|
+
x402RequirementsToIntent, // or ap2CheckoutToIntent
|
|
111
|
+
} from "twzrd-x402-gate";
|
|
112
|
+
|
|
113
|
+
const signer = createLocalDecisionSigner();
|
|
114
|
+
const registry = createDecisionRegistry(); // consume-once
|
|
115
|
+
|
|
116
|
+
const intent = x402RequirementsToIntent(selectedRequirements, { resourceUrl });
|
|
117
|
+
const token = await evaluateIntent(intent, {
|
|
118
|
+
signer,
|
|
119
|
+
mandate, // user/company mandate (purpose, ceilings, resource scope)
|
|
120
|
+
policy, // local hard controls (caps, lists, recurring checks)
|
|
121
|
+
intelligence: twzrdIntel, // optional remote counterparty intelligence
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Wallet-side, immediately before signing the EXACT intent:
|
|
125
|
+
assertIntentApproved(intentBeingSigned, token, {
|
|
126
|
+
registry, // replay / consume-once
|
|
127
|
+
publicKeyPem: signer.publicKeyPem, // signature verification
|
|
128
|
+
});
|
|
129
|
+
// throws INTENT_HASH_MISMATCH | DECISION_EXPIRED | DECISION_NOT_ALLOW |
|
|
130
|
+
// DECISION_REPLAYED | BAD_SIGNATURE -> the signer is never invoked
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- `PaymentIntent` v1 (frozen): protocol `x402 | ap2 | ucp | mpp | direct` +
|
|
134
|
+
network/asset/amount/payTo + resource + facilitator + mandate + recurrence
|
|
135
|
+
context, bound into one canonical `tiv1:` intent hash.
|
|
136
|
+
- Decisions are signed, expiring `DecisionToken`s - "policy version X approved
|
|
137
|
+
this exact transaction at this timestamp", auditable offline.
|
|
138
|
+
- A **block is a signed decision**, not an exception - refusals audit the same
|
|
139
|
+
way approvals do.
|
|
140
|
+
- Local hard controls (mandate scope, ceilings, allow/blocklists, cumulative
|
|
141
|
+
caps, recurring price checks) never depend on API availability; remote
|
|
142
|
+
intelligence (wash/fleet, counterparty score) plugs in via a provider.
|
|
143
|
+
- The category test lives in
|
|
144
|
+
[`test/payment-control.test.ts`](./test/payment-control.test.ts): a mandate
|
|
145
|
+
permits software under $100, a $12 checkout is approved, orchestration
|
|
146
|
+
mutates `payTo` after approval, the wallet refuses on hash mismatch,
|
|
147
|
+
`signerInvocationCount === 0`, and both the decision and the refusal verify
|
|
148
|
+
from audit records alone.
|
|
149
|
+
|
|
150
|
+
### On the client hook (opt-in)
|
|
151
|
+
|
|
152
|
+
`installTwzrdX402ClientHook` wires the runtime into the official
|
|
153
|
+
`onBeforePaymentCreation` seat. Pass `paymentControl` to build the canonical
|
|
154
|
+
intent, run the policy runtime (with the hook's own preflight fed in as remote
|
|
155
|
+
intelligence), and surface a signed, intent-bound `PaymentDecision`:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { installTwzrdX402ClientHook, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
159
|
+
|
|
160
|
+
const signer = createLocalDecisionSigner();
|
|
161
|
+
installTwzrdX402ClientHook(client, {
|
|
162
|
+
paymentControl: {
|
|
163
|
+
signer,
|
|
164
|
+
mandate, // optional user/company mandate
|
|
165
|
+
policy: { maxAmountUsd: "50" }, // optional local hard controls
|
|
166
|
+
},
|
|
167
|
+
onDecision: ({ intent, decision }) => handOff(intent, decision), // → assertIntentApproved
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- **Tighten-only composition:** a `paymentControl` block aborts even when the
|
|
172
|
+
legacy preflight allowed; it never loosens a legacy denial.
|
|
173
|
+
- **Opt-in:** with `paymentControl` unset the hook behaves exactly as before.
|
|
174
|
+
- x402 wire amounts (USDC micro units) are converted to the decimal USD the
|
|
175
|
+
runtime expects before policy evaluation. (The `x402RequirementsToIntent`
|
|
176
|
+
adapter still passes the raw wire amount through into the decimal `amount`
|
|
177
|
+
field — a latent unit bug fixed at the hook boundary here; the adapter fix is
|
|
178
|
+
a follow-up.)
|
|
179
|
+
- Hook binding test:
|
|
180
|
+
[`test/intent-binding.test.ts`](./test/intent-binding.test.ts).
|
|
181
|
+
|
|
92
182
|
### Experimental CLI: `twzrd-safe-fetch` (AgentCash advisory pre-check)
|
|
93
183
|
|
|
94
184
|
> **Not a challenge-bound firewall.** Classification: `advisory_precheck`.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — signed, expiring DecisionTokens + intent binding.
|
|
3
|
+
*
|
|
4
|
+
* A DecisionToken is not "we checked this merchant earlier". It is:
|
|
5
|
+
* "at this timestamp, policy version X approved this EXACT transaction".
|
|
6
|
+
* The wallet (or payment client) must verify, before signing:
|
|
7
|
+
*
|
|
8
|
+
* hash(intent being signed) === token.intentHash
|
|
9
|
+
*
|
|
10
|
+
* plus expiry, decision === allow, signature, and consume-once. Any mismatch
|
|
11
|
+
* means refuse — the signer is never invoked.
|
|
12
|
+
*/
|
|
13
|
+
import { type PaymentIntent } from "./intent.js";
|
|
14
|
+
export type PaymentDecisionVerdict = "allow" | "warn" | "block";
|
|
15
|
+
export type DecisionConstraints = {
|
|
16
|
+
maxAmount?: string;
|
|
17
|
+
allowedAssets?: string[];
|
|
18
|
+
requireHumanApproval?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type PaymentDecision = {
|
|
21
|
+
decision: PaymentDecisionVerdict;
|
|
22
|
+
reasonCodes: string[];
|
|
23
|
+
intentHash: string;
|
|
24
|
+
policyVersion: string;
|
|
25
|
+
decisionId: string;
|
|
26
|
+
/** ISO-8601. Tokens are short-lived by design. */
|
|
27
|
+
expiresAt: string;
|
|
28
|
+
constraints?: DecisionConstraints;
|
|
29
|
+
/** Signer key identifier (for rotation / audit). */
|
|
30
|
+
keyId: string;
|
|
31
|
+
/** base64 Ed25519 signature over the domain-separated canonical payload. */
|
|
32
|
+
signature: string;
|
|
33
|
+
};
|
|
34
|
+
export type DecisionSigner = {
|
|
35
|
+
keyId: string;
|
|
36
|
+
sign(preimage: Uint8Array): Uint8Array | Promise<Uint8Array>;
|
|
37
|
+
/** PEM (SPKI) for local verification; remote signers publish theirs. */
|
|
38
|
+
publicKeyPem?: string;
|
|
39
|
+
};
|
|
40
|
+
/** Everything signed — the token minus the signature itself. */
|
|
41
|
+
export declare function decisionPreimage(token: Omit<PaymentDecision, "signature">): Buffer;
|
|
42
|
+
/**
|
|
43
|
+
* Local Ed25519 decision signer. Pass a PKCS#8 PEM to pin a key; otherwise an
|
|
44
|
+
* ephemeral keypair is generated (fine for tests and per-process runtimes).
|
|
45
|
+
*/
|
|
46
|
+
export declare function createLocalDecisionSigner(options?: {
|
|
47
|
+
privateKeyPem?: string;
|
|
48
|
+
keyId?: string;
|
|
49
|
+
}): DecisionSigner & {
|
|
50
|
+
publicKeyPem: string;
|
|
51
|
+
};
|
|
52
|
+
export declare function signDecision(unsigned: Omit<PaymentDecision, "signature" | "keyId">, signer: DecisionSigner): Promise<PaymentDecision>;
|
|
53
|
+
export declare function verifyDecisionSignature(token: PaymentDecision, publicKeyPem: string): boolean;
|
|
54
|
+
export type BindingErrorCode = "INTENT_HASH_MISMATCH" | "DECISION_EXPIRED" | "DECISION_NOT_ALLOW" | "DECISION_REPLAYED" | "BAD_SIGNATURE";
|
|
55
|
+
export declare class TwzrdIntentBindingError extends Error {
|
|
56
|
+
readonly code: BindingErrorCode;
|
|
57
|
+
readonly decisionId: string;
|
|
58
|
+
constructor(code: BindingErrorCode, decisionId: string, detail: string);
|
|
59
|
+
}
|
|
60
|
+
export type DecisionRegistry = {
|
|
61
|
+
/** True exactly once per decisionId; false on every replay. */
|
|
62
|
+
consume(decisionId: string): boolean;
|
|
63
|
+
};
|
|
64
|
+
/** In-process consume-once ledger. Entries expire with the tokens. */
|
|
65
|
+
export declare function createDecisionRegistry(): DecisionRegistry;
|
|
66
|
+
export type AssertIntentApprovedOptions = {
|
|
67
|
+
now?: number;
|
|
68
|
+
/** Enforce consume-once semantics (recommended for anything non-idempotent). */
|
|
69
|
+
registry?: DecisionRegistry;
|
|
70
|
+
/** Verify the token signature against this key before trusting it. */
|
|
71
|
+
publicKeyPem?: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The wallet-side check. Call with the EXACT intent about to be signed.
|
|
75
|
+
* Throws TwzrdIntentBindingError on any violation; returns void when the
|
|
76
|
+
* signature may proceed. A caught error means: do not invoke the signer.
|
|
77
|
+
*/
|
|
78
|
+
export declare function assertIntentApproved(intent: PaymentIntent, token: PaymentDecision, options?: AssertIntentApprovedOptions): void;
|
|
79
|
+
export declare function newDecisionId(): string;
|
|
80
|
+
//# sourceMappingURL=decision-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-token.d.ts","sourceRoot":"","sources":["../src/decision-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5E,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAElC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7D,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,MAAM,CAKlF;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,cAAc,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAe5C;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,OAAO,CAAC,EACtD,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,YAAY,EAAE,MAAM,GACnB,OAAO,CAYT;AAMD,MAAM,MAAM,gBAAgB,GACxB,sBAAsB,GACtB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,eAAe,CAAC;AAEpB,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAChB,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMvE;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+DAA+D;IAC/D,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;CACtC,CAAC;AAEF,sEAAsE;AACtE,wBAAgB,sBAAsB,IAAI,gBAAgB,CASzD;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,2BAA2B,GACpC,IAAI,CA2CN;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — signed, expiring DecisionTokens + intent binding.
|
|
3
|
+
*
|
|
4
|
+
* A DecisionToken is not "we checked this merchant earlier". It is:
|
|
5
|
+
* "at this timestamp, policy version X approved this EXACT transaction".
|
|
6
|
+
* The wallet (or payment client) must verify, before signing:
|
|
7
|
+
*
|
|
8
|
+
* hash(intent being signed) === token.intentHash
|
|
9
|
+
*
|
|
10
|
+
* plus expiry, decision === allow, signature, and consume-once. Any mismatch
|
|
11
|
+
* means refuse — the signer is never invoked.
|
|
12
|
+
*/
|
|
13
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync, randomUUID, sign as edSign, verify as edVerify, } from "node:crypto";
|
|
14
|
+
import { canonicalJson, intentHash } from "./intent.js";
|
|
15
|
+
const DECISION_DOMAIN = "twzrd-decision-v1\n";
|
|
16
|
+
/** Everything signed — the token minus the signature itself. */
|
|
17
|
+
export function decisionPreimage(token) {
|
|
18
|
+
return Buffer.concat([
|
|
19
|
+
Buffer.from(DECISION_DOMAIN, "utf8"),
|
|
20
|
+
Buffer.from(canonicalJson(token), "utf8"),
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Local Ed25519 decision signer. Pass a PKCS#8 PEM to pin a key; otherwise an
|
|
25
|
+
* ephemeral keypair is generated (fine for tests and per-process runtimes).
|
|
26
|
+
*/
|
|
27
|
+
export function createLocalDecisionSigner(options) {
|
|
28
|
+
let privateKey;
|
|
29
|
+
if (options?.privateKeyPem) {
|
|
30
|
+
privateKey = createPrivateKey(options.privateKeyPem);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
({ privateKey } = generateKeyPairSync("ed25519"));
|
|
34
|
+
}
|
|
35
|
+
const publicKeyPem = createPublicKey(privateKey)
|
|
36
|
+
.export({ type: "spki", format: "pem" })
|
|
37
|
+
.toString();
|
|
38
|
+
return {
|
|
39
|
+
keyId: options?.keyId ?? "local-ed25519",
|
|
40
|
+
publicKeyPem,
|
|
41
|
+
sign: (preimage) => edSign(null, Buffer.from(preimage), privateKey),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function signDecision(unsigned, signer) {
|
|
45
|
+
const payload = { ...unsigned, keyId: signer.keyId };
|
|
46
|
+
const signature = Buffer.from(await signer.sign(decisionPreimage(payload))).toString("base64");
|
|
47
|
+
return { ...payload, signature };
|
|
48
|
+
}
|
|
49
|
+
export function verifyDecisionSignature(token, publicKeyPem) {
|
|
50
|
+
const { signature, ...payload } = token;
|
|
51
|
+
try {
|
|
52
|
+
return edVerify(null, decisionPreimage(payload), createPublicKey(publicKeyPem), Buffer.from(signature, "base64"));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class TwzrdIntentBindingError extends Error {
|
|
59
|
+
code;
|
|
60
|
+
decisionId;
|
|
61
|
+
constructor(code, decisionId, detail) {
|
|
62
|
+
super(`[twzrd] ${code}: ${detail} (decisionId=${decisionId})`);
|
|
63
|
+
this.name = "TwzrdIntentBindingError";
|
|
64
|
+
this.code = code;
|
|
65
|
+
this.decisionId = decisionId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** In-process consume-once ledger. Entries expire with the tokens. */
|
|
69
|
+
export function createDecisionRegistry() {
|
|
70
|
+
const consumed = new Set();
|
|
71
|
+
return {
|
|
72
|
+
consume(decisionId) {
|
|
73
|
+
if (consumed.has(decisionId))
|
|
74
|
+
return false;
|
|
75
|
+
consumed.add(decisionId);
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The wallet-side check. Call with the EXACT intent about to be signed.
|
|
82
|
+
* Throws TwzrdIntentBindingError on any violation; returns void when the
|
|
83
|
+
* signature may proceed. A caught error means: do not invoke the signer.
|
|
84
|
+
*/
|
|
85
|
+
export function assertIntentApproved(intent, token, options) {
|
|
86
|
+
const now = options?.now ?? Date.now();
|
|
87
|
+
if (options?.publicKeyPem && !verifyDecisionSignature(token, options.publicKeyPem)) {
|
|
88
|
+
throw new TwzrdIntentBindingError("BAD_SIGNATURE", token.decisionId, "decision token signature did not verify");
|
|
89
|
+
}
|
|
90
|
+
if (token.decision !== "allow" && token.decision !== "warn") {
|
|
91
|
+
throw new TwzrdIntentBindingError("DECISION_NOT_ALLOW", token.decisionId, `decision is ${token.decision}`);
|
|
92
|
+
}
|
|
93
|
+
if (now >= Date.parse(token.expiresAt)) {
|
|
94
|
+
throw new TwzrdIntentBindingError("DECISION_EXPIRED", token.decisionId, `expired at ${token.expiresAt}`);
|
|
95
|
+
}
|
|
96
|
+
const actual = intentHash(intent);
|
|
97
|
+
if (actual !== token.intentHash) {
|
|
98
|
+
throw new TwzrdIntentBindingError("INTENT_HASH_MISMATCH", token.decisionId, `approved ${token.intentHash} but signing ${actual}`);
|
|
99
|
+
}
|
|
100
|
+
if (options?.registry && !options.registry.consume(token.decisionId)) {
|
|
101
|
+
throw new TwzrdIntentBindingError("DECISION_REPLAYED", token.decisionId, "decision token already consumed");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function newDecisionId() {
|
|
105
|
+
return randomUUID();
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=decision-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-token.js","sourceRoot":"","sources":["../src/decision-token.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,8 +11,12 @@ export { evaluate_x402_resource, type EvaluateX402Options, type EvaluateX402Resu
|
|
|
11
11
|
export { withTwzrdGuard, type TwzrdGuardOptions } from "./with-guard.js";
|
|
12
12
|
export { installTwzrdAutoGate, type PayWrap, type InstallAutoGateOptions, } from "./auto-gate.js";
|
|
13
13
|
export { safeFetch, runAgentcashFetch, main as safeFetchMain, type SafeFetchOptions, type SafeFetchResult, } from "./safe-fetch.js";
|
|
14
|
-
export { installTwzrdX402ClientHook, twzrdBeforePaymentCreation, type X402ClientLike, type X402SelectedRequirements, type BeforePaymentCreationContext, type BeforePaymentCreationResult, type InstallX402ClientHookOptions, } from "./x402-client-hook.js";
|
|
14
|
+
export { installTwzrdX402ClientHook, twzrdBeforePaymentCreation, type X402ClientLike, type X402SelectedRequirements, type BeforePaymentCreationContext, type BeforePaymentCreationResult, type InstallX402ClientHookOptions, type X402PaymentControlOptions, } from "./x402-client-hook.js";
|
|
15
15
|
export { quickCheck, QUICK_PRICE_USDC, type QuickCheckResult, type QuickCheckOptions, type TwzrdTier, } from "./quick.js";
|
|
16
16
|
export { createSponsoredX402Fetch, type SponsorSettle, type SponsoredX402Options, } from "./sponsored.js";
|
|
17
17
|
export type { TwzrdDecision, TwzrdReadinessCard, TwzrdPreflightInput, TwzrdGateConfig, TwzrdApproveContext, TwzrdApprovalResult, TwzrdUpsellContext, X402PaymentRequirements, X402PaymentRequiredBody, X402McpPaymentRequest, X402McpPaymentRequestedContext, } from "./types.js";
|
|
18
|
+
export { canonicalJson, intentHash, toMicroUsd, INTENT_HASH_PREFIX, type PaymentIntent, type PaymentProtocol, } from "./intent.js";
|
|
19
|
+
export { assertIntentApproved, createDecisionRegistry, createLocalDecisionSigner, decisionPreimage, newDecisionId, signDecision, verifyDecisionSignature, TwzrdIntentBindingError, type AssertIntentApprovedOptions, type BindingErrorCode, type DecisionConstraints, type DecisionRegistry, type DecisionSigner, type PaymentDecision, type PaymentDecisionVerdict, } from "./decision-token.js";
|
|
20
|
+
export { createMemorySpendLedger, evaluateIntent, POLICY_VERSION, type CounterpartyIntelligence, type EvaluateIntentOptions, type IntelligenceProvider, type Mandate, type SpendLedger, type SpendPolicy, } from "./policy-runtime.js";
|
|
21
|
+
export { ap2CheckoutToIntent, x402RequirementsToIntent, type Ap2Cart, type Ap2UserMandate, type X402IntentContext, } from "./intent-adapters.js";
|
|
18
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,GAChC,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EACL,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACL,oBAAoB,EACpB,KAAK,OAAO,EACZ,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,IAAI,IAAI,aAAa,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -17,4 +17,9 @@ export { safeFetch, runAgentcashFetch, main as safeFetchMain, } from "./safe-fet
|
|
|
17
17
|
export { installTwzrdX402ClientHook, twzrdBeforePaymentCreation, } from "./x402-client-hook.js";
|
|
18
18
|
export { quickCheck, QUICK_PRICE_USDC, } from "./quick.js";
|
|
19
19
|
export { createSponsoredX402Fetch, } from "./sponsored.js";
|
|
20
|
+
/* ── TWZRD Payment Control (protocol-neutral authorization core) ── */
|
|
21
|
+
export { canonicalJson, intentHash, toMicroUsd, INTENT_HASH_PREFIX, } from "./intent.js";
|
|
22
|
+
export { assertIntentApproved, createDecisionRegistry, createLocalDecisionSigner, decisionPreimage, newDecisionId, signDecision, verifyDecisionSignature, TwzrdIntentBindingError, } from "./decision-token.js";
|
|
23
|
+
export { createMemorySpendLedger, evaluateIntent, POLICY_VERSION, } from "./policy-runtime.js";
|
|
24
|
+
export { ap2CheckoutToIntent, x402RequirementsToIntent, } from "./intent-adapters.js";
|
|
20
25
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,qFAAqF;AACrF,uFAAuF;AACvF,qFAAqF;AACrF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,aAAa,EAAgC,MAAM,aAAa,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,YAAY,GAKb,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,iBAAiB,EACjB,sBAAsB,GAIvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EACL,sBAAsB,GAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAA0B,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACL,oBAAoB,GAGrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,IAAI,IAAI,aAAa,GAGtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,qFAAqF;AACrF,uFAAuF;AACvF,qFAAqF;AACrF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,aAAa,EAAgC,MAAM,aAAa,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,YAAY,GAKb,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,iBAAiB,EACjB,sBAAsB,GAIvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EACL,sBAAsB,GAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAA0B,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACL,oBAAoB,GAGrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,IAAI,IAAI,aAAa,GAGtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,GAO3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,gBAAgB,GAIjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,wBAAwB,GAGzB,MAAM,gBAAgB,CAAC;AAexB,uEAAuE;AACvE,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,GAGnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,GAQxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,GAOf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GAIzB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — protocol adapters into PaymentIntent v1.
|
|
3
|
+
*
|
|
4
|
+
* Adapters normalize protocol-specific payment shapes into the one canonical
|
|
5
|
+
* intent the policy runtime evaluates. The x402 adapter keeps the existing
|
|
6
|
+
* gate's behavior available on the new core; the AP2/UCP adapter is the
|
|
7
|
+
* non-x402 reference — it exists to prove the runtime combines a USER MANDATE
|
|
8
|
+
* with counterparty risk, not merely that it parses another crypto request.
|
|
9
|
+
*/
|
|
10
|
+
import type { PaymentIntent } from "./intent.js";
|
|
11
|
+
import type { Mandate } from "./policy-runtime.js";
|
|
12
|
+
import type { X402SelectedRequirements } from "./x402-client-hook.js";
|
|
13
|
+
export type X402IntentContext = {
|
|
14
|
+
/** Resource URL when the requirement omits `resource` (v2 top-level). */
|
|
15
|
+
resourceUrl?: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
facilitator?: string;
|
|
18
|
+
agent?: PaymentIntent["agent"];
|
|
19
|
+
purpose?: string;
|
|
20
|
+
};
|
|
21
|
+
/** Normalize a selected x402 payment requirement (v1 or v2 field names). */
|
|
22
|
+
export declare function x402RequirementsToIntent(req: X402SelectedRequirements, ctx?: X402IntentContext): PaymentIntent;
|
|
23
|
+
/**
|
|
24
|
+
* Minimal AP2-style cart: what an agent is about to check out under a user
|
|
25
|
+
* mandate. Field names follow the AP2 mandate/cart split — the mandate says
|
|
26
|
+
* what MAY be spent; the cart is the concrete transaction.
|
|
27
|
+
*/
|
|
28
|
+
export type Ap2Cart = {
|
|
29
|
+
merchantId: string;
|
|
30
|
+
/** Settlement destination (merchant account / wallet). */
|
|
31
|
+
payTo: string;
|
|
32
|
+
currency: string;
|
|
33
|
+
total: string;
|
|
34
|
+
items?: Array<{
|
|
35
|
+
sku?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
price?: string;
|
|
38
|
+
}>;
|
|
39
|
+
checkoutUrl?: string;
|
|
40
|
+
category?: string;
|
|
41
|
+
recurring?: boolean;
|
|
42
|
+
priorCharge?: string;
|
|
43
|
+
};
|
|
44
|
+
export type Ap2UserMandate = {
|
|
45
|
+
mandateId: string;
|
|
46
|
+
/** Purchase categories the user authorized (e.g. ["software"]). */
|
|
47
|
+
categories?: string[];
|
|
48
|
+
maxPerTransaction?: string;
|
|
49
|
+
monthlyCeiling?: string;
|
|
50
|
+
merchantAllowPrefixes?: string[];
|
|
51
|
+
expiresAt?: string;
|
|
52
|
+
};
|
|
53
|
+
/** Cart -> canonical intent. The mandate rides along for the policy runtime. */
|
|
54
|
+
export declare function ap2CheckoutToIntent(cart: Ap2Cart, mandate: Ap2UserMandate, options?: {
|
|
55
|
+
network?: string;
|
|
56
|
+
agentId?: string;
|
|
57
|
+
organization?: string;
|
|
58
|
+
}): {
|
|
59
|
+
intent: PaymentIntent;
|
|
60
|
+
mandate: Mandate;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=intent-adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-adapters.d.ts","sourceRoot":"","sources":["../src/intent-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAMtE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,wBAAwB,EAC7B,GAAG,CAAC,EAAE,iBAAiB,GACtB,aAAa,CAmBf;AAMD;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA+B7C"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — protocol adapters into PaymentIntent v1.
|
|
3
|
+
*
|
|
4
|
+
* Adapters normalize protocol-specific payment shapes into the one canonical
|
|
5
|
+
* intent the policy runtime evaluates. The x402 adapter keeps the existing
|
|
6
|
+
* gate's behavior available on the new core; the AP2/UCP adapter is the
|
|
7
|
+
* non-x402 reference — it exists to prove the runtime combines a USER MANDATE
|
|
8
|
+
* with counterparty risk, not merely that it parses another crypto request.
|
|
9
|
+
*/
|
|
10
|
+
/** Normalize a selected x402 payment requirement (v1 or v2 field names). */
|
|
11
|
+
export function x402RequirementsToIntent(req, ctx) {
|
|
12
|
+
const payTo = req.payTo ?? req.pay_to;
|
|
13
|
+
const amount = req.amount ?? req.maxAmountRequired;
|
|
14
|
+
if (!payTo)
|
|
15
|
+
throw new Error("[twzrd] x402 requirement missing payTo");
|
|
16
|
+
if (!amount)
|
|
17
|
+
throw new Error("[twzrd] x402 requirement missing amount");
|
|
18
|
+
return {
|
|
19
|
+
protocol: "x402",
|
|
20
|
+
network: req.network ?? "unknown",
|
|
21
|
+
asset: req.asset ?? "unknown",
|
|
22
|
+
amount,
|
|
23
|
+
payTo,
|
|
24
|
+
resource: {
|
|
25
|
+
url: req.resource ?? ctx?.resourceUrl,
|
|
26
|
+
method: ctx?.method,
|
|
27
|
+
},
|
|
28
|
+
facilitator: ctx?.facilitator,
|
|
29
|
+
agent: ctx?.agent,
|
|
30
|
+
context: ctx?.purpose ? { purpose: ctx.purpose } : undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Cart -> canonical intent. The mandate rides along for the policy runtime. */
|
|
34
|
+
export function ap2CheckoutToIntent(cart, mandate, options) {
|
|
35
|
+
const intent = {
|
|
36
|
+
protocol: "ap2",
|
|
37
|
+
network: options?.network ?? "fiat:card",
|
|
38
|
+
asset: cart.currency,
|
|
39
|
+
amount: cart.total,
|
|
40
|
+
payTo: cart.payTo,
|
|
41
|
+
resource: {
|
|
42
|
+
url: cart.checkoutUrl,
|
|
43
|
+
operation: `checkout:${cart.merchantId}`,
|
|
44
|
+
},
|
|
45
|
+
agent: {
|
|
46
|
+
id: options?.agentId,
|
|
47
|
+
organization: options?.organization,
|
|
48
|
+
mandateId: mandate.mandateId,
|
|
49
|
+
},
|
|
50
|
+
context: {
|
|
51
|
+
purpose: cart.category,
|
|
52
|
+
recurring: cart.recurring,
|
|
53
|
+
priorSpend: cart.priorCharge,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const normalizedMandate = {
|
|
57
|
+
mandateId: mandate.mandateId,
|
|
58
|
+
purposes: mandate.categories,
|
|
59
|
+
maxPerTransactionUsd: mandate.maxPerTransaction,
|
|
60
|
+
monthlyCeilingUsd: mandate.monthlyCeiling,
|
|
61
|
+
resourceAllow: mandate.merchantAllowPrefixes,
|
|
62
|
+
expiresAt: mandate.expiresAt,
|
|
63
|
+
};
|
|
64
|
+
return { intent, mandate: normalizedMandate };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=intent-adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-adapters.js","sourceRoot":"","sources":["../src/intent-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAmBH,4EAA4E;AAC5E,MAAM,UAAU,wBAAwB,CACtC,GAA6B,EAC7B,GAAuB;IAEvB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC;IACtC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC;IACnD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACtE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACxE,OAAO;QACL,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,SAAS;QACjC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;QAC7B,MAAM;QACN,KAAK;QACL,QAAQ,EAAE;YACR,GAAG,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,EAAE,WAAW;YACrC,MAAM,EAAE,GAAG,EAAE,MAAM;SACpB;QACD,WAAW,EAAE,GAAG,EAAE,WAAW;QAC7B,KAAK,EAAE,GAAG,EAAE,KAAK;QACjB,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;KAC7D,CAAC;AACJ,CAAC;AAkCD,gFAAgF;AAChF,MAAM,UAAU,mBAAmB,CACjC,IAAa,EACb,OAAuB,EACvB,OAAuE;IAEvE,MAAM,MAAM,GAAkB;QAC5B,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,WAAW;QACxC,KAAK,EAAE,IAAI,CAAC,QAAQ;QACpB,MAAM,EAAE,IAAI,CAAC,KAAK;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE;YACR,GAAG,EAAE,IAAI,CAAC,WAAW;YACrB,SAAS,EAAE,YAAY,IAAI,CAAC,UAAU,EAAE;SACzC;QACD,KAAK,EAAE;YACL,EAAE,EAAE,OAAO,EAAE,OAAO;YACpB,YAAY,EAAE,OAAO,EAAE,YAAY;YACnC,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B;QACD,OAAO,EAAE;YACP,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,WAAW;SAC7B;KACF,CAAC;IACF,MAAM,iBAAiB,GAAY;QACjC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,OAAO,CAAC,UAAU;QAC5B,oBAAoB,EAAE,OAAO,CAAC,iBAAiB;QAC/C,iBAAiB,EAAE,OAAO,CAAC,cAAc;QACzC,aAAa,EAAE,OAAO,CAAC,qBAAqB;QAC5C,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAChD,CAAC"}
|
package/dist/intent.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — PaymentIntent v1 (FROZEN).
|
|
3
|
+
*
|
|
4
|
+
* The canonical, protocol-neutral description of one autonomous payment,
|
|
5
|
+
* evaluated exactly once at the last deterministic checkpoint before signing.
|
|
6
|
+
*
|
|
7
|
+
* Defining invariant (honest scope): a signer path that calls
|
|
8
|
+
* assertIntentApproved will not sign an intent that differs from what TWZRD
|
|
9
|
+
* evaluated. TWZRD does not own third-party wallets - the binding holds
|
|
10
|
+
* exactly where the check runs before the signer. Everything that
|
|
11
|
+
* identifies the transaction — payee, resource, amount, asset, network,
|
|
12
|
+
* facilitator, method, mandate, recurrence context — is bound into ONE
|
|
13
|
+
* canonical intent hash. `hash(intent being signed) === decision.intentHash`
|
|
14
|
+
* or the wallet refuses.
|
|
15
|
+
*
|
|
16
|
+
* v1 is frozen: field additions require a new hash prefix (tiv2:), never a
|
|
17
|
+
* silent change to canonicalization.
|
|
18
|
+
*/
|
|
19
|
+
export type PaymentProtocol = "x402" | "ap2" | "ucp" | "mpp" | "direct";
|
|
20
|
+
export type PaymentIntent = {
|
|
21
|
+
protocol: PaymentProtocol;
|
|
22
|
+
/** CAIP-2 where applicable (e.g. "solana:5eykt4...", "eip155:8453"). */
|
|
23
|
+
network: string;
|
|
24
|
+
/** Asset identifier (mint / contract / ISO code for fiat-denominated mandates). */
|
|
25
|
+
asset: string;
|
|
26
|
+
/** Decimal string. Money is NEVER a float. */
|
|
27
|
+
amount: string;
|
|
28
|
+
/** Receiving counterparty (wallet, contract, merchant account). */
|
|
29
|
+
payTo: string;
|
|
30
|
+
resource?: {
|
|
31
|
+
url?: string;
|
|
32
|
+
method?: string;
|
|
33
|
+
operation?: string;
|
|
34
|
+
/** Hash of the exact request body when the resource is request-bound. */
|
|
35
|
+
requestHash?: string;
|
|
36
|
+
};
|
|
37
|
+
facilitator?: string;
|
|
38
|
+
agent?: {
|
|
39
|
+
id?: string;
|
|
40
|
+
organization?: string;
|
|
41
|
+
mandateId?: string;
|
|
42
|
+
};
|
|
43
|
+
context?: {
|
|
44
|
+
purpose?: string;
|
|
45
|
+
recurring?: boolean;
|
|
46
|
+
/** Prior charge for this recurring counterparty, decimal string. */
|
|
47
|
+
priorSpend?: string;
|
|
48
|
+
/** ISO-8601 expiry of the intent itself. */
|
|
49
|
+
expiresAt?: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export declare const INTENT_HASH_PREFIX = "tiv1:";
|
|
53
|
+
/**
|
|
54
|
+
* Canonical JSON (frozen with v1):
|
|
55
|
+
* - object keys sorted lexicographically (code-unit order)
|
|
56
|
+
* - `undefined` and `null` members omitted
|
|
57
|
+
* - arrays keep order; `undefined`/`null` elements are rejected
|
|
58
|
+
* - numbers must be finite (money fields are strings by type)
|
|
59
|
+
* - no insignificant whitespace
|
|
60
|
+
*/
|
|
61
|
+
export declare function canonicalJson(value: unknown): string;
|
|
62
|
+
/** sha256 over the domain-separated canonical form, `tiv1:`-prefixed hex. */
|
|
63
|
+
export declare function intentHash(intent: PaymentIntent): string;
|
|
64
|
+
/**
|
|
65
|
+
* Parse a decimal money string into micro-units (6dp) as bigint.
|
|
66
|
+
* Rejects floats-by-stealth: only `[digits].[<=6 digits]` accepted.
|
|
67
|
+
*/
|
|
68
|
+
export declare function toMicroUsd(amount: string): bigint;
|
|
69
|
+
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAExE,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IAEd,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,yEAAyE;QACzE,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,KAAK,CAAC,EAAE;QACN,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,oEAAoE;QACpE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,4CAA4C;QAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAG1C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKpD;AA2BD,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAOxD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
package/dist/intent.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — PaymentIntent v1 (FROZEN).
|
|
3
|
+
*
|
|
4
|
+
* The canonical, protocol-neutral description of one autonomous payment,
|
|
5
|
+
* evaluated exactly once at the last deterministic checkpoint before signing.
|
|
6
|
+
*
|
|
7
|
+
* Defining invariant (honest scope): a signer path that calls
|
|
8
|
+
* assertIntentApproved will not sign an intent that differs from what TWZRD
|
|
9
|
+
* evaluated. TWZRD does not own third-party wallets - the binding holds
|
|
10
|
+
* exactly where the check runs before the signer. Everything that
|
|
11
|
+
* identifies the transaction — payee, resource, amount, asset, network,
|
|
12
|
+
* facilitator, method, mandate, recurrence context — is bound into ONE
|
|
13
|
+
* canonical intent hash. `hash(intent being signed) === decision.intentHash`
|
|
14
|
+
* or the wallet refuses.
|
|
15
|
+
*
|
|
16
|
+
* v1 is frozen: field additions require a new hash prefix (tiv2:), never a
|
|
17
|
+
* silent change to canonicalization.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
export const INTENT_HASH_PREFIX = "tiv1:";
|
|
21
|
+
const INTENT_DOMAIN = "twzrd-intent-v1\n";
|
|
22
|
+
/**
|
|
23
|
+
* Canonical JSON (frozen with v1):
|
|
24
|
+
* - object keys sorted lexicographically (code-unit order)
|
|
25
|
+
* - `undefined` and `null` members omitted
|
|
26
|
+
* - arrays keep order; `undefined`/`null` elements are rejected
|
|
27
|
+
* - numbers must be finite (money fields are strings by type)
|
|
28
|
+
* - no insignificant whitespace
|
|
29
|
+
*/
|
|
30
|
+
export function canonicalJson(value) {
|
|
31
|
+
if (value === null || value === undefined) {
|
|
32
|
+
throw new Error("[twzrd] canonicalJson: top-level null/undefined");
|
|
33
|
+
}
|
|
34
|
+
return serialize(value);
|
|
35
|
+
}
|
|
36
|
+
function serialize(value) {
|
|
37
|
+
if (value === null || value === undefined) {
|
|
38
|
+
throw new Error("[twzrd] canonicalJson: null/undefined array element");
|
|
39
|
+
}
|
|
40
|
+
const t = typeof value;
|
|
41
|
+
if (t === "string" || t === "boolean")
|
|
42
|
+
return JSON.stringify(value);
|
|
43
|
+
if (t === "number") {
|
|
44
|
+
if (!Number.isFinite(value)) {
|
|
45
|
+
throw new Error("[twzrd] canonicalJson: non-finite number");
|
|
46
|
+
}
|
|
47
|
+
return JSON.stringify(value);
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
return `[${value.map(serialize).join(",")}]`;
|
|
51
|
+
}
|
|
52
|
+
if (t === "object") {
|
|
53
|
+
const entries = Object.entries(value)
|
|
54
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
55
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
56
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${serialize(v)}`);
|
|
57
|
+
return `{${entries.join(",")}}`;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`[twzrd] canonicalJson: unsupported type ${t}`);
|
|
60
|
+
}
|
|
61
|
+
/** sha256 over the domain-separated canonical form, `tiv1:`-prefixed hex. */
|
|
62
|
+
export function intentHash(intent) {
|
|
63
|
+
const canonical = canonicalJson(intent);
|
|
64
|
+
const digest = createHash("sha256")
|
|
65
|
+
.update(INTENT_DOMAIN)
|
|
66
|
+
.update(canonical)
|
|
67
|
+
.digest("hex");
|
|
68
|
+
return `${INTENT_HASH_PREFIX}${digest}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse a decimal money string into micro-units (6dp) as bigint.
|
|
72
|
+
* Rejects floats-by-stealth: only `[digits].[<=6 digits]` accepted.
|
|
73
|
+
*/
|
|
74
|
+
export function toMicroUsd(amount) {
|
|
75
|
+
const m = /^(\d+)(?:\.(\d{1,6}))?$/.exec(amount.trim());
|
|
76
|
+
if (!m)
|
|
77
|
+
throw new Error(`[twzrd] bad decimal amount: ${JSON.stringify(amount)}`);
|
|
78
|
+
const whole = BigInt(m[1]);
|
|
79
|
+
const frac = BigInt((m[2] ?? "").padEnd(6, "0") || "0");
|
|
80
|
+
return whole * 1000000n + frac;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — the protocol-neutral policy runtime.
|
|
3
|
+
*
|
|
4
|
+
* `evaluateIntent(intent, { policy, mandate, intelligence })` combines:
|
|
5
|
+
* - LOCAL hard controls (ceilings, network/asset restrictions, allow/block
|
|
6
|
+
* lists, mandate validation, cumulative caps, recurring price checks) —
|
|
7
|
+
* deterministic, no network, keeps working through an API outage;
|
|
8
|
+
* - REMOTE intelligence (counterparty score, wash/fleet detection) via an
|
|
9
|
+
* injectable async provider;
|
|
10
|
+
* and returns a signed, expiring DecisionToken bound to the intent hash.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately NOT a policy language. Five concrete policies, evaluated in a
|
|
13
|
+
* fixed order with stable reason codes.
|
|
14
|
+
*/
|
|
15
|
+
import { type DecisionSigner, type PaymentDecision } from "./decision-token.js";
|
|
16
|
+
import { type PaymentIntent } from "./intent.js";
|
|
17
|
+
export declare const POLICY_VERSION = "twzrd-pc-v1";
|
|
18
|
+
export type SpendPolicy = {
|
|
19
|
+
/** Refuse wash-flagged counterparties (default true when intelligence runs). */
|
|
20
|
+
refuseWashFlagged?: boolean;
|
|
21
|
+
/** Unknown merchant: allow small spend, act above the line. */
|
|
22
|
+
unknownCounterparty?: {
|
|
23
|
+
allowUnderUsd: string;
|
|
24
|
+
aboveAction?: "block" | "warn";
|
|
25
|
+
};
|
|
26
|
+
/** New counterparty: cumulative cap over a rolling window. */
|
|
27
|
+
newCounterpartyCap?: {
|
|
28
|
+
capUsd: string;
|
|
29
|
+
windowHours: number;
|
|
30
|
+
};
|
|
31
|
+
/** Recurring service: block a price increase above this percentage. */
|
|
32
|
+
recurringMaxPriceIncreasePct?: number;
|
|
33
|
+
allowedNetworks?: string[];
|
|
34
|
+
allowedAssets?: string[];
|
|
35
|
+
maxAmountUsd?: string;
|
|
36
|
+
blocklist?: string[];
|
|
37
|
+
allowlist?: string[];
|
|
38
|
+
};
|
|
39
|
+
export type Mandate = {
|
|
40
|
+
mandateId: string;
|
|
41
|
+
/** Allowed spend purposes (e.g. ["software", "research_api"]). */
|
|
42
|
+
purposes?: string[];
|
|
43
|
+
maxPerTransactionUsd?: string;
|
|
44
|
+
monthlyCeilingUsd?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Resource binding: URL prefixes this mandate may pay for.
|
|
47
|
+
* Approval for /weather cannot pay /admin/export.
|
|
48
|
+
*/
|
|
49
|
+
resourceAllow?: string[];
|
|
50
|
+
/** Explicitly forbidden payees (e.g. personal wallets). */
|
|
51
|
+
payeeBlocklist?: string[];
|
|
52
|
+
expiresAt?: string;
|
|
53
|
+
};
|
|
54
|
+
/** What remote intelligence contributes. All fields optional; absent = unknown. */
|
|
55
|
+
export type CounterpartyIntelligence = {
|
|
56
|
+
known?: boolean;
|
|
57
|
+
washFlagged?: boolean;
|
|
58
|
+
decision?: "allow" | "warn" | "block";
|
|
59
|
+
trustScore?: number;
|
|
60
|
+
};
|
|
61
|
+
export type IntelligenceProvider = (intent: PaymentIntent) => Promise<CounterpartyIntelligence> | CounterpartyIntelligence;
|
|
62
|
+
export type SpendLedger = {
|
|
63
|
+
/** Total recorded spend (micro-USD) for a scope key within the window. */
|
|
64
|
+
spentMicro(scopeKey: string, windowMs: number, now: number): bigint;
|
|
65
|
+
record(scopeKey: string, amountMicro: bigint, at: number): void;
|
|
66
|
+
/** First time this scope key was seen, if ever. */
|
|
67
|
+
firstSeen(scopeKey: string): number | undefined;
|
|
68
|
+
};
|
|
69
|
+
export declare function createMemorySpendLedger(): SpendLedger;
|
|
70
|
+
export type EvaluateIntentOptions = {
|
|
71
|
+
policy?: SpendPolicy;
|
|
72
|
+
mandate?: Mandate;
|
|
73
|
+
intelligence?: IntelligenceProvider;
|
|
74
|
+
ledger?: SpendLedger;
|
|
75
|
+
signer: DecisionSigner;
|
|
76
|
+
/** Token time-to-live in ms (default 120s — decisions are point-in-time). */
|
|
77
|
+
ttlMs?: number;
|
|
78
|
+
now?: number;
|
|
79
|
+
/** Record allowed spend into the ledger (default true). */
|
|
80
|
+
recordSpend?: boolean;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Evaluate one PaymentIntent. Never throws on a policy outcome — a block is a
|
|
84
|
+
* signed block decision (auditable), not an exception. Throws only on
|
|
85
|
+
* malformed input (bad amounts) or signer failure.
|
|
86
|
+
*/
|
|
87
|
+
export declare function evaluateIntent(intent: PaymentIntent, options: EvaluateIntentOptions): Promise<PaymentDecision>;
|
|
88
|
+
//# sourceMappingURL=policy-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-runtime.d.ts","sourceRoot":"","sources":["../src/policy-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — the protocol-neutral policy runtime.
|
|
3
|
+
*
|
|
4
|
+
* `evaluateIntent(intent, { policy, mandate, intelligence })` combines:
|
|
5
|
+
* - LOCAL hard controls (ceilings, network/asset restrictions, allow/block
|
|
6
|
+
* lists, mandate validation, cumulative caps, recurring price checks) —
|
|
7
|
+
* deterministic, no network, keeps working through an API outage;
|
|
8
|
+
* - REMOTE intelligence (counterparty score, wash/fleet detection) via an
|
|
9
|
+
* injectable async provider;
|
|
10
|
+
* and returns a signed, expiring DecisionToken bound to the intent hash.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately NOT a policy language. Five concrete policies, evaluated in a
|
|
13
|
+
* fixed order with stable reason codes.
|
|
14
|
+
*/
|
|
15
|
+
import { newDecisionId, signDecision, } from "./decision-token.js";
|
|
16
|
+
import { intentHash, toMicroUsd } from "./intent.js";
|
|
17
|
+
export const POLICY_VERSION = "twzrd-pc-v1";
|
|
18
|
+
export function createMemorySpendLedger() {
|
|
19
|
+
const entries = new Map();
|
|
20
|
+
const first = new Map();
|
|
21
|
+
return {
|
|
22
|
+
spentMicro(scopeKey, windowMs, now) {
|
|
23
|
+
const list = entries.get(scopeKey) ?? [];
|
|
24
|
+
let total = 0n;
|
|
25
|
+
for (const e of list)
|
|
26
|
+
if (now - e.at <= windowMs)
|
|
27
|
+
total += e.micro;
|
|
28
|
+
return total;
|
|
29
|
+
},
|
|
30
|
+
record(scopeKey, amountMicro, at) {
|
|
31
|
+
const list = entries.get(scopeKey) ?? [];
|
|
32
|
+
list.push({ at, micro: amountMicro });
|
|
33
|
+
entries.set(scopeKey, list);
|
|
34
|
+
if (!first.has(scopeKey))
|
|
35
|
+
first.set(scopeKey, at);
|
|
36
|
+
},
|
|
37
|
+
firstSeen(scopeKey) {
|
|
38
|
+
return first.get(scopeKey);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const MONTH_MS = 30 * 24 * 60 * 60 * 1000;
|
|
43
|
+
/**
|
|
44
|
+
* Evaluate one PaymentIntent. Never throws on a policy outcome — a block is a
|
|
45
|
+
* signed block decision (auditable), not an exception. Throws only on
|
|
46
|
+
* malformed input (bad amounts) or signer failure.
|
|
47
|
+
*/
|
|
48
|
+
export async function evaluateIntent(intent, options) {
|
|
49
|
+
const now = options.now ?? Date.now();
|
|
50
|
+
const { policy, mandate } = options;
|
|
51
|
+
const amountMicro = toMicroUsd(intent.amount);
|
|
52
|
+
const reasons = [];
|
|
53
|
+
let blocked = false;
|
|
54
|
+
let warned = false;
|
|
55
|
+
const block = (code) => {
|
|
56
|
+
blocked = true;
|
|
57
|
+
reasons.push(code);
|
|
58
|
+
};
|
|
59
|
+
const warn = (code) => {
|
|
60
|
+
warned = true;
|
|
61
|
+
reasons.push(code);
|
|
62
|
+
};
|
|
63
|
+
/* 1. Mandate validation (local, deterministic) */
|
|
64
|
+
if (mandate) {
|
|
65
|
+
if (mandate.expiresAt && now >= Date.parse(mandate.expiresAt)) {
|
|
66
|
+
block("MANDATE_EXPIRED");
|
|
67
|
+
}
|
|
68
|
+
if (mandate.purposes && !mandate.purposes.includes(intent.context?.purpose ?? "")) {
|
|
69
|
+
block("MANDATE_PURPOSE");
|
|
70
|
+
}
|
|
71
|
+
if (mandate.resourceAllow) {
|
|
72
|
+
const url = intent.resource?.url ?? "";
|
|
73
|
+
if (!mandate.resourceAllow.some((prefix) => url.startsWith(prefix))) {
|
|
74
|
+
block("MANDATE_RESOURCE_SCOPE");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (mandate.payeeBlocklist?.includes(intent.payTo)) {
|
|
78
|
+
block("MANDATE_PAYEE_BLOCKED");
|
|
79
|
+
}
|
|
80
|
+
if (mandate.maxPerTransactionUsd !== undefined &&
|
|
81
|
+
amountMicro > toMicroUsd(mandate.maxPerTransactionUsd)) {
|
|
82
|
+
block("MANDATE_MAX_PER_TX");
|
|
83
|
+
}
|
|
84
|
+
if (mandate.monthlyCeilingUsd !== undefined && options.ledger) {
|
|
85
|
+
const spent = options.ledger.spentMicro(`mandate:${mandate.mandateId}`, MONTH_MS, now);
|
|
86
|
+
if (spent + amountMicro > toMicroUsd(mandate.monthlyCeilingUsd)) {
|
|
87
|
+
block("MANDATE_MONTHLY_CEILING");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/* 2. Company policy — local hard controls */
|
|
92
|
+
if (policy) {
|
|
93
|
+
if (policy.blocklist?.includes(intent.payTo))
|
|
94
|
+
block("POLICY_BLOCKLIST");
|
|
95
|
+
if (policy.allowlist && !policy.allowlist.includes(intent.payTo)) {
|
|
96
|
+
block("POLICY_NOT_ALLOWLISTED");
|
|
97
|
+
}
|
|
98
|
+
if (policy.allowedNetworks && !policy.allowedNetworks.includes(intent.network)) {
|
|
99
|
+
block("POLICY_NETWORK");
|
|
100
|
+
}
|
|
101
|
+
if (policy.allowedAssets && !policy.allowedAssets.includes(intent.asset)) {
|
|
102
|
+
block("POLICY_ASSET");
|
|
103
|
+
}
|
|
104
|
+
if (policy.maxAmountUsd !== undefined && amountMicro > toMicroUsd(policy.maxAmountUsd)) {
|
|
105
|
+
block("POLICY_MAX_AMOUNT");
|
|
106
|
+
}
|
|
107
|
+
if (policy.recurringMaxPriceIncreasePct !== undefined &&
|
|
108
|
+
intent.context?.recurring &&
|
|
109
|
+
intent.context.priorSpend) {
|
|
110
|
+
const prior = toMicroUsd(intent.context.priorSpend);
|
|
111
|
+
const ceiling = prior + (prior * BigInt(Math.round(policy.recurringMaxPriceIncreasePct * 100))) / 10000n;
|
|
112
|
+
if (amountMicro > ceiling)
|
|
113
|
+
block("RECURRING_PRICE_INCREASE");
|
|
114
|
+
}
|
|
115
|
+
if (policy.newCounterpartyCap && options.ledger) {
|
|
116
|
+
const windowMs = policy.newCounterpartyCap.windowHours * 3_600_000;
|
|
117
|
+
const scope = `counterparty:${intent.payTo}`;
|
|
118
|
+
const seen = options.ledger.firstSeen(scope);
|
|
119
|
+
const isNew = seen === undefined || now - seen <= windowMs;
|
|
120
|
+
if (isNew) {
|
|
121
|
+
const spent = options.ledger.spentMicro(scope, windowMs, now);
|
|
122
|
+
if (spent + amountMicro > toMicroUsd(policy.newCounterpartyCap.capUsd)) {
|
|
123
|
+
block("NEW_COUNTERPARTY_CAP");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/* 3. Remote intelligence (skipped when already blocked locally) */
|
|
129
|
+
if (!blocked && options.intelligence) {
|
|
130
|
+
const intel = await options.intelligence(intent);
|
|
131
|
+
if (intel.washFlagged && policy?.refuseWashFlagged !== false) {
|
|
132
|
+
block("WASH_FLAGGED");
|
|
133
|
+
}
|
|
134
|
+
if (intel.decision === "block")
|
|
135
|
+
block("INTEL_BLOCK");
|
|
136
|
+
if (intel.decision === "warn")
|
|
137
|
+
warn("INTEL_WARN");
|
|
138
|
+
if (policy?.unknownCounterparty && intel.known === false) {
|
|
139
|
+
if (amountMicro <= toMicroUsd(policy.unknownCounterparty.allowUnderUsd)) {
|
|
140
|
+
warn("UNKNOWN_UNDER_LIMIT");
|
|
141
|
+
}
|
|
142
|
+
else if (policy.unknownCounterparty.aboveAction === "warn") {
|
|
143
|
+
warn("UNKNOWN_ABOVE_LIMIT");
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
block("UNKNOWN_ABOVE_LIMIT");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const verdict = blocked ? "block" : warned ? "warn" : "allow";
|
|
151
|
+
if (verdict === "allow" && reasons.length === 0)
|
|
152
|
+
reasons.push("ALLOW");
|
|
153
|
+
/* 4. Record spend that will be permitted (feeds cumulative/monthly caps) */
|
|
154
|
+
if (!blocked && options.ledger && options.recordSpend !== false) {
|
|
155
|
+
options.ledger.record(`counterparty:${intent.payTo}`, amountMicro, now);
|
|
156
|
+
if (mandate)
|
|
157
|
+
options.ledger.record(`mandate:${mandate.mandateId}`, amountMicro, now);
|
|
158
|
+
}
|
|
159
|
+
/* 5. Signed, expiring token bound to the exact intent */
|
|
160
|
+
return signDecision({
|
|
161
|
+
decision: verdict,
|
|
162
|
+
reasonCodes: reasons,
|
|
163
|
+
intentHash: intentHash(intent),
|
|
164
|
+
policyVersion: POLICY_VERSION,
|
|
165
|
+
decisionId: newDecisionId(),
|
|
166
|
+
expiresAt: new Date(now + (options.ttlMs ?? 120_000)).toISOString(),
|
|
167
|
+
}, options.signer);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=policy-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-runtime.js","sourceRoot":"","sources":["../src/policy-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* @see https://docs.x402.org/advanced-concepts/lifecycle-hooks
|
|
11
11
|
*/
|
|
12
12
|
import type { TwzrdGateConfig } from "./types.js";
|
|
13
|
+
import { type Mandate, type SpendLedger, type SpendPolicy } from "./policy-runtime.js";
|
|
14
|
+
import type { DecisionSigner, PaymentDecision } from "./decision-token.js";
|
|
15
|
+
import type { PaymentIntent } from "./intent.js";
|
|
13
16
|
/** Minimal shape of x402 payment requirements used by the hook. */
|
|
14
17
|
export type X402SelectedRequirements = {
|
|
15
18
|
payTo?: string;
|
|
@@ -43,7 +46,38 @@ export type BeforePaymentCreationResult = {
|
|
|
43
46
|
export type X402ClientLike = {
|
|
44
47
|
onBeforePaymentCreation: (hook: (context: BeforePaymentCreationContext) => Promise<BeforePaymentCreationResult> | BeforePaymentCreationResult) => X402ClientLike | void;
|
|
45
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Opt-in TWZRD Payment Control on the client hook. When set, the hook builds a
|
|
51
|
+
* canonical PaymentIntent from the selected requirement and runs the policy
|
|
52
|
+
* runtime — local mandate + company policy as deterministic hard controls, with
|
|
53
|
+
* the hook's own preflight fed in as remote intelligence — producing a signed,
|
|
54
|
+
* expiring PaymentDecision bound to the exact intent (surfaced on
|
|
55
|
+
* onDecision.decision). A downstream cooperating signer re-checks it with
|
|
56
|
+
* assertIntentApproved and refuses if the intent changed after approval.
|
|
57
|
+
*
|
|
58
|
+
* The decision can only TIGHTEN the legacy gate: a policy/mandate block aborts
|
|
59
|
+
* even when preflight allowed; it never loosens a legacy denial. Leaving
|
|
60
|
+
* paymentControl unset preserves the exact prior behavior.
|
|
61
|
+
*/
|
|
62
|
+
export type X402PaymentControlOptions = {
|
|
63
|
+
/** Ed25519 decision signer (createLocalDecisionSigner() or a remote signer). */
|
|
64
|
+
signer: DecisionSigner;
|
|
65
|
+
policy?: SpendPolicy;
|
|
66
|
+
mandate?: Mandate;
|
|
67
|
+
ledger?: SpendLedger;
|
|
68
|
+
/** Token time-to-live in ms (default 120s). */
|
|
69
|
+
ttlMs?: number;
|
|
70
|
+
/** Spend purpose bound into the intent (matched against mandate.purposes). */
|
|
71
|
+
purpose?: string;
|
|
72
|
+
facilitator?: string;
|
|
73
|
+
/** Resource method bound into the intent (GET/POST/…). */
|
|
74
|
+
method?: string;
|
|
75
|
+
};
|
|
46
76
|
export type InstallX402ClientHookOptions = TwzrdGateConfig & {
|
|
77
|
+
/** Opt-in Payment Control: signed intent-bound decisions + local policy/mandate. */
|
|
78
|
+
paymentControl?: X402PaymentControlOptions;
|
|
79
|
+
/** Clock for decision issue time, Unix ms. Injectable for tests. Default Date.now. */
|
|
80
|
+
now?: () => number;
|
|
47
81
|
/**
|
|
48
82
|
* Optional callback after each policy decision (telemetry / logging).
|
|
49
83
|
* Never throws into the payment path.
|
|
@@ -57,6 +91,10 @@ export type InstallX402ClientHookOptions = TwzrdGateConfig & {
|
|
|
57
91
|
amountMicro?: string;
|
|
58
92
|
reputationScored?: boolean;
|
|
59
93
|
policyAction?: string;
|
|
94
|
+
/** Canonical evaluated intent (present when paymentControl is set). */
|
|
95
|
+
intent?: PaymentIntent;
|
|
96
|
+
/** Signed, intent-bound decision (present when paymentControl is set). */
|
|
97
|
+
decision?: PaymentDecision;
|
|
60
98
|
}) => void;
|
|
61
99
|
};
|
|
62
100
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402-client-hook.d.ts","sourceRoot":"","sources":["../src/x402-client-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,mEAAmE;AACnE,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,oBAAoB,EAAE,wBAAwB,CAAC;IAC/C,uDAAuD;IACvD,YAAY,CAAC,EAAE,wBAAwB,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GACnC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACjB,IAAI,CAAC;AAET;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,uBAAuB,EAAE,CACvB,IAAI,EAAE,CACJ,OAAO,EAAE,4BAA4B,KAClC,OAAO,CAAC,2BAA2B,CAAC,GAAG,2BAA2B,KACpE,cAAc,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,eAAe,GAAG;IAC3D;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE;QACpB,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"x402-client-hook.d.ts","sourceRoot":"","sources":["../src/x402-client-hook.ts"],"names":[],"mappings":"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"}
|
package/dist/x402-client-hook.js
CHANGED
|
@@ -12,9 +12,33 @@
|
|
|
12
12
|
import { resolveConfig } from "./config.js";
|
|
13
13
|
import { priceUsdcFromAmountMicro } from "./payto.js";
|
|
14
14
|
import { twzrdApprovePayment } from "./policy.js";
|
|
15
|
+
import { x402RequirementsToIntent } from "./intent-adapters.js";
|
|
16
|
+
import { evaluateIntent } from "./policy-runtime.js";
|
|
15
17
|
function pickReq(ctx) {
|
|
16
18
|
return ctx.selectedRequirements ?? ctx.requirements ?? {};
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* x402 wire amounts are integer base units (USDC = 6-decimal micro), but
|
|
22
|
+
* PaymentIntent.amount is a DECIMAL USD string (the policy runtime runs it
|
|
23
|
+
* through toMicroUsd). Convert precisely with bigint so amount-based policies
|
|
24
|
+
* are not mis-scaled by 1e6. USDC 6dp matches the gate's existing
|
|
25
|
+
* priceUsdcFromAmountMicro assumption.
|
|
26
|
+
*
|
|
27
|
+
* NOTE: `x402RequirementsToIntent` itself still passes the raw wire amount
|
|
28
|
+
* through into the decimal field — a latent unit bug when that adapter feeds
|
|
29
|
+
* evaluateIntent directly. Corrected here at the hook boundary; the adapter
|
|
30
|
+
* fix belongs in a follow-up (it changes a merged test contract).
|
|
31
|
+
*/
|
|
32
|
+
function microToDecimalUsd(micro, decimals = 6) {
|
|
33
|
+
const m = /^\d+$/.exec(micro.trim());
|
|
34
|
+
if (!m)
|
|
35
|
+
return micro; // non-integer wire amount: leave as-is, let toMicroUsd validate
|
|
36
|
+
const scale = 10n ** BigInt(decimals);
|
|
37
|
+
const value = BigInt(micro.trim());
|
|
38
|
+
const whole = value / scale;
|
|
39
|
+
const frac = (value % scale).toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
40
|
+
return frac ? `${whole}.${frac}` : `${whole}`;
|
|
41
|
+
}
|
|
18
42
|
/**
|
|
19
43
|
* Install TWZRD as the default onBeforePaymentCreation policy engine.
|
|
20
44
|
*
|
|
@@ -48,6 +72,40 @@ export function installTwzrdX402ClientHook(client, options) {
|
|
|
48
72
|
agentIntent: "x402_onBeforePaymentCreation",
|
|
49
73
|
chain: network,
|
|
50
74
|
}, cfg);
|
|
75
|
+
// Opt-in Payment Control: build the canonical intent and run the policy
|
|
76
|
+
// runtime, feeding the preflight result in as remote intelligence. Skipped
|
|
77
|
+
// when payTo/amount are missing — the legacy gate already denies those.
|
|
78
|
+
let intent;
|
|
79
|
+
let decision;
|
|
80
|
+
if (options?.paymentControl && payTo && amountMicro) {
|
|
81
|
+
const pc = options.paymentControl;
|
|
82
|
+
const rawIntent = x402RequirementsToIntent(req, {
|
|
83
|
+
resourceUrl: req.resource,
|
|
84
|
+
method: pc.method,
|
|
85
|
+
facilitator: pc.facilitator,
|
|
86
|
+
purpose: pc.purpose,
|
|
87
|
+
});
|
|
88
|
+
// Correct the wire micro-amount into the decimal USD the runtime expects.
|
|
89
|
+
intent = { ...rawIntent, amount: microToDecimalUsd(amountMicro) };
|
|
90
|
+
decision = await evaluateIntent(intent, {
|
|
91
|
+
signer: pc.signer,
|
|
92
|
+
policy: pc.policy,
|
|
93
|
+
mandate: pc.mandate,
|
|
94
|
+
ledger: pc.ledger,
|
|
95
|
+
ttlMs: pc.ttlMs,
|
|
96
|
+
now: options.now?.(),
|
|
97
|
+
intelligence: () => ({
|
|
98
|
+
known: approval.reputationScored ? true : undefined,
|
|
99
|
+
washFlagged: approval.washFlagged ?? undefined,
|
|
100
|
+
decision: approval.verdict === "allow" ||
|
|
101
|
+
approval.verdict === "warn" ||
|
|
102
|
+
approval.verdict === "block"
|
|
103
|
+
? approval.verdict
|
|
104
|
+
: undefined,
|
|
105
|
+
trustScore: approval.score ?? undefined,
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
51
109
|
try {
|
|
52
110
|
options?.onDecision?.({
|
|
53
111
|
approved: approval.approved,
|
|
@@ -58,16 +116,21 @@ export function installTwzrdX402ClientHook(client, options) {
|
|
|
58
116
|
amountMicro,
|
|
59
117
|
reputationScored: approval.reputationScored,
|
|
60
118
|
policyAction: approval.policyAction,
|
|
119
|
+
intent,
|
|
120
|
+
decision,
|
|
61
121
|
});
|
|
62
122
|
}
|
|
63
123
|
catch {
|
|
64
124
|
// never break payment path on telemetry
|
|
65
125
|
}
|
|
66
|
-
if
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
126
|
+
// Payment Control can only TIGHTEN: abort if the legacy gate denied OR the
|
|
127
|
+
// signed decision blocks. Never loosen a legacy denial.
|
|
128
|
+
const pcBlocks = decision?.decision === "block";
|
|
129
|
+
if (!approval.approved || pcBlocks) {
|
|
130
|
+
const reason = pcBlocks && approval.approved
|
|
131
|
+
? `[twzrd] payment_control_block:${decision?.reasonCodes.join(",")} payTo=${payTo ?? "unknown"}`
|
|
132
|
+
: `[twzrd] ${approval.reason} payTo=${payTo ?? "unknown"} network=${network ?? "unknown"}`;
|
|
133
|
+
return { abort: true, reason };
|
|
71
134
|
}
|
|
72
135
|
// void / undefined → proceed to payment payload creation (same selectedRequirements)
|
|
73
136
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402-client-hook.js","sourceRoot":"","sources":["../src/x402-client-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAgC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"x402-client-hook.js","sourceRoot":"","sources":["../src/x402-client-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAgC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAoD,MAAM,qBAAqB,CAAC;AAgGvG,SAAS,OAAO,CAAC,GAAiC;IAChD,OAAO,GAAG,CAAC,oBAAoB,IAAI,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,KAAa,EAAE,QAAQ,GAAG,CAAC;IACpD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,gEAAgE;IACtF,MAAM,KAAK,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAC5B,MAAM,IAAI,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnF,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAsB,EACtB,OAAsC;IAEtC,MAAM,GAAG,GAA4B,aAAa,CAAC,OAAO,CAAC,CAAC;IAE5D,MAAM,CAAC,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC;QACtC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC;QACxD,MAAM,SAAS,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAE5B,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CACxC;YACE,WAAW,EAAE,GAAG,CAAC,QAAQ;YACzB,KAAK;YACL,SAAS;YACT,WAAW,EAAE,8BAA8B;YAC3C,KAAK,EAAE,OAAO;SACf,EACD,GAAG,CACJ,CAAC;QAEF,wEAAwE;QACxE,2EAA2E;QAC3E,wEAAwE;QACxE,IAAI,MAAiC,CAAC;QACtC,IAAI,QAAqC,CAAC;QAC1C,IAAI,OAAO,EAAE,cAAc,IAAI,KAAK,IAAI,WAAW,EAAE,CAAC;YACpD,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;YAClC,MAAM,SAAS,GAAG,wBAAwB,CAAC,GAAG,EAAE;gBAC9C,WAAW,EAAE,GAAG,CAAC,QAAQ;gBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,WAAW,EAAE,EAAE,CAAC,WAAW;gBAC3B,OAAO,EAAE,EAAE,CAAC,OAAO;aACpB,CAAC,CAAC;YACH,0EAA0E;YAC1E,MAAM,GAAG,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;YAClE,QAAQ,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE;gBACtC,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;gBACnB,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;gBACpB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;oBACnB,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;oBACnD,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,SAAS;oBAC9C,QAAQ,EACN,QAAQ,CAAC,OAAO,KAAK,OAAO;wBAC5B,QAAQ,CAAC,OAAO,KAAK,MAAM;wBAC3B,QAAQ,CAAC,OAAO,KAAK,OAAO;wBAC1B,CAAC,CAAC,QAAQ,CAAC,OAAO;wBAClB,CAAC,CAAC,SAAS;oBACf,UAAU,EAAE,QAAQ,CAAC,KAAK,IAAI,SAAS;iBACxC,CAAC;aACH,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,CAAC;gBACpB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACjC,KAAK;gBACL,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,OAAO;gBACpC,WAAW;gBACX,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;gBAC3C,YAAY,EAAE,QAAQ,CAAC,YAAY;gBACnC,MAAM;gBACN,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;QAED,2EAA2E;QAC3E,wDAAwD;QACxD,MAAM,QAAQ,GAAG,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;QAChD,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC;YACnC,MAAM,MAAM,GACV,QAAQ,IAAI,QAAQ,CAAC,QAAQ;gBAC3B,CAAC,CAAC,iCAAiC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,KAAK,IAAI,SAAS,EAAE;gBAChG,CAAC,CAAC,WAAW,QAAQ,CAAC,MAAM,UAAU,KAAK,IAAI,SAAS,YAAY,OAAO,IAAI,SAAS,EAAE,CAAC;YAC/F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACjC,CAAC;QACD,qFAAqF;IACvF,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,oBAA8C,EAC9C,OAAsC;IAEtC,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,IAAI,oBAAoB,CAAC,MAAM,CAAC;IACxE,MAAM,WAAW,GACf,oBAAoB,CAAC,MAAM,IAAI,oBAAoB,CAAC,iBAAiB,CAAC;IACxE,MAAM,SAAS,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CACxC;QACE,WAAW,EAAE,oBAAoB,CAAC,QAAQ;QAC1C,KAAK;QACL,SAAS;QACT,WAAW,EAAE,8BAA8B;QAC3C,KAAK,EAAE,oBAAoB,CAAC,OAAO;KACpC,EACD,GAAG,CACJ,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,WAAW,QAAQ,CAAC,MAAM,UAAU,KAAK,IAAI,SAAS,EAAE;SACjE,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twzrd-x402-gate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Buyer-side x402 trust gate. Run a free TWZRD preflight (ReadinessCard) before signing USDC to any x402 merchant, and autonomously escalate a `warn` to a $0.001 paid trust check that re-decides
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Buyer-side x402 trust gate. Run a free TWZRD preflight (ReadinessCard) before signing USDC to any x402 merchant, and autonomously escalate a `warn` to a $0.001 paid trust check that re-decides \u2014 blocking the spend on bad paid intel, no human in the loop. Default refuse when free merchant_card.wash_flagged (opt out TWZRD_REFUSE_WASH_FLAGGED=0). A 402 with no extractable payTo fails closed (twzrd_unidentifiable_payment_recipient) instead of falling through to the generic unknown-seller warn. Wraps fetch (HTTP 402) and the @x402/mcp onPaymentRequested hook \u2014 fixed in 0.5.2 to read the real @x402/mcp v2 nested `paymentRequired.accepts` shape (verified against @x402/mcp@2.17.0; previously only the flat legacy shape was read, causing a silent 100%-false-block once wired into a real runtime). installTwzrdAutoGate(payWrap) is the default-on one-liner: guards the raw fetch, then hands it to your x402 client, so the correct guard-before-pay composition can't be built backwards. BREAKING(0.2.0): fail-closed by default \u2014 set TWZRD_FAIL_OPEN=true to restore legacy allow-on-outage behavior.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"build": "tsc",
|
|
28
28
|
"prepublishOnly": "tsc",
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
|
-
"test": "tsx test/policy.test.ts && tsx test/wrap-fetch.test.ts && tsx test/mcp-hook.test.ts && tsx test/attribution.test.ts && tsx test/auto-receipt.test.ts && tsx test/quick.test.ts && tsx test/sponsored.test.ts && tsx test/escalation.test.ts && tsx test/wash-refuse.test.ts && tsx test/auto-gate.test.ts && tsx test/network.test.ts && tsx test/safe-fetch.test.ts && tsx test/challenge-swap.test.ts && tsx test/x402-client-hook.test.ts && tsx test/run-attribution.test.ts",
|
|
30
|
+
"test": "tsx test/policy.test.ts && tsx test/wrap-fetch.test.ts && tsx test/mcp-hook.test.ts && tsx test/attribution.test.ts && tsx test/auto-receipt.test.ts && tsx test/quick.test.ts && tsx test/sponsored.test.ts && tsx test/escalation.test.ts && tsx test/wash-refuse.test.ts && tsx test/auto-gate.test.ts && tsx test/network.test.ts && tsx test/safe-fetch.test.ts && tsx test/challenge-swap.test.ts && tsx test/x402-client-hook.test.ts && tsx test/run-attribution.test.ts && tsx test/payment-control.test.ts && tsx test/intent-binding.test.ts",
|
|
31
31
|
"safe-fetch": "tsx src/safe-fetch.ts",
|
|
32
32
|
"guard-demo": "tsx examples/agentic-market-gate.ts",
|
|
33
33
|
"autoreceipt-demo": "tsx examples/auto-receipt.ts --dry-run",
|
|
@@ -69,11 +69,21 @@
|
|
|
69
69
|
"@x402/svm": ">=2.0.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
|
72
|
-
"@scure/base": {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"@
|
|
76
|
-
|
|
72
|
+
"@scure/base": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@solana/kit": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@x402/core": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@x402/fetch": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@x402/svm": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
77
87
|
},
|
|
78
88
|
"devDependencies": {
|
|
79
89
|
"@scure/base": "^1.2.0",
|