twzrd-x402-gate 0.6.1 → 0.7.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 +53 -0
- package/bin/twzrd-safe-fetch.js +0 -0
- package/dist/decision-token.d.ts +18 -0
- package/dist/decision-token.d.ts.map +1 -1
- package/dist/decision-token.js +99 -1
- package/dist/decision-token.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +51 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +119 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/mpp-hook.d.ts +205 -0
- package/dist/mpp-hook.d.ts.map +1 -0
- package/dist/mpp-hook.js +336 -0
- package/dist/mpp-hook.js.map +1 -0
- package/dist/x402-client-hook.d.ts +34 -10
- package/dist/x402-client-hook.d.ts.map +1 -1
- package/dist/x402-client-hook.js +29 -2
- package/dist/x402-client-hook.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -178,6 +178,59 @@ installTwzrdX402ClientHook(client, {
|
|
|
178
178
|
- Hook binding test:
|
|
179
179
|
[`test/intent-binding.test.ts`](./test/intent-binding.test.ts).
|
|
180
180
|
|
|
181
|
+
### On MPP (Machine Payments Protocol) — Solana charge only
|
|
182
|
+
|
|
183
|
+
`createTwzrdMppOnChallenge` guards `Mppx.create({ onChallenge })`. The mppx
|
|
184
|
+
Solana method signs AND broadcasts the transaction inside `createCredential()`,
|
|
185
|
+
so `onChallenge` is the last deterministic checkpoint before money moves - and
|
|
186
|
+
mppx re-throws `onChallenge` errors, so a TWZRD block is an exception that means
|
|
187
|
+
**`createCredential()` never runs and nothing signs**. On allow, the guard
|
|
188
|
+
creates the credential for the exact challenge it evaluated; non-`solana/charge`
|
|
189
|
+
challenges fail closed (`allowUnevaluated: true` to opt out).
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { Mppx } from "mppx/client";
|
|
193
|
+
import { client as solanaClient } from "mppx-solana";
|
|
194
|
+
import { createTwzrdMppOnChallenge, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
195
|
+
|
|
196
|
+
const mppx = Mppx.create({
|
|
197
|
+
methods: [solanaClient({ signer: wallet })],
|
|
198
|
+
onChallenge: createTwzrdMppOnChallenge({
|
|
199
|
+
signer: createLocalDecisionSigner(),
|
|
200
|
+
policy: { maxAmountUsd: "1.00" },
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Scope limit (honest):** the guard is authoritative only when no
|
|
206
|
+
`onChallengeReceived` event handler supplies a credential. mppx resolves
|
|
207
|
+
`eventCredential ?? onChallenge(...)`, so an event handler returning a credential
|
|
208
|
+
short-circuits the guard and pays ungated. Do not register both on one client.
|
|
209
|
+
|
|
210
|
+
**What it refuses, and why.** Each of these is a case where the transaction mppx
|
|
211
|
+
would actually sign is *not* the transaction the decision covers - so the guard
|
|
212
|
+
fails closed rather than approve a payment it cannot bind:
|
|
213
|
+
|
|
214
|
+
| Case | Code | Reason |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| Sponsored charge | `SPONSORED_CHARGE` | `sponsored` / `sponsorPath` / `feeTokenAmount` make mppx-solana build a **second transfer** to the sponsor on top of the advertised amount. PaymentIntent v1 binds one amount to one payTo, so approving it would authorize strictly more value than the decision covers. |
|
|
217
|
+
| Non-USD-pegged asset | `UNPRICED_ASSET` | Policy ceilings are USD; the wire amount is base-unit tokens. Pricing SOL would store `asset: solana:native` beside a **dollar** `amount` and lose the token quantity actually transferred. (A 1.5 SOL charge of `1500000000` at 9 decimals would otherwise evaluate as "$1.50" and sail under a $5 ceiling while moving ~$270.) USDC/USDT only until an intent version carries token amount + quote. |
|
|
218
|
+
| Unknown cluster | `UNKNOWN_CLUSTER` | mppx-solana's `resolveEndpoint` returns an unrecognized `cluster` **verbatim as the RPC endpoint URL**, and a network string containing "solana" is otherwise scored as mainnet - so `solana:https://seller-rpc.example` would inherit mainnet reputation for a chain never observed. Known cluster names only. |
|
|
219
|
+
| Misdeclared decimals | `MALFORMED_CHALLENGE` | A known stablecoin declaring the wrong decimals is a discount attempt, not a rounding error. |
|
|
220
|
+
|
|
221
|
+
The intent binds a **digest of the entire normalized challenge**, not just
|
|
222
|
+
`realm:id` - swapping `recipient` or `amount` under the same challenge id changes
|
|
223
|
+
the intent hash, so the decision no longer matches.
|
|
224
|
+
|
|
225
|
+
**Verdicts:** `block` throws. `warn` **proceeds to pay** by default - set
|
|
226
|
+
`treatWarnAsBlock: true` to refuse on warn too.
|
|
227
|
+
|
|
228
|
+
- `mppChallengeToIntent` binds the challenge id + realm into the intent
|
|
229
|
+
(`resource.operation`), so the signed decision covers this exact challenge.
|
|
230
|
+
- Cluster names stay honest: `solana:devnet` classifies recognized-but-unscored.
|
|
231
|
+
- Proof: [`test/mpp-hook.test.ts`](./test/mpp-hook.test.ts) - block means
|
|
232
|
+
credential count 0 and signer count 0.
|
|
233
|
+
|
|
181
234
|
### Experimental CLI: `twzrd-safe-fetch` (AgentCash advisory pre-check)
|
|
182
235
|
|
|
183
236
|
> **Not a challenge-bound firewall.** Classification: `advisory_precheck`.
|
package/bin/twzrd-safe-fetch.js
CHANGED
|
File without changes
|
package/dist/decision-token.d.ts
CHANGED
|
@@ -49,6 +49,24 @@ export declare function createLocalDecisionSigner(options?: {
|
|
|
49
49
|
}): DecisionSigner & {
|
|
50
50
|
publicKeyPem: string;
|
|
51
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* Deterministic Ed25519 signer derived from high-entropy key material
|
|
54
|
+
* (issuer==verifier semantics with real signatures). The same secret always
|
|
55
|
+
* yields the same keypair, so an issuer can re-derive its signer across
|
|
56
|
+
* restarts without a keystore.
|
|
57
|
+
*
|
|
58
|
+
* The secret MUST be 32+ bytes of random material, hex or base64 encoded:
|
|
59
|
+
*
|
|
60
|
+
* openssl rand -hex 32
|
|
61
|
+
*
|
|
62
|
+
* Verifiers receive `publicKeyPem` — never the secret. Anyone holding the
|
|
63
|
+
* secret can mint ALLOW decisions, so it is signing key material, not a
|
|
64
|
+
* shared password. Prefer createLocalDecisionSigner with a managed key for
|
|
65
|
+
* anything beyond single-operator deployments.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createSeededDecisionSigner(secret: string, keyId?: string): DecisionSigner & {
|
|
68
|
+
publicKeyPem: string;
|
|
69
|
+
};
|
|
52
70
|
export declare function signDecision(unsigned: Omit<PaymentDecision, "signature" | "keyId">, signer: DecisionSigner): Promise<PaymentDecision>;
|
|
53
71
|
export declare function verifyDecisionSignature(token: PaymentDecision, publicKeyPem: string): boolean;
|
|
54
72
|
export type BindingErrorCode = "INTENT_HASH_MISMATCH" | "DECISION_EXPIRED" | "DECISION_NOT_ALLOW" | "DECISION_REPLAYED" | "BAD_SIGNATURE";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-token.d.ts","sourceRoot":"","sources":["../src/decision-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"decision-token.d.ts","sourceRoot":"","sources":["../src/decision-token.ts"],"names":[],"mappings":"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"}
|
package/dist/decision-token.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* plus expiry, decision === allow, signature, and consume-once. Any mismatch
|
|
11
11
|
* means refuse — the signer is never invoked.
|
|
12
12
|
*/
|
|
13
|
-
import { createPrivateKey, createPublicKey, generateKeyPairSync, randomUUID, sign as edSign, verify as edVerify, } from "node:crypto";
|
|
13
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync, hkdfSync, randomUUID, sign as edSign, verify as edVerify, } from "node:crypto";
|
|
14
14
|
import { canonicalJson, intentHash } from "./intent.js";
|
|
15
15
|
const DECISION_DOMAIN = "twzrd-decision-v1\n";
|
|
16
16
|
/** Everything signed — the token minus the signature itself. */
|
|
@@ -41,6 +41,104 @@ export function createLocalDecisionSigner(options) {
|
|
|
41
41
|
sign: (preimage) => edSign(null, Buffer.from(preimage), privateKey),
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
/** HKDF domain separation for the seeded signer (never reuse this IKM elsewhere). */
|
|
45
|
+
const SEED_SALT = "twzrd-payment-control/decision-signer";
|
|
46
|
+
const SEED_INFO = "ed25519-seed/v1";
|
|
47
|
+
/** Ed25519 seeds are 32 bytes; we demand at least that much real key material. */
|
|
48
|
+
const MIN_SECRET_BYTES = 32;
|
|
49
|
+
/**
|
|
50
|
+
* Decode supplied key material. We accept ONLY encoded random bytes (hex or
|
|
51
|
+
* base64/base64url) — never an arbitrary passphrase.
|
|
52
|
+
*
|
|
53
|
+
* The decision public key is published to verifiers, so a passphrase-derived
|
|
54
|
+
* key is guessable offline: an attacker enumerates candidate passphrases,
|
|
55
|
+
* derives each candidate's public key, and compares. Whoever wins that race
|
|
56
|
+
* can forge ALLOW decisions. Requiring 32 bytes of real entropy makes the
|
|
57
|
+
* search space infeasible rather than merely inconvenient.
|
|
58
|
+
*/
|
|
59
|
+
function decodeKeyMaterial(secret) {
|
|
60
|
+
if (/^[0-9a-fA-F]+$/.test(secret) && secret.length % 2 === 0) {
|
|
61
|
+
return Buffer.from(secret, "hex");
|
|
62
|
+
}
|
|
63
|
+
if (/^[A-Za-z0-9+/_-]+={0,2}$/.test(secret)) {
|
|
64
|
+
const buf = Buffer.from(secret, "base64");
|
|
65
|
+
if (buf.length < MIN_SECRET_BYTES)
|
|
66
|
+
return Buffer.alloc(0);
|
|
67
|
+
// Round-trip guard: a genuine base64 encoding re-encodes to itself, but a
|
|
68
|
+
// memorable passphrase that merely happens to use the base64 alphabet
|
|
69
|
+
// (e.g. "MySuperSecretSigningKey2026Production") does NOT — it round-trips
|
|
70
|
+
// to different bytes. Without this check the length + byte-entropy floors
|
|
71
|
+
// both pass for such a passphrase (ASCII text looks high-entropy per byte),
|
|
72
|
+
// and a guessable secret is exactly the offline-forgery target we reject.
|
|
73
|
+
const normalize = (s) => s.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
74
|
+
if (buf.toString("base64").replace(/=+$/, "") !== normalize(secret)) {
|
|
75
|
+
return Buffer.alloc(0);
|
|
76
|
+
}
|
|
77
|
+
return buf;
|
|
78
|
+
}
|
|
79
|
+
return Buffer.alloc(0);
|
|
80
|
+
}
|
|
81
|
+
/** Shannon entropy per byte. Real random material sits near 8; "aaaa…" sits at 0. */
|
|
82
|
+
function entropyBitsPerByte(buf) {
|
|
83
|
+
if (buf.length === 0)
|
|
84
|
+
return 0;
|
|
85
|
+
const counts = new Map();
|
|
86
|
+
for (const b of buf)
|
|
87
|
+
counts.set(b, (counts.get(b) ?? 0) + 1);
|
|
88
|
+
let h = 0;
|
|
89
|
+
for (const n of counts.values()) {
|
|
90
|
+
const p = n / buf.length;
|
|
91
|
+
h -= p * Math.log2(p);
|
|
92
|
+
}
|
|
93
|
+
return h;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Deterministic Ed25519 signer derived from high-entropy key material
|
|
97
|
+
* (issuer==verifier semantics with real signatures). The same secret always
|
|
98
|
+
* yields the same keypair, so an issuer can re-derive its signer across
|
|
99
|
+
* restarts without a keystore.
|
|
100
|
+
*
|
|
101
|
+
* The secret MUST be 32+ bytes of random material, hex or base64 encoded:
|
|
102
|
+
*
|
|
103
|
+
* openssl rand -hex 32
|
|
104
|
+
*
|
|
105
|
+
* Verifiers receive `publicKeyPem` — never the secret. Anyone holding the
|
|
106
|
+
* secret can mint ALLOW decisions, so it is signing key material, not a
|
|
107
|
+
* shared password. Prefer createLocalDecisionSigner with a managed key for
|
|
108
|
+
* anything beyond single-operator deployments.
|
|
109
|
+
*/
|
|
110
|
+
export function createSeededDecisionSigner(secret, keyId) {
|
|
111
|
+
if (!secret)
|
|
112
|
+
throw new Error("[twzrd] decision secret must be non-empty");
|
|
113
|
+
const ikm = decodeKeyMaterial(secret);
|
|
114
|
+
if (ikm.length < MIN_SECRET_BYTES) {
|
|
115
|
+
throw new Error(`[twzrd] decision secret must carry >=${MIN_SECRET_BYTES} bytes of high-entropy key material, ` +
|
|
116
|
+
"hex or base64 encoded (generate with: openssl rand -hex 32). " +
|
|
117
|
+
"Passphrases are rejected: the decision public key is published, so a guessable " +
|
|
118
|
+
"secret lets an attacker brute-force the signing key offline and forge ALLOW decisions.");
|
|
119
|
+
}
|
|
120
|
+
if (entropyBitsPerByte(ikm) < 3) {
|
|
121
|
+
throw new Error("[twzrd] decision secret decodes to low-entropy material (repeated/patterned bytes). " +
|
|
122
|
+
"Generate real key material: openssl rand -hex 32");
|
|
123
|
+
}
|
|
124
|
+
// Domain-separated derivation: this IKM cannot collide with any other TWZRD
|
|
125
|
+
// key derived from the same secret.
|
|
126
|
+
const seed = Buffer.from(hkdfSync("sha256", ikm, Buffer.from(SEED_SALT), Buffer.from(SEED_INFO), 32));
|
|
127
|
+
// PKCS#8 DER envelope for a raw Ed25519 seed (RFC 8410).
|
|
128
|
+
const pkcs8 = Buffer.concat([
|
|
129
|
+
Buffer.from("302e020100300506032b657004220420", "hex"),
|
|
130
|
+
seed,
|
|
131
|
+
]);
|
|
132
|
+
const privateKey = createPrivateKey({ key: pkcs8, format: "der", type: "pkcs8" });
|
|
133
|
+
const publicKeyPem = createPublicKey(privateKey)
|
|
134
|
+
.export({ type: "spki", format: "pem" })
|
|
135
|
+
.toString();
|
|
136
|
+
return {
|
|
137
|
+
keyId: keyId ?? "seeded-ed25519",
|
|
138
|
+
publicKeyPem,
|
|
139
|
+
sign: (preimage) => edSign(null, Buffer.from(preimage), privateKey),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
44
142
|
export async function signDecision(unsigned, signer) {
|
|
45
143
|
const payload = { ...unsigned, keyId: signer.keyId };
|
|
46
144
|
const signature = Buffer.from(await signer.sign(decisionPreimage(payload))).toString("base64");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-token.js","sourceRoot":"","sources":["../src/decision-token.ts"],"names":[],"mappings":"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"}
|
|
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
|
@@ -16,7 +16,9 @@ export { quickCheck, QUICK_PRICE_USDC, type QuickCheckResult, type QuickCheckOpt
|
|
|
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
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";
|
|
19
|
+
export { assertIntentApproved, createDecisionRegistry, createLocalDecisionSigner, createSeededDecisionSigner, decisionPreimage, newDecisionId, signDecision, verifyDecisionSignature, TwzrdIntentBindingError, type AssertIntentApprovedOptions, type BindingErrorCode, type DecisionConstraints, type DecisionRegistry, type DecisionSigner, type PaymentDecision, type PaymentDecisionVerdict, } from "./decision-token.js";
|
|
20
20
|
export { createMemorySpendLedger, evaluateIntent, POLICY_VERSION, type CounterpartyIntelligence, type EvaluateIntentOptions, type IntelligenceProvider, type Mandate, type SpendLedger, type SpendPolicy, } from "./policy-runtime.js";
|
|
21
21
|
export { ap2CheckoutToIntent, x402RequirementsToIntent, type Ap2Cart, type Ap2UserMandate, type X402IntentContext, } from "./intent-adapters.js";
|
|
22
|
+
export { createTwzrdMppOnChallenge, mppChallengeToIntent, mppChallengeDigest, NATIVE_SOL_CURRENCY, TwzrdMppBlockError, type MppChallenge, type MppIntentContext, type MppOnChallengeHelpers, type MppOnChallengeOptions, type MppSolanaChargeRequest, } from "./mpp-hook.js";
|
|
23
|
+
export { approvalToIntelligence, counterpartyKnownFromApproval, createTwzrdIntelligenceProvider, intentAmountToPriceUsd, type TwzrdIntelligenceOptions, } from "./intelligence.js";
|
|
22
24
|
//# 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,EACjC,KAAK,yBAAyB,GAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,wBAAwB,EACxB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAC"}
|
|
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,EACjC,KAAK,yBAAyB,GAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,wBAAwB,EACxB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,+BAA+B,EAC/B,sBAAsB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,9 @@ export { quickCheck, QUICK_PRICE_USDC, } from "./quick.js";
|
|
|
19
19
|
export { createSponsoredX402Fetch, } from "./sponsored.js";
|
|
20
20
|
/* ── TWZRD Payment Control (protocol-neutral authorization core) ── */
|
|
21
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";
|
|
22
|
+
export { assertIntentApproved, createDecisionRegistry, createLocalDecisionSigner, createSeededDecisionSigner, decisionPreimage, newDecisionId, signDecision, verifyDecisionSignature, TwzrdIntentBindingError, } from "./decision-token.js";
|
|
23
23
|
export { createMemorySpendLedger, evaluateIntent, POLICY_VERSION, } from "./policy-runtime.js";
|
|
24
24
|
export { ap2CheckoutToIntent, x402RequirementsToIntent, } from "./intent-adapters.js";
|
|
25
|
+
export { createTwzrdMppOnChallenge, mppChallengeToIntent, mppChallengeDigest, NATIVE_SOL_CURRENCY, TwzrdMppBlockError, } from "./mpp-hook.js";
|
|
26
|
+
export { approvalToIntelligence, counterpartyKnownFromApproval, createTwzrdIntelligenceProvider, intentAmountToPriceUsd, } from "./intelligence.js";
|
|
25
27
|
//# 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,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"}
|
|
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,0BAA0B,EAC1B,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;AAC9B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,GAMnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,+BAA+B,EAC/B,sBAAsB,GAEvB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — the live TWZRD preflight as an IntelligenceProvider.
|
|
3
|
+
*
|
|
4
|
+
* The x402 client hook feeds its own preflight into evaluateIntent inline;
|
|
5
|
+
* this module gives every OTHER core consumer (AP2/UCP adapters, direct
|
|
6
|
+
* evaluateIntent callers, future protocol surfaces) the same live pipeline
|
|
7
|
+
* (network classify -> free preflight -> readiness policy -> merchant_card
|
|
8
|
+
* wash refuse) as an injectable intelligence provider, without duplicating
|
|
9
|
+
* its behavior.
|
|
10
|
+
*/
|
|
11
|
+
import type { PaymentIntent } from "./intent.js";
|
|
12
|
+
import type { CounterpartyIntelligence, IntelligenceProvider } from "./policy-runtime.js";
|
|
13
|
+
import type { TwzrdApprovalResult, TwzrdGateConfig } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Is this counterparty KNOWN to us — did the score rest on observed evidence
|
|
16
|
+
* about this specific wallet?
|
|
17
|
+
*
|
|
18
|
+
* Deliberately NOT `reputationScored`. That flag only says TWZRD ran Solana
|
|
19
|
+
* behavioral scoring on the payment path; it is true even for a wallet we have
|
|
20
|
+
* never seen — precisely the case a caller most needs flagged. Reading it as
|
|
21
|
+
* "known" marks every scored Solana recipient known:true and silently disables
|
|
22
|
+
* the unknownCounterparty policy, which fires only on `known === false`.
|
|
23
|
+
*
|
|
24
|
+
* Ground truth is `trust_score_basis`, published in `card.caveats[]`:
|
|
25
|
+
* corpus_teaser_v1:provider_reputation_v1 -> evidence -> true
|
|
26
|
+
* corpus_teaser_v1:insufficient_free_evidence -> none -> false
|
|
27
|
+
* absent / unrecognized -> ambiguous -> undefined
|
|
28
|
+
*
|
|
29
|
+
* `undefined` ("we cannot say") must never collapse into `false` ("we looked,
|
|
30
|
+
* and there is nothing"). Only `false` may drive a policy.
|
|
31
|
+
*/
|
|
32
|
+
export declare function counterpartyKnownFromApproval(approval: TwzrdApprovalResult): boolean | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Intent amounts are canonical decimal strings, and the deterministic money
|
|
35
|
+
* policy already runs on bigint micro-units. This Number() conversion exists
|
|
36
|
+
* only to price the preflight call — so guard the boundary rather than letting
|
|
37
|
+
* NaN or Infinity reach the network.
|
|
38
|
+
*/
|
|
39
|
+
export declare function intentAmountToPriceUsd(amount: string): number;
|
|
40
|
+
/** Map a gate approval onto the core intelligence shape. Pure. */
|
|
41
|
+
export declare function approvalToIntelligence(approval: TwzrdApprovalResult): CounterpartyIntelligence;
|
|
42
|
+
export type TwzrdIntelligenceOptions = TwzrdGateConfig & {
|
|
43
|
+
/** Observe the full approval (telemetry / audit). Never throws into evaluation. */
|
|
44
|
+
onApproval?: (approval: TwzrdApprovalResult, intent: PaymentIntent) => void;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Wrap the existing preflight path as an IntelligenceProvider for
|
|
48
|
+
* `evaluateIntent`. `intent.amount` is the canonical decimal asset string.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createTwzrdIntelligenceProvider(options?: TwzrdIntelligenceOptions): IntelligenceProvider;
|
|
51
|
+
//# sourceMappingURL=intelligence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intelligence.d.ts","sourceRoot":"","sources":["../src/intelligence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAcvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,GAAG,SAAS,CAiBrB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAe7D;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,mBAAmB,GAC5B,wBAAwB,CAgB1B;AAED,MAAM,MAAM,wBAAwB,GAAG,eAAe,GAAG;IACvD,mFAAmF;IACnF,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;CAC7E,CAAC;AAEF;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,CAAC,EAAE,wBAAwB,GACjC,oBAAoB,CAoBtB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control — the live TWZRD preflight as an IntelligenceProvider.
|
|
3
|
+
*
|
|
4
|
+
* The x402 client hook feeds its own preflight into evaluateIntent inline;
|
|
5
|
+
* this module gives every OTHER core consumer (AP2/UCP adapters, direct
|
|
6
|
+
* evaluateIntent callers, future protocol surfaces) the same live pipeline
|
|
7
|
+
* (network classify -> free preflight -> readiness policy -> merchant_card
|
|
8
|
+
* wash refuse) as an injectable intelligence provider, without duplicating
|
|
9
|
+
* its behavior.
|
|
10
|
+
*/
|
|
11
|
+
import { resolveConfig } from "./config.js";
|
|
12
|
+
import { twzrdApprovePayment } from "./policy.js";
|
|
13
|
+
/** Prefix of the readiness-card caveat that carries the scoring basis. */
|
|
14
|
+
const BASIS_PREFIX = "trust_score_basis:";
|
|
15
|
+
/**
|
|
16
|
+
* Basis markers meaning "we hold no real evidence on this wallet". The server
|
|
17
|
+
* states this explicitly rather than leaving it to be inferred from a low score.
|
|
18
|
+
*/
|
|
19
|
+
const NO_EVIDENCE = ["insufficient_free_evidence", "default_no_data"];
|
|
20
|
+
/** Basis markers meaning "this verdict rests on observed behavior". */
|
|
21
|
+
const HAS_EVIDENCE = ["provider_reputation"];
|
|
22
|
+
/**
|
|
23
|
+
* Is this counterparty KNOWN to us — did the score rest on observed evidence
|
|
24
|
+
* about this specific wallet?
|
|
25
|
+
*
|
|
26
|
+
* Deliberately NOT `reputationScored`. That flag only says TWZRD ran Solana
|
|
27
|
+
* behavioral scoring on the payment path; it is true even for a wallet we have
|
|
28
|
+
* never seen — precisely the case a caller most needs flagged. Reading it as
|
|
29
|
+
* "known" marks every scored Solana recipient known:true and silently disables
|
|
30
|
+
* the unknownCounterparty policy, which fires only on `known === false`.
|
|
31
|
+
*
|
|
32
|
+
* Ground truth is `trust_score_basis`, published in `card.caveats[]`:
|
|
33
|
+
* corpus_teaser_v1:provider_reputation_v1 -> evidence -> true
|
|
34
|
+
* corpus_teaser_v1:insufficient_free_evidence -> none -> false
|
|
35
|
+
* absent / unrecognized -> ambiguous -> undefined
|
|
36
|
+
*
|
|
37
|
+
* `undefined` ("we cannot say") must never collapse into `false` ("we looked,
|
|
38
|
+
* and there is nothing"). Only `false` may drive a policy.
|
|
39
|
+
*/
|
|
40
|
+
export function counterpartyKnownFromApproval(approval) {
|
|
41
|
+
// Unscored network (Base/EVM, Stripe deposit addresses): no reputation ran at
|
|
42
|
+
// all, so we know nothing about the recipient. Never imply otherwise.
|
|
43
|
+
if (approval.reputationScored !== true)
|
|
44
|
+
return undefined;
|
|
45
|
+
const caveats = approval.card?.caveats;
|
|
46
|
+
if (!Array.isArray(caveats))
|
|
47
|
+
return undefined;
|
|
48
|
+
const basis = caveats
|
|
49
|
+
.filter((c) => typeof c === "string")
|
|
50
|
+
.find((c) => c.trim().toLowerCase().startsWith(BASIS_PREFIX));
|
|
51
|
+
if (!basis)
|
|
52
|
+
return undefined;
|
|
53
|
+
const b = basis.toLowerCase();
|
|
54
|
+
if (NO_EVIDENCE.some((m) => b.includes(m)))
|
|
55
|
+
return false;
|
|
56
|
+
if (HAS_EVIDENCE.some((m) => b.includes(m)))
|
|
57
|
+
return true;
|
|
58
|
+
return undefined; // an unrecognized basis is ambiguous, not evidence
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Intent amounts are canonical decimal strings, and the deterministic money
|
|
62
|
+
* policy already runs on bigint micro-units. This Number() conversion exists
|
|
63
|
+
* only to price the preflight call — so guard the boundary rather than letting
|
|
64
|
+
* NaN or Infinity reach the network.
|
|
65
|
+
*/
|
|
66
|
+
export function intentAmountToPriceUsd(amount) {
|
|
67
|
+
// Number("") and Number(" ") are 0, not NaN — an empty amount would price
|
|
68
|
+
// the call as free instead of being rejected. Reject before coercing.
|
|
69
|
+
if (typeof amount !== "string" || amount.trim() === "") {
|
|
70
|
+
throw new Error(`[twzrd] intent.amount is not a finite, non-negative decimal: ${JSON.stringify(amount)}`);
|
|
71
|
+
}
|
|
72
|
+
const price = Number(amount);
|
|
73
|
+
if (!Number.isFinite(price) || price < 0) {
|
|
74
|
+
throw new Error(`[twzrd] intent.amount is not a finite, non-negative decimal: ${JSON.stringify(amount)}`);
|
|
75
|
+
}
|
|
76
|
+
return price;
|
|
77
|
+
}
|
|
78
|
+
/** Map a gate approval onto the core intelligence shape. Pure. */
|
|
79
|
+
export function approvalToIntelligence(approval) {
|
|
80
|
+
return {
|
|
81
|
+
known: counterpartyKnownFromApproval(approval),
|
|
82
|
+
washFlagged: approval.washFlagged ?? undefined,
|
|
83
|
+
// Any gate DENIAL is a block for the runtime, even when the card verdict
|
|
84
|
+
// was "warn" (can_spend gate, score floor, wash refuse). The verdict only
|
|
85
|
+
// grades approved outcomes.
|
|
86
|
+
decision: !approval.approved
|
|
87
|
+
? "block"
|
|
88
|
+
: approval.verdict === "warn"
|
|
89
|
+
? "warn"
|
|
90
|
+
: approval.verdict === "allow"
|
|
91
|
+
? "allow"
|
|
92
|
+
: undefined,
|
|
93
|
+
trustScore: approval.score ?? undefined,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Wrap the existing preflight path as an IntelligenceProvider for
|
|
98
|
+
* `evaluateIntent`. `intent.amount` is the canonical decimal asset string.
|
|
99
|
+
*/
|
|
100
|
+
export function createTwzrdIntelligenceProvider(options) {
|
|
101
|
+
const cfg = resolveConfig(options);
|
|
102
|
+
return async (intent) => {
|
|
103
|
+
const approval = await twzrdApprovePayment({
|
|
104
|
+
resourceUrl: intent.resource?.url,
|
|
105
|
+
payTo: intent.payTo,
|
|
106
|
+
priceUsdc: intentAmountToPriceUsd(intent.amount),
|
|
107
|
+
agentIntent: `${intent.protocol}_payment_control`,
|
|
108
|
+
chain: intent.network,
|
|
109
|
+
}, cfg);
|
|
110
|
+
try {
|
|
111
|
+
options?.onApproval?.(approval, intent);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// observation must never alter the decision path
|
|
115
|
+
}
|
|
116
|
+
return approvalToIntelligence(approval);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=intelligence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intelligence.js","sourceRoot":"","sources":["../src/intelligence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control on the Machine Payments Protocol (MPP) client path.
|
|
3
|
+
*
|
|
4
|
+
* Narrow scope (deliberate): Solana `charge` only. MPP's client SDK (`mppx`)
|
|
5
|
+
* signs AND broadcasts the Solana transaction inside `createCredential()`, so
|
|
6
|
+
* the last deterministic checkpoint before money moves is the `onChallenge`
|
|
7
|
+
* callback of `Mppx.create`. The guard returned by `createTwzrdMppOnChallenge`
|
|
8
|
+
* decides there:
|
|
9
|
+
*
|
|
10
|
+
* - block → the guard THROWS. `createCredential()` is never invoked; nothing
|
|
11
|
+
* is signed, nothing broadcasts. (mppx re-throws onChallenge
|
|
12
|
+
* errors after emitting `payment.failed` — returning `undefined`
|
|
13
|
+
* would proceed to pay, so refusal must be an exception.)
|
|
14
|
+
* - warn → PROCEEDS TO PAY by default; a warn is not a refusal. Set
|
|
15
|
+
* `treatWarnAsBlock: true` to refuse on warn too.
|
|
16
|
+
* - allow → the guard calls `helpers.createCredential()` itself and returns
|
|
17
|
+
* the credential for the exact challenge it evaluated.
|
|
18
|
+
* - other methods/intents (tempo, stripe, session) → fail-closed by default
|
|
19
|
+
* (`UNEVALUATED_METHOD`); opt out with `allowUnevaluated: true`.
|
|
20
|
+
*
|
|
21
|
+
* SCOPE LIMIT (honest): the guard is authoritative only when no
|
|
22
|
+
* `onChallengeReceived` event handler supplies a credential. mppx resolves
|
|
23
|
+
* `eventCredential ?? onChallenge(...)`, so an event handler that returns a
|
|
24
|
+
* credential SHORT-CIRCUITS this callback entirely and pays ungated. Do not
|
|
25
|
+
* register both on one client.
|
|
26
|
+
*
|
|
27
|
+
* PRICING (the load-bearing constraint): `amount` is a base-unit token
|
|
28
|
+
* quantity; the policy runtime enforces ceilings in USD. Converting one to the
|
|
29
|
+
* other is sound ONLY for assets pegged 1:1 to the dollar, so PaymentIntent v1
|
|
30
|
+
* carries USD-pegged charges ONLY. Native SOL and unknown mints fail closed
|
|
31
|
+
* (`UNPRICED_ASSET`) — there is no caller-supplied price escape hatch, because
|
|
32
|
+
* a priced non-stablecoin intent would record `asset: SOL` beside `amount: <USD>`
|
|
33
|
+
* and LOSE the exact token quantity actually being transferred. Binding a
|
|
34
|
+
* quantity the intent does not carry is precisely the divergence this guard
|
|
35
|
+
* exists to prevent. Pricing non-pegged assets needs a future intent version
|
|
36
|
+
* with explicit token-amount + quote fields; until then, refuse.
|
|
37
|
+
*
|
|
38
|
+
* WHAT THE INTENT MUST BIND (verified against the published mppx-solana@0.2.0
|
|
39
|
+
* `charge` request schema — do not infer this from docs):
|
|
40
|
+
*
|
|
41
|
+
* - SPONSORED charges are refused (`SPONSORED_CHARGE`). When `sponsored` /
|
|
42
|
+
* `sponsorPath` / `feeTokenAmount` are present, mppx-solana builds a
|
|
43
|
+
* SECOND transfer — `feeTokenAmount` to the sponsor's fee payer — on top of
|
|
44
|
+
* the advertised `amount`. PaymentIntent v1 binds a single amount to a
|
|
45
|
+
* single payTo, so an approved sponsored charge would move strictly more
|
|
46
|
+
* money than the decision covered.
|
|
47
|
+
* - CLUSTER must be a known cluster name (`UNKNOWN_CLUSTER` otherwise).
|
|
48
|
+
* mppx-solana's `resolveEndpoint` ends in `return cluster`, so a
|
|
49
|
+
* seller-supplied `cluster` is used verbatim AS THE RPC ENDPOINT URL. Our
|
|
50
|
+
* classifier scores any network string containing "solana", so
|
|
51
|
+
* `solana:https://seller-rpc.example` would be scored as mainnet — reputation
|
|
52
|
+
* invented for a chain we never observed. Refuse rather than score a URL.
|
|
53
|
+
* - The DIGEST of the whole normalized challenge is bound into the intent, not
|
|
54
|
+
* just `realm:id`. Every field mppx will act on (amount, currency, decimals,
|
|
55
|
+
* recipient, cluster, memo, expiry) is covered, so any post-approval mutation
|
|
56
|
+
* changes the intent hash.
|
|
57
|
+
*
|
|
58
|
+
* No dependency on `mppx` — challenge types are structural, matching
|
|
59
|
+
* mppx@0.8.6 `Challenge` and mppx-solana@0.2.0's `charge` request schema.
|
|
60
|
+
*/
|
|
61
|
+
import type { DecisionSigner, PaymentDecision } from "./decision-token.js";
|
|
62
|
+
import { type PaymentIntent } from "./intent.js";
|
|
63
|
+
import { type IntelligenceProvider, type Mandate, type SpendLedger, type SpendPolicy } from "./policy-runtime.js";
|
|
64
|
+
/** Structural mirror of mppx `Challenge` (WWW-Authenticate: Payment ...). */
|
|
65
|
+
export type MppChallenge = {
|
|
66
|
+
id: string;
|
|
67
|
+
realm: string;
|
|
68
|
+
/** Payment method name (e.g. "solana", "tempo", "stripe"). */
|
|
69
|
+
method: string;
|
|
70
|
+
/** Intent kind (e.g. "charge", "session"). */
|
|
71
|
+
intent: string;
|
|
72
|
+
/** Method-specific request data. */
|
|
73
|
+
request: Record<string, unknown>;
|
|
74
|
+
/** ISO-8601 challenge expiry. */
|
|
75
|
+
expires?: string;
|
|
76
|
+
description?: string;
|
|
77
|
+
};
|
|
78
|
+
/** mppx-solana `charge` request fields the adapter consumes. */
|
|
79
|
+
export type MppSolanaChargeRequest = {
|
|
80
|
+
/** Base-unit token amount (e.g. "50000" = 0.05 USDC at 6dp). */
|
|
81
|
+
amount: string;
|
|
82
|
+
/** Mint address of the payment token, or "solana:native" for SOL. */
|
|
83
|
+
currency: string;
|
|
84
|
+
decimals: number;
|
|
85
|
+
/** Receiving wallet — the payTo. */
|
|
86
|
+
recipient: string;
|
|
87
|
+
cluster?: string;
|
|
88
|
+
memo?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Sponsorship fields. Their presence means a SECOND transfer that
|
|
91
|
+
* PaymentIntent v1 cannot bind — the guard refuses rather than approve a
|
|
92
|
+
* payment larger than the decision covers.
|
|
93
|
+
*/
|
|
94
|
+
sponsored?: boolean;
|
|
95
|
+
sponsorPath?: string;
|
|
96
|
+
feeTokenAmount?: string;
|
|
97
|
+
feePayer?: string;
|
|
98
|
+
};
|
|
99
|
+
/** mppx-solana's native-SOL sentinel (NATIVE_SOL_CURRENCY). */
|
|
100
|
+
export declare const NATIVE_SOL_CURRENCY = "solana:native";
|
|
101
|
+
export declare class TwzrdMppBlockError extends Error {
|
|
102
|
+
readonly code: "PAYMENT_CONTROL_BLOCK" | "UNEVALUATED_METHOD" | "MALFORMED_CHALLENGE" | "UNPRICED_ASSET"
|
|
103
|
+
/** Sponsored charge: a second, unbindable transfer to the sponsor. */
|
|
104
|
+
| "SPONSORED_CHARGE"
|
|
105
|
+
/** Cluster is not a known cluster name (i.e. it is a raw RPC endpoint). */
|
|
106
|
+
| "UNKNOWN_CLUSTER";
|
|
107
|
+
readonly reasonCodes?: string[];
|
|
108
|
+
readonly decisionId?: string;
|
|
109
|
+
constructor(code: TwzrdMppBlockError["code"], message: string, detail?: {
|
|
110
|
+
reasonCodes?: string[];
|
|
111
|
+
decisionId?: string;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export type MppIntentContext = {
|
|
115
|
+
/** Resource URL bound into the intent (the paid request's URL). */
|
|
116
|
+
resourceUrl?: string;
|
|
117
|
+
/** HTTP method of the paid request. */
|
|
118
|
+
method?: string;
|
|
119
|
+
/** Spend purpose (matched against mandate.purposes). */
|
|
120
|
+
purpose?: string;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Digest of the ENTIRE normalized challenge — every field mppx-solana will act
|
|
124
|
+
* on, not just `realm:id`.
|
|
125
|
+
*
|
|
126
|
+
* Binding only the identifiers would let a seller (or a compromised
|
|
127
|
+
* orchestration layer) keep the same challenge id while swapping `recipient`,
|
|
128
|
+
* `amount`, `currency`, or `cluster` after TWZRD approved it, and the intent
|
|
129
|
+
* hash would not move. The digest makes any such mutation a different intent.
|
|
130
|
+
*/
|
|
131
|
+
export declare function mppChallengeDigest(challenge: MppChallenge): string;
|
|
132
|
+
/**
|
|
133
|
+
* Normalize an MPP Solana `charge` challenge into PaymentIntent v1.
|
|
134
|
+
*
|
|
135
|
+
* `intent.amount` is always a USD decimal string, because that is what the
|
|
136
|
+
* policy runtime's ceilings mean. Stablecoin charges convert 1:1 from base
|
|
137
|
+
* units; every other asset is refused (UNPRICED_ASSET) rather than priced.
|
|
138
|
+
*
|
|
139
|
+
* Only `method === "solana" && intent === "charge"` is supported. The challenge
|
|
140
|
+
* id + realm are bound into `resource.operation`, so the signed decision covers
|
|
141
|
+
* this exact challenge, not merely an equivalent-looking payment.
|
|
142
|
+
*/
|
|
143
|
+
export declare function mppChallengeToIntent(challenge: MppChallenge, ctx?: MppIntentContext): PaymentIntent;
|
|
144
|
+
export type MppOnChallengeOptions = {
|
|
145
|
+
/** Ed25519 decision signer (createLocalDecisionSigner() or a remote signer). */
|
|
146
|
+
signer: DecisionSigner;
|
|
147
|
+
policy?: SpendPolicy;
|
|
148
|
+
mandate?: Mandate;
|
|
149
|
+
ledger?: SpendLedger;
|
|
150
|
+
intelligence?: IntelligenceProvider;
|
|
151
|
+
/** Decision token time-to-live in ms (default 120s). */
|
|
152
|
+
ttlMs?: number;
|
|
153
|
+
/** Clock, Unix ms. Injectable for tests. */
|
|
154
|
+
now?: () => number;
|
|
155
|
+
/** Spend purpose bound into the intent. */
|
|
156
|
+
purpose?: string;
|
|
157
|
+
/** Resource URL bound into the intent (static or derived per challenge). */
|
|
158
|
+
resourceUrl?: string | ((challenge: MppChallenge) => string | undefined);
|
|
159
|
+
/** HTTP method bound into the intent. */
|
|
160
|
+
method?: string;
|
|
161
|
+
/** Refuse on a `warn` verdict too (default false: warn proceeds to pay). */
|
|
162
|
+
treatWarnAsBlock?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Non-solana-charge challenges: fail-closed by default (throw
|
|
165
|
+
* UNEVALUATED_METHOD before any credential exists). Set true to let mppx's
|
|
166
|
+
* default flow handle them UNGATED.
|
|
167
|
+
*/
|
|
168
|
+
allowUnevaluated?: boolean;
|
|
169
|
+
/** Telemetry after each evaluation. Never throws into the payment path. */
|
|
170
|
+
onDecision?: (detail: {
|
|
171
|
+
challenge: MppChallenge;
|
|
172
|
+
intent?: PaymentIntent;
|
|
173
|
+
decision?: PaymentDecision;
|
|
174
|
+
refusal?: TwzrdMppBlockError;
|
|
175
|
+
}) => void;
|
|
176
|
+
};
|
|
177
|
+
/** Helpers passed by mppx to onChallenge. */
|
|
178
|
+
export type MppOnChallengeHelpers = {
|
|
179
|
+
createCredential: (context?: unknown) => Promise<string>;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* TWZRD guard for `Mppx.create({ onChallenge })`.
|
|
183
|
+
*
|
|
184
|
+
* Authoritative at this seat (see the SCOPE LIMIT note at the top of the file:
|
|
185
|
+
* an `onChallengeReceived` handler that returns a credential bypasses it).
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* import { Mppx } from "mppx/client";
|
|
190
|
+
* import { client as solanaClient } from "mppx-solana";
|
|
191
|
+
* import { createTwzrdMppOnChallenge, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
192
|
+
*
|
|
193
|
+
* const mppx = Mppx.create({
|
|
194
|
+
* methods: [solanaClient({ signer: wallet })],
|
|
195
|
+
* onChallenge: createTwzrdMppOnChallenge({
|
|
196
|
+
* signer: createLocalDecisionSigner(),
|
|
197
|
+
* policy: { maxAmountUsd: "1.00" },
|
|
198
|
+
* // USDC/USDT only. SOL, sponsored charges, and custom clusters are refused.
|
|
199
|
+
* }),
|
|
200
|
+
* });
|
|
201
|
+
* // A blocked seller now throws BEFORE the Solana tx is signed or broadcast.
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export declare function createTwzrdMppOnChallenge(options: MppOnChallengeOptions): (challenge: MppChallenge, helpers: MppOnChallengeHelpers) => Promise<string | undefined>;
|
|
205
|
+
//# sourceMappingURL=mpp-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mpp-hook.d.ts","sourceRoot":"","sources":["../src/mpp-hook.ts"],"names":[],"mappings":"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"}
|
package/dist/mpp-hook.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TWZRD Payment Control on the Machine Payments Protocol (MPP) client path.
|
|
3
|
+
*
|
|
4
|
+
* Narrow scope (deliberate): Solana `charge` only. MPP's client SDK (`mppx`)
|
|
5
|
+
* signs AND broadcasts the Solana transaction inside `createCredential()`, so
|
|
6
|
+
* the last deterministic checkpoint before money moves is the `onChallenge`
|
|
7
|
+
* callback of `Mppx.create`. The guard returned by `createTwzrdMppOnChallenge`
|
|
8
|
+
* decides there:
|
|
9
|
+
*
|
|
10
|
+
* - block → the guard THROWS. `createCredential()` is never invoked; nothing
|
|
11
|
+
* is signed, nothing broadcasts. (mppx re-throws onChallenge
|
|
12
|
+
* errors after emitting `payment.failed` — returning `undefined`
|
|
13
|
+
* would proceed to pay, so refusal must be an exception.)
|
|
14
|
+
* - warn → PROCEEDS TO PAY by default; a warn is not a refusal. Set
|
|
15
|
+
* `treatWarnAsBlock: true` to refuse on warn too.
|
|
16
|
+
* - allow → the guard calls `helpers.createCredential()` itself and returns
|
|
17
|
+
* the credential for the exact challenge it evaluated.
|
|
18
|
+
* - other methods/intents (tempo, stripe, session) → fail-closed by default
|
|
19
|
+
* (`UNEVALUATED_METHOD`); opt out with `allowUnevaluated: true`.
|
|
20
|
+
*
|
|
21
|
+
* SCOPE LIMIT (honest): the guard is authoritative only when no
|
|
22
|
+
* `onChallengeReceived` event handler supplies a credential. mppx resolves
|
|
23
|
+
* `eventCredential ?? onChallenge(...)`, so an event handler that returns a
|
|
24
|
+
* credential SHORT-CIRCUITS this callback entirely and pays ungated. Do not
|
|
25
|
+
* register both on one client.
|
|
26
|
+
*
|
|
27
|
+
* PRICING (the load-bearing constraint): `amount` is a base-unit token
|
|
28
|
+
* quantity; the policy runtime enforces ceilings in USD. Converting one to the
|
|
29
|
+
* other is sound ONLY for assets pegged 1:1 to the dollar, so PaymentIntent v1
|
|
30
|
+
* carries USD-pegged charges ONLY. Native SOL and unknown mints fail closed
|
|
31
|
+
* (`UNPRICED_ASSET`) — there is no caller-supplied price escape hatch, because
|
|
32
|
+
* a priced non-stablecoin intent would record `asset: SOL` beside `amount: <USD>`
|
|
33
|
+
* and LOSE the exact token quantity actually being transferred. Binding a
|
|
34
|
+
* quantity the intent does not carry is precisely the divergence this guard
|
|
35
|
+
* exists to prevent. Pricing non-pegged assets needs a future intent version
|
|
36
|
+
* with explicit token-amount + quote fields; until then, refuse.
|
|
37
|
+
*
|
|
38
|
+
* WHAT THE INTENT MUST BIND (verified against the published mppx-solana@0.2.0
|
|
39
|
+
* `charge` request schema — do not infer this from docs):
|
|
40
|
+
*
|
|
41
|
+
* - SPONSORED charges are refused (`SPONSORED_CHARGE`). When `sponsored` /
|
|
42
|
+
* `sponsorPath` / `feeTokenAmount` are present, mppx-solana builds a
|
|
43
|
+
* SECOND transfer — `feeTokenAmount` to the sponsor's fee payer — on top of
|
|
44
|
+
* the advertised `amount`. PaymentIntent v1 binds a single amount to a
|
|
45
|
+
* single payTo, so an approved sponsored charge would move strictly more
|
|
46
|
+
* money than the decision covered.
|
|
47
|
+
* - CLUSTER must be a known cluster name (`UNKNOWN_CLUSTER` otherwise).
|
|
48
|
+
* mppx-solana's `resolveEndpoint` ends in `return cluster`, so a
|
|
49
|
+
* seller-supplied `cluster` is used verbatim AS THE RPC ENDPOINT URL. Our
|
|
50
|
+
* classifier scores any network string containing "solana", so
|
|
51
|
+
* `solana:https://seller-rpc.example` would be scored as mainnet — reputation
|
|
52
|
+
* invented for a chain we never observed. Refuse rather than score a URL.
|
|
53
|
+
* - The DIGEST of the whole normalized challenge is bound into the intent, not
|
|
54
|
+
* just `realm:id`. Every field mppx will act on (amount, currency, decimals,
|
|
55
|
+
* recipient, cluster, memo, expiry) is covered, so any post-approval mutation
|
|
56
|
+
* changes the intent hash.
|
|
57
|
+
*
|
|
58
|
+
* No dependency on `mppx` — challenge types are structural, matching
|
|
59
|
+
* mppx@0.8.6 `Challenge` and mppx-solana@0.2.0's `charge` request schema.
|
|
60
|
+
*/
|
|
61
|
+
import { createHash } from "node:crypto";
|
|
62
|
+
import { canonicalJson } from "./intent.js";
|
|
63
|
+
import { evaluateIntent, } from "./policy-runtime.js";
|
|
64
|
+
/** mppx-solana's native-SOL sentinel (NATIVE_SOL_CURRENCY). */
|
|
65
|
+
export const NATIVE_SOL_CURRENCY = "solana:native";
|
|
66
|
+
/**
|
|
67
|
+
* Cluster names mppx-solana maps to a real Solana RPC. Anything else is passed
|
|
68
|
+
* through as a raw endpoint URL (`resolveEndpoint` ends in `return cluster`),
|
|
69
|
+
* so it is not a cluster we can identify — and must never be scored.
|
|
70
|
+
*/
|
|
71
|
+
const KNOWN_CLUSTERS = new Set([
|
|
72
|
+
"mainnet-beta",
|
|
73
|
+
"mainnet",
|
|
74
|
+
"devnet",
|
|
75
|
+
"testnet",
|
|
76
|
+
"localnet",
|
|
77
|
+
"localhost",
|
|
78
|
+
]);
|
|
79
|
+
/**
|
|
80
|
+
* Assets we can price at 1:1 USD without an oracle, with their REAL decimals.
|
|
81
|
+
* A challenge that declares different decimals for these mints is lying (or
|
|
82
|
+
* broken) — either way it is not evaluable, so it is refused.
|
|
83
|
+
*/
|
|
84
|
+
const USD_PEGGED_ASSETS = {
|
|
85
|
+
// USDC mainnet
|
|
86
|
+
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: 6,
|
|
87
|
+
// USDC devnet
|
|
88
|
+
Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr: 6,
|
|
89
|
+
// USDT mainnet
|
|
90
|
+
Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: 6,
|
|
91
|
+
};
|
|
92
|
+
export class TwzrdMppBlockError extends Error {
|
|
93
|
+
code;
|
|
94
|
+
reasonCodes;
|
|
95
|
+
decisionId;
|
|
96
|
+
constructor(code, message, detail) {
|
|
97
|
+
super(message);
|
|
98
|
+
this.name = "TwzrdMppBlockError";
|
|
99
|
+
this.code = code;
|
|
100
|
+
this.reasonCodes = detail?.reasonCodes;
|
|
101
|
+
this.decisionId = detail?.decisionId;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Base units -> whole-token decimal string. Rejects anything it cannot scale
|
|
106
|
+
* exactly (non-integer amount, non-integer/negative decimals) rather than
|
|
107
|
+
* returning the raw string, which the runtime would misread as a USD figure.
|
|
108
|
+
*/
|
|
109
|
+
function baseUnitsToDecimal(units, decimals) {
|
|
110
|
+
const t = String(units).trim();
|
|
111
|
+
if (!/^\d+$/.test(t)) {
|
|
112
|
+
throw new TwzrdMppBlockError("MALFORMED_CHALLENGE", `[twzrd] MPP charge amount is not a base-unit integer: ${JSON.stringify(units)}`);
|
|
113
|
+
}
|
|
114
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18) {
|
|
115
|
+
throw new TwzrdMppBlockError("MALFORMED_CHALLENGE", `[twzrd] MPP charge has unusable decimals: ${JSON.stringify(decimals)}`);
|
|
116
|
+
}
|
|
117
|
+
if (decimals === 0)
|
|
118
|
+
return t;
|
|
119
|
+
const scale = 10n ** BigInt(decimals);
|
|
120
|
+
const value = BigInt(t);
|
|
121
|
+
const whole = value / scale;
|
|
122
|
+
const frac = (value % scale).toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
123
|
+
return frac ? `${whole}.${frac}` : `${whole}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Digest of the ENTIRE normalized challenge — every field mppx-solana will act
|
|
127
|
+
* on, not just `realm:id`.
|
|
128
|
+
*
|
|
129
|
+
* Binding only the identifiers would let a seller (or a compromised
|
|
130
|
+
* orchestration layer) keep the same challenge id while swapping `recipient`,
|
|
131
|
+
* `amount`, `currency`, or `cluster` after TWZRD approved it, and the intent
|
|
132
|
+
* hash would not move. The digest makes any such mutation a different intent.
|
|
133
|
+
*/
|
|
134
|
+
export function mppChallengeDigest(challenge) {
|
|
135
|
+
const req = (challenge.request ?? {});
|
|
136
|
+
const normalized = {
|
|
137
|
+
id: challenge.id,
|
|
138
|
+
realm: challenge.realm,
|
|
139
|
+
method: challenge.method,
|
|
140
|
+
intent: challenge.intent,
|
|
141
|
+
expires: challenge.expires ?? null,
|
|
142
|
+
// Every request field, not a curated subset: an unknown field that mppx
|
|
143
|
+
// acts on must still change the digest.
|
|
144
|
+
request: Object.fromEntries(Object.keys(req)
|
|
145
|
+
.sort()
|
|
146
|
+
.map((k) => [k, req[k] ?? null])),
|
|
147
|
+
};
|
|
148
|
+
return createHash("sha256").update(canonicalJson(normalized), "utf8").digest("hex");
|
|
149
|
+
}
|
|
150
|
+
/** Structural deep-freeze so telemetry cannot mutate the challenge mid-flight. */
|
|
151
|
+
function frozenClone(value) {
|
|
152
|
+
return Object.freeze(JSON.parse(JSON.stringify(value), (_k, v) => v && typeof v === "object" ? Object.freeze(v) : v));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Normalize an MPP Solana `charge` challenge into PaymentIntent v1.
|
|
156
|
+
*
|
|
157
|
+
* `intent.amount` is always a USD decimal string, because that is what the
|
|
158
|
+
* policy runtime's ceilings mean. Stablecoin charges convert 1:1 from base
|
|
159
|
+
* units; every other asset is refused (UNPRICED_ASSET) rather than priced.
|
|
160
|
+
*
|
|
161
|
+
* Only `method === "solana" && intent === "charge"` is supported. The challenge
|
|
162
|
+
* id + realm are bound into `resource.operation`, so the signed decision covers
|
|
163
|
+
* this exact challenge, not merely an equivalent-looking payment.
|
|
164
|
+
*/
|
|
165
|
+
export function mppChallengeToIntent(challenge, ctx) {
|
|
166
|
+
if (challenge.method !== "solana" || challenge.intent !== "charge") {
|
|
167
|
+
throw new TwzrdMppBlockError("UNEVALUATED_METHOD", `[twzrd] MPP guard evaluates solana/charge only; got ${challenge.method}/${challenge.intent}`);
|
|
168
|
+
}
|
|
169
|
+
const req = challenge.request;
|
|
170
|
+
if (typeof req.recipient !== "string" ||
|
|
171
|
+
!req.recipient ||
|
|
172
|
+
typeof req.currency !== "string" ||
|
|
173
|
+
!req.currency ||
|
|
174
|
+
(typeof req.amount !== "string" && typeof req.amount !== "number") ||
|
|
175
|
+
typeof req.decimals !== "number") {
|
|
176
|
+
throw new TwzrdMppBlockError("MALFORMED_CHALLENGE", "[twzrd] MPP solana charge challenge missing/mistyped recipient, amount, currency, or decimals");
|
|
177
|
+
}
|
|
178
|
+
// SPONSORSHIP: mppx-solana adds a SECOND transfer of `feeTokenAmount` to the
|
|
179
|
+
// sponsor's fee payer, on top of `amount` to `recipient`. PaymentIntent v1
|
|
180
|
+
// binds one amount to one payTo, so approving this would authorize strictly
|
|
181
|
+
// more money than the decision covers — the exact approve/execute divergence
|
|
182
|
+
// the intent hash exists to prevent. Refuse until an intent version can carry
|
|
183
|
+
// multiple legs.
|
|
184
|
+
if (req.sponsored === true ||
|
|
185
|
+
typeof req.sponsorPath === "string" ||
|
|
186
|
+
typeof req.feeTokenAmount === "string") {
|
|
187
|
+
throw new TwzrdMppBlockError("SPONSORED_CHARGE", "[twzrd] MPP sponsored charge refused: it transfers feeTokenAmount" +
|
|
188
|
+
`${req.feeTokenAmount ? ` (${req.feeTokenAmount})` : ""} to a sponsor in addition to` +
|
|
189
|
+
" the advertised amount, and PaymentIntent v1 binds only a single transfer." +
|
|
190
|
+
" Approving it would authorize more value than the signed decision covers.");
|
|
191
|
+
}
|
|
192
|
+
// CLUSTER: mppx-solana's resolveEndpoint returns an unrecognized cluster
|
|
193
|
+
// verbatim as the RPC endpoint URL. A seller-supplied endpoint is not a
|
|
194
|
+
// cluster we can identify, and our classifier scores anything containing
|
|
195
|
+
// "solana" — so `solana:https://seller-rpc.example` would be scored as
|
|
196
|
+
// mainnet. Never score a URL; refuse it.
|
|
197
|
+
const cluster = req.cluster ?? "mainnet-beta";
|
|
198
|
+
if (!KNOWN_CLUSTERS.has(cluster)) {
|
|
199
|
+
throw new TwzrdMppBlockError("UNKNOWN_CLUSTER", `[twzrd] MPP charge names an unknown cluster ${JSON.stringify(cluster)}.` +
|
|
200
|
+
" mppx-solana uses this verbatim as the RPC endpoint, so it identifies no known" +
|
|
201
|
+
" chain and must not inherit mainnet reputation. Known clusters: " +
|
|
202
|
+
[...KNOWN_CLUSTERS].join(", "));
|
|
203
|
+
}
|
|
204
|
+
const pegDecimals = USD_PEGGED_ASSETS[req.currency];
|
|
205
|
+
// ASSET: PaymentIntent v1 carries USD-pegged assets ONLY. A priced SOL charge
|
|
206
|
+
// would store `asset: solana:native` beside `amount: <USD>` and lose the token
|
|
207
|
+
// quantity actually transferred — binding a number that is not the number the
|
|
208
|
+
// wallet signs. Refuse; a future intent version needs tokenAmount + quote.
|
|
209
|
+
if (pegDecimals === undefined) {
|
|
210
|
+
const raw = String(req.amount);
|
|
211
|
+
throw new TwzrdMppBlockError("UNPRICED_ASSET", `[twzrd] MPP charge in non-USD-pegged asset ${req.currency} (${raw} base units).` +
|
|
212
|
+
" PaymentIntent v1 expresses amounts in USD, so pricing this would record a dollar" +
|
|
213
|
+
" figure while losing the exact token quantity being transferred. Refused by design.");
|
|
214
|
+
}
|
|
215
|
+
// A stablecoin challenge that misdeclares decimals is not evaluable: the
|
|
216
|
+
// declared value scales the amount, so trusting it would let a seller pass
|
|
217
|
+
// 1e9 USDC base units off as "$1" (mppx-solana's transfer_checked would also
|
|
218
|
+
// reject the mismatch on-chain — refuse before signing, not after).
|
|
219
|
+
if (req.decimals !== pegDecimals) {
|
|
220
|
+
throw new TwzrdMppBlockError("MALFORMED_CHALLENGE", `[twzrd] MPP charge declares decimals=${req.decimals} for a mint with ${pegDecimals} decimals`);
|
|
221
|
+
}
|
|
222
|
+
const amountUsd = baseUnitsToDecimal(String(req.amount), req.decimals); // 1 token == 1 USD
|
|
223
|
+
return {
|
|
224
|
+
protocol: "mpp",
|
|
225
|
+
network: `solana:${cluster}`,
|
|
226
|
+
asset: req.currency,
|
|
227
|
+
amount: amountUsd,
|
|
228
|
+
payTo: req.recipient,
|
|
229
|
+
resource: {
|
|
230
|
+
url: ctx?.resourceUrl,
|
|
231
|
+
method: ctx?.method,
|
|
232
|
+
// Bind the WHOLE challenge, not just its identifiers: swapping recipient
|
|
233
|
+
// or amount under the same id changes this digest, hence the intent hash.
|
|
234
|
+
operation: `mpp-charge:${challenge.realm}:${challenge.id}:${mppChallengeDigest(challenge)}`,
|
|
235
|
+
},
|
|
236
|
+
context: {
|
|
237
|
+
purpose: ctx?.purpose,
|
|
238
|
+
expiresAt: challenge.expires,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* TWZRD guard for `Mppx.create({ onChallenge })`.
|
|
244
|
+
*
|
|
245
|
+
* Authoritative at this seat (see the SCOPE LIMIT note at the top of the file:
|
|
246
|
+
* an `onChallengeReceived` handler that returns a credential bypasses it).
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* import { Mppx } from "mppx/client";
|
|
251
|
+
* import { client as solanaClient } from "mppx-solana";
|
|
252
|
+
* import { createTwzrdMppOnChallenge, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
253
|
+
*
|
|
254
|
+
* const mppx = Mppx.create({
|
|
255
|
+
* methods: [solanaClient({ signer: wallet })],
|
|
256
|
+
* onChallenge: createTwzrdMppOnChallenge({
|
|
257
|
+
* signer: createLocalDecisionSigner(),
|
|
258
|
+
* policy: { maxAmountUsd: "1.00" },
|
|
259
|
+
* // USDC/USDT only. SOL, sponsored charges, and custom clusters are refused.
|
|
260
|
+
* }),
|
|
261
|
+
* });
|
|
262
|
+
* // A blocked seller now throws BEFORE the Solana tx is signed or broadcast.
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function createTwzrdMppOnChallenge(options) {
|
|
266
|
+
return async (challenge, helpers) => {
|
|
267
|
+
let intent;
|
|
268
|
+
try {
|
|
269
|
+
intent = mppChallengeToIntent(challenge, {
|
|
270
|
+
resourceUrl: typeof options.resourceUrl === "function"
|
|
271
|
+
? options.resourceUrl(challenge)
|
|
272
|
+
: options.resourceUrl,
|
|
273
|
+
method: options.method,
|
|
274
|
+
purpose: options.purpose,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const refusal = asBlockError(error, "MALFORMED_CHALLENGE");
|
|
279
|
+
if (refusal.code === "UNEVALUATED_METHOD" && options.allowUnevaluated) {
|
|
280
|
+
emitDecision(options, { challenge });
|
|
281
|
+
return undefined; // explicit opt-out: mppx default flow, ungated
|
|
282
|
+
}
|
|
283
|
+
emitDecision(options, { challenge, refusal });
|
|
284
|
+
throw refusal;
|
|
285
|
+
}
|
|
286
|
+
let decision;
|
|
287
|
+
try {
|
|
288
|
+
decision = await evaluateIntent(intent, {
|
|
289
|
+
signer: options.signer,
|
|
290
|
+
policy: options.policy,
|
|
291
|
+
mandate: options.mandate,
|
|
292
|
+
ledger: options.ledger,
|
|
293
|
+
intelligence: options.intelligence,
|
|
294
|
+
ttlMs: options.ttlMs,
|
|
295
|
+
now: options.now?.(),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
// A runtime failure (bad amount, signer error) must never fall through to
|
|
300
|
+
// payment: refuse, exactly as a block would.
|
|
301
|
+
const refusal = asBlockError(error, "MALFORMED_CHALLENGE");
|
|
302
|
+
emitDecision(options, { challenge, intent, refusal });
|
|
303
|
+
throw refusal;
|
|
304
|
+
}
|
|
305
|
+
emitDecision(options, { challenge, intent, decision });
|
|
306
|
+
const refuse = decision.decision === "block" ||
|
|
307
|
+
(decision.decision === "warn" && options.treatWarnAsBlock === true);
|
|
308
|
+
if (refuse) {
|
|
309
|
+
throw new TwzrdMppBlockError("PAYMENT_CONTROL_BLOCK", `[twzrd] mpp_payment_control_${decision.decision}:${decision.reasonCodes.join(",")} payTo=${intent.payTo}`, { reasonCodes: decision.reasonCodes, decisionId: decision.decisionId });
|
|
310
|
+
}
|
|
311
|
+
// allow (or warn, when not treated as block): create the credential for the
|
|
312
|
+
// EXACT challenge evaluated.
|
|
313
|
+
return helpers.createCredential();
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/** Normalize any thrown value into a TwzrdMppBlockError so callers can branch on .code. */
|
|
317
|
+
function asBlockError(error, fallback) {
|
|
318
|
+
if (error instanceof TwzrdMppBlockError)
|
|
319
|
+
return error;
|
|
320
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
321
|
+
return new TwzrdMppBlockError(fallback, `[twzrd] MPP evaluation failed: ${message}`);
|
|
322
|
+
}
|
|
323
|
+
function emitDecision(options, detail) {
|
|
324
|
+
if (!options.onDecision)
|
|
325
|
+
return;
|
|
326
|
+
try {
|
|
327
|
+
// Telemetry runs BEFORE createCredential(). Handing it the live challenge
|
|
328
|
+
// object would let an observer mutate the very fields the decision was
|
|
329
|
+
// signed over, between evaluation and signing. Emit a frozen copy.
|
|
330
|
+
options.onDecision({ ...detail, challenge: frozenClone(detail.challenge) });
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// never break the payment path on telemetry
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=mpp-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mpp-hook.js","sourceRoot":"","sources":["../src/mpp-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { TwzrdGateConfig } from "./types.js";
|
|
13
13
|
import { type Mandate, type SpendLedger, type SpendPolicy } from "./policy-runtime.js";
|
|
14
|
-
import type
|
|
14
|
+
import { type DecisionSigner, type PaymentDecision } from "./decision-token.js";
|
|
15
15
|
import type { PaymentIntent } from "./intent.js";
|
|
16
16
|
/** Minimal shape of x402 payment requirements used by the hook. */
|
|
17
17
|
export type X402SelectedRequirements = {
|
|
@@ -30,21 +30,27 @@ export type X402SelectedRequirements = {
|
|
|
30
30
|
*/
|
|
31
31
|
export type BeforePaymentCreationContext = {
|
|
32
32
|
selectedRequirements: X402SelectedRequirements;
|
|
33
|
+
/** Full 402 response body; present on the official client, unused by the gate. */
|
|
34
|
+
paymentRequired?: unknown;
|
|
33
35
|
/** Some client versions may nest under requirements */
|
|
34
36
|
requirements?: X402SelectedRequirements;
|
|
35
37
|
};
|
|
36
|
-
export type BeforePaymentCreationResult = {
|
|
38
|
+
export type BeforePaymentCreationResult = void | {
|
|
37
39
|
abort: true;
|
|
38
40
|
reason: string;
|
|
39
|
-
}
|
|
40
|
-
abort?: false;
|
|
41
|
-
} | void;
|
|
41
|
+
};
|
|
42
42
|
/**
|
|
43
43
|
* Minimal client surface — no hard dependency on @x402/core at compile time.
|
|
44
|
-
*
|
|
44
|
+
*
|
|
45
|
+
* Must stay assignable FROM the official x402Client under strictFunctionTypes:
|
|
46
|
+
* the hook type we claim the client accepts must satisfy the client's own
|
|
47
|
+
* BeforePaymentCreationHook — async-only, returning nothing or
|
|
48
|
+
* `{ abort: true, reason }`. A sync return branch or an `{ abort: false }`
|
|
49
|
+
* variant here makes `new x402Client()` fail to typecheck against this
|
|
50
|
+
* surface. Proven by test/x402-official-compat.test.ts against @x402/fetch.
|
|
45
51
|
*/
|
|
46
52
|
export type X402ClientLike = {
|
|
47
|
-
onBeforePaymentCreation: (hook: (context: BeforePaymentCreationContext) => Promise<BeforePaymentCreationResult>
|
|
53
|
+
onBeforePaymentCreation: (hook: (context: BeforePaymentCreationContext) => Promise<BeforePaymentCreationResult>) => unknown;
|
|
48
54
|
};
|
|
49
55
|
/**
|
|
50
56
|
* Opt-in TWZRD Payment Control on the client hook. When set, the hook builds a
|
|
@@ -59,9 +65,7 @@ export type X402ClientLike = {
|
|
|
59
65
|
* even when preflight allowed; it never loosens a legacy denial. Leaving
|
|
60
66
|
* paymentControl unset preserves the exact prior behavior.
|
|
61
67
|
*/
|
|
62
|
-
|
|
63
|
-
/** Ed25519 decision signer (createLocalDecisionSigner() or a remote signer). */
|
|
64
|
-
signer: DecisionSigner;
|
|
68
|
+
type X402PaymentControlCommon = {
|
|
65
69
|
policy?: SpendPolicy;
|
|
66
70
|
mandate?: Mandate;
|
|
67
71
|
ledger?: SpendLedger;
|
|
@@ -73,6 +77,25 @@ export type X402PaymentControlOptions = {
|
|
|
73
77
|
/** Resource method bound into the intent (GET/POST/…). */
|
|
74
78
|
method?: string;
|
|
75
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Exactly one signing source. Modeled as a union so the type system rejects
|
|
82
|
+
* both-at-once, and checked again at runtime for JS callers: silently
|
|
83
|
+
* preferring one over the other would let an operator believe decisions are
|
|
84
|
+
* signed by a key that is not in fact signing them.
|
|
85
|
+
*/
|
|
86
|
+
export type X402PaymentControlOptions = X402PaymentControlCommon & ({
|
|
87
|
+
/** Ed25519 decision signer (createLocalDecisionSigner() or a remote signer). */
|
|
88
|
+
signer: DecisionSigner;
|
|
89
|
+
secret?: never;
|
|
90
|
+
} | {
|
|
91
|
+
/**
|
|
92
|
+
* High-entropy key material (32+ bytes, hex or base64 — `openssl rand -hex 32`)
|
|
93
|
+
* deterministically derived into an Ed25519 signer. Verifiers receive the
|
|
94
|
+
* signer's `publicKeyPem`, never this secret.
|
|
95
|
+
*/
|
|
96
|
+
secret: string;
|
|
97
|
+
signer?: never;
|
|
98
|
+
});
|
|
76
99
|
export type InstallX402ClientHookOptions = TwzrdGateConfig & {
|
|
77
100
|
/** Opt-in Payment Control: signed intent-bound decisions + local policy/mandate. */
|
|
78
101
|
paymentControl?: X402PaymentControlOptions;
|
|
@@ -122,4 +145,5 @@ export declare function installTwzrdX402ClientHook(client: X402ClientLike, optio
|
|
|
122
145
|
* Returns abort result without requiring a client instance.
|
|
123
146
|
*/
|
|
124
147
|
export declare function twzrdBeforePaymentCreation(selectedRequirements: X402SelectedRequirements, options?: InstallX402ClientHookOptions): Promise<BeforePaymentCreationResult>;
|
|
148
|
+
export {};
|
|
125
149
|
//# sourceMappingURL=x402-client-hook.d.ts.map
|
|
@@ -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,OAAO,EAAkB,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvG,OAAO,KAAK,
|
|
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
|
@@ -14,6 +14,8 @@ import { priceUsdcFromAmountMicro } from "./payto.js";
|
|
|
14
14
|
import { twzrdApprovePayment } from "./policy.js";
|
|
15
15
|
import { x402RequirementsToIntent } from "./intent-adapters.js";
|
|
16
16
|
import { evaluateIntent } from "./policy-runtime.js";
|
|
17
|
+
import { createSeededDecisionSigner, } from "./decision-token.js";
|
|
18
|
+
import { counterpartyKnownFromApproval } from "./intelligence.js";
|
|
17
19
|
function pickReq(ctx) {
|
|
18
20
|
return ctx.selectedRequirements ?? ctx.requirements ?? {};
|
|
19
21
|
}
|
|
@@ -37,6 +39,23 @@ function pickReq(ctx) {
|
|
|
37
39
|
*/
|
|
38
40
|
export function installTwzrdX402ClientHook(client, options) {
|
|
39
41
|
const cfg = resolveConfig(options);
|
|
42
|
+
// Resolve the Payment Control signer once at install time (fail fast).
|
|
43
|
+
// EXACTLY one of signer/secret — never silently prefer one when both are set.
|
|
44
|
+
let pcSigner;
|
|
45
|
+
if (options?.paymentControl) {
|
|
46
|
+
const { signer, secret } = options.paymentControl;
|
|
47
|
+
const hasSigner = signer != null;
|
|
48
|
+
const hasSecret = typeof secret === "string" && secret.length > 0;
|
|
49
|
+
if (hasSigner && hasSecret) {
|
|
50
|
+
throw new Error("[twzrd] paymentControl: provide exactly one of `signer` or `secret` — both were given. " +
|
|
51
|
+
"Silently choosing one would sign decisions with a key you did not intend.");
|
|
52
|
+
}
|
|
53
|
+
if (!hasSigner && !hasSecret) {
|
|
54
|
+
throw new Error("[twzrd] paymentControl: provide exactly one of `signer` or `secret` — neither was given. " +
|
|
55
|
+
"Decisions must be signed to be verifiable at the signer boundary.");
|
|
56
|
+
}
|
|
57
|
+
pcSigner = hasSigner ? signer : createSeededDecisionSigner(secret);
|
|
58
|
+
}
|
|
40
59
|
client.onBeforePaymentCreation(async (context) => {
|
|
41
60
|
const req = pickReq(context);
|
|
42
61
|
const payTo = req.payTo ?? req.pay_to;
|
|
@@ -65,14 +84,22 @@ export function installTwzrdX402ClientHook(client, options) {
|
|
|
65
84
|
purpose: pc.purpose,
|
|
66
85
|
});
|
|
67
86
|
decision = await evaluateIntent(intent, {
|
|
68
|
-
signer:
|
|
87
|
+
signer: pcSigner,
|
|
69
88
|
policy: pc.policy,
|
|
70
89
|
mandate: pc.mandate,
|
|
71
90
|
ledger: pc.ledger,
|
|
72
91
|
ttlMs: pc.ttlMs,
|
|
73
92
|
now: options.now?.(),
|
|
93
|
+
// Ledger hygiene: never record spend for a payment the legacy gate
|
|
94
|
+
// already denied — the hook aborts it regardless of this decision,
|
|
95
|
+
// so recording would pollute cumulative/monthly caps.
|
|
96
|
+
recordSpend: approval.approved,
|
|
74
97
|
intelligence: () => ({
|
|
75
|
-
|
|
98
|
+
// NOT reputationScored — that flag is true for any scored Solana path,
|
|
99
|
+
// including a wallet we have never observed, which would mark every
|
|
100
|
+
// recipient known:true and disable unknownCounterparty entirely.
|
|
101
|
+
// Same shared mapping as the standalone intelligence provider.
|
|
102
|
+
known: counterpartyKnownFromApproval(approval),
|
|
76
103
|
washFlagged: approval.washFlagged ?? undefined,
|
|
77
104
|
decision: approval.verdict === "allow" ||
|
|
78
105
|
approval.verdict === "warn" ||
|
|
@@ -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;AAElD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAoD,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"x402-client-hook.js","sourceRoot":"","sources":["../src/x402-client-hook.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twzrd-x402-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
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",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc",
|
|
28
28
|
"prepublishOnly": "tsc",
|
|
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 && tsx test/payment-control.test.ts && tsx test/intent-binding.test.ts",
|
|
29
|
+
"typecheck": "tsc --noEmit -p tsconfig.check.json",
|
|
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 && tsx test/mpp-hook.test.ts && tsx test/mpp-safety.test.ts && tsx test/payment-control-followup.test.ts && tsx test/payment-control-hardening.test.ts && tsx test/x402-official-compat.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",
|