@reinconsole/mock-rails 0.1.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/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/index.d.ts +295 -0
- package/dist/index.js +725 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rein contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @reinconsole/mock-rails
|
|
2
|
+
|
|
3
|
+
The simulated payment world for **[Rein](https://github.com/bugiiiii11/rein)** — a fully offline twin of the real [x402](https://www.x402.org) rails. Try the whole guard loop — budgets, caps, kill switch, shadow-spend detection — with no accounts, no chain, no funds.
|
|
4
|
+
|
|
5
|
+
> **Status: v0.1 — early open-source infrastructure.** APIs may change before 1.0. See it running (the live console world runs on these rails): [Rein console](https://reinconsole-production.up.railway.app/).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @reinconsole/mock-rails
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What's in it
|
|
14
|
+
|
|
15
|
+
Three pieces, mirroring the production architecture (their live siblings live in [`@reinconsole/x402-rails`](https://www.npmjs.com/package/@reinconsole/x402-rails)):
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
MockLedger, // the chain: append-only transfers, open to anyone
|
|
20
|
+
MockFacilitator, // x402 settlement (the facilitator's role); payerFor() plugs into the SDK guard
|
|
21
|
+
MockIndexer, // watches the ledger, reconciles spend against ALLOW decisions,
|
|
22
|
+
// emits payment.settled — or shadow.spend, the bypass signal
|
|
23
|
+
createMockVendor, // an in-process x402-paywalled vendor to complete the loop
|
|
24
|
+
} from '@reinconsole/mock-rails';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **The ledger is honestly open** — anything can append a transfer, which is exactly what makes shadow-spend detection meaningful: a payment that bypassed the guard still lands on the ledger, and the indexer flags it as unreconciled.
|
|
28
|
+
- **The facilitator speaks real x402 shapes** — payment headers encode/decode through the same wire schemas the guard parses, so tests exercise the actual parsing paths.
|
|
29
|
+
- **The indexer closes the loop** — managed-wallet spend reconciles against engine decisions; everything else becomes a `shadow.spend` event on the bus.
|
|
30
|
+
- **`createMockVendor()`** gives you a paywalled endpoint in-process — the standard test/demo counterpart for [`@reinconsole/sdk`](https://www.npmjs.com/package/@reinconsole/sdk)'s guard and [`@reinconsole/gate`](https://www.npmjs.com/package/@reinconsole/gate)'s gated fetch.
|
|
31
|
+
|
|
32
|
+
The [monorepo](https://github.com/bugiiiii11/rein)'s offline demos (5 scenarios in under 500 ms) are wired entirely on this package.
|
|
33
|
+
|
|
34
|
+
MIT © Rein contributors · [Repository](https://github.com/bugiiiii11/rein) · [Issues](https://github.com/bugiiiii11/rein/issues)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { Chain, Asset, ReinEvent, Agent, SettledPayment } from '@reinconsole/core';
|
|
2
|
+
import { Payer, PaymentRequirement, FetchLike } from '@reinconsole/sdk';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The simulated chain. One MockLedger plays every chain at once: entries carry
|
|
7
|
+
* their `chain` tag instead of living on separate ledgers, which keeps the
|
|
8
|
+
* mock world to a single object.
|
|
9
|
+
*
|
|
10
|
+
* Crucially, ANYONE can call `transfer()` directly — that is the bypass path.
|
|
11
|
+
* An agent that pays a vendor without going through the guard still leaves a
|
|
12
|
+
* ledger entry, and the indexer turns that into a `shadow.spend` event.
|
|
13
|
+
*/
|
|
14
|
+
interface TransferInput {
|
|
15
|
+
chain: Chain;
|
|
16
|
+
asset: Asset;
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
/** Human-unit decimal amount, e.g. "0.01". */
|
|
20
|
+
amount: string;
|
|
21
|
+
/** Opaque settlement memo; the mock facilitator writes the intent id here. */
|
|
22
|
+
memo?: string;
|
|
23
|
+
}
|
|
24
|
+
interface LedgerEntry extends TransferInput {
|
|
25
|
+
txHash: string;
|
|
26
|
+
blockNumber: bigint;
|
|
27
|
+
at: Date;
|
|
28
|
+
}
|
|
29
|
+
declare class MockLedger {
|
|
30
|
+
private readonly log;
|
|
31
|
+
private readonly bus;
|
|
32
|
+
private height;
|
|
33
|
+
/** Append a confirmed transfer (one entry == one tx in its own block). */
|
|
34
|
+
transfer(input: TransferInput): LedgerEntry;
|
|
35
|
+
/** All transfers, oldest first. */
|
|
36
|
+
entries(): readonly LedgerEntry[];
|
|
37
|
+
/** Subscribe to new transfers as they confirm (what the indexer watches). */
|
|
38
|
+
onEntry(handler: (entry: LedgerEntry) => void): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Wire codecs for the two x402 headers the mock rails speak: `X-PAYMENT`
|
|
43
|
+
* (agent -> vendor -> facilitator) and `X-PAYMENT-RESPONSE` (vendor -> agent).
|
|
44
|
+
* Both are base64-encoded JSON per the x402 spec.
|
|
45
|
+
*/
|
|
46
|
+
/** The mock `exact` scheme payload carried inside X-PAYMENT. */
|
|
47
|
+
declare const MockExactPayload: z.ZodObject<{
|
|
48
|
+
from: z.ZodString;
|
|
49
|
+
to: z.ZodString;
|
|
50
|
+
/** Atomic-unit amount, mirroring the requirement's maxAmountRequired. */
|
|
51
|
+
value: z.ZodString;
|
|
52
|
+
asset: z.ZodString;
|
|
53
|
+
/**
|
|
54
|
+
* Rein linkage: the guard's payer stamps the intent id here, the facilitator
|
|
55
|
+
* memos it onto the ledger entry, and the indexer reconciles exactly. A real
|
|
56
|
+
* x402 transfer authorization carries a nonce that serves the same role.
|
|
57
|
+
*/
|
|
58
|
+
intentId: z.ZodOptional<z.ZodString>;
|
|
59
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
60
|
+
}, "strip", z.ZodTypeAny, {
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
value: string;
|
|
64
|
+
asset: string;
|
|
65
|
+
intentId?: string | undefined;
|
|
66
|
+
nonce?: string | undefined;
|
|
67
|
+
}, {
|
|
68
|
+
from: string;
|
|
69
|
+
to: string;
|
|
70
|
+
value: string;
|
|
71
|
+
asset: string;
|
|
72
|
+
intentId?: string | undefined;
|
|
73
|
+
nonce?: string | undefined;
|
|
74
|
+
}>;
|
|
75
|
+
type MockExactPayload = z.infer<typeof MockExactPayload>;
|
|
76
|
+
/** The full X-PAYMENT header body (x402 spec v1 envelope). */
|
|
77
|
+
declare const MockPaymentHeader: z.ZodObject<{
|
|
78
|
+
x402Version: z.ZodLiteral<1>;
|
|
79
|
+
scheme: z.ZodString;
|
|
80
|
+
network: z.ZodString;
|
|
81
|
+
payload: z.ZodObject<{
|
|
82
|
+
from: z.ZodString;
|
|
83
|
+
to: z.ZodString;
|
|
84
|
+
/** Atomic-unit amount, mirroring the requirement's maxAmountRequired. */
|
|
85
|
+
value: z.ZodString;
|
|
86
|
+
asset: z.ZodString;
|
|
87
|
+
/**
|
|
88
|
+
* Rein linkage: the guard's payer stamps the intent id here, the facilitator
|
|
89
|
+
* memos it onto the ledger entry, and the indexer reconciles exactly. A real
|
|
90
|
+
* x402 transfer authorization carries a nonce that serves the same role.
|
|
91
|
+
*/
|
|
92
|
+
intentId: z.ZodOptional<z.ZodString>;
|
|
93
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
from: string;
|
|
96
|
+
to: string;
|
|
97
|
+
value: string;
|
|
98
|
+
asset: string;
|
|
99
|
+
intentId?: string | undefined;
|
|
100
|
+
nonce?: string | undefined;
|
|
101
|
+
}, {
|
|
102
|
+
from: string;
|
|
103
|
+
to: string;
|
|
104
|
+
value: string;
|
|
105
|
+
asset: string;
|
|
106
|
+
intentId?: string | undefined;
|
|
107
|
+
nonce?: string | undefined;
|
|
108
|
+
}>;
|
|
109
|
+
}, "strip", z.ZodTypeAny, {
|
|
110
|
+
x402Version: 1;
|
|
111
|
+
scheme: string;
|
|
112
|
+
network: string;
|
|
113
|
+
payload: {
|
|
114
|
+
from: string;
|
|
115
|
+
to: string;
|
|
116
|
+
value: string;
|
|
117
|
+
asset: string;
|
|
118
|
+
intentId?: string | undefined;
|
|
119
|
+
nonce?: string | undefined;
|
|
120
|
+
};
|
|
121
|
+
}, {
|
|
122
|
+
x402Version: 1;
|
|
123
|
+
scheme: string;
|
|
124
|
+
network: string;
|
|
125
|
+
payload: {
|
|
126
|
+
from: string;
|
|
127
|
+
to: string;
|
|
128
|
+
value: string;
|
|
129
|
+
asset: string;
|
|
130
|
+
intentId?: string | undefined;
|
|
131
|
+
nonce?: string | undefined;
|
|
132
|
+
};
|
|
133
|
+
}>;
|
|
134
|
+
type MockPaymentHeader = z.infer<typeof MockPaymentHeader>;
|
|
135
|
+
declare function encodePaymentHeader(header: MockPaymentHeader): string;
|
|
136
|
+
declare function decodePaymentHeader(raw: string): MockPaymentHeader;
|
|
137
|
+
/** What the facilitator returns on settle; the vendor base64s it into X-PAYMENT-RESPONSE. */
|
|
138
|
+
declare const SettlementResponse: z.ZodObject<{
|
|
139
|
+
success: z.ZodBoolean;
|
|
140
|
+
/** The tx hash on the mock ledger (the guard surfaces this on the receipt). */
|
|
141
|
+
transaction: z.ZodString;
|
|
142
|
+
network: z.ZodString;
|
|
143
|
+
payer: z.ZodOptional<z.ZodString>;
|
|
144
|
+
}, "strip", z.ZodTypeAny, {
|
|
145
|
+
network: string;
|
|
146
|
+
success: boolean;
|
|
147
|
+
transaction: string;
|
|
148
|
+
payer?: string | undefined;
|
|
149
|
+
}, {
|
|
150
|
+
network: string;
|
|
151
|
+
success: boolean;
|
|
152
|
+
transaction: string;
|
|
153
|
+
payer?: string | undefined;
|
|
154
|
+
}>;
|
|
155
|
+
type SettlementResponse = z.infer<typeof SettlementResponse>;
|
|
156
|
+
declare function encodeSettlementHeader(response: SettlementResponse): string;
|
|
157
|
+
|
|
158
|
+
interface MockFacilitatorOptions {
|
|
159
|
+
ledger: MockLedger;
|
|
160
|
+
/** Recorded on settled payments (shows up in SettledPayment.facilitator). */
|
|
161
|
+
name?: string;
|
|
162
|
+
/** Extra token-address -> symbol mappings, mirroring the guard's option. */
|
|
163
|
+
assetAddresses?: Record<string, Asset>;
|
|
164
|
+
}
|
|
165
|
+
interface SettleResult {
|
|
166
|
+
response: SettlementResponse;
|
|
167
|
+
/** Ready-made value for the vendor's X-PAYMENT-RESPONSE header. */
|
|
168
|
+
header: string;
|
|
169
|
+
/** The transfer as written on the mock chain. */
|
|
170
|
+
entry: LedgerEntry;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The mock x402 facilitator: verifies X-PAYMENT headers and "settles" them by
|
|
174
|
+
* writing transfers onto the mock ledger. Plays the role Coinbase's hosted
|
|
175
|
+
* facilitator plays in production — vendors call `settle()`, agents pay via
|
|
176
|
+
* `payerFor()`, and nothing here is Rein-privileged: settlement happens
|
|
177
|
+
* whether or not a policy engine ever saw the payment. Catching the ones it
|
|
178
|
+
* didn't see is the indexer's job.
|
|
179
|
+
*/
|
|
180
|
+
declare class MockFacilitator {
|
|
181
|
+
readonly name: string;
|
|
182
|
+
private readonly ledger;
|
|
183
|
+
private readonly assetAddresses;
|
|
184
|
+
constructor(options: MockFacilitatorOptions);
|
|
185
|
+
/**
|
|
186
|
+
* A `Payer` for the SDK guard: builds the X-PAYMENT header for an allowed
|
|
187
|
+
* intent, stamping the intent id so settlement reconciles exactly. This is
|
|
188
|
+
* the plug-in point the real x402 signer replaces.
|
|
189
|
+
*/
|
|
190
|
+
payerFor(fromAddress: string): Payer;
|
|
191
|
+
/** Verify an X-PAYMENT header against the requirement it claims to satisfy. */
|
|
192
|
+
verify(paymentHeader: string, requirement: PaymentRequirement): MockPaymentHeader;
|
|
193
|
+
/**
|
|
194
|
+
* Verify, then settle: write the transfer on the mock chain and return the
|
|
195
|
+
* settlement response the vendor relays via X-PAYMENT-RESPONSE.
|
|
196
|
+
*/
|
|
197
|
+
settle(paymentHeader: string, requirement: PaymentRequirement): SettleResult;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** The slice of the policy engine the indexer subscribes to (NATS in prod). */
|
|
201
|
+
interface EngineEvents {
|
|
202
|
+
onEvent(handler: (event: ReinEvent) => void): void;
|
|
203
|
+
}
|
|
204
|
+
interface MockIndexerOptions {
|
|
205
|
+
ledger: MockLedger;
|
|
206
|
+
/**
|
|
207
|
+
* Directory of managed agents and their wallets (the agents service in
|
|
208
|
+
* prod). Called per ledger entry, so agents registered later are still seen.
|
|
209
|
+
*/
|
|
210
|
+
agents: () => readonly Agent[];
|
|
211
|
+
/** Recorded as SettledPayment.facilitator on reconciled payments. */
|
|
212
|
+
facilitator?: string;
|
|
213
|
+
}
|
|
214
|
+
/** A `shadow.spend` event, extracted for convenience accessors. */
|
|
215
|
+
type ShadowSpend = Extract<ReinEvent, {
|
|
216
|
+
type: 'shadow.spend';
|
|
217
|
+
}>;
|
|
218
|
+
/**
|
|
219
|
+
* The mock indexer: watches the ledger and classifies every transfer that
|
|
220
|
+
* leaves a managed agent wallet.
|
|
221
|
+
*
|
|
222
|
+
* - Matches an allowed, unsettled intent -> `payment.settled` (reconciled).
|
|
223
|
+
* - No ALLOW decision behind it -> `shadow.spend` — the bypass signal that
|
|
224
|
+
* catches SDK-mode evasion and drives the upgrade to the signer tier.
|
|
225
|
+
*
|
|
226
|
+
* Reconciliation is memo-first (the facilitator stamps the intent id onto the
|
|
227
|
+
* transfer), with a fallback match on chain+asset+amount+recipient for
|
|
228
|
+
* transfers that arrive without a memo. A memo that does NOT resolve to an
|
|
229
|
+
* allowed unsettled intent (denied, unknown, or already settled — a replay)
|
|
230
|
+
* is never fuzzy-matched: it stays a shadow spend.
|
|
231
|
+
*/
|
|
232
|
+
declare class MockIndexer {
|
|
233
|
+
private readonly options;
|
|
234
|
+
private readonly bus;
|
|
235
|
+
private readonly emitted;
|
|
236
|
+
/** Every intent the engine has seen, by id (from `intent.created`). */
|
|
237
|
+
private readonly intents;
|
|
238
|
+
/** Intents with an ALLOW decision, by id. */
|
|
239
|
+
private readonly allowed;
|
|
240
|
+
private readonly settledIntents;
|
|
241
|
+
constructor(options: MockIndexerOptions);
|
|
242
|
+
/** Subscribe to a policy engine's event stream to learn which intents were allowed. */
|
|
243
|
+
connectEngine(engine: EngineEvents): void;
|
|
244
|
+
onEvent(handler: (event: ReinEvent) => void): void;
|
|
245
|
+
/** Everything the indexer has emitted, oldest first. */
|
|
246
|
+
events(): readonly ReinEvent[];
|
|
247
|
+
settledPayments(): SettledPayment[];
|
|
248
|
+
shadowSpends(): ShadowSpend[];
|
|
249
|
+
private emit;
|
|
250
|
+
private agentFor;
|
|
251
|
+
private observe;
|
|
252
|
+
private reconcile;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface MockVendorOptions {
|
|
256
|
+
facilitator: MockFacilitator;
|
|
257
|
+
/** Price in atomic units (e.g. "10000" = 0.01 USDC at 6 decimals). */
|
|
258
|
+
atomicPrice: string;
|
|
259
|
+
/** Where the vendor wants to be paid. */
|
|
260
|
+
payTo: string;
|
|
261
|
+
network?: string;
|
|
262
|
+
asset?: string;
|
|
263
|
+
scheme?: string;
|
|
264
|
+
/** JSON body served once payment settles. */
|
|
265
|
+
body?: unknown;
|
|
266
|
+
}
|
|
267
|
+
interface VendorCall {
|
|
268
|
+
url: string;
|
|
269
|
+
payment: string | null;
|
|
270
|
+
}
|
|
271
|
+
interface MockVendor {
|
|
272
|
+
/** Drop-in fetch for the guard's vendor-facing side. */
|
|
273
|
+
fetch: FetchLike;
|
|
274
|
+
/** Every request the vendor saw, in order. */
|
|
275
|
+
calls: VendorCall[];
|
|
276
|
+
/** The payment requirement this vendor quotes for a given URL. */
|
|
277
|
+
requirementFor(url: string): PaymentRequirement;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* An in-process x402 vendor wired to the mock facilitator: quotes a 402 with
|
|
281
|
+
* payment requirements until the request carries X-PAYMENT, then settles
|
|
282
|
+
* through the facilitator (writing the transfer on the mock ledger) and serves
|
|
283
|
+
* the content with the settlement in X-PAYMENT-RESPONSE. The third leg of the
|
|
284
|
+
* mock rails — guard pays it, indexer watches the ledger behind it.
|
|
285
|
+
*/
|
|
286
|
+
declare function createMockVendor(options: MockVendorOptions): MockVendor;
|
|
287
|
+
|
|
288
|
+
type FacilitatorErrorCode = 'malformed_payment' | 'unsupported_scheme' | 'unsupported_network' | 'unsupported_asset' | 'network_mismatch' | 'amount_mismatch' | 'recipient_mismatch';
|
|
289
|
+
/** A payment the facilitator refused to settle, with a machine-readable code. */
|
|
290
|
+
declare class FacilitatorError extends Error {
|
|
291
|
+
readonly code: FacilitatorErrorCode;
|
|
292
|
+
constructor(code: FacilitatorErrorCode, message: string);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export { type EngineEvents, FacilitatorError, type FacilitatorErrorCode, type LedgerEntry, MockExactPayload, MockFacilitator, type MockFacilitatorOptions, MockIndexer, type MockIndexerOptions, MockLedger, MockPaymentHeader, type MockVendor, type MockVendorOptions, type SettleResult, SettlementResponse, type ShadowSpend, type TransferInput, type VendorCall, createMockVendor, decodePaymentHeader, encodePaymentHeader, encodeSettlementHeader };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
// src/ledger.ts
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
4
|
+
|
|
5
|
+
// ../../packages/core/dist/index.js
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var Chain = z.enum(["base", "solana", "polygon", "bnb"]);
|
|
8
|
+
var Asset = z.enum(["USDC", "USDT", "EURC"]);
|
|
9
|
+
var DecimalString = z.string().regex(/^\d+(\.\d+)?$/, 'must be a non-negative decimal string, e.g. "5.00"');
|
|
10
|
+
var DECIMAL_RE = /^\d+(\.\d+)?$/;
|
|
11
|
+
function isValidDecimal(value) {
|
|
12
|
+
return DECIMAL_RE.test(value);
|
|
13
|
+
}
|
|
14
|
+
function assertDecimal(value) {
|
|
15
|
+
if (!isValidDecimal(value)) {
|
|
16
|
+
throw new TypeError(`invalid decimal string: ${JSON.stringify(value)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function fractionLength(value) {
|
|
20
|
+
const dot = value.indexOf(".");
|
|
21
|
+
return dot === -1 ? 0 : value.length - dot - 1;
|
|
22
|
+
}
|
|
23
|
+
function toScaled(value, scale) {
|
|
24
|
+
const dot = value.indexOf(".");
|
|
25
|
+
const intPart = dot === -1 ? value : value.slice(0, dot);
|
|
26
|
+
const fracPart = dot === -1 ? "" : value.slice(dot + 1);
|
|
27
|
+
const paddedFrac = (fracPart + "0".repeat(scale)).slice(0, scale);
|
|
28
|
+
return BigInt(intPart + paddedFrac);
|
|
29
|
+
}
|
|
30
|
+
function compareDecimal(a, b) {
|
|
31
|
+
assertDecimal(a);
|
|
32
|
+
assertDecimal(b);
|
|
33
|
+
const scale = Math.max(fractionLength(a), fractionLength(b));
|
|
34
|
+
const av = toScaled(a, scale);
|
|
35
|
+
const bv = toScaled(b, scale);
|
|
36
|
+
return av < bv ? -1 : av > bv ? 1 : 0;
|
|
37
|
+
}
|
|
38
|
+
var ULID_BODY = "[0-9A-HJKMNP-TV-Z]{26}";
|
|
39
|
+
function prefixedId(prefix) {
|
|
40
|
+
return z.string().regex(new RegExp(`^${prefix}_${ULID_BODY}$`), `expected a "${prefix}_" prefixed id`);
|
|
41
|
+
}
|
|
42
|
+
var OrgId = prefixedId("org");
|
|
43
|
+
var AgentId = prefixedId("agt");
|
|
44
|
+
var PolicyId = prefixedId("pol");
|
|
45
|
+
var IntentId = prefixedId("int");
|
|
46
|
+
var DecisionId = prefixedId("dec");
|
|
47
|
+
var ReceiptId = prefixedId("rcp");
|
|
48
|
+
var SessionId = prefixedId("ses");
|
|
49
|
+
var GateReceiptId = prefixedId("grc");
|
|
50
|
+
var EnforcementMode = z.enum(["observed", "sdk", "session-key"]);
|
|
51
|
+
var AgentWallet = z.object({
|
|
52
|
+
chain: Chain,
|
|
53
|
+
address: z.string().min(1),
|
|
54
|
+
mode: EnforcementMode
|
|
55
|
+
});
|
|
56
|
+
var AgentStatus = z.enum(["active", "frozen"]);
|
|
57
|
+
var Agent = z.object({
|
|
58
|
+
id: AgentId,
|
|
59
|
+
orgId: OrgId,
|
|
60
|
+
name: z.string().min(1).max(200),
|
|
61
|
+
/** On-chain ERC-8004 identity, if the agent is registered. */
|
|
62
|
+
erc8004Id: z.string().optional(),
|
|
63
|
+
wallets: z.array(AgentWallet).default([]),
|
|
64
|
+
status: AgentStatus.default("active"),
|
|
65
|
+
createdAt: z.coerce.date()
|
|
66
|
+
});
|
|
67
|
+
var ID_PATTERN = /^eip155:(\d+):(0x[0-9a-fA-F]{40})\/(\d+)$/;
|
|
68
|
+
function parseErc8004Id(value) {
|
|
69
|
+
const match = ID_PATTERN.exec(value);
|
|
70
|
+
if (!match) return void 0;
|
|
71
|
+
const chainId = Number(match[1]);
|
|
72
|
+
if (!Number.isSafeInteger(chainId) || chainId < 1) return void 0;
|
|
73
|
+
return {
|
|
74
|
+
chainId,
|
|
75
|
+
registry: match[2].toLowerCase(),
|
|
76
|
+
tokenId: BigInt(match[3])
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
var Erc8004Id = z.string().refine((value) => parseErc8004Id(value) !== void 0, {
|
|
80
|
+
message: 'expected "eip155:{chainId}:{registry}/{tokenId}"'
|
|
81
|
+
});
|
|
82
|
+
var Vendor = z.object({
|
|
83
|
+
host: z.string().min(1),
|
|
84
|
+
address: z.string().min(1),
|
|
85
|
+
erc8004Id: z.string().optional()
|
|
86
|
+
});
|
|
87
|
+
var TaskContext = z.object({
|
|
88
|
+
taskId: z.string().optional(),
|
|
89
|
+
parentRunId: z.string().optional(),
|
|
90
|
+
purpose: z.string().max(500).optional()
|
|
91
|
+
});
|
|
92
|
+
var PaymentIntent = z.object({
|
|
93
|
+
id: IntentId,
|
|
94
|
+
agentId: AgentId,
|
|
95
|
+
vendor: Vendor,
|
|
96
|
+
resource: z.string(),
|
|
97
|
+
amount: DecimalString,
|
|
98
|
+
asset: Asset,
|
|
99
|
+
chain: Chain,
|
|
100
|
+
taskContext: TaskContext.default({}),
|
|
101
|
+
nonce: z.string().min(1),
|
|
102
|
+
createdAt: z.coerce.date()
|
|
103
|
+
});
|
|
104
|
+
var DecisionOutcome = z.enum(["allow", "deny", "escalate"]);
|
|
105
|
+
var Decision = z.object({
|
|
106
|
+
id: DecisionId,
|
|
107
|
+
intentId: IntentId,
|
|
108
|
+
/**
|
|
109
|
+
* sha256 of the intent's canonical content (see canonical.ts). Binds the
|
|
110
|
+
* decision to the exact transfer it judged — amount, recipient, asset,
|
|
111
|
+
* chain — so a signer can verify an {intent, decision} pair offline as a
|
|
112
|
+
* self-contained spend voucher, not just a reference by id.
|
|
113
|
+
*/
|
|
114
|
+
intentHash: z.string(),
|
|
115
|
+
outcome: DecisionOutcome,
|
|
116
|
+
/** Ids of the rules that fired, for explainability. */
|
|
117
|
+
matchedRules: z.array(z.string()).default([]),
|
|
118
|
+
/** Human-readable explanation surfaced in the dashboard. */
|
|
119
|
+
reason: z.string().optional(),
|
|
120
|
+
policyId: z.string(),
|
|
121
|
+
policyVersion: z.string(),
|
|
122
|
+
/** Hash of the previous decision in the chain (tamper-evident log). */
|
|
123
|
+
prevHash: z.string(),
|
|
124
|
+
/** Hash of this decision's canonical content. */
|
|
125
|
+
hash: z.string(),
|
|
126
|
+
/** Service signing-key signature over `hash`. */
|
|
127
|
+
signature: z.string(),
|
|
128
|
+
latencyMs: z.number().nonnegative(),
|
|
129
|
+
decidedAt: z.coerce.date()
|
|
130
|
+
});
|
|
131
|
+
var Session = z.object({
|
|
132
|
+
id: SessionId,
|
|
133
|
+
agentId: AgentId,
|
|
134
|
+
/** sha256 hex of the bearer token. */
|
|
135
|
+
tokenHash: z.string(),
|
|
136
|
+
/** Cumulative ceiling across the session's lifetime. Absent = uncapped. */
|
|
137
|
+
capAmount: DecimalString.optional(),
|
|
138
|
+
/** Ceiling per individual signature. Absent = uncapped. */
|
|
139
|
+
maxPerPayment: DecimalString.optional(),
|
|
140
|
+
expiresAt: z.coerce.date(),
|
|
141
|
+
createdAt: z.coerce.date(),
|
|
142
|
+
revokedAt: z.coerce.date().optional()
|
|
143
|
+
});
|
|
144
|
+
var SettledPayment = z.object({
|
|
145
|
+
intentId: IntentId,
|
|
146
|
+
txHash: z.string(),
|
|
147
|
+
chain: Chain,
|
|
148
|
+
blockNumber: z.coerce.bigint(),
|
|
149
|
+
facilitator: z.string().optional(),
|
|
150
|
+
feePaid: DecimalString.optional(),
|
|
151
|
+
confirmedAt: z.coerce.date()
|
|
152
|
+
});
|
|
153
|
+
var ReceiptSettlement = z.object({
|
|
154
|
+
txHash: z.string().optional(),
|
|
155
|
+
networkId: z.string().optional(),
|
|
156
|
+
/** Raw header payload, kept for forensics until the indexer confirms. */
|
|
157
|
+
raw: z.string().optional()
|
|
158
|
+
});
|
|
159
|
+
var Receipt = z.object({
|
|
160
|
+
id: ReceiptId,
|
|
161
|
+
agentId: AgentId,
|
|
162
|
+
intentId: IntentId,
|
|
163
|
+
decisionId: DecisionId,
|
|
164
|
+
outcome: DecisionOutcome,
|
|
165
|
+
/** The URL the agent actually fetched (the paywalled resource). */
|
|
166
|
+
url: z.string(),
|
|
167
|
+
method: z.string().default("GET"),
|
|
168
|
+
vendorHost: z.string(),
|
|
169
|
+
amount: DecimalString,
|
|
170
|
+
asset: Asset,
|
|
171
|
+
chain: Chain,
|
|
172
|
+
taskContext: TaskContext.default({}),
|
|
173
|
+
/** Human-readable explanation copied from the decision. */
|
|
174
|
+
reason: z.string().optional(),
|
|
175
|
+
/** Present only once a payment was made and the vendor confirmed it. */
|
|
176
|
+
settlement: ReceiptSettlement.optional(),
|
|
177
|
+
createdAt: z.coerce.date()
|
|
178
|
+
});
|
|
179
|
+
var GateReceipt = z.object({
|
|
180
|
+
id: GateReceiptId,
|
|
181
|
+
at: z.coerce.date(),
|
|
182
|
+
/** The route pattern that priced this request, e.g. "/api/reports/*". */
|
|
183
|
+
route: z.string().min(1),
|
|
184
|
+
/** The concrete resource paid for (URL path actually requested). */
|
|
185
|
+
resource: z.string().min(1),
|
|
186
|
+
method: z.string().min(1),
|
|
187
|
+
/** The paying wallet address, as asserted by the settled payment. */
|
|
188
|
+
payer: z.string().min(1),
|
|
189
|
+
payTo: z.string().min(1),
|
|
190
|
+
/** Human-unit decimal amount, e.g. "0.05". */
|
|
191
|
+
amount: DecimalString,
|
|
192
|
+
/** The same amount in the asset's atomic units, e.g. "50000". */
|
|
193
|
+
amountAtomic: z.string().regex(/^\d+$/, "atomic amount must be an integer string"),
|
|
194
|
+
/** Token symbol or contract address, exactly as quoted in the requirement. */
|
|
195
|
+
asset: z.string().min(1),
|
|
196
|
+
network: z.string().min(1),
|
|
197
|
+
/** Settlement transaction hash (mock ledger or on-chain). */
|
|
198
|
+
transaction: z.string().min(1)
|
|
199
|
+
});
|
|
200
|
+
var Window = z.string().regex(/^\d+[smhd]$/, 'window must be like "30s", "15m", "1h", or "7d"');
|
|
201
|
+
var Multiplier = z.string().regex(/^\d+(\.\d+)?x$/, 'multiplier must be like "3x"');
|
|
202
|
+
var Condition = z.object({
|
|
203
|
+
/** Per-transaction amount exceeds this value. */
|
|
204
|
+
amountGt: DecimalString.optional(),
|
|
205
|
+
/** Sum of spend in a rolling window exceeds `gt`. */
|
|
206
|
+
rollingSum: z.object({ window: Window, gt: DecimalString }).optional(),
|
|
207
|
+
/** Transaction count in a rolling window exceeds `gt`. */
|
|
208
|
+
txCount: z.object({ window: Window, gt: z.number().int().nonnegative() }).optional(),
|
|
209
|
+
/** Vendor host matches one of these patterns (supports `*` globs). */
|
|
210
|
+
vendorHostIn: z.array(z.string()).optional(),
|
|
211
|
+
/** First time Rein has seen this vendor for the agent. */
|
|
212
|
+
vendorFirstSeen: z.boolean().optional(),
|
|
213
|
+
/** Vendor reputation score is below this threshold (Phase 3 hook). */
|
|
214
|
+
vendorReputationLt: z.number().min(0).max(100).optional(),
|
|
215
|
+
/** Amount is more than `gt`-times the observed median for this resource. */
|
|
216
|
+
amountVsResourceMedian: z.object({ gt: Multiplier }).optional()
|
|
217
|
+
}).refine((c) => Object.values(c).some((v) => v !== void 0), {
|
|
218
|
+
message: "a condition must specify at least one predicate"
|
|
219
|
+
});
|
|
220
|
+
var Rule = z.object({
|
|
221
|
+
id: z.string().min(1),
|
|
222
|
+
allow: Condition.optional(),
|
|
223
|
+
deny: Condition.optional(),
|
|
224
|
+
escalate: Condition.optional()
|
|
225
|
+
}).refine((r) => [r.allow, r.deny, r.escalate].filter((v) => v !== void 0).length === 1, {
|
|
226
|
+
message: "a rule must specify exactly one of allow / deny / escalate"
|
|
227
|
+
});
|
|
228
|
+
var PolicyDefault = z.enum(["allow", "deny"]);
|
|
229
|
+
var AppliesTo = z.object({
|
|
230
|
+
/** Agent id patterns (supports `*` globs, e.g. "agt_research_*"). */
|
|
231
|
+
agents: z.array(z.string()).optional(),
|
|
232
|
+
chains: z.array(Chain).optional()
|
|
233
|
+
});
|
|
234
|
+
var Escalation = z.object({
|
|
235
|
+
approvers: z.array(z.string()).default([]),
|
|
236
|
+
timeoutAction: PolicyDefault.default("deny"),
|
|
237
|
+
timeoutMin: z.number().int().positive().default(15)
|
|
238
|
+
});
|
|
239
|
+
var Policy = z.object({
|
|
240
|
+
policyId: z.string().min(1),
|
|
241
|
+
version: z.string().default("1"),
|
|
242
|
+
appliesTo: AppliesTo.default({}),
|
|
243
|
+
rules: z.array(Rule).default([]),
|
|
244
|
+
default: PolicyDefault.default("deny"),
|
|
245
|
+
/** Below this amount, fail-open is permitted during a policy-service outage. */
|
|
246
|
+
denyFloor: DecimalString.default("0.05"),
|
|
247
|
+
escalation: Escalation.optional()
|
|
248
|
+
});
|
|
249
|
+
var ReputationSubject = z.object({
|
|
250
|
+
kind: z.enum(["agent", "vendor"]),
|
|
251
|
+
id: z.string()
|
|
252
|
+
});
|
|
253
|
+
var ReputationComponents = z.object({
|
|
254
|
+
volume: z.number(),
|
|
255
|
+
longevity: z.number(),
|
|
256
|
+
disputeRate: z.number(),
|
|
257
|
+
counterpartyQuality: z.number(),
|
|
258
|
+
settlementReliability: z.number()
|
|
259
|
+
});
|
|
260
|
+
var ReputationScore = z.object({
|
|
261
|
+
subject: ReputationSubject,
|
|
262
|
+
score: z.number().min(0).max(100),
|
|
263
|
+
components: ReputationComponents,
|
|
264
|
+
confidence: z.number().min(0).max(1),
|
|
265
|
+
asOf: z.coerce.date(),
|
|
266
|
+
/** Hash-anchored attestation, optionally published on-chain (ERC-8004). */
|
|
267
|
+
evidenceUri: z.string().optional()
|
|
268
|
+
});
|
|
269
|
+
var ReinEvent = z.discriminatedUnion("type", [
|
|
270
|
+
z.object({ type: z.literal("intent.created"), at: z.coerce.date(), intent: PaymentIntent }),
|
|
271
|
+
z.object({ type: z.literal("decision.made"), at: z.coerce.date(), decision: Decision }),
|
|
272
|
+
z.object({ type: z.literal("payment.settled"), at: z.coerce.date(), payment: SettledPayment }),
|
|
273
|
+
z.object({
|
|
274
|
+
type: z.literal("shadow.spend"),
|
|
275
|
+
at: z.coerce.date(),
|
|
276
|
+
agentId: AgentId,
|
|
277
|
+
txHash: z.string(),
|
|
278
|
+
chain: Chain,
|
|
279
|
+
amount: DecimalString
|
|
280
|
+
}),
|
|
281
|
+
z.object({
|
|
282
|
+
type: z.literal("signature.released"),
|
|
283
|
+
at: z.coerce.date(),
|
|
284
|
+
sessionId: SessionId,
|
|
285
|
+
agentId: AgentId,
|
|
286
|
+
intentId: IntentId,
|
|
287
|
+
decisionId: DecisionId,
|
|
288
|
+
amount: DecimalString
|
|
289
|
+
}),
|
|
290
|
+
z.object({
|
|
291
|
+
type: z.literal("signature.refused"),
|
|
292
|
+
at: z.coerce.date(),
|
|
293
|
+
/** Refusal code, e.g. "decision_replayed" (see @reinconsole/signer). */
|
|
294
|
+
code: z.string(),
|
|
295
|
+
reason: z.string(),
|
|
296
|
+
sessionId: SessionId.optional(),
|
|
297
|
+
agentId: AgentId.optional(),
|
|
298
|
+
intentId: IntentId.optional()
|
|
299
|
+
}),
|
|
300
|
+
z.object({
|
|
301
|
+
type: z.literal("gate.quoted"),
|
|
302
|
+
at: z.coerce.date(),
|
|
303
|
+
resource: z.string(),
|
|
304
|
+
method: z.string(),
|
|
305
|
+
amount: DecimalString,
|
|
306
|
+
asset: z.string(),
|
|
307
|
+
network: z.string()
|
|
308
|
+
}),
|
|
309
|
+
z.object({ type: z.literal("gate.settled"), at: z.coerce.date(), receipt: GateReceipt }),
|
|
310
|
+
z.object({
|
|
311
|
+
type: z.literal("gate.refused"),
|
|
312
|
+
at: z.coerce.date(),
|
|
313
|
+
/** Refusal code, e.g. "payment_replayed" (see @reinconsole/gate). */
|
|
314
|
+
code: z.string(),
|
|
315
|
+
reason: z.string(),
|
|
316
|
+
resource: z.string(),
|
|
317
|
+
/** Known only when the payment header decoded far enough to name a payer. */
|
|
318
|
+
payer: z.string().optional()
|
|
319
|
+
})
|
|
320
|
+
]);
|
|
321
|
+
|
|
322
|
+
// src/ledger.ts
|
|
323
|
+
var MockLedger = class {
|
|
324
|
+
log = [];
|
|
325
|
+
bus = new EventEmitter();
|
|
326
|
+
height = 0n;
|
|
327
|
+
/** Append a confirmed transfer (one entry == one tx in its own block). */
|
|
328
|
+
transfer(input) {
|
|
329
|
+
const entry = {
|
|
330
|
+
...input,
|
|
331
|
+
chain: Chain.parse(input.chain),
|
|
332
|
+
asset: Asset.parse(input.asset),
|
|
333
|
+
amount: DecimalString.parse(input.amount),
|
|
334
|
+
txHash: `0x${randomBytes(32).toString("hex")}`,
|
|
335
|
+
blockNumber: ++this.height,
|
|
336
|
+
at: /* @__PURE__ */ new Date()
|
|
337
|
+
};
|
|
338
|
+
this.log.push(entry);
|
|
339
|
+
this.bus.emit("entry", entry);
|
|
340
|
+
return entry;
|
|
341
|
+
}
|
|
342
|
+
/** All transfers, oldest first. */
|
|
343
|
+
entries() {
|
|
344
|
+
return this.log;
|
|
345
|
+
}
|
|
346
|
+
/** Subscribe to new transfers as they confirm (what the indexer watches). */
|
|
347
|
+
onEntry(handler) {
|
|
348
|
+
this.bus.on("entry", handler);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// ../../packages/sdk/dist/index.js
|
|
353
|
+
import { z as z2 } from "zod";
|
|
354
|
+
var Health = z2.object({ status: z2.string(), publicKey: z2.string() });
|
|
355
|
+
var EvaluateResponse = z2.object({ intent: PaymentIntent, decision: Decision });
|
|
356
|
+
var PaymentRequirement = z2.object({
|
|
357
|
+
scheme: z2.string(),
|
|
358
|
+
network: z2.string(),
|
|
359
|
+
/** Amount in the asset's atomic units (e.g. "10000" = 0.01 USDC at 6 decimals). */
|
|
360
|
+
maxAmountRequired: z2.string().regex(/^\d+$/, "atomic amount must be an integer string"),
|
|
361
|
+
resource: z2.string().optional(),
|
|
362
|
+
description: z2.string().optional(),
|
|
363
|
+
mimeType: z2.string().optional(),
|
|
364
|
+
payTo: z2.string().min(1),
|
|
365
|
+
maxTimeoutSeconds: z2.number().optional(),
|
|
366
|
+
/** Token contract address — or, mock-first, a plain symbol like "USDC". */
|
|
367
|
+
asset: z2.string().min(1),
|
|
368
|
+
extra: z2.record(z2.unknown()).optional()
|
|
369
|
+
});
|
|
370
|
+
var PaymentRequired = z2.object({
|
|
371
|
+
x402Version: z2.number(),
|
|
372
|
+
accepts: z2.array(PaymentRequirement).min(1),
|
|
373
|
+
error: z2.string().optional()
|
|
374
|
+
});
|
|
375
|
+
var NETWORK_TO_CHAIN = {
|
|
376
|
+
base: "base",
|
|
377
|
+
"base-sepolia": "base",
|
|
378
|
+
"eip155:8453": "base",
|
|
379
|
+
"eip155:84532": "base",
|
|
380
|
+
solana: "solana",
|
|
381
|
+
"solana-devnet": "solana",
|
|
382
|
+
polygon: "polygon",
|
|
383
|
+
"polygon-amoy": "polygon",
|
|
384
|
+
bnb: "bnb",
|
|
385
|
+
bsc: "bnb"
|
|
386
|
+
};
|
|
387
|
+
var KNOWN_ASSET_ADDRESSES = {
|
|
388
|
+
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": "USDC",
|
|
389
|
+
// USDC on Base
|
|
390
|
+
"0x036cbd53842c5426634e7929541ec2318f3dcf7e": "USDC",
|
|
391
|
+
// USDC on Base Sepolia
|
|
392
|
+
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC"
|
|
393
|
+
// USDC on Solana
|
|
394
|
+
};
|
|
395
|
+
function networkToChain(network) {
|
|
396
|
+
return NETWORK_TO_CHAIN[network.toLowerCase()];
|
|
397
|
+
}
|
|
398
|
+
function resolveAsset(requirement, extraAddresses = {}) {
|
|
399
|
+
const direct = Asset.safeParse(requirement.asset.toUpperCase());
|
|
400
|
+
if (direct.success) return direct.data;
|
|
401
|
+
const symbol = requirement.extra?.["symbol"];
|
|
402
|
+
if (typeof symbol === "string") {
|
|
403
|
+
const fromExtra = Asset.safeParse(symbol.toUpperCase());
|
|
404
|
+
if (fromExtra.success) return fromExtra.data;
|
|
405
|
+
}
|
|
406
|
+
return extraAddresses[requirement.asset] ?? extraAddresses[requirement.asset.toLowerCase()] ?? KNOWN_ASSET_ADDRESSES[requirement.asset] ?? KNOWN_ASSET_ADDRESSES[requirement.asset.toLowerCase()];
|
|
407
|
+
}
|
|
408
|
+
function atomicToDecimal(atomic, decimals) {
|
|
409
|
+
if (!/^\d+$/.test(atomic)) throw new TypeError(`invalid atomic amount: ${atomic}`);
|
|
410
|
+
if (decimals === 0) return atomic.replace(/^0+(?=\d)/, "");
|
|
411
|
+
const digits = atomic.padStart(decimals + 1, "0");
|
|
412
|
+
const intPart = digits.slice(0, digits.length - decimals).replace(/^0+(?=\d)/, "");
|
|
413
|
+
const fracPart = digits.slice(digits.length - decimals).replace(/0+$/, "");
|
|
414
|
+
return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;
|
|
415
|
+
}
|
|
416
|
+
function requirementDecimals(requirement) {
|
|
417
|
+
const decimals = requirement.extra?.["decimals"];
|
|
418
|
+
return typeof decimals === "number" && Number.isInteger(decimals) && decimals >= 0 ? decimals : 6;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/errors.ts
|
|
422
|
+
var FacilitatorError = class extends Error {
|
|
423
|
+
constructor(code, message) {
|
|
424
|
+
super(message);
|
|
425
|
+
this.code = code;
|
|
426
|
+
this.name = "FacilitatorError";
|
|
427
|
+
}
|
|
428
|
+
code;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// src/payload.ts
|
|
432
|
+
import { z as z3 } from "zod";
|
|
433
|
+
var MockExactPayload = z3.object({
|
|
434
|
+
from: z3.string().min(1),
|
|
435
|
+
to: z3.string().min(1),
|
|
436
|
+
/** Atomic-unit amount, mirroring the requirement's maxAmountRequired. */
|
|
437
|
+
value: z3.string().regex(/^\d+$/, "atomic amount must be an integer string"),
|
|
438
|
+
asset: z3.string().min(1),
|
|
439
|
+
/**
|
|
440
|
+
* Rein linkage: the guard's payer stamps the intent id here, the facilitator
|
|
441
|
+
* memos it onto the ledger entry, and the indexer reconciles exactly. A real
|
|
442
|
+
* x402 transfer authorization carries a nonce that serves the same role.
|
|
443
|
+
*/
|
|
444
|
+
intentId: z3.string().optional(),
|
|
445
|
+
nonce: z3.string().optional()
|
|
446
|
+
});
|
|
447
|
+
var MockPaymentHeader = z3.object({
|
|
448
|
+
x402Version: z3.literal(1),
|
|
449
|
+
scheme: z3.string(),
|
|
450
|
+
network: z3.string(),
|
|
451
|
+
payload: MockExactPayload
|
|
452
|
+
});
|
|
453
|
+
function encodePaymentHeader(header) {
|
|
454
|
+
return Buffer.from(JSON.stringify(header)).toString("base64");
|
|
455
|
+
}
|
|
456
|
+
function decodePaymentHeader(raw) {
|
|
457
|
+
let json;
|
|
458
|
+
try {
|
|
459
|
+
json = JSON.parse(Buffer.from(raw, "base64").toString("utf8"));
|
|
460
|
+
} catch {
|
|
461
|
+
throw new FacilitatorError("malformed_payment", "X-PAYMENT is not base64-encoded JSON");
|
|
462
|
+
}
|
|
463
|
+
const parsed = MockPaymentHeader.safeParse(json);
|
|
464
|
+
if (!parsed.success) {
|
|
465
|
+
throw new FacilitatorError("malformed_payment", `invalid X-PAYMENT body: ${parsed.error.message}`);
|
|
466
|
+
}
|
|
467
|
+
return parsed.data;
|
|
468
|
+
}
|
|
469
|
+
var SettlementResponse = z3.object({
|
|
470
|
+
success: z3.boolean(),
|
|
471
|
+
/** The tx hash on the mock ledger (the guard surfaces this on the receipt). */
|
|
472
|
+
transaction: z3.string(),
|
|
473
|
+
network: z3.string(),
|
|
474
|
+
payer: z3.string().optional()
|
|
475
|
+
});
|
|
476
|
+
function encodeSettlementHeader(response) {
|
|
477
|
+
return Buffer.from(JSON.stringify(response)).toString("base64");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/facilitator.ts
|
|
481
|
+
var MockFacilitator = class {
|
|
482
|
+
name;
|
|
483
|
+
ledger;
|
|
484
|
+
assetAddresses;
|
|
485
|
+
constructor(options) {
|
|
486
|
+
this.ledger = options.ledger;
|
|
487
|
+
this.name = options.name ?? "mock-facilitator";
|
|
488
|
+
this.assetAddresses = options.assetAddresses ?? {};
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* A `Payer` for the SDK guard: builds the X-PAYMENT header for an allowed
|
|
492
|
+
* intent, stamping the intent id so settlement reconciles exactly. This is
|
|
493
|
+
* the plug-in point the real x402 signer replaces.
|
|
494
|
+
*/
|
|
495
|
+
payerFor(fromAddress) {
|
|
496
|
+
return (requirement, intent) => encodePaymentHeader({
|
|
497
|
+
x402Version: 1,
|
|
498
|
+
scheme: requirement.scheme,
|
|
499
|
+
network: requirement.network,
|
|
500
|
+
payload: {
|
|
501
|
+
from: fromAddress,
|
|
502
|
+
to: requirement.payTo,
|
|
503
|
+
value: requirement.maxAmountRequired,
|
|
504
|
+
asset: requirement.asset,
|
|
505
|
+
intentId: intent.id,
|
|
506
|
+
nonce: intent.nonce
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
/** Verify an X-PAYMENT header against the requirement it claims to satisfy. */
|
|
511
|
+
verify(paymentHeader, requirement) {
|
|
512
|
+
const header = decodePaymentHeader(paymentHeader);
|
|
513
|
+
if (header.scheme.toLowerCase() !== "exact" || requirement.scheme.toLowerCase() !== "exact") {
|
|
514
|
+
throw new FacilitatorError(
|
|
515
|
+
"unsupported_scheme",
|
|
516
|
+
`mock facilitator only settles "exact", got "${header.scheme}"/"${requirement.scheme}"`
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
if (header.network !== requirement.network) {
|
|
520
|
+
throw new FacilitatorError(
|
|
521
|
+
"network_mismatch",
|
|
522
|
+
`payment is on "${header.network}" but the requirement wants "${requirement.network}"`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
if (!networkToChain(header.network)) {
|
|
526
|
+
throw new FacilitatorError("unsupported_network", `unknown network "${header.network}"`);
|
|
527
|
+
}
|
|
528
|
+
if (header.payload.value !== requirement.maxAmountRequired) {
|
|
529
|
+
throw new FacilitatorError(
|
|
530
|
+
"amount_mismatch",
|
|
531
|
+
`payment of ${header.payload.value} does not match required ${requirement.maxAmountRequired}`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
if (header.payload.to !== requirement.payTo) {
|
|
535
|
+
throw new FacilitatorError(
|
|
536
|
+
"recipient_mismatch",
|
|
537
|
+
`payment pays "${header.payload.to}" but the requirement pays "${requirement.payTo}"`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
return header;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Verify, then settle: write the transfer on the mock chain and return the
|
|
544
|
+
* settlement response the vendor relays via X-PAYMENT-RESPONSE.
|
|
545
|
+
*/
|
|
546
|
+
settle(paymentHeader, requirement) {
|
|
547
|
+
const header = this.verify(paymentHeader, requirement);
|
|
548
|
+
const chain = networkToChain(header.network);
|
|
549
|
+
if (!chain) throw new FacilitatorError("unsupported_network", `unknown network "${header.network}"`);
|
|
550
|
+
const asset = resolveAsset(requirement, this.assetAddresses);
|
|
551
|
+
if (!asset) {
|
|
552
|
+
throw new FacilitatorError("unsupported_asset", `cannot resolve asset "${requirement.asset}"`);
|
|
553
|
+
}
|
|
554
|
+
const entry = this.ledger.transfer({
|
|
555
|
+
chain,
|
|
556
|
+
asset,
|
|
557
|
+
from: header.payload.from,
|
|
558
|
+
to: header.payload.to,
|
|
559
|
+
amount: atomicToDecimal(header.payload.value, requirementDecimals(requirement)),
|
|
560
|
+
memo: header.payload.intentId
|
|
561
|
+
});
|
|
562
|
+
const response = {
|
|
563
|
+
success: true,
|
|
564
|
+
transaction: entry.txHash,
|
|
565
|
+
network: header.network,
|
|
566
|
+
payer: header.payload.from
|
|
567
|
+
};
|
|
568
|
+
return { response, header: encodeSettlementHeader(response), entry };
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// src/indexer.ts
|
|
573
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
574
|
+
var MockIndexer = class {
|
|
575
|
+
options;
|
|
576
|
+
bus = new EventEmitter2();
|
|
577
|
+
emitted = [];
|
|
578
|
+
/** Every intent the engine has seen, by id (from `intent.created`). */
|
|
579
|
+
intents = /* @__PURE__ */ new Map();
|
|
580
|
+
/** Intents with an ALLOW decision, by id. */
|
|
581
|
+
allowed = /* @__PURE__ */ new Map();
|
|
582
|
+
settledIntents = /* @__PURE__ */ new Set();
|
|
583
|
+
constructor(options) {
|
|
584
|
+
this.options = options;
|
|
585
|
+
options.ledger.onEntry((entry) => this.observe(entry));
|
|
586
|
+
}
|
|
587
|
+
/** Subscribe to a policy engine's event stream to learn which intents were allowed. */
|
|
588
|
+
connectEngine(engine) {
|
|
589
|
+
engine.onEvent((event) => {
|
|
590
|
+
if (event.type === "intent.created") {
|
|
591
|
+
this.intents.set(event.intent.id, event.intent);
|
|
592
|
+
} else if (event.type === "decision.made" && event.decision.outcome === "allow") {
|
|
593
|
+
const intent = this.intents.get(event.decision.intentId);
|
|
594
|
+
if (intent) this.allowed.set(intent.id, intent);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
onEvent(handler) {
|
|
599
|
+
this.bus.on("event", handler);
|
|
600
|
+
}
|
|
601
|
+
/** Everything the indexer has emitted, oldest first. */
|
|
602
|
+
events() {
|
|
603
|
+
return this.emitted;
|
|
604
|
+
}
|
|
605
|
+
settledPayments() {
|
|
606
|
+
return this.emitted.flatMap((e) => e.type === "payment.settled" ? [e.payment] : []);
|
|
607
|
+
}
|
|
608
|
+
shadowSpends() {
|
|
609
|
+
return this.emitted.filter((e) => e.type === "shadow.spend");
|
|
610
|
+
}
|
|
611
|
+
emit(event) {
|
|
612
|
+
const parsed = ReinEvent.parse(event);
|
|
613
|
+
this.emitted.push(parsed);
|
|
614
|
+
this.bus.emit("event", parsed);
|
|
615
|
+
}
|
|
616
|
+
agentFor(entry) {
|
|
617
|
+
for (const agent of this.options.agents()) {
|
|
618
|
+
const owns = agent.wallets.some(
|
|
619
|
+
(w) => w.chain === entry.chain && sameAddress(w.address, entry.from)
|
|
620
|
+
);
|
|
621
|
+
if (owns) return agent.id;
|
|
622
|
+
}
|
|
623
|
+
return void 0;
|
|
624
|
+
}
|
|
625
|
+
observe(entry) {
|
|
626
|
+
const agentId = this.agentFor(entry);
|
|
627
|
+
if (!agentId) return;
|
|
628
|
+
const intent = this.reconcile(entry, agentId);
|
|
629
|
+
if (intent) {
|
|
630
|
+
this.settledIntents.add(intent.id);
|
|
631
|
+
this.emit({
|
|
632
|
+
type: "payment.settled",
|
|
633
|
+
at: entry.at,
|
|
634
|
+
payment: {
|
|
635
|
+
intentId: intent.id,
|
|
636
|
+
txHash: entry.txHash,
|
|
637
|
+
chain: entry.chain,
|
|
638
|
+
blockNumber: entry.blockNumber,
|
|
639
|
+
facilitator: this.options.facilitator,
|
|
640
|
+
confirmedAt: entry.at
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
this.emit({
|
|
646
|
+
type: "shadow.spend",
|
|
647
|
+
at: entry.at,
|
|
648
|
+
agentId,
|
|
649
|
+
txHash: entry.txHash,
|
|
650
|
+
chain: entry.chain,
|
|
651
|
+
amount: entry.amount
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
reconcile(entry, agentId) {
|
|
655
|
+
if (entry.memo !== void 0) {
|
|
656
|
+
const intent = this.allowed.get(entry.memo);
|
|
657
|
+
return intent && intent.agentId === agentId && !this.settledIntents.has(intent.id) ? intent : void 0;
|
|
658
|
+
}
|
|
659
|
+
for (const intent of this.allowed.values()) {
|
|
660
|
+
if (this.settledIntents.has(intent.id)) continue;
|
|
661
|
+
if (intent.agentId !== agentId) continue;
|
|
662
|
+
if (intent.chain !== entry.chain || intent.asset !== entry.asset) continue;
|
|
663
|
+
if (intent.vendor.address !== entry.to) continue;
|
|
664
|
+
if (compareDecimal(intent.amount, entry.amount) !== 0) continue;
|
|
665
|
+
return intent;
|
|
666
|
+
}
|
|
667
|
+
return void 0;
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
function sameAddress(a, b) {
|
|
671
|
+
if (a === b) return true;
|
|
672
|
+
return a.startsWith("0x") && b.startsWith("0x") && a.toLowerCase() === b.toLowerCase();
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// src/vendor.ts
|
|
676
|
+
function createMockVendor(options) {
|
|
677
|
+
const calls = [];
|
|
678
|
+
const requirementFor = (url) => PaymentRequirement.parse({
|
|
679
|
+
scheme: options.scheme ?? "exact",
|
|
680
|
+
network: options.network ?? "base",
|
|
681
|
+
maxAmountRequired: options.atomicPrice,
|
|
682
|
+
resource: new URL(url).pathname,
|
|
683
|
+
payTo: options.payTo,
|
|
684
|
+
asset: options.asset ?? "USDC"
|
|
685
|
+
});
|
|
686
|
+
const paymentRequired = (url, error) => new Response(
|
|
687
|
+
JSON.stringify({ x402Version: 1, accepts: [requirementFor(url)], error }),
|
|
688
|
+
{ status: 402, headers: { "content-type": "application/json" } }
|
|
689
|
+
);
|
|
690
|
+
const fetch = async (input, init) => {
|
|
691
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
692
|
+
const headers = new Headers(
|
|
693
|
+
init?.headers ?? (input instanceof Request ? input.headers : void 0)
|
|
694
|
+
);
|
|
695
|
+
const payment = headers.get("X-PAYMENT");
|
|
696
|
+
calls.push({ url, payment });
|
|
697
|
+
if (payment === null) return paymentRequired(url, "X-PAYMENT header is required");
|
|
698
|
+
let settled;
|
|
699
|
+
try {
|
|
700
|
+
settled = options.facilitator.settle(payment, requirementFor(url));
|
|
701
|
+
} catch (err) {
|
|
702
|
+
if (err instanceof FacilitatorError) return paymentRequired(url, err.message);
|
|
703
|
+
throw err;
|
|
704
|
+
}
|
|
705
|
+
return new Response(JSON.stringify(options.body ?? { ok: true }), {
|
|
706
|
+
status: 200,
|
|
707
|
+
headers: { "content-type": "application/json", "X-PAYMENT-RESPONSE": settled.header }
|
|
708
|
+
});
|
|
709
|
+
};
|
|
710
|
+
return { fetch, calls, requirementFor };
|
|
711
|
+
}
|
|
712
|
+
export {
|
|
713
|
+
FacilitatorError,
|
|
714
|
+
MockExactPayload,
|
|
715
|
+
MockFacilitator,
|
|
716
|
+
MockIndexer,
|
|
717
|
+
MockLedger,
|
|
718
|
+
MockPaymentHeader,
|
|
719
|
+
SettlementResponse,
|
|
720
|
+
createMockVendor,
|
|
721
|
+
decodePaymentHeader,
|
|
722
|
+
encodePaymentHeader,
|
|
723
|
+
encodeSettlementHeader
|
|
724
|
+
};
|
|
725
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ledger.ts","../../../packages/core/src/chain.ts","../../../packages/core/src/money.ts","../../../packages/core/src/glob.ts","../../../packages/core/src/ulid.ts","../../../packages/core/src/ids.ts","../../../packages/core/src/agent.ts","../../../packages/core/src/erc8004.ts","../../../packages/core/src/intent.ts","../../../packages/core/src/decision.ts","../../../packages/core/src/canonical.ts","../../../packages/core/src/session.ts","../../../packages/core/src/payment.ts","../../../packages/core/src/receipt.ts","../../../packages/core/src/gate-receipt.ts","../../../packages/core/src/policy.ts","../../../packages/core/src/reputation.ts","../../../packages/core/src/events.ts","../../../packages/sdk/src/errors.ts","../../../packages/sdk/src/client.ts","../../../packages/sdk/src/x402.ts","../../../packages/sdk/src/guard.ts","../src/errors.ts","../src/payload.ts","../src/facilitator.ts","../src/indexer.ts","../src/vendor.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { randomBytes } from 'node:crypto';\nimport { Chain, Asset, DecimalString } from '@reinconsole/core';\n\n/**\n * The simulated chain. One MockLedger plays every chain at once: entries carry\n * their `chain` tag instead of living on separate ledgers, which keeps the\n * mock world to a single object.\n *\n * Crucially, ANYONE can call `transfer()` directly — that is the bypass path.\n * An agent that pays a vendor without going through the guard still leaves a\n * ledger entry, and the indexer turns that into a `shadow.spend` event.\n */\nexport interface TransferInput {\n chain: Chain;\n asset: Asset;\n from: string;\n to: string;\n /** Human-unit decimal amount, e.g. \"0.01\". */\n amount: string;\n /** Opaque settlement memo; the mock facilitator writes the intent id here. */\n memo?: string;\n}\n\nexport interface LedgerEntry extends TransferInput {\n txHash: string;\n blockNumber: bigint;\n at: Date;\n}\n\nexport class MockLedger {\n private readonly log: LedgerEntry[] = [];\n private readonly bus = new EventEmitter();\n private height = 0n;\n\n /** Append a confirmed transfer (one entry == one tx in its own block). */\n transfer(input: TransferInput): LedgerEntry {\n const entry: LedgerEntry = {\n ...input,\n chain: Chain.parse(input.chain),\n asset: Asset.parse(input.asset),\n amount: DecimalString.parse(input.amount),\n txHash: `0x${randomBytes(32).toString('hex')}`,\n blockNumber: ++this.height,\n at: new Date(),\n };\n this.log.push(entry);\n this.bus.emit('entry', entry);\n return entry;\n }\n\n /** All transfers, oldest first. */\n entries(): readonly LedgerEntry[] {\n return this.log;\n }\n\n /** Subscribe to new transfers as they confirm (what the indexer watches). */\n onEntry(handler: (entry: LedgerEntry) => void): void {\n this.bus.on('entry', handler);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Chains Rein observes/governs. x402 is multi-chain; Base and Solana ship\n * first, Polygon and BNB follow. The policy/ledger domain is rail-agnostic —\n * this enum is the only place chains are enumerated.\n */\nexport const Chain = z.enum(['base', 'solana', 'polygon', 'bnb']);\nexport type Chain = z.infer<typeof Chain>;\n\n/** Stablecoins settled over x402. */\nexport const Asset = z.enum(['USDC', 'USDT', 'EURC']);\nexport type Asset = z.infer<typeof Asset>;\n","import { z } from 'zod';\n\n/**\n * Monetary amounts in Rein are always non-negative **decimal strings**, never\n * floats. Float arithmetic silently loses precision on values like 0.1 + 0.2,\n * which is unacceptable for money. All comparison/aggregation goes through the\n * BigInt-backed helpers below.\n */\nexport const DecimalString = z\n .string()\n .regex(/^\\d+(\\.\\d+)?$/, 'must be a non-negative decimal string, e.g. \"5.00\"');\nexport type DecimalString = z.infer<typeof DecimalString>;\n\nconst DECIMAL_RE = /^\\d+(\\.\\d+)?$/;\n\nexport function isValidDecimal(value: string): boolean {\n return DECIMAL_RE.test(value);\n}\n\nfunction assertDecimal(value: string): void {\n if (!isValidDecimal(value)) {\n throw new TypeError(`invalid decimal string: ${JSON.stringify(value)}`);\n }\n}\n\nfunction fractionLength(value: string): number {\n const dot = value.indexOf('.');\n return dot === -1 ? 0 : value.length - dot - 1;\n}\n\n/** Scale a decimal string into an integer BigInt at a fixed number of fraction digits. */\nfunction toScaled(value: string, scale: number): bigint {\n const dot = value.indexOf('.');\n const intPart = dot === -1 ? value : value.slice(0, dot);\n const fracPart = dot === -1 ? '' : value.slice(dot + 1);\n const paddedFrac = (fracPart + '0'.repeat(scale)).slice(0, scale);\n return BigInt(intPart + paddedFrac);\n}\n\n/** Render a scaled BigInt back to a normalized decimal string (no trailing zeros). */\nfunction fromScaled(scaled: bigint, scale: number): string {\n if (scale === 0) return scaled.toString();\n const digits = scaled.toString().padStart(scale + 1, '0');\n const intPart = digits.slice(0, digits.length - scale);\n const fracPart = digits.slice(digits.length - scale).replace(/0+$/, '');\n return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;\n}\n\n/** Compare two decimal strings. Returns -1 (a<b), 0 (a==b), or 1 (a>b). */\nexport function compareDecimal(a: string, b: string): -1 | 0 | 1 {\n assertDecimal(a);\n assertDecimal(b);\n const scale = Math.max(fractionLength(a), fractionLength(b));\n const av = toScaled(a, scale);\n const bv = toScaled(b, scale);\n return av < bv ? -1 : av > bv ? 1 : 0;\n}\n\n/** Sum a list of decimal strings without floating-point error. */\nexport function sumDecimal(values: readonly string[]): string {\n if (values.length === 0) return '0';\n let scale = 0;\n for (const v of values) {\n assertDecimal(v);\n scale = Math.max(scale, fractionLength(v));\n }\n let total = 0n;\n for (const v of values) total += toScaled(v, scale);\n return fromScaled(total, scale);\n}\n\n/** Multiply two decimal strings exactly (e.g. median * \"3\" for price-sanity). */\nexport function mulDecimal(a: string, b: string): string {\n assertDecimal(a);\n assertDecimal(b);\n const scaleA = fractionLength(a);\n const scaleB = fractionLength(b);\n const product = toScaled(a, scaleA) * toScaled(b, scaleB);\n return fromScaled(product, scaleA + scaleB);\n}\n\n/** Convenience: a > b. */\nexport function gt(a: string, b: string): boolean {\n return compareDecimal(a, b) === 1;\n}\n\n/** Convenience: a < b. */\nexport function lt(a: string, b: string): boolean {\n return compareDecimal(a, b) === -1;\n}\n","/**\n * Minimal, safe glob matcher supporting only the `*` wildcard (matches any run\n * of characters, including none). Used for vendor host allowlists\n * (e.g. \"*.trusted.io\"), agent-id targeting (e.g. \"agt_research_*\"), and\n * Gate route pricing (e.g. \"/api/reports/*\").\n *\n * Deliberately NOT a regex from user input — the pattern is escaped so a\n * malicious policy value cannot inject regex behavior (ReDoS, etc.).\n */\nexport function globMatch(pattern: string, value: string): boolean {\n const escaped = pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // escape regex metachars\n const withWildcard = escaped.replace(/\\\\\\*/g, '.*'); // re-enable only `*`\n return new RegExp(`^${withWildcard}$`).test(value);\n}\n\n/** True if `value` matches any pattern in the list. */\nexport function globMatchAny(patterns: readonly string[], value: string): boolean {\n return patterns.some((p) => globMatch(p, value));\n}\n","/**\n * Minimal ULID generation, dependency-free. ULIDs are lexicographically\n * sortable (time-prefixed) and url-safe, which is why Rein uses them for all\n * primary keys instead of UUIDs. Works in Node 22 and modern browsers/edge via\n * the WebCrypto `globalThis.crypto`.\n */\n\n// Crockford's base32 alphabet (excludes I, L, O, U to avoid ambiguity).\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction randomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n\nfunction encodeTime(now: number): string {\n let time = now;\n let out = '';\n for (let i = TIME_LEN - 1; i >= 0; i--) {\n out = ENCODING.charAt(time % 32) + out;\n time = Math.floor(time / 32);\n }\n return out;\n}\n\nfunction encodeRandom(): string {\n let out = '';\n for (const byte of randomBytes(RANDOM_LEN)) {\n out += ENCODING.charAt(byte % 32);\n }\n return out;\n}\n\n/** Generate a 26-character ULID. */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime) + encodeRandom();\n}\n\n/** Generate a Stripe-style prefixed id, e.g. `newId('agt')` -> `agt_01J...`. */\nexport function newId<P extends string>(prefix: P): `${P}_${string}` {\n return `${prefix}_${ulid()}`;\n}\n","import { z } from 'zod';\n\n/** Crockford base32 ULID body (26 chars), as produced by `ulid()`. */\nconst ULID_BODY = '[0-9A-HJKMNP-TV-Z]{26}';\n\n/**\n * A zod schema for a Stripe-style prefixed ULID, e.g. `agt_01J7...`.\n * Centralizing this keeps id formats consistent across DB rows, API payloads,\n * and SDK types.\n */\nexport function prefixedId(prefix: string) {\n return z\n .string()\n .regex(new RegExp(`^${prefix}_${ULID_BODY}$`), `expected a \"${prefix}_\" prefixed id`);\n}\n\nexport const OrgId = prefixedId('org');\nexport const AgentId = prefixedId('agt');\nexport const PolicyId = prefixedId('pol');\nexport const IntentId = prefixedId('int');\nexport const DecisionId = prefixedId('dec');\nexport const ReceiptId = prefixedId('rcp');\nexport const SessionId = prefixedId('ses');\nexport const GateReceiptId = prefixedId('grc');\n\nexport type OrgId = z.infer<typeof OrgId>;\nexport type AgentId = z.infer<typeof AgentId>;\nexport type PolicyId = z.infer<typeof PolicyId>;\nexport type IntentId = z.infer<typeof IntentId>;\nexport type DecisionId = z.infer<typeof DecisionId>;\nexport type ReceiptId = z.infer<typeof ReceiptId>;\nexport type SessionId = z.infer<typeof SessionId>;\nexport type GateReceiptId = z.infer<typeof GateReceiptId>;\n","import { z } from 'zod';\nimport { AgentId, OrgId } from './ids.js';\nimport { Chain } from './chain.js';\n\n/**\n * Enforcement tier of a given agent wallet:\n * - `observed` — Rein sees spend on-chain but has no control (no SDK either).\n * - `sdk` — agent uses @reinconsole/sdk; advisory + observability (bypassable).\n * - `session-key` — signer-level scope; out-of-policy txs cannot be signed.\n */\nexport const EnforcementMode = z.enum(['observed', 'sdk', 'session-key']);\nexport type EnforcementMode = z.infer<typeof EnforcementMode>;\n\nexport const AgentWallet = z.object({\n chain: Chain,\n address: z.string().min(1),\n mode: EnforcementMode,\n});\nexport type AgentWallet = z.infer<typeof AgentWallet>;\n\nexport const AgentStatus = z.enum(['active', 'frozen']);\nexport type AgentStatus = z.infer<typeof AgentStatus>;\n\nexport const Agent = z.object({\n id: AgentId,\n orgId: OrgId,\n name: z.string().min(1).max(200),\n /** On-chain ERC-8004 identity, if the agent is registered. */\n erc8004Id: z.string().optional(),\n wallets: z.array(AgentWallet).default([]),\n status: AgentStatus.default('active'),\n createdAt: z.coerce.date(),\n});\nexport type Agent = z.infer<typeof Agent>;\n","import { z } from 'zod';\n\n/**\n * The canonical string form of an ERC-8004 agent identity:\n *\n * eip155:{chainId}:{identityRegistry}/{tokenId}\n *\n * The part before the `/` is the spec's registry reference verbatim (the\n * `agentRegistry` field of a registration file, CAIP-10 account form); the\n * tokenId is the ERC-721 id the Identity Registry minted (`agentId` in the\n * spec). This exact string is what `Agent.erc8004Id` / `Vendor.erc8004Id`\n * carry, AND the reputation-subject id a registered agent's evidence keys by.\n *\n * Casing is load-bearing: reputation subject keys for agent-kind ids are\n * case-sensitive unless they start with `0x` (see @reinconsole/graph\n * `normalizeSubject`), and an eip155 string does not. `formatErc8004Id`\n * therefore always emits the registry address in lowercase, and consumers must\n * never hand-build the string — parse then re-format to normalize.\n */\n\nexport interface Erc8004Ref {\n /** EIP-155 chain id (e.g. 8453 Base, 84532 Base Sepolia). */\n chainId: number;\n /** Identity Registry contract address, lowercase 0x-hex. */\n registry: string;\n /** ERC-721 tokenId — the spec's `agentId`. bigint: token ids exceed 2^53. */\n tokenId: bigint;\n}\n\nconst REGISTRY_PATTERN = /^0x[0-9a-fA-F]{40}$/;\nconst ID_PATTERN = /^eip155:(\\d+):(0x[0-9a-fA-F]{40})\\/(\\d+)$/;\n\n/** Build the canonical id string. Throws on a malformed ref (programmer error). */\nexport function formatErc8004Id(ref: Erc8004Ref): string {\n if (!Number.isSafeInteger(ref.chainId) || ref.chainId < 1) {\n throw new RangeError(`erc8004: bad chainId ${ref.chainId}`);\n }\n if (!REGISTRY_PATTERN.test(ref.registry)) {\n throw new RangeError(`erc8004: bad registry address ${ref.registry}`);\n }\n if (typeof ref.tokenId !== 'bigint' || ref.tokenId < 0n) {\n // The typeof guard matters for plain-JS callers: 1.5 < 0n is legal JS and\n // false, and would mint the unparseable \"…/1.5\".\n throw new RangeError(`erc8004: bad tokenId ${ref.tokenId}`);\n }\n return `eip155:${ref.chainId}:${ref.registry.toLowerCase()}/${ref.tokenId}`;\n}\n\n/**\n * Parse a canonical (or hand-written) id string. Accepts any address casing\n * and returns a lowercase ref, or undefined when malformed — so\n * `formatErc8004Id(parseErc8004Id(x)!)` is the normalization step.\n */\nexport function parseErc8004Id(value: string): Erc8004Ref | undefined {\n const match = ID_PATTERN.exec(value);\n if (!match) return undefined;\n const chainId = Number(match[1]);\n if (!Number.isSafeInteger(chainId) || chainId < 1) return undefined;\n return {\n chainId,\n registry: match[2]!.toLowerCase(),\n tokenId: BigInt(match[3]!),\n };\n}\n\n/** Strict boundary schema for the id string (the fields themselves stay free-form). */\nexport const Erc8004Id = z\n .string()\n .refine((value) => parseErc8004Id(value) !== undefined, {\n message: 'expected \"eip155:{chainId}:{registry}/{tokenId}\"',\n });\nexport type Erc8004Id = z.infer<typeof Erc8004Id>;\n","import { z } from 'zod';\nimport { AgentId, IntentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\n\nexport const Vendor = z.object({\n host: z.string().min(1),\n address: z.string().min(1),\n erc8004Id: z.string().optional(),\n});\nexport type Vendor = z.infer<typeof Vendor>;\n\n/**\n * The observability gold: links every micro-payment back to the task and run\n * that caused it, so finance teams can answer \"what was this spend for?\".\n */\nexport const TaskContext = z.object({\n taskId: z.string().optional(),\n parentRunId: z.string().optional(),\n purpose: z.string().max(500).optional(),\n});\nexport type TaskContext = z.infer<typeof TaskContext>;\n\n/**\n * A request to spend, submitted to the policy engine BEFORE any signature is\n * released. The `nonce` provides replay protection on the signer path.\n */\nexport const PaymentIntent = z.object({\n id: IntentId,\n agentId: AgentId,\n vendor: Vendor,\n resource: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n nonce: z.string().min(1),\n createdAt: z.coerce.date(),\n});\nexport type PaymentIntent = z.infer<typeof PaymentIntent>;\n","import { z } from 'zod';\nimport { DecisionId, IntentId } from './ids.js';\n\nexport const DecisionOutcome = z.enum(['allow', 'deny', 'escalate']);\nexport type DecisionOutcome = z.infer<typeof DecisionOutcome>;\n\n/**\n * The signed, hash-chained record of a single policy evaluation. Written BEFORE\n * any signature is released. `prevHash` + `hash` form a tamper-evident chain;\n * `signature` is the policy service's signing key over `hash`. The eventual\n * on-chain `txHash` is attached later by the indexer (see SettledPayment).\n */\nexport const Decision = z.object({\n id: DecisionId,\n intentId: IntentId,\n /**\n * sha256 of the intent's canonical content (see canonical.ts). Binds the\n * decision to the exact transfer it judged — amount, recipient, asset,\n * chain — so a signer can verify an {intent, decision} pair offline as a\n * self-contained spend voucher, not just a reference by id.\n */\n intentHash: z.string(),\n outcome: DecisionOutcome,\n /** Ids of the rules that fired, for explainability. */\n matchedRules: z.array(z.string()).default([]),\n /** Human-readable explanation surfaced in the dashboard. */\n reason: z.string().optional(),\n policyId: z.string(),\n policyVersion: z.string(),\n /** Hash of the previous decision in the chain (tamper-evident log). */\n prevHash: z.string(),\n /** Hash of this decision's canonical content. */\n hash: z.string(),\n /** Service signing-key signature over `hash`. */\n signature: z.string(),\n latencyMs: z.number().nonnegative(),\n decidedAt: z.coerce.date(),\n});\nexport type Decision = z.infer<typeof Decision>;\n","import type { PaymentIntent } from './intent.js';\nimport type { DecisionOutcome } from './decision.js';\n\n/**\n * Canonical byte forms shared by the policy engine (which hashes and signs\n * them) and the signer (which verifies them offline). Field order is fixed,\n * dates are ISO strings, and absent optionals are explicit nulls, so the same\n * parsed object always canonicalizes to the same bytes — including after a\n * JSON round-trip over HTTP.\n *\n * These builders are pure (no node:crypto) so @reinconsole/core stays loadable in a\n * browser; services hash the strings with whatever sha256 they have.\n */\n\n/** Canonical form of an intent — what `Decision.intentHash` commits to. */\nexport function canonicalIntent(intent: PaymentIntent): string {\n return JSON.stringify({\n id: intent.id,\n agentId: intent.agentId,\n vendor: {\n host: intent.vendor.host,\n address: intent.vendor.address,\n erc8004Id: intent.vendor.erc8004Id ?? null,\n },\n resource: intent.resource,\n amount: intent.amount,\n asset: intent.asset,\n chain: intent.chain,\n taskContext: {\n taskId: intent.taskContext.taskId ?? null,\n parentRunId: intent.taskContext.parentRunId ?? null,\n purpose: intent.taskContext.purpose ?? null,\n },\n nonce: intent.nonce,\n createdAt: intent.createdAt.toISOString(),\n });\n}\n\n/** The decision fields covered by `Decision.hash` (and thus its signature). */\nexport interface DecisionContent {\n intentId: string;\n intentHash: string;\n outcome: DecisionOutcome;\n matchedRules: string[];\n policyId: string;\n policyVersion: string;\n prevHash: string;\n decidedAt: Date;\n}\n\n/** Canonical form of a decision — what `Decision.hash` is computed over. */\nexport function canonicalDecision(d: DecisionContent): string {\n return JSON.stringify({\n intentId: d.intentId,\n intentHash: d.intentHash,\n outcome: d.outcome,\n matchedRules: d.matchedRules,\n policyId: d.policyId,\n policyVersion: d.policyVersion,\n prevHash: d.prevHash,\n decidedAt: d.decidedAt.toISOString(),\n });\n}\n","import { z } from 'zod';\nimport { AgentId, SessionId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * A session-key grant: the signer tier's unit of delegated authority. The\n * agent process holds only the bearer token — the wallet key never leaves the\n * signer — and the stored record keeps a hash of the token, never the token\n * itself (it is returned exactly once, at creation).\n *\n * Caps here are the signer-side backstop UNDER whatever policy says: a\n * signature is released only when the engine allowed the intent AND the\n * session has room. Amounts are decimal strings in the asset's human units.\n */\nexport const Session = z.object({\n id: SessionId,\n agentId: AgentId,\n /** sha256 hex of the bearer token. */\n tokenHash: z.string(),\n /** Cumulative ceiling across the session's lifetime. Absent = uncapped. */\n capAmount: DecimalString.optional(),\n /** Ceiling per individual signature. Absent = uncapped. */\n maxPerPayment: DecimalString.optional(),\n expiresAt: z.coerce.date(),\n createdAt: z.coerce.date(),\n revokedAt: z.coerce.date().optional(),\n});\nexport type Session = z.infer<typeof Session>;\n","import { z } from 'zod';\nimport { IntentId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/**\n * On-chain confirmation of a settled payment, reconciled by the indexer against\n * the originating intent (amount + recipient + nonce, or facilitator webhook).\n */\nexport const SettledPayment = z.object({\n intentId: IntentId,\n txHash: z.string(),\n chain: Chain,\n blockNumber: z.coerce.bigint(),\n facilitator: z.string().optional(),\n feePaid: DecimalString.optional(),\n confirmedAt: z.coerce.date(),\n});\nexport type SettledPayment = z.infer<typeof SettledPayment>;\n","import { z } from 'zod';\nimport { ReceiptId, IntentId, DecisionId, AgentId } from './ids.js';\nimport { Chain, Asset } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { TaskContext } from './intent.js';\nimport { DecisionOutcome } from './decision.js';\n\n/**\n * What the SDK reports back after settlement: the `X-PAYMENT-RESPONSE` header\n * a vendor returns once payment clears. Mock facilitators fill this in v0.1;\n * the indexer reconciles it against on-chain truth later (see SettledPayment).\n */\nexport const ReceiptSettlement = z.object({\n txHash: z.string().optional(),\n networkId: z.string().optional(),\n /** Raw header payload, kept for forensics until the indexer confirms. */\n raw: z.string().optional(),\n});\nexport type ReceiptSettlement = z.infer<typeof ReceiptSettlement>;\n\n/**\n * The SDK-side record of one guarded payment attempt: which request triggered\n * it, what the engine decided, and (if allowed and paid) how it settled.\n * Receipts are the observability primitive — every 402 the guard touches\n * produces exactly one, whether the payment was allowed or blocked.\n */\nexport const Receipt = z.object({\n id: ReceiptId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n outcome: DecisionOutcome,\n /** The URL the agent actually fetched (the paywalled resource). */\n url: z.string(),\n method: z.string().default('GET'),\n vendorHost: z.string(),\n amount: DecimalString,\n asset: Asset,\n chain: Chain,\n taskContext: TaskContext.default({}),\n /** Human-readable explanation copied from the decision. */\n reason: z.string().optional(),\n /** Present only once a payment was made and the vendor confirmed it. */\n settlement: ReceiptSettlement.optional(),\n createdAt: z.coerce.date(),\n});\nexport type Receipt = z.infer<typeof Receipt>;\n","import { z } from 'zod';\nimport { GateReceiptId } from './ids.js';\nimport { DecimalString } from './money.js';\n\n/**\n * The vendor-side twin of `Receipt`: one settled x402 payment as the GATE saw\n * it — who paid, for which priced route, and the settlement transaction. The\n * agent-side Receipt records what an agent tried to spend; a GateReceipt\n * records what a vendor actually earned.\n */\nexport const GateReceipt = z.object({\n id: GateReceiptId,\n at: z.coerce.date(),\n /** The route pattern that priced this request, e.g. \"/api/reports/*\". */\n route: z.string().min(1),\n /** The concrete resource paid for (URL path actually requested). */\n resource: z.string().min(1),\n method: z.string().min(1),\n /** The paying wallet address, as asserted by the settled payment. */\n payer: z.string().min(1),\n payTo: z.string().min(1),\n /** Human-unit decimal amount, e.g. \"0.05\". */\n amount: DecimalString,\n /** The same amount in the asset's atomic units, e.g. \"50000\". */\n amountAtomic: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n /** Token symbol or contract address, exactly as quoted in the requirement. */\n asset: z.string().min(1),\n network: z.string().min(1),\n /** Settlement transaction hash (mock ledger or on-chain). */\n transaction: z.string().min(1),\n});\nexport type GateReceipt = z.infer<typeof GateReceipt>;\n","import { z } from 'zod';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\n\n/** A rolling-window spec, e.g. \"24h\", \"1h\", \"30m\", \"7d\". */\nexport const Window = z\n .string()\n .regex(/^\\d+[smhd]$/, 'window must be like \"30s\", \"15m\", \"1h\", or \"7d\"');\nexport type Window = z.infer<typeof Window>;\n\n/** A relative multiplier, e.g. \"3x\" (used for price-sanity checks). */\nexport const Multiplier = z.string().regex(/^\\d+(\\.\\d+)?x$/, 'multiplier must be like \"3x\"');\nexport type Multiplier = z.infer<typeof Multiplier>;\n\n/**\n * The set of predicates a rule can test. Multiple predicates in one condition\n * are ANDed together. Each maps to an evaluator in the policy engine.\n */\nexport const Condition = z\n .object({\n /** Per-transaction amount exceeds this value. */\n amountGt: DecimalString.optional(),\n /** Sum of spend in a rolling window exceeds `gt`. */\n rollingSum: z.object({ window: Window, gt: DecimalString }).optional(),\n /** Transaction count in a rolling window exceeds `gt`. */\n txCount: z.object({ window: Window, gt: z.number().int().nonnegative() }).optional(),\n /** Vendor host matches one of these patterns (supports `*` globs). */\n vendorHostIn: z.array(z.string()).optional(),\n /** First time Rein has seen this vendor for the agent. */\n vendorFirstSeen: z.boolean().optional(),\n /** Vendor reputation score is below this threshold (Phase 3 hook). */\n vendorReputationLt: z.number().min(0).max(100).optional(),\n /** Amount is more than `gt`-times the observed median for this resource. */\n amountVsResourceMedian: z.object({ gt: Multiplier }).optional(),\n })\n .refine((c) => Object.values(c).some((v) => v !== undefined), {\n message: 'a condition must specify at least one predicate',\n });\nexport type Condition = z.infer<typeof Condition>;\n\n/**\n * A single rule: an id plus exactly one action (allow / deny / escalate) whose\n * value is the condition that triggers it.\n */\nexport const Rule = z\n .object({\n id: z.string().min(1),\n allow: Condition.optional(),\n deny: Condition.optional(),\n escalate: Condition.optional(),\n })\n .refine((r) => [r.allow, r.deny, r.escalate].filter((v) => v !== undefined).length === 1, {\n message: 'a rule must specify exactly one of allow / deny / escalate',\n });\nexport type Rule = z.infer<typeof Rule>;\n\nexport const PolicyDefault = z.enum(['allow', 'deny']);\nexport type PolicyDefault = z.infer<typeof PolicyDefault>;\n\nexport const AppliesTo = z.object({\n /** Agent id patterns (supports `*` globs, e.g. \"agt_research_*\"). */\n agents: z.array(z.string()).optional(),\n chains: z.array(Chain).optional(),\n});\nexport type AppliesTo = z.infer<typeof AppliesTo>;\n\nexport const Escalation = z.object({\n approvers: z.array(z.string()).default([]),\n timeoutAction: PolicyDefault.default('deny'),\n timeoutMin: z.number().int().positive().default(15),\n});\nexport type Escalation = z.infer<typeof Escalation>;\n\n/**\n * A declarative, versioned, immutable-once-active policy. Evaluation order in\n * the engine is: explicit DENY > ESCALATE > ALLOW > `default`.\n */\nexport const Policy = z.object({\n policyId: z.string().min(1),\n version: z.string().default('1'),\n appliesTo: AppliesTo.default({}),\n rules: z.array(Rule).default([]),\n default: PolicyDefault.default('deny'),\n /** Below this amount, fail-open is permitted during a policy-service outage. */\n denyFloor: DecimalString.default('0.05'),\n escalation: Escalation.optional(),\n});\nexport type Policy = z.infer<typeof Policy>;\n","import { z } from 'zod';\n\nexport const ReputationSubject = z.object({\n kind: z.enum(['agent', 'vendor']),\n id: z.string(),\n});\nexport type ReputationSubject = z.infer<typeof ReputationSubject>;\n\nexport const ReputationComponents = z.object({\n volume: z.number(),\n longevity: z.number(),\n disputeRate: z.number(),\n counterpartyQuality: z.number(),\n settlementReliability: z.number(),\n});\nexport type ReputationComponents = z.infer<typeof ReputationComponents>;\n\n/**\n * A reputation score for an agent or vendor (Phase 3 — Graph). `confidence` is\n * first-class: a thin-history subject returns LOW confidence rather than a\n * misleadingly high score, so policy can avoid both false trust and unfair\n * freezing.\n */\nexport const ReputationScore = z.object({\n subject: ReputationSubject,\n score: z.number().min(0).max(100),\n components: ReputationComponents,\n confidence: z.number().min(0).max(1),\n asOf: z.coerce.date(),\n /** Hash-anchored attestation, optionally published on-chain (ERC-8004). */\n evidenceUri: z.string().optional(),\n});\nexport type ReputationScore = z.infer<typeof ReputationScore>;\n","import { z } from 'zod';\nimport { AgentId, DecisionId, IntentId, SessionId } from './ids.js';\nimport { Chain } from './chain.js';\nimport { DecimalString } from './money.js';\nimport { PaymentIntent } from './intent.js';\nimport { Decision } from './decision.js';\nimport { SettledPayment } from './payment.js';\nimport { GateReceipt } from './gate-receipt.js';\n\n/**\n * The canonical event envelope published on the bus (NATS in production).\n * `shadow.spend` is emitted when the indexer sees on-chain spend from a managed\n * wallet with NO corresponding ALLOW decision — the bypass signal that catches\n * SDK-mode evasion and drives the upgrade to the signer tier.\n * `signature.released` / `signature.refused` are that tier's heartbeat: every\n * time the signer does or does not put a key to work, the bus knows why.\n * `gate.*` is the vendor side of the wire: every quote a Gate issues, every\n * payment it accepts, every payment it turns away.\n */\nexport const ReinEvent = z.discriminatedUnion('type', [\n z.object({ type: z.literal('intent.created'), at: z.coerce.date(), intent: PaymentIntent }),\n z.object({ type: z.literal('decision.made'), at: z.coerce.date(), decision: Decision }),\n z.object({ type: z.literal('payment.settled'), at: z.coerce.date(), payment: SettledPayment }),\n z.object({\n type: z.literal('shadow.spend'),\n at: z.coerce.date(),\n agentId: AgentId,\n txHash: z.string(),\n chain: Chain,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.released'),\n at: z.coerce.date(),\n sessionId: SessionId,\n agentId: AgentId,\n intentId: IntentId,\n decisionId: DecisionId,\n amount: DecimalString,\n }),\n z.object({\n type: z.literal('signature.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"decision_replayed\" (see @reinconsole/signer). */\n code: z.string(),\n reason: z.string(),\n sessionId: SessionId.optional(),\n agentId: AgentId.optional(),\n intentId: IntentId.optional(),\n }),\n z.object({\n type: z.literal('gate.quoted'),\n at: z.coerce.date(),\n resource: z.string(),\n method: z.string(),\n amount: DecimalString,\n asset: z.string(),\n network: z.string(),\n }),\n z.object({ type: z.literal('gate.settled'), at: z.coerce.date(), receipt: GateReceipt }),\n z.object({\n type: z.literal('gate.refused'),\n at: z.coerce.date(),\n /** Refusal code, e.g. \"payment_replayed\" (see @reinconsole/gate). */\n code: z.string(),\n reason: z.string(),\n resource: z.string(),\n /** Known only when the payment header decoded far enough to name a payer. */\n payer: z.string().optional(),\n }),\n]);\nexport type ReinEvent = z.infer<typeof ReinEvent>;\n","import type { Decision, PaymentIntent, Receipt } from '@reinconsole/core';\n\n/** Base class for everything the SDK throws, so callers can catch broadly. */\nexport class ReinError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** The policy engine answered with a non-2xx status. */\nexport class EngineError extends ReinError {\n constructor(\n readonly status: number,\n readonly body: unknown,\n message = `policy engine responded ${status}`,\n ) {\n super(message);\n }\n}\n\n/**\n * The engine evaluated the intent and did NOT allow it (deny or escalate —\n * v0.1 blocks both; escalation approval flows land later). The payment was\n * never constructed, so no funds moved. Carries the full evidence trail.\n */\nexport class PaymentBlockedError extends ReinError {\n constructor(\n readonly intent: PaymentIntent,\n readonly decision: Decision,\n readonly receipt: Receipt,\n ) {\n super(\n `rein blocked payment of ${intent.amount} ${intent.asset} to ${intent.vendor.host}: ` +\n `${decision.outcome}${decision.reason ? ` (${decision.reason})` : ''}`,\n );\n }\n}\n\n/**\n * The vendor's 402 was x402-shaped but offered no requirement the guard can\n * govern (unknown scheme/network/asset). The guard fails closed rather than\n * letting an ungoverned payment through.\n */\nexport class UnsupportedRequirementError extends ReinError {\n constructor(readonly url: string) {\n super(`no supported x402 payment requirement in 402 from ${url}; failing closed`);\n }\n}\n","import { z } from 'zod';\nimport { Agent, Decision, PaymentIntent, Policy } from '@reinconsole/core';\nimport { EngineError } from './errors.js';\nimport type { IntentSubmission } from './x402.js';\n\n/** Any fetch-compatible function (global fetch, undici, or a test double). */\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nconst Health = z.object({ status: z.string(), publicKey: z.string() });\nconst EvaluateResponse = z.object({ intent: PaymentIntent, decision: Decision });\nexport type EvaluateResponse = z.infer<typeof EvaluateResponse>;\n\nexport interface EngineClientOptions {\n /** Base URL of the policy engine, e.g. \"http://localhost:8787\". */\n baseUrl: string;\n /** Override the transport (tests, custom agents). Defaults to global fetch. */\n fetch?: FetchLike;\n}\n\n/**\n * Thin typed client for the policy-engine HTTP API. Every response is parsed\n * through the @reinconsole/core schemas, so wire drift fails loudly at the boundary.\n */\nexport class EngineClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: EngineClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const f = options.fetch ?? globalThis.fetch;\n this.fetchImpl = (input, init) => f(input, init);\n }\n\n private async request<T>(\n method: string,\n path: string,\n schema: z.ZodType<T, z.ZodTypeDef, unknown>,\n body?: unknown,\n ): Promise<T> {\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: body === undefined ? undefined : { 'content-type': 'application/json' },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = await res\n .clone()\n .json()\n .catch(() => res.text().catch(() => undefined));\n throw new EngineError(res.status, payload);\n }\n if (res.status === 204) return schema.parse(undefined);\n return schema.parse(await res.json());\n }\n\n health(): Promise<z.infer<typeof Health>> {\n return this.request('GET', '/health', Health);\n }\n\n registerAgent(input: {\n orgId: string;\n name: string;\n erc8004Id?: string;\n wallets?: Agent['wallets'];\n }): Promise<Agent> {\n return this.request('POST', '/v1/agents', Agent, input);\n }\n\n listAgents(): Promise<Agent[]> {\n return this.request('GET', '/v1/agents', z.array(Agent));\n }\n\n freeze(agentId: string): Promise<void> {\n return this.request('POST', `/v1/agents/${agentId}/freeze`, z.void());\n }\n\n unfreeze(agentId: string): Promise<void> {\n return this.request('POST', `/v1/agents/${agentId}/unfreeze`, z.void());\n }\n\n addPolicy(policy: z.input<typeof Policy>): Promise<Policy> {\n return this.request('POST', '/v1/policies', Policy, policy);\n }\n\n listPolicies(): Promise<Policy[]> {\n return this.request('GET', '/v1/policies', z.array(Policy));\n }\n\n /** The hot path: submit an intent, get the normalized intent + signed decision. */\n evaluate(submission: IntentSubmission): Promise<EvaluateResponse> {\n return this.request('POST', '/v1/evaluate', EvaluateResponse, submission);\n }\n\n decisions(): Promise<Decision[]> {\n return this.request('GET', '/v1/decisions', z.array(Decision));\n }\n}\n","import { z } from 'zod';\nimport { Chain, Asset, type TaskContext, type Vendor } from '@reinconsole/core';\n\n/**\n * x402 wire shapes (spec v1), mock-first: these schemas are what Rein's mock\n * facilitator and tests speak today, and they track the published x402 spec so\n * real vendors parse identically when the live integration lands.\n */\n\n/** One way to pay, as offered inside a 402 response body. */\nexport const PaymentRequirement = z.object({\n scheme: z.string(),\n network: z.string(),\n /** Amount in the asset's atomic units (e.g. \"10000\" = 0.01 USDC at 6 decimals). */\n maxAmountRequired: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n resource: z.string().optional(),\n description: z.string().optional(),\n mimeType: z.string().optional(),\n payTo: z.string().min(1),\n maxTimeoutSeconds: z.number().optional(),\n /** Token contract address — or, mock-first, a plain symbol like \"USDC\". */\n asset: z.string().min(1),\n extra: z.record(z.unknown()).optional(),\n});\nexport type PaymentRequirement = z.infer<typeof PaymentRequirement>;\n\n/** The full 402 body a paywalled vendor returns. */\nexport const PaymentRequired = z.object({\n x402Version: z.number(),\n accepts: z.array(PaymentRequirement).min(1),\n error: z.string().optional(),\n});\nexport type PaymentRequired = z.infer<typeof PaymentRequired>;\n\n/**\n * x402 network ids → the chains Rein governs. Testnets map to their mainnet.\n * Both v1 names (\"base-sepolia\") and v2 CAIP-2 ids (\"eip155:84532\") appear in\n * the wild, so the guard accepts either.\n */\nconst NETWORK_TO_CHAIN: Record<string, Chain> = {\n base: 'base',\n 'base-sepolia': 'base',\n 'eip155:8453': 'base',\n 'eip155:84532': 'base',\n solana: 'solana',\n 'solana-devnet': 'solana',\n polygon: 'polygon',\n 'polygon-amoy': 'polygon',\n bnb: 'bnb',\n bsc: 'bnb',\n};\n\n/** Canonical stablecoin contract addresses (EVM keys lowercased). */\nconst KNOWN_ASSET_ADDRESSES: Record<string, Asset> = {\n '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 'USDC', // USDC on Base\n '0x036cbd53842c5426634e7929541ec2318f3dcf7e': 'USDC', // USDC on Base Sepolia\n EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: 'USDC', // USDC on Solana\n};\n\nexport function networkToChain(network: string): Chain | undefined {\n return NETWORK_TO_CHAIN[network.toLowerCase()];\n}\n\n/**\n * Resolve a requirement's asset to a symbol Rein knows. Tries, in order: the\n * asset field as a literal symbol (mock-first), `extra.symbol`, then known\n * contract addresses (exact case for Solana, lowercased for EVM), then any\n * caller-supplied address map.\n */\nexport function resolveAsset(\n requirement: PaymentRequirement,\n extraAddresses: Record<string, Asset> = {},\n): Asset | undefined {\n const direct = Asset.safeParse(requirement.asset.toUpperCase());\n if (direct.success) return direct.data;\n\n const symbol = requirement.extra?.['symbol'];\n if (typeof symbol === 'string') {\n const fromExtra = Asset.safeParse(symbol.toUpperCase());\n if (fromExtra.success) return fromExtra.data;\n }\n\n return (\n extraAddresses[requirement.asset] ??\n extraAddresses[requirement.asset.toLowerCase()] ??\n KNOWN_ASSET_ADDRESSES[requirement.asset] ??\n KNOWN_ASSET_ADDRESSES[requirement.asset.toLowerCase()]\n );\n}\n\n/**\n * Convert an atomic-unit amount to a normalized decimal string without floats,\n * e.g. (\"10000\", 6) -> \"0.01\". Stablecoins Rein supports all use 6 decimals;\n * a requirement can override via `extra.decimals`.\n */\nexport function atomicToDecimal(atomic: string, decimals: number): string {\n if (!/^\\d+$/.test(atomic)) throw new TypeError(`invalid atomic amount: ${atomic}`);\n if (decimals === 0) return atomic.replace(/^0+(?=\\d)/, '');\n const digits = atomic.padStart(decimals + 1, '0');\n const intPart = digits.slice(0, digits.length - decimals).replace(/^0+(?=\\d)/, '');\n const fracPart = digits.slice(digits.length - decimals).replace(/0+$/, '');\n return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;\n}\n\n/**\n * The inverse of {@link atomicToDecimal}: a human-unit decimal string to atomic\n * units without floats, e.g. (\"0.05\", 6) -> \"50000\". Throws if the value has\n * more fraction digits than the asset carries (sub-atomic precision is a\n * pricing bug, not something to round silently).\n */\nexport function decimalToAtomic(decimal: string, decimals: number): string {\n if (!/^\\d+(\\.\\d+)?$/.test(decimal)) throw new TypeError(`invalid decimal amount: ${decimal}`);\n const dot = decimal.indexOf('.');\n const intPart = dot === -1 ? decimal : decimal.slice(0, dot);\n const fracPart = dot === -1 ? '' : decimal.slice(dot + 1);\n if (fracPart.length > decimals) {\n throw new TypeError(`amount ${decimal} has more than ${decimals} fraction digits`);\n }\n const atomic = intPart + fracPart.padEnd(decimals, '0');\n return atomic.replace(/^0+(?=\\d)/, '');\n}\n\nexport function requirementDecimals(requirement: PaymentRequirement): number {\n const decimals = requirement.extra?.['decimals'];\n return typeof decimals === 'number' && Number.isInteger(decimals) && decimals >= 0 ? decimals : 6;\n}\n\n/** A requirement the guard fully understood, mapped into Rein's domain. */\nexport interface ResolvedRequirement {\n requirement: PaymentRequirement;\n chain: Chain;\n asset: Asset;\n /** Human-unit decimal amount, e.g. \"0.01\". */\n amount: string;\n}\n\n/**\n * Pick the first offer the guard can govern: scheme `exact`, a network that\n * maps to a supported chain, and a resolvable asset. Returns undefined when\n * no offer qualifies — callers must treat that as fail-closed.\n */\nexport function selectRequirement(\n accepts: readonly PaymentRequirement[],\n extraAddresses: Record<string, Asset> = {},\n): ResolvedRequirement | undefined {\n for (const requirement of accepts) {\n if (requirement.scheme.toLowerCase() !== 'exact') continue;\n const chain = networkToChain(requirement.network);\n if (!chain) continue;\n const asset = resolveAsset(requirement, extraAddresses);\n if (!asset) continue;\n const amount = atomicToDecimal(requirement.maxAmountRequired, requirementDecimals(requirement));\n return { requirement, chain, asset, amount };\n }\n return undefined;\n}\n\n/** What the guard submits to the policy engine (the `/v1/evaluate` shape). */\nexport interface IntentSubmission {\n agentId: string;\n vendor: Vendor;\n resource: string;\n amount: string;\n asset: Asset;\n chain: Chain;\n taskContext?: TaskContext;\n}\n\n/** Build the evaluate payload for a resolved offer on a given request URL. */\nexport function toIntentSubmission(\n resolved: ResolvedRequirement,\n url: string,\n agentId: string,\n taskContext: TaskContext | undefined,\n): IntentSubmission {\n const parsed = new URL(url);\n return {\n agentId,\n vendor: {\n host: parsed.host,\n address: resolved.requirement.payTo,\n },\n resource: resolved.requirement.resource ?? parsed.pathname,\n amount: resolved.amount,\n asset: resolved.asset,\n chain: resolved.chain,\n taskContext,\n };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n AgentId,\n Receipt,\n newId,\n type Asset,\n type Decision,\n type PaymentIntent,\n type ReceiptSettlement,\n type TaskContext,\n} from '@reinconsole/core';\nimport { EngineClient, type FetchLike } from './client.js';\nimport { PaymentBlockedError, UnsupportedRequirementError } from './errors.js';\nimport {\n PaymentRequired,\n selectRequirement,\n toIntentSubmission,\n type PaymentRequirement,\n} from './x402.js';\n\n/**\n * Builds the `X-PAYMENT` header for an allowed intent. Mock payers and the\n * local EIP-3009 payer ignore the decision; the session-key signer tier\n * requires it — the {intent, decision} pair is the engine-signed voucher the\n * signer verifies before any key is put to work.\n */\nexport type Payer = (\n requirement: PaymentRequirement,\n intent: PaymentIntent,\n decision: Decision,\n) => string | Promise<string>;\n\nexport interface GuardOptions {\n /** Policy engine base URL, e.g. \"http://localhost:8787\". */\n engineUrl: string;\n /** The agent this guard speaks for (must be registered with the engine). */\n agentId: string;\n /** Base fetch the guard wraps by default (vendor-facing). Defaults to global fetch. */\n fetch?: FetchLike;\n /** Transport for talking to the policy engine. Defaults to global fetch. */\n engineFetch?: FetchLike;\n /** Task context attached to every intent unless overridden via withTask(). */\n taskContext?: TaskContext;\n /**\n * What a blocked payment looks like to the caller: 'throw' (default) raises\n * PaymentBlockedError; 'respond' returns a synthetic 402 JSON response, for\n * agent loops that prefer inspecting responses over catching.\n */\n onBlocked?: 'throw' | 'respond';\n /** If set, the guard settles allowed payments itself (pay + retry). */\n payer?: Payer;\n /** Extra token-address -> symbol mappings for asset resolution. */\n assetAddresses?: Record<string, Asset>;\n /** Called once per receipt, as it is recorded. */\n onReceipt?: (receipt: Receipt) => void;\n}\n\n/**\n * The Rein guard: wrap an agent's fetch once, and every x402 paywall it hits\n * is policy-checked before any payment exists, with a receipt either way.\n *\n * Layering: the guard wraps the BASE fetch, underneath any x402 payment\n * library. The first (unpaid) request surfaces the 402; the guard evaluates\n * it and either blocks — so the payment layer never sees the paywall — or\n * releases the 402 upward. The payment layer's `X-PAYMENT` retry then flows\n * back through the guard, which attaches the settlement to the receipt.\n * Alternatively, pass a `payer` and the guard completes the payment itself.\n */\nexport class Guard {\n readonly client: EngineClient;\n private readonly options: GuardOptions;\n private readonly baseFetch: FetchLike;\n private readonly task = new AsyncLocalStorage<TaskContext>();\n private readonly log: Receipt[] = [];\n /** Allowed-but-unsettled receipts awaiting the payment layer's retry, by URL. */\n private readonly pendingByUrl = new Map<string, Receipt>();\n\n constructor(options: GuardOptions) {\n AgentId.parse(options.agentId);\n this.options = options;\n const f = options.fetch ?? globalThis.fetch;\n this.baseFetch = (input, init) => f(input, init);\n this.client = new EngineClient({ baseUrl: options.engineUrl, fetch: options.engineFetch });\n }\n\n /** Every receipt this guard has recorded, oldest first. */\n receipts(): readonly Receipt[] {\n return this.log;\n }\n\n /** Run `fn` with a task context attached to every intent submitted inside it. */\n withTask<T>(context: TaskContext, fn: () => T): T {\n return this.task.run({ ...this.options.taskContext, ...context }, fn);\n }\n\n /**\n * The one-liner: returns a fetch-compatible function that enforces policy on\n * every x402 paywall. Wraps the guard's base fetch unless one is passed.\n */\n wrap(fetchImpl?: FetchLike): FetchLike {\n const inner: FetchLike = fetchImpl ? (input, init) => fetchImpl(input, init) : this.baseFetch;\n\n return async (input, init) => {\n const url = requestUrl(input);\n // A request that already carries a payment is the release leg of an\n // intent this guard allowed — pass it through and capture settlement.\n if (readHeader(input, init, 'X-PAYMENT') !== null) {\n const res = await inner(input, init);\n this.settlePending(url, res);\n return res;\n }\n\n const res = await inner(input, init);\n if (res.status !== 402) return res;\n\n const body = await res\n .clone()\n .json()\n .catch(() => undefined);\n const parsed = PaymentRequired.safeParse(body);\n // Not an x402 paywall — nothing to govern, hand it back untouched.\n if (!parsed.success) return res;\n\n const resolved = selectRequirement(parsed.data.accepts, this.options.assetAddresses);\n if (!resolved) throw new UnsupportedRequirementError(url);\n\n const taskContext = this.task.getStore() ?? this.options.taskContext;\n const submission = toIntentSubmission(resolved, url, this.options.agentId, taskContext);\n const { intent, decision } = await this.client.evaluate(submission);\n\n if (decision.outcome !== 'allow') {\n const receipt = this.record(intent, decision, url, init);\n if (this.options.onBlocked === 'respond') return blockedResponse(receipt);\n throw new PaymentBlockedError(intent, decision, receipt);\n }\n\n if (!this.options.payer) {\n // Advisory mode: release the 402 to the payment layer above us; its\n // X-PAYMENT retry will come back through and settle the receipt.\n const receipt = this.record(intent, decision, url, init);\n this.pendingByUrl.set(url, receipt);\n return res;\n }\n\n const paymentHeader = await this.options.payer(resolved.requirement, intent, decision);\n const retry = await inner(input, withHeader(input, init, 'X-PAYMENT', paymentHeader));\n this.record(intent, decision, url, init, parseSettlement(retry));\n return retry;\n };\n }\n\n private record(\n intent: PaymentIntent,\n decision: Decision,\n url: string,\n init: RequestInit | undefined,\n settlement?: ReceiptSettlement,\n ): Receipt {\n const receipt = Receipt.parse({\n id: newId('rcp'),\n agentId: intent.agentId,\n intentId: intent.id,\n decisionId: decision.id,\n outcome: decision.outcome,\n url,\n method: init?.method ?? 'GET',\n vendorHost: intent.vendor.host,\n amount: intent.amount,\n asset: intent.asset,\n chain: intent.chain,\n taskContext: intent.taskContext,\n reason: decision.reason,\n settlement,\n createdAt: new Date(),\n });\n this.log.push(receipt);\n this.options.onReceipt?.(receipt);\n return receipt;\n }\n\n private settlePending(url: string, res: Response): void {\n const receipt = this.pendingByUrl.get(url);\n if (!receipt || !res.ok) return;\n const settlement = parseSettlement(res);\n if (settlement) receipt.settlement = settlement;\n this.pendingByUrl.delete(url);\n }\n}\n\n/** Convenience factory mirroring the docs: `const guard = createGuard({...})`. */\nexport function createGuard(options: GuardOptions): Guard {\n return new Guard(options);\n}\n\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction readHeader(\n input: string | URL | Request,\n init: RequestInit | undefined,\n name: string,\n): string | null {\n const source = init?.headers ?? (input instanceof Request ? input.headers : undefined);\n return source === undefined ? null : new Headers(source).get(name);\n}\n\nfunction withHeader(\n input: string | URL | Request,\n init: RequestInit | undefined,\n name: string,\n value: string,\n): RequestInit {\n const headers = new Headers(\n init?.headers ?? (input instanceof Request ? input.headers : undefined),\n );\n headers.set(name, value);\n return { ...init, headers };\n}\n\n/**\n * Decode the vendor's `X-PAYMENT-RESPONSE` header (base64 JSON per the x402\n * spec; plain JSON tolerated mock-first). Absent or undecodable -> undefined.\n */\nfunction parseSettlement(res: Response): ReceiptSettlement | undefined {\n const raw = res.headers.get('X-PAYMENT-RESPONSE');\n if (raw === null) return undefined;\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n try {\n payload = JSON.parse(Buffer.from(raw, 'base64').toString('utf8'));\n } catch {\n return { raw };\n }\n }\n const record =\n typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {};\n return {\n txHash: typeof record['transaction'] === 'string' ? record['transaction'] : undefined,\n networkId: typeof record['network'] === 'string' ? record['network'] : undefined,\n raw,\n };\n}\n\nfunction blockedResponse(receipt: Receipt): Response {\n return new Response(\n JSON.stringify({\n error: 'payment_blocked_by_rein',\n outcome: receipt.outcome,\n reason: receipt.reason,\n intentId: receipt.intentId,\n decisionId: receipt.decisionId,\n receiptId: receipt.id,\n }),\n {\n status: 402,\n headers: { 'content-type': 'application/json', 'x-rein-outcome': receipt.outcome },\n },\n );\n}\n","export type FacilitatorErrorCode =\n | 'malformed_payment'\n | 'unsupported_scheme'\n | 'unsupported_network'\n | 'unsupported_asset'\n | 'network_mismatch'\n | 'amount_mismatch'\n | 'recipient_mismatch';\n\n/** A payment the facilitator refused to settle, with a machine-readable code. */\nexport class FacilitatorError extends Error {\n constructor(\n readonly code: FacilitatorErrorCode,\n message: string,\n ) {\n super(message);\n this.name = 'FacilitatorError';\n }\n}\n","import { z } from 'zod';\nimport { FacilitatorError } from './errors.js';\n\n/**\n * Wire codecs for the two x402 headers the mock rails speak: `X-PAYMENT`\n * (agent -> vendor -> facilitator) and `X-PAYMENT-RESPONSE` (vendor -> agent).\n * Both are base64-encoded JSON per the x402 spec.\n */\n\n/** The mock `exact` scheme payload carried inside X-PAYMENT. */\nexport const MockExactPayload = z.object({\n from: z.string().min(1),\n to: z.string().min(1),\n /** Atomic-unit amount, mirroring the requirement's maxAmountRequired. */\n value: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n asset: z.string().min(1),\n /**\n * Rein linkage: the guard's payer stamps the intent id here, the facilitator\n * memos it onto the ledger entry, and the indexer reconciles exactly. A real\n * x402 transfer authorization carries a nonce that serves the same role.\n */\n intentId: z.string().optional(),\n nonce: z.string().optional(),\n});\nexport type MockExactPayload = z.infer<typeof MockExactPayload>;\n\n/** The full X-PAYMENT header body (x402 spec v1 envelope). */\nexport const MockPaymentHeader = z.object({\n x402Version: z.literal(1),\n scheme: z.string(),\n network: z.string(),\n payload: MockExactPayload,\n});\nexport type MockPaymentHeader = z.infer<typeof MockPaymentHeader>;\n\nexport function encodePaymentHeader(header: MockPaymentHeader): string {\n return Buffer.from(JSON.stringify(header)).toString('base64');\n}\n\nexport function decodePaymentHeader(raw: string): MockPaymentHeader {\n let json: unknown;\n try {\n json = JSON.parse(Buffer.from(raw, 'base64').toString('utf8'));\n } catch {\n throw new FacilitatorError('malformed_payment', 'X-PAYMENT is not base64-encoded JSON');\n }\n const parsed = MockPaymentHeader.safeParse(json);\n if (!parsed.success) {\n throw new FacilitatorError('malformed_payment', `invalid X-PAYMENT body: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\n/** What the facilitator returns on settle; the vendor base64s it into X-PAYMENT-RESPONSE. */\nexport const SettlementResponse = z.object({\n success: z.boolean(),\n /** The tx hash on the mock ledger (the guard surfaces this on the receipt). */\n transaction: z.string(),\n network: z.string(),\n payer: z.string().optional(),\n});\nexport type SettlementResponse = z.infer<typeof SettlementResponse>;\n\nexport function encodeSettlementHeader(response: SettlementResponse): string {\n return Buffer.from(JSON.stringify(response)).toString('base64');\n}\n","import type { Asset } from '@reinconsole/core';\nimport {\n atomicToDecimal,\n networkToChain,\n requirementDecimals,\n resolveAsset,\n type PaymentRequirement,\n type Payer,\n} from '@reinconsole/sdk';\nimport { FacilitatorError } from './errors.js';\nimport { MockLedger, type LedgerEntry } from './ledger.js';\nimport {\n decodePaymentHeader,\n encodePaymentHeader,\n encodeSettlementHeader,\n type MockPaymentHeader,\n type SettlementResponse,\n} from './payload.js';\n\nexport interface MockFacilitatorOptions {\n ledger: MockLedger;\n /** Recorded on settled payments (shows up in SettledPayment.facilitator). */\n name?: string;\n /** Extra token-address -> symbol mappings, mirroring the guard's option. */\n assetAddresses?: Record<string, Asset>;\n}\n\nexport interface SettleResult {\n response: SettlementResponse;\n /** Ready-made value for the vendor's X-PAYMENT-RESPONSE header. */\n header: string;\n /** The transfer as written on the mock chain. */\n entry: LedgerEntry;\n}\n\n/**\n * The mock x402 facilitator: verifies X-PAYMENT headers and \"settles\" them by\n * writing transfers onto the mock ledger. Plays the role Coinbase's hosted\n * facilitator plays in production — vendors call `settle()`, agents pay via\n * `payerFor()`, and nothing here is Rein-privileged: settlement happens\n * whether or not a policy engine ever saw the payment. Catching the ones it\n * didn't see is the indexer's job.\n */\nexport class MockFacilitator {\n readonly name: string;\n private readonly ledger: MockLedger;\n private readonly assetAddresses: Record<string, Asset>;\n\n constructor(options: MockFacilitatorOptions) {\n this.ledger = options.ledger;\n this.name = options.name ?? 'mock-facilitator';\n this.assetAddresses = options.assetAddresses ?? {};\n }\n\n /**\n * A `Payer` for the SDK guard: builds the X-PAYMENT header for an allowed\n * intent, stamping the intent id so settlement reconciles exactly. This is\n * the plug-in point the real x402 signer replaces.\n */\n payerFor(fromAddress: string): Payer {\n return (requirement, intent) =>\n encodePaymentHeader({\n x402Version: 1,\n scheme: requirement.scheme,\n network: requirement.network,\n payload: {\n from: fromAddress,\n to: requirement.payTo,\n value: requirement.maxAmountRequired,\n asset: requirement.asset,\n intentId: intent.id,\n nonce: intent.nonce,\n },\n });\n }\n\n /** Verify an X-PAYMENT header against the requirement it claims to satisfy. */\n verify(paymentHeader: string, requirement: PaymentRequirement): MockPaymentHeader {\n const header = decodePaymentHeader(paymentHeader);\n if (header.scheme.toLowerCase() !== 'exact' || requirement.scheme.toLowerCase() !== 'exact') {\n throw new FacilitatorError(\n 'unsupported_scheme',\n `mock facilitator only settles \"exact\", got \"${header.scheme}\"/\"${requirement.scheme}\"`,\n );\n }\n if (header.network !== requirement.network) {\n throw new FacilitatorError(\n 'network_mismatch',\n `payment is on \"${header.network}\" but the requirement wants \"${requirement.network}\"`,\n );\n }\n if (!networkToChain(header.network)) {\n throw new FacilitatorError('unsupported_network', `unknown network \"${header.network}\"`);\n }\n if (header.payload.value !== requirement.maxAmountRequired) {\n throw new FacilitatorError(\n 'amount_mismatch',\n `payment of ${header.payload.value} does not match required ${requirement.maxAmountRequired}`,\n );\n }\n if (header.payload.to !== requirement.payTo) {\n throw new FacilitatorError(\n 'recipient_mismatch',\n `payment pays \"${header.payload.to}\" but the requirement pays \"${requirement.payTo}\"`,\n );\n }\n return header;\n }\n\n /**\n * Verify, then settle: write the transfer on the mock chain and return the\n * settlement response the vendor relays via X-PAYMENT-RESPONSE.\n */\n settle(paymentHeader: string, requirement: PaymentRequirement): SettleResult {\n const header = this.verify(paymentHeader, requirement);\n const chain = networkToChain(header.network);\n if (!chain) throw new FacilitatorError('unsupported_network', `unknown network \"${header.network}\"`);\n const asset = resolveAsset(requirement, this.assetAddresses);\n if (!asset) {\n throw new FacilitatorError('unsupported_asset', `cannot resolve asset \"${requirement.asset}\"`);\n }\n const entry = this.ledger.transfer({\n chain,\n asset,\n from: header.payload.from,\n to: header.payload.to,\n amount: atomicToDecimal(header.payload.value, requirementDecimals(requirement)),\n memo: header.payload.intentId,\n });\n const response: SettlementResponse = {\n success: true,\n transaction: entry.txHash,\n network: header.network,\n payer: header.payload.from,\n };\n return { response, header: encodeSettlementHeader(response), entry };\n }\n}\n","import { EventEmitter } from 'node:events';\nimport {\n ReinEvent,\n compareDecimal,\n type Agent,\n type PaymentIntent,\n type SettledPayment,\n} from '@reinconsole/core';\nimport type { LedgerEntry, MockLedger } from './ledger.js';\n\n/** The slice of the policy engine the indexer subscribes to (NATS in prod). */\nexport interface EngineEvents {\n onEvent(handler: (event: ReinEvent) => void): void;\n}\n\nexport interface MockIndexerOptions {\n ledger: MockLedger;\n /**\n * Directory of managed agents and their wallets (the agents service in\n * prod). Called per ledger entry, so agents registered later are still seen.\n */\n agents: () => readonly Agent[];\n /** Recorded as SettledPayment.facilitator on reconciled payments. */\n facilitator?: string;\n}\n\n/** A `shadow.spend` event, extracted for convenience accessors. */\nexport type ShadowSpend = Extract<ReinEvent, { type: 'shadow.spend' }>;\n\n/**\n * The mock indexer: watches the ledger and classifies every transfer that\n * leaves a managed agent wallet.\n *\n * - Matches an allowed, unsettled intent -> `payment.settled` (reconciled).\n * - No ALLOW decision behind it -> `shadow.spend` — the bypass signal that\n * catches SDK-mode evasion and drives the upgrade to the signer tier.\n *\n * Reconciliation is memo-first (the facilitator stamps the intent id onto the\n * transfer), with a fallback match on chain+asset+amount+recipient for\n * transfers that arrive without a memo. A memo that does NOT resolve to an\n * allowed unsettled intent (denied, unknown, or already settled — a replay)\n * is never fuzzy-matched: it stays a shadow spend.\n */\nexport class MockIndexer {\n private readonly options: MockIndexerOptions;\n private readonly bus = new EventEmitter();\n private readonly emitted: ReinEvent[] = [];\n /** Every intent the engine has seen, by id (from `intent.created`). */\n private readonly intents = new Map<string, PaymentIntent>();\n /** Intents with an ALLOW decision, by id. */\n private readonly allowed = new Map<string, PaymentIntent>();\n private readonly settledIntents = new Set<string>();\n\n constructor(options: MockIndexerOptions) {\n this.options = options;\n options.ledger.onEntry((entry) => this.observe(entry));\n }\n\n /** Subscribe to a policy engine's event stream to learn which intents were allowed. */\n connectEngine(engine: EngineEvents): void {\n engine.onEvent((event) => {\n if (event.type === 'intent.created') {\n this.intents.set(event.intent.id, event.intent);\n } else if (event.type === 'decision.made' && event.decision.outcome === 'allow') {\n const intent = this.intents.get(event.decision.intentId);\n if (intent) this.allowed.set(intent.id, intent);\n }\n });\n }\n\n onEvent(handler: (event: ReinEvent) => void): void {\n this.bus.on('event', handler);\n }\n\n /** Everything the indexer has emitted, oldest first. */\n events(): readonly ReinEvent[] {\n return this.emitted;\n }\n\n settledPayments(): SettledPayment[] {\n return this.emitted.flatMap((e) => (e.type === 'payment.settled' ? [e.payment] : []));\n }\n\n shadowSpends(): ShadowSpend[] {\n return this.emitted.filter((e): e is ShadowSpend => e.type === 'shadow.spend');\n }\n\n private emit(event: ReinEvent): void {\n const parsed = ReinEvent.parse(event);\n this.emitted.push(parsed);\n this.bus.emit('event', parsed);\n }\n\n private agentFor(entry: LedgerEntry): string | undefined {\n for (const agent of this.options.agents()) {\n const owns = agent.wallets.some(\n (w) => w.chain === entry.chain && sameAddress(w.address, entry.from),\n );\n if (owns) return agent.id;\n }\n return undefined;\n }\n\n private observe(entry: LedgerEntry): void {\n const agentId = this.agentFor(entry);\n // Spend from a wallet Rein does not manage is someone else's problem.\n if (!agentId) return;\n\n const intent = this.reconcile(entry, agentId);\n if (intent) {\n this.settledIntents.add(intent.id);\n this.emit({\n type: 'payment.settled',\n at: entry.at,\n payment: {\n intentId: intent.id,\n txHash: entry.txHash,\n chain: entry.chain,\n blockNumber: entry.blockNumber,\n facilitator: this.options.facilitator,\n confirmedAt: entry.at,\n },\n });\n return;\n }\n\n this.emit({\n type: 'shadow.spend',\n at: entry.at,\n agentId,\n txHash: entry.txHash,\n chain: entry.chain,\n amount: entry.amount,\n });\n }\n\n private reconcile(entry: LedgerEntry, agentId: string): PaymentIntent | undefined {\n if (entry.memo !== undefined) {\n const intent = this.allowed.get(entry.memo);\n return intent && intent.agentId === agentId && !this.settledIntents.has(intent.id)\n ? intent\n : undefined;\n }\n for (const intent of this.allowed.values()) {\n if (this.settledIntents.has(intent.id)) continue;\n if (intent.agentId !== agentId) continue;\n if (intent.chain !== entry.chain || intent.asset !== entry.asset) continue;\n if (intent.vendor.address !== entry.to) continue;\n if (compareDecimal(intent.amount, entry.amount) !== 0) continue;\n return intent;\n }\n return undefined;\n }\n}\n\n/** EVM addresses compare case-insensitively; everything else compares exact. */\nfunction sameAddress(a: string, b: string): boolean {\n if (a === b) return true;\n return a.startsWith('0x') && b.startsWith('0x') && a.toLowerCase() === b.toLowerCase();\n}\n","import { PaymentRequirement, type FetchLike } from '@reinconsole/sdk';\nimport { FacilitatorError } from './errors.js';\nimport type { MockFacilitator } from './facilitator.js';\n\nexport interface MockVendorOptions {\n facilitator: MockFacilitator;\n /** Price in atomic units (e.g. \"10000\" = 0.01 USDC at 6 decimals). */\n atomicPrice: string;\n /** Where the vendor wants to be paid. */\n payTo: string;\n network?: string;\n asset?: string;\n scheme?: string;\n /** JSON body served once payment settles. */\n body?: unknown;\n}\n\nexport interface VendorCall {\n url: string;\n payment: string | null;\n}\n\nexport interface MockVendor {\n /** Drop-in fetch for the guard's vendor-facing side. */\n fetch: FetchLike;\n /** Every request the vendor saw, in order. */\n calls: VendorCall[];\n /** The payment requirement this vendor quotes for a given URL. */\n requirementFor(url: string): PaymentRequirement;\n}\n\n/**\n * An in-process x402 vendor wired to the mock facilitator: quotes a 402 with\n * payment requirements until the request carries X-PAYMENT, then settles\n * through the facilitator (writing the transfer on the mock ledger) and serves\n * the content with the settlement in X-PAYMENT-RESPONSE. The third leg of the\n * mock rails — guard pays it, indexer watches the ledger behind it.\n */\nexport function createMockVendor(options: MockVendorOptions): MockVendor {\n const calls: VendorCall[] = [];\n\n const requirementFor = (url: string): PaymentRequirement =>\n PaymentRequirement.parse({\n scheme: options.scheme ?? 'exact',\n network: options.network ?? 'base',\n maxAmountRequired: options.atomicPrice,\n resource: new URL(url).pathname,\n payTo: options.payTo,\n asset: options.asset ?? 'USDC',\n });\n\n const paymentRequired = (url: string, error: string): Response =>\n new Response(\n JSON.stringify({ x402Version: 1, accepts: [requirementFor(url)], error }),\n { status: 402, headers: { 'content-type': 'application/json' } },\n );\n\n const fetch: FetchLike = async (input, init) => {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const headers = new Headers(\n init?.headers ?? (input instanceof Request ? input.headers : undefined),\n );\n const payment = headers.get('X-PAYMENT');\n calls.push({ url, payment });\n\n if (payment === null) return paymentRequired(url, 'X-PAYMENT header is required');\n\n let settled;\n try {\n settled = options.facilitator.settle(payment, requirementFor(url));\n } catch (err) {\n if (err instanceof FacilitatorError) return paymentRequired(url, err.message);\n throw err;\n }\n return new Response(JSON.stringify(options.body ?? { ok: true }), {\n status: 200,\n headers: { 'content-type': 'application/json', 'X-PAYMENT-RESPONSE': settled.header },\n });\n };\n\n return { fetch, calls, requirementFor };\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","EventEmitter","EventEmitter"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reinconsole/mock-rails",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Simulated payment rails for Rein v0.1 — mock x402 facilitator, on-chain ledger, and indexer with shadow-spend detection.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/bugiiiii11/rein#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/bugiiiii11/rein.git",
|
|
10
|
+
"directory": "services/mock-rails"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/bugiiiii11/rein/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"rein",
|
|
15
|
+
"x402",
|
|
16
|
+
"mock",
|
|
17
|
+
"testing",
|
|
18
|
+
"payments",
|
|
19
|
+
"ai-agents"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"main": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"zod": "^3.24.1",
|
|
42
|
+
"@reinconsole/sdk": "^0.1.0",
|
|
43
|
+
"@reinconsole/core": "^0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.10.0",
|
|
47
|
+
"tsup": "^8.3.5",
|
|
48
|
+
"typescript": "^5.7.2",
|
|
49
|
+
"vitest": "^2.1.8",
|
|
50
|
+
"@reinconsole/policy-engine": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"clean": "rimraf dist .turbo"
|
|
58
|
+
}
|
|
59
|
+
}
|