@dvmkit/sdk 0.1.5-rc.9 → 0.2.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/{chunk-BIP6G74V.js → chunk-2ATUAUAO.js} +8 -8
- package/dist/{chunk-27V2ILSR.js → chunk-4A2RAKCW.js} +2 -2
- package/dist/{chunk-EVBK675R.js → chunk-6GRIKOFB.js} +28 -23
- package/dist/{chunk-CEOAHV2I.js → chunk-FDKRXOZO.js} +0 -5
- package/dist/{chunk-L4OYF4DQ.js → chunk-FT6HTUM4.js} +1 -1
- package/dist/{chunk-BTZY7VPH.js → chunk-GAIPXGM3.js} +1 -1
- package/dist/{chunk-U6M3ATSG.js → chunk-JDT5LCJC.js} +40 -6
- package/dist/{chunk-FROTD5XQ.js → chunk-JLXYOV4Y.js} +1 -2
- package/dist/{chunk-M7LHFJ5K.js → chunk-KMZXTBLA.js} +2 -2
- package/dist/{chunk-6BQM7TOW.js → chunk-L67WTZX2.js} +3 -7
- package/dist/{chunk-CGKZDODG.js → chunk-MG67KXU7.js} +0 -5
- package/dist/{chunk-JZWELPFH.js → chunk-MRAGS5VP.js} +1 -1
- package/dist/{chunk-2UUXIIOC.js → chunk-O2X2CCKH.js} +3 -3
- package/dist/{chunk-TQWGQCNV.js → chunk-OMIQMMME.js} +3 -3
- package/dist/{chunk-5PBOA25N.js → chunk-PCUQZDZA.js} +436 -355
- package/dist/{chunk-KVEHHC7W.js → chunk-PHHAYRQV.js} +7 -9
- package/dist/{chunk-SSSZUVWM.js → chunk-QP53RWAD.js} +88 -38
- package/dist/{chunk-DMNLFNTW.js → chunk-QT4ONTST.js} +1 -1
- package/dist/{chunk-RW5LP57K.js → chunk-SDK6KDJN.js} +0 -1
- package/dist/{chunk-MLRCSJYX.js → chunk-V7EVFLAK.js} +87 -90
- package/dist/{chunk-E4EVGPDX.js → chunk-XQXJKJ3P.js} +0 -2
- package/dist/{credit-ledger-2DFQHNLB.js → credit-ledger-5ZEJRI46.js} +1 -1
- package/dist/{credit-menu-enwMbn55.d.ts → credit-menu-D4Gcdgc4.d.ts} +487 -643
- package/dist/{fx-D860pZvP.d.ts → fx-B0SLBe5x.d.ts} +38 -82
- package/dist/index.d.ts +11 -14
- package/dist/index.js +2 -2
- package/dist/internal/caller.d.ts +618 -1525
- package/dist/internal/caller.js +28 -60
- package/dist/internal/server.d.ts +36 -61
- package/dist/internal/server.js +11 -11
- package/dist/{job-store-BUGqvCfL.d.ts → job-store-B2uZvga4.d.ts} +70 -59
- package/dist/{lightning-backend-BozcevPZ.d.ts → lightning-backend-CQBnQgsT.d.ts} +19 -27
- package/dist/{memory-credit-ledger-MNUOTQO5.js → memory-credit-ledger-ZOH6C3N4.js} +2 -2
- package/dist/{mpp-setup-4FJD6ZHV.js → mpp-setup-IOJBF7DB.js} +1 -1
- package/dist/{payout-reporter-RG6XNGPI.js → payout-reporter-5PIRYFVQ.js} +1 -1
- package/dist/{postgres-consumed-credential-store-VHBT4KEA.js → postgres-consumed-credential-store-ISRHBMOU.js} +1 -1
- package/dist/{postgres-job-store-3RAXMNSY.js → postgres-job-store-OGQ6IT4U.js} +1 -1
- package/dist/{postgres-kv-store-JFBDP5IP.js → postgres-kv-store-D5E2EZ24.js} +1 -1
- package/dist/{postgres-replay-store-UJXRT6VO.js → postgres-replay-store-IZFLTTAC.js} +1 -1
- package/dist/{pricing-4CEB34RM.js → pricing-MU5GNUJZ.js} +1 -1
- package/dist/{processed-payment-store-HAA4SFNK.js → processed-payment-store-FIDI3RNH.js} +1 -1
- package/dist/{revenue-reporter-ASZ7SHHH.js → revenue-reporter-NNCNRY4C.js} +1 -1
- package/dist/server/index.d.ts +53 -59
- package/dist/server/index.js +38 -37
- package/dist/{ssrf-DbFkpDv0.d.ts → ssrf-dMooihtY.d.ts} +1 -2
- package/dist/{step-cache-5dljDqrQ.d.ts → step-cache-CXg7ziML.d.ts} +389 -551
- package/dist/{tempo-charge-store-RIFTALZK.js → tempo-charge-store-76TDAF34.js} +1 -1
- package/dist/{tempo-lifecycle-DFIXQ54Q.js → tempo-lifecycle-DXM7QXJQ.js} +3 -3
- package/dist/{tempo-wallet-4QKSV65O.js → tempo-wallet-O67H5M4N.js} +2 -2
- package/dist/testing/index.d.ts +5 -15
- package/dist/testing/index.js +4 -11
- package/dist/{usd-DoRuAckA.d.ts → usd-BNDg1715.d.ts} +14 -16
- package/dist/{wallet-CJC8lwxx.d.ts → wallet-Dwjs5n_M.d.ts} +1 -1
- package/dist/{x402-5H27DCBE.js → x402-7S2EFINY.js} +2 -2
- package/package.json +2 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { ah as FundingMethod, o as PaymentMethod, bM as CreditLedgerLike, a1 as Message, X as FundingReceipt, bO as CreditSnapshot, Y as JobReceipt, S as SDKJobContext, aw as StepCache, u as ResponseContent, P as PaymentContent, a2 as MessageType, bj as X402Receipt, aE as X402Config, bi as X402ExactVersionSupport,
|
|
1
|
+
import { ah as FundingMethod, o as PaymentMethod, bM as CreditLedgerLike, a1 as Message, X as FundingReceipt, bO as CreditSnapshot, Y as JobReceipt, S as SDKJobContext, aw as StepCache, u as ResponseContent, P as PaymentContent, a2 as MessageType, bj as X402Receipt, aE as X402Config, bi as X402ExactVersionSupport, cT as X402SettlementIntent, aq as PaymentRequirementsV2, c1 as CreditLedgerQuerier, cU as X402SettlementCursor, bQ as X402SettlementStatus, cV as X402SettlementWriteOff, bN as X402RefundSettlementGate, cW as X402FacilitatorAuth, cX as X402BatchSettlementConfig, cz as PostgresX402ChannelStorage, cY as X402PayoutObserver, br as MppxServer, ab as CashuMode, cZ as CreditDepositEnqueue, c_ as X402SettlementReconciliationReason, ao as PaymentRequirements, a0 as MppxCredential, bm as CreditDepositPayload, bP as DrawResult, cn as CreditLedgerError, a6 as ReceiptCredit, ae as DrainReceiptEvent, ad as DrainReceipt, K as KVStore, y as SignedRequestAudience, c$ as CreditDrawReleaseEnqueue, cx as JobCostReportPayload, cy as JobTerminalReportPayload, cB as RevenueSkippedNoRailPayload, Z as ZodLike, a as DVMDescriptor, bV as CreditInvoiceRecord, bW as InvoiceSettlement, ch as ClientCompatibilityGate, cf as ClientCompatibility, bu as PayoutReporter, R as ResolvedCreditConfig, g as CreditView } from './step-cache-CXg7ziML.js';
|
|
2
2
|
import { ProofLike, SerializedDLEQ, Proof } from '@cashu/cashu-ts';
|
|
3
3
|
import { Hono, Context } from 'hono';
|
|
4
4
|
import { Pool } from 'pg';
|
|
5
|
-
import { b as FxRateSnapshot, F as FxFetcher } from './fx-
|
|
6
|
-
import { g as LockPubkey, f as CheckMintHealthOptions, b as LightningBackend, A as AttestationPayload } from './lightning-backend-
|
|
7
|
-
import { T as TopUpCapUnenforcedReason, A as AppendOutgoingOptions, J as JobRecord, b as JobStore } from './job-store-
|
|
5
|
+
import { b as FxRateSnapshot, F as FxFetcher } from './fx-B0SLBe5x.js';
|
|
6
|
+
import { g as LockPubkey, f as CheckMintHealthOptions, b as LightningBackend, A as AttestationPayload } from './lightning-backend-CQBnQgsT.js';
|
|
7
|
+
import { T as TopUpCapUnenforcedReason, A as AppendOutgoingOptions, J as JobRecord, b as JobStore } from './job-store-B2uZvga4.js';
|
|
8
8
|
import { Challenge } from 'mppx';
|
|
9
9
|
import { SettleResponse, SupportedResponse } from '@x402/core/types';
|
|
10
10
|
import { Channel, AutoSettlementConfig } from '@x402/evm/batch-settlement/server';
|
|
11
11
|
import { FacilitatorClient } from '@x402/core/server';
|
|
12
12
|
|
|
13
|
-
/** A row in the per-DVM `wallet_accumulator` table
|
|
13
|
+
/** A row in the per-DVM `wallet_accumulator` table. */
|
|
14
14
|
interface WalletAccumulatorRow {
|
|
15
15
|
id: string;
|
|
16
16
|
dvmId: string;
|
|
@@ -38,7 +38,7 @@ interface WalletAccumulatorRow {
|
|
|
38
38
|
meltQuoteId: string | null;
|
|
39
39
|
/**
|
|
40
40
|
* NUT-11 locktime captured at accept time (ms since epoch). Null when the
|
|
41
|
-
* accepted proof has no `locktime` tag
|
|
41
|
+
* accepted proof has no `locktime` tag. Drives oldest-first
|
|
42
42
|
* ordering and the future `wallet_accumulator_batch_expiring_soon` monitor.
|
|
43
43
|
*/
|
|
44
44
|
tExpire: number | null;
|
|
@@ -71,7 +71,7 @@ declare function initWalletAccumulatorTable(db: AccumulatorPool): Promise<void>;
|
|
|
71
71
|
*
|
|
72
72
|
* With `tx` (a client inside a caller-owned `BEGIN`), issues **no**
|
|
73
73
|
* transaction control: the inserts join the caller's transaction so the rail
|
|
74
|
-
* commit and the `CreditLedger.fund` + `draw` land atomically (
|
|
74
|
+
* commit and the `CreditLedger.fund` + `draw` land atomically (spec
|
|
75
75
|
* §2 condition 3). A `unique_violation` still surfaces as
|
|
76
76
|
* `AccumulatorReplayError`, but the caller owns the ROLLBACK — note the
|
|
77
77
|
* transaction is poisoned after the violation (Postgres aborts it), so the
|
|
@@ -82,7 +82,7 @@ declare function insertAccumulatorRows(db: AccumulatorPool, args: {
|
|
|
82
82
|
/**
|
|
83
83
|
* Per-proof matched lock pubkey, parallel to `proofs`. Recording the
|
|
84
84
|
* matched value per row lets the monitor surface retired-batch alerts
|
|
85
|
-
*
|
|
85
|
+
* and lets `dvmctl melt-pending`'s rotation walker pick the
|
|
86
86
|
* right privkey per batch. Branded `LockPubkey` so the lowercase invariant
|
|
87
87
|
* is enforced at the type boundary, not by every call site.
|
|
88
88
|
*/
|
|
@@ -97,7 +97,7 @@ declare function insertAccumulatorRows(db: AccumulatorPool, args: {
|
|
|
97
97
|
*/
|
|
98
98
|
tExpireMsByIndex?: (number | null)[];
|
|
99
99
|
receivedAt?: number;
|
|
100
|
-
/** External transaction handle — caller owns BEGIN/COMMIT/ROLLBACK
|
|
100
|
+
/** External transaction handle — caller owns BEGIN/COMMIT/ROLLBACK. */
|
|
101
101
|
tx?: AccumulatorQuerier;
|
|
102
102
|
}): Promise<WalletAccumulatorRow[]>;
|
|
103
103
|
/** Delete every accumulator row for a DVM. Used by `dvmadmin wallet-accumulator clear`. */
|
|
@@ -117,8 +117,8 @@ declare function hashLockKey(input: string): bigint;
|
|
|
117
117
|
*
|
|
118
118
|
* Used by `verifyUpfrontPayment` to reject replay of an MPP credential whose
|
|
119
119
|
* `(realm, challenge.id)` was already accepted within the current TTL window
|
|
120
|
-
*
|
|
121
|
-
* `pendingMppChallengeIds
|
|
120
|
+
* Mid-job replay is handled separately via per-job
|
|
121
|
+
* `pendingMppChallengeIds`.
|
|
122
122
|
*/
|
|
123
123
|
interface ConsumedCredentialStore {
|
|
124
124
|
/** True iff `(realm, challengeId)` was marked and the entry has not expired. */
|
|
@@ -126,7 +126,6 @@ interface ConsumedCredentialStore {
|
|
|
126
126
|
/** Mark `(realm, challengeId)` as consumed for `ttlSeconds`. */
|
|
127
127
|
mark(realm: string, challengeId: string, ttlSeconds: number): Promise<void>;
|
|
128
128
|
}
|
|
129
|
-
/** Options for `MemoryConsumedCredentialStore`. */
|
|
130
129
|
interface MemoryConsumedCredentialStoreOpts {
|
|
131
130
|
/** FIFO eviction kicks in when entry count would exceed this. Default: 10_000. */
|
|
132
131
|
maxEntries?: number;
|
|
@@ -138,8 +137,8 @@ interface MemoryConsumedCredentialStoreOpts {
|
|
|
138
137
|
*
|
|
139
138
|
* Per-process and not multi-instance-safe — each replica tracks its own
|
|
140
139
|
* consumed set, so a credential redirected to a different replica still works
|
|
141
|
-
* once.
|
|
142
|
-
*
|
|
140
|
+
* once. Use only for a single-instance DVM; swap to a shared backend before
|
|
141
|
+
* deploying multiple replicas.
|
|
143
142
|
*/
|
|
144
143
|
declare class MemoryConsumedCredentialStore implements ConsumedCredentialStore {
|
|
145
144
|
private readonly maxEntries;
|
|
@@ -203,7 +202,7 @@ interface ServerJob {
|
|
|
203
202
|
id: string;
|
|
204
203
|
tags: string[];
|
|
205
204
|
/**
|
|
206
|
-
* Capability name this job belongs to
|
|
205
|
+
* Capability name this job belongs to. Set at submission from
|
|
207
206
|
* `POST /v1/job` body's `capability` field; routes the SDK runtime to the
|
|
208
207
|
* right `onJob` / `onResponse` / `onPayment` handler and the right input
|
|
209
208
|
* schema. Required — the wire layer rejects requests that omit it.
|
|
@@ -214,12 +213,12 @@ interface ServerJob {
|
|
|
214
213
|
requesterId: string;
|
|
215
214
|
/**
|
|
216
215
|
* SHA-256 hex of the per-job opaque `job_token` minted at creation for
|
|
217
|
-
* anonymous callers
|
|
216
|
+
* anonymous callers. Mirror of `JobRecord.requesterTokenHash`;
|
|
218
217
|
* see that field for the wire-level gating semantics.
|
|
219
218
|
*/
|
|
220
219
|
requesterTokenHash?: string;
|
|
221
220
|
/**
|
|
222
|
-
* Submission provenance
|
|
221
|
+
* Submission provenance — mirrors `JobRecord.requesterToken` /
|
|
223
222
|
* `requestFingerprint` / `requesterPubkey`. Carried on the job purely so
|
|
224
223
|
* `toJobRecord` persists it; nothing in the runtime reads it. It's the
|
|
225
224
|
* replay gate: a retried paid submit that reproduces the fingerprint (and,
|
|
@@ -232,6 +231,16 @@ interface ServerJob {
|
|
|
232
231
|
/** HTTP path independently recorded when the caller proof was accepted. */
|
|
233
232
|
authRequestPath?: string;
|
|
234
233
|
status: "processing" | "completed" | "failed" | "awaiting-input" | "cancelled" | "working";
|
|
234
|
+
/** Mirrors the durable completeness marker; false for legacy accepted jobs. */
|
|
235
|
+
timingKnown?: boolean;
|
|
236
|
+
/** SDK terminal attribution; never accepted from a handler. */
|
|
237
|
+
endedBy?: "caller" | "provider";
|
|
238
|
+
/** Unix ms when the terminal status committed; immutable afterwards. */
|
|
239
|
+
terminalAtMs?: number;
|
|
240
|
+
/** Accumulated awaiting-input time, excluding the currently open stretch. */
|
|
241
|
+
callerWaitMs?: number;
|
|
242
|
+
/** Unix ms at which the current awaiting-input stretch began. */
|
|
243
|
+
awaitingInputAtMs?: number;
|
|
235
244
|
summary?: string;
|
|
236
245
|
messages: Message[];
|
|
237
246
|
seq: number;
|
|
@@ -243,9 +252,9 @@ interface ServerJob {
|
|
|
243
252
|
* Settlement reference for the most-recent successful credit. Threaded
|
|
244
253
|
* into `onJobCompleted` so the platform revenue ledger can populate
|
|
245
254
|
* `revenue_events.tx_hash`. mpp/x402 carry the credential's `challenge.id`
|
|
246
|
-
* / EVM tx hash from the facilitator
|
|
247
|
-
* the `X-Cashu-Request-Id` UUID
|
|
248
|
-
* (upfront or mid-job via `processIncomingPayment`)
|
|
255
|
+
* / EVM tx hash from the facilitator; cashu accumulator carries
|
|
256
|
+
* the `X-Cashu-Request-Id` UUID. Updated on every credit
|
|
257
|
+
* (upfront or mid-job via `processIncomingPayment`).
|
|
249
258
|
* Single-row-per-job accounting keeps the most recent credit's hash,
|
|
250
259
|
* mirroring `paymentRail`.
|
|
251
260
|
*/
|
|
@@ -256,19 +265,19 @@ interface ServerJob {
|
|
|
256
265
|
* Rail-native amount accumulated across all successful credits on this
|
|
257
266
|
* job (sats for mpp, USDC microunits for x402). The single
|
|
258
267
|
* `revenue_events` row written at completion uses this as `gross_native`,
|
|
259
|
-
* so multi-credit jobs sum the per-credit native amounts
|
|
260
|
-
*
|
|
268
|
+
* so multi-credit jobs sum the per-credit native amounts.
|
|
269
|
+
* The upfront credit initializes this value; mid-job credits accumulate same-rail
|
|
261
270
|
* and reset on a cross-rail switch (see `processIncomingPayment`).
|
|
262
271
|
*/
|
|
263
272
|
nativeAmount?: number;
|
|
264
|
-
/** Native asset tag
|
|
273
|
+
/** Native asset tag paired with `nativeAmount`. */
|
|
265
274
|
nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
|
|
266
|
-
/** Cashu flow discriminator written into `revenue_events.metadata.cashu_flow
|
|
275
|
+
/** Cashu flow discriminator written into `revenue_events.metadata.cashu_flow`. */
|
|
267
276
|
cashuFlow?: "p2pk_accumulator";
|
|
268
277
|
/**
|
|
269
278
|
* What this job cost the **builder** to serve, in 1e-6 of
|
|
270
279
|
* {@link ServerJob.costCurrency} — the running total the handler declared
|
|
271
|
-
* through `ctx.cost()
|
|
280
|
+
* through `ctx.cost()`. Absent until a handler declares
|
|
272
281
|
* something; absent is "unreported", which is not the same fact as a
|
|
273
282
|
* declared zero and is reported differently.
|
|
274
283
|
*
|
|
@@ -284,9 +293,9 @@ interface ServerJob {
|
|
|
284
293
|
costCurrency?: string;
|
|
285
294
|
/** Number of `ctx.cost()` declarations incorporated into the current cost state. */
|
|
286
295
|
costRevision?: number;
|
|
287
|
-
/** Credit the upfront payment funded/drew
|
|
296
|
+
/** Credit the upfront payment funded/drew. See `JobRecord.creditId`. */
|
|
288
297
|
creditId?: string;
|
|
289
|
-
/** The draw placed for this job on `creditId
|
|
298
|
+
/** The draw placed for this job on `creditId`. */
|
|
290
299
|
drawId?: string;
|
|
291
300
|
/** Original funding artifact returned by an explicit fund-and-draw. */
|
|
292
301
|
fundingReceipt?: FundingReceipt;
|
|
@@ -299,11 +308,11 @@ interface ServerJob {
|
|
|
299
308
|
* binds challenges to realm/method/amount/expiry, not to the dvmkit job-id;
|
|
300
309
|
* verifying the incoming credential's `challenge.id` is in this set blocks
|
|
301
310
|
* a credential lifted from a sibling job that requested the same fields.
|
|
302
|
-
* Cleared on successful credit
|
|
311
|
+
* Cleared on successful credit.
|
|
303
312
|
*/
|
|
304
313
|
pendingMppChallengeIds?: string[];
|
|
305
314
|
/**
|
|
306
|
-
* Per-job binding for x402 mid-job credentials
|
|
315
|
+
* Per-job binding for x402 mid-job credentials. 32-byte 0x-hex
|
|
307
316
|
* nonce issued by `requestPayment` and stamped onto the outbound x402
|
|
308
317
|
* envelope; the caller signs `TransferWithAuthorization` against this exact
|
|
309
318
|
* `bytes32`. Verification rejects an incoming `x402_payment` whose decoded
|
|
@@ -318,30 +327,30 @@ interface ServerJob {
|
|
|
318
327
|
pendingX402AmountUsdcMicro?: string;
|
|
319
328
|
/**
|
|
320
329
|
* Fiat micro-units the outstanding `requestPayment` ask is worth, pinned when
|
|
321
|
-
* the payment-request was emitted
|
|
330
|
+
* the payment-request was emitted. See `JobRecord`.
|
|
322
331
|
*/
|
|
323
332
|
pendingPaymentFiatMicro?: number;
|
|
324
333
|
/** Currency of {@link pendingPaymentFiatMicro} (lowercase ISO-4217). */
|
|
325
334
|
pendingPaymentFiatCurrency?: string;
|
|
326
335
|
/**
|
|
327
336
|
* Cumulative fiat micro this job has asked for mid-job, the ceiling on its
|
|
328
|
-
* draw growth
|
|
337
|
+
* draw growth. See `JobRecord.askedTopUpMicro` for the sentinel.
|
|
329
338
|
*/
|
|
330
339
|
askedTopUpMicro?: number;
|
|
331
340
|
/**
|
|
332
341
|
* Currency of {@link askedTopUpMicro} — and this job's sticky ask
|
|
333
|
-
* denomination
|
|
342
|
+
* denomination. See `JobRecord.askedTopUpCurrency`.
|
|
334
343
|
*/
|
|
335
344
|
askedTopUpCurrency?: string;
|
|
336
345
|
/**
|
|
337
|
-
* Why this job's draw growth is not capped, when it isn't
|
|
346
|
+
* Why this job's draw growth is not capped, when it isn't. Mirror
|
|
338
347
|
* of `JobRecord.topUpCapUnenforcedReason`.
|
|
339
348
|
*/
|
|
340
349
|
topUpCapUnenforcedReason?: TopUpCapUnenforcedReason;
|
|
341
350
|
/** Price the job was charged at (msats). Used to detect overpayment for change/melt gating. */
|
|
342
351
|
requiredMsats?: number;
|
|
343
352
|
/**
|
|
344
|
-
* The signed receipt for this job's terminal outcome
|
|
353
|
+
* The signed receipt for this job's terminal outcome. Mirror of
|
|
345
354
|
* `JobRecord.receipt`, set once the terminal funnel has signed and persisted
|
|
346
355
|
* it so the local read paths (which serve from `activeJobs` during the
|
|
347
356
|
* cleanup window) hand back the same bytes as a cross-machine re-read.
|
|
@@ -349,7 +358,7 @@ interface ServerJob {
|
|
|
349
358
|
receipt?: JobReceipt;
|
|
350
359
|
/**
|
|
351
360
|
* The in-flight terminal chain — persist, then sign + store the receipt
|
|
352
|
-
*
|
|
361
|
+
* Assigned synchronously by `ctx.complete()` / `ctx.fail()` at
|
|
353
362
|
* the instant the job goes terminal, so the `POST /v1/job` fast path can
|
|
354
363
|
* await it (bounded) and put the receipt on the synchronous 200 rather than
|
|
355
364
|
* making the caller poll for it. Never rejects — the chain swallows its own
|
|
@@ -358,7 +367,7 @@ interface ServerJob {
|
|
|
358
367
|
terminalWork?: Promise<void>;
|
|
359
368
|
listeners: Set<(msg: Message) => void>;
|
|
360
369
|
/**
|
|
361
|
-
* Cancellation signal for the running handler
|
|
370
|
+
* Cancellation signal for the running handler. Aborted whenever
|
|
362
371
|
* the job is cancelled on this process — caller cancel, idle timeout,
|
|
363
372
|
* stale-sweep teardown, supersession. Surfaced to builders as `ctx.signal`
|
|
364
373
|
* and auto-threaded into `ctx.fetch`, so an in-flight provider call
|
|
@@ -368,18 +377,18 @@ interface ServerJob {
|
|
|
368
377
|
*/
|
|
369
378
|
abort: AbortController;
|
|
370
379
|
/**
|
|
371
|
-
* Hook for cross-machine message persistence (Tx A
|
|
380
|
+
* Hook for cross-machine message persistence (Tx A). Set by
|
|
372
381
|
* `JobManager` when the store is streamable. The appender runs the
|
|
373
382
|
* outgoing-message tx atomically with any payment-request counter bump so
|
|
374
383
|
* `(message, pending_payment_msats)` land together — never separately.
|
|
375
384
|
*
|
|
376
|
-
* Per-job serialised
|
|
385
|
+
* Per-job serialised: each call chains onto `messageAppenderTail`
|
|
377
386
|
* so two synchronous `providerMessage` calls (e.g. `artifact` followed by
|
|
378
387
|
* `complete`) can't race for the DB-side seq allocation.
|
|
379
388
|
*/
|
|
380
389
|
messageAppender?: (msg: Message, opts?: AppendOutgoingOptions) => void;
|
|
381
390
|
/**
|
|
382
|
-
* Tail of the per-job appender chain
|
|
391
|
+
* Tail of the per-job appender chain. `wireMessageAppender` updates
|
|
383
392
|
* this on every call so `persistJob` can await it before the terminal snapshot
|
|
384
393
|
* save runs `DELETE FROM job_messages`.
|
|
385
394
|
*/
|
|
@@ -411,7 +420,7 @@ declare function toJobRecord(job: ServerJob): JobRecord;
|
|
|
411
420
|
/** Reconstitute a ServerJob from a persisted JobRecord with fresh runtime fields. */
|
|
412
421
|
declare function fromJobRecord(record: JobRecord): ServerJob;
|
|
413
422
|
/**
|
|
414
|
-
* Signal cancellation to a running handler
|
|
423
|
+
* Signal cancellation to a running handler. Idempotent — aborting an
|
|
415
424
|
* already-aborted controller is a no-op, so the overlapping teardown paths
|
|
416
425
|
* (caller cancel then stale sweep, say) can each call it unconditionally.
|
|
417
426
|
*/
|
|
@@ -426,22 +435,21 @@ declare class JobCancelledError extends Error {
|
|
|
426
435
|
}
|
|
427
436
|
/** Append a provider-sent message to the job and notify SSE listeners. */
|
|
428
437
|
declare function providerMessage(job: ServerJob, type: MessageType, content: Record<string, unknown> | object): void;
|
|
429
|
-
/** Check if a job status is terminal (completed, failed, cancelled). */
|
|
430
438
|
declare function isTerminal(status: ServerJob["status"]): boolean;
|
|
431
439
|
/** Check if a message is a yield point (ends the provider's turn). */
|
|
432
440
|
declare function isYieldMessage(msg: Message): boolean;
|
|
433
441
|
|
|
434
442
|
/**
|
|
435
|
-
* Per-DVM runtime mint-health tracker
|
|
443
|
+
* Per-DVM runtime mint-health tracker. Alongside the platform's
|
|
436
444
|
* durable mint-health cron, probes every configured
|
|
437
445
|
* mint on a timer, tracks consecutive failures, and flips a mint to `sick`
|
|
438
446
|
* once the threshold is crossed. The SDK uses the resulting view to:
|
|
439
447
|
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
448
|
+
* 1. Filter `/v1/info`'s advertised `mints` to currently-healthy ones.
|
|
449
|
+
* 2. Reject new accumulator receives at sick mints before they touch the
|
|
450
|
+
* DB (`cashu_mint_sick` 503 from `verifyAccumulatorReceipt`).
|
|
443
451
|
*
|
|
444
|
-
* Since
|
|
452
|
+
* Since this tracker is the sole owner of mint health: boot no longer
|
|
445
453
|
* runs a fail-fast check before binding the port. To preserve what boot used
|
|
446
454
|
* to guarantee it (a) treats a mint as healthy only when it's reachable AND
|
|
447
455
|
* NUT-compliant (reproducing `assertNutSupport` via `nutCompliant`), and (b)
|
|
@@ -454,7 +462,7 @@ declare function isYieldMessage(msg: Message): boolean;
|
|
|
454
462
|
* pages the builder when a stuck batch nears `t_expire`.
|
|
455
463
|
*
|
|
456
464
|
* In-memory only — no DB persistence. The platform cron keeps the durable
|
|
457
|
-
* record + email path
|
|
465
|
+
* record + email path; the SDK only needs fresh in-process state
|
|
458
466
|
* to make routing decisions.
|
|
459
467
|
*/
|
|
460
468
|
|
|
@@ -467,8 +475,8 @@ interface MintHealthSnapshot {
|
|
|
467
475
|
/**
|
|
468
476
|
* True once the mint is considered unhealthy for routing: either
|
|
469
477
|
* `consecutiveFailures` crossed the threshold, or the last probe reached the
|
|
470
|
-
* mint but it fails the NUT gate
|
|
471
|
-
* that flips `sick` immediately
|
|
478
|
+
* mint but it fails the NUT gate, a deterministic, permanent config error
|
|
479
|
+
* that flips `sick` immediately.
|
|
472
480
|
*/
|
|
473
481
|
sick: boolean;
|
|
474
482
|
/**
|
|
@@ -483,10 +491,11 @@ interface MintHealthSnapshot {
|
|
|
483
491
|
/** Error category from the most recent failed probe. */
|
|
484
492
|
errorCode?: string;
|
|
485
493
|
}
|
|
486
|
-
/** Tunables for {@link MintHealthTracker}. */
|
|
487
494
|
interface MintHealthTrackerOptions {
|
|
488
495
|
/** Cashu mint URLs the DVM accepts. The tracker probes every entry. */
|
|
489
496
|
mints: string[];
|
|
497
|
+
/** Host environment used for default timing and failure settings. */
|
|
498
|
+
env?: Record<string, string | undefined>;
|
|
490
499
|
/**
|
|
491
500
|
* Tick cadence in ms. Defaults to {@link DEFAULT_TICK_INTERVAL_MS} or the
|
|
492
501
|
* `DVMKIT_MINT_HEALTH_TICK_MS` env var when set.
|
|
@@ -497,9 +506,7 @@ interface MintHealthTrackerOptions {
|
|
|
497
506
|
* {@link DEFAULT_FAILURES_TO_SICK} or `DVMKIT_MINT_FAILURES_TO_SICK` env.
|
|
498
507
|
*/
|
|
499
508
|
consecutiveFailuresToSick?: number;
|
|
500
|
-
/** Injected `fetch` for tests. */
|
|
501
509
|
fetchImpl?: typeof fetch;
|
|
502
|
-
/** Injected `Date.now` for tests. */
|
|
503
510
|
now?: () => number;
|
|
504
511
|
/**
|
|
505
512
|
* Per-probe timeout / retry / backoff, forwarded to {@link checkMintHealth}.
|
|
@@ -542,7 +549,7 @@ declare class MintHealthTracker {
|
|
|
542
549
|
/**
|
|
543
550
|
* True once the first full probe tick has landed. Before this, paid calls
|
|
544
551
|
* are held at `503 mint_health_pending` rather than accepted against
|
|
545
|
-
* unvalidated mints
|
|
552
|
+
* unvalidated mints.
|
|
546
553
|
*/
|
|
547
554
|
initialProbeComplete(): boolean;
|
|
548
555
|
/**
|
|
@@ -559,7 +566,7 @@ declare class MintHealthTracker {
|
|
|
559
566
|
* gate — a deterministic, permanent config error, unlike a transient outage.
|
|
560
567
|
* Unknown mints return false. `advertisedMints()` uses this to keep a
|
|
561
568
|
* permanently-unsettleable mint off `/v1/info` even in the all-sick fallback,
|
|
562
|
-
* where a merely-flapping mint is still published
|
|
569
|
+
* where a merely-flapping mint is still published.
|
|
563
570
|
*/
|
|
564
571
|
isNutIncompatible(mintUrl: string): boolean;
|
|
565
572
|
/** Plain-object snapshot keyed by mint URL — for tests + observability. */
|
|
@@ -569,37 +576,37 @@ declare class MintHealthTracker {
|
|
|
569
576
|
* One-shot structured summary emitted after the first probe tick. Replaces
|
|
570
577
|
* the boot check's per-mint `cashu_mint_startup_health` lines and surfaces an
|
|
571
578
|
* all-mints-unhealthy warning — the operator alert that the Fly restart-loop
|
|
572
|
-
* used to be before boot stopped refusing to start
|
|
579
|
+
* used to be before boot stopped refusing to start.
|
|
573
580
|
*/
|
|
574
581
|
private logBootHealth;
|
|
575
582
|
}
|
|
576
583
|
|
|
577
584
|
/**
|
|
578
|
-
* Credit fields
|
|
585
|
+
* Credit fields carried inside the signed request body.
|
|
579
586
|
*
|
|
580
587
|
* An explicit draw references the credit INSIDE the secp256k1/Schnorr-signed
|
|
581
588
|
* `body.data` — no new caller crypto. Wire field names (snake_case, matching
|
|
582
589
|
* the rest of the wire surface):
|
|
583
590
|
*
|
|
584
591
|
* - `credit_id` — the credit to draw against.
|
|
585
|
-
* - `draw_id`
|
|
586
|
-
* - `fund`
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
592
|
+
* - `draw_id` — client-generated draw id; the ledger's idempotency key.
|
|
593
|
+
* - `fund` — optional `{ amount_micro, commitment }` funding commitment:
|
|
594
|
+
* present iff a funding artifact (X-Cashu / X-PAYMENT / mpp credential)
|
|
595
|
+
* rides the same request. `commitment` is the SHA-256 hex over the raw
|
|
596
|
+
* artifact bytes, binding the header-borne proof — which sits OUTSIDE the
|
|
597
|
+
* signed body — to the credit its sender intended (the funding-commitment rule).
|
|
591
598
|
*
|
|
592
599
|
* These are envelope-level fields, not capability input: **the SDK** removes
|
|
593
600
|
* them before any builder-owned schema parses ({@link stripCreditEnvelope} in
|
|
594
601
|
* `JobManager.parseInput` and on the quote path; {@link
|
|
595
602
|
* creditEnvelopeIgnoreFields} feeding the auth verifier's own schema check), so
|
|
596
603
|
* handlers never see them and a top-level `.strict()` capability schema is
|
|
597
|
-
* fine
|
|
604
|
+
* fine. Relying on Zod's default strip mode instead was the trap:
|
|
598
605
|
* `.strict()` rejects unknown keys, so an explicit draw 400'd at parse before
|
|
599
606
|
* the envelope was ever extracted.
|
|
600
607
|
*
|
|
601
608
|
* The strip never touches the canonical signing bytes. Every auth call site
|
|
602
|
-
* verifies the raw wire body (`signedRequestInput
|
|
609
|
+
* verifies the raw wire body (`signedRequestInput`), so the credit
|
|
603
610
|
* fields are in the verified bytes because they were never taken out of them —
|
|
604
611
|
* the strip is only ever applied to what a schema is about to parse.
|
|
605
612
|
*/
|
|
@@ -634,7 +641,7 @@ declare class CreditEnvelopeError extends Error {
|
|
|
634
641
|
declare function extractCreditEnvelope(data: unknown): CreditEnvelope | undefined;
|
|
635
642
|
/**
|
|
636
643
|
* The reserved keys `schema` does not itself declare — the ones the SDK is
|
|
637
|
-
* free to remove before handing it an input
|
|
644
|
+
* free to remove before handing it an input.
|
|
638
645
|
*
|
|
639
646
|
* A capability schema declares none of them, so all three are removable. The
|
|
640
647
|
* one exception this exists for is `/v1/credit`'s own `CREDIT_REQUEST_SCHEMA`,
|
|
@@ -648,7 +655,7 @@ declare function extractCreditEnvelope(data: unknown): CreditEnvelope | undefine
|
|
|
648
655
|
declare function creditEnvelopeIgnoreFields(schema: unknown): readonly string[];
|
|
649
656
|
/**
|
|
650
657
|
* Remove the reserved credit keys from `data` before `schema` parses it, so a
|
|
651
|
-
* strict schema doesn't reject the protocol's own envelope
|
|
658
|
+
* strict schema doesn't reject the protocol's own envelope. Keys
|
|
652
659
|
* `schema` declares are left alone — see {@link creditEnvelopeIgnoreFields}.
|
|
653
660
|
*
|
|
654
661
|
* Returns `data` unchanged when it isn't a plain object or carries none of the
|
|
@@ -659,10 +666,10 @@ declare function creditEnvelopeIgnoreFields(schema: unknown): readonly string[];
|
|
|
659
666
|
declare function stripCreditEnvelope(schema: unknown, data: unknown): unknown;
|
|
660
667
|
|
|
661
668
|
/**
|
|
662
|
-
* Fiat denomination of the job price, pinned per request
|
|
669
|
+
* Fiat denomination of the job price, pinned per request.
|
|
663
670
|
*
|
|
664
|
-
* The credit ledger is fiat-micro denominated
|
|
665
|
-
*
|
|
671
|
+
* The credit ledger is fiat-micro denominated: 1e-6 of the DVM's pricing
|
|
672
|
+
* currency, never msats. The conversion pins at
|
|
666
673
|
* quote time: the verify path already fixes both the fiat price and its msat
|
|
667
674
|
* equivalent, so the implicit N=1 fund credits the QUOTED fiat amount when
|
|
668
675
|
* the attached token satisfies the quoted msats — no fresh BTC/USD lookup in
|
|
@@ -677,36 +684,13 @@ interface PriceFiat {
|
|
|
677
684
|
}
|
|
678
685
|
|
|
679
686
|
/**
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
* price form that needs one, and the ledger can be denominated for every paid
|
|
685
|
-
* request rather than only the ones a rate provider happened to be up for.
|
|
686
|
-
*
|
|
687
|
-
* - `dynamicUpfront` — the validated `QuoteResult.upfront` fiat envelope a
|
|
688
|
-
* dynamic-priced capability returned for THIS request.
|
|
689
|
-
* - a static `"$X.XX"` price — parsed directly.
|
|
690
|
-
*
|
|
691
|
-
* Returns `undefined` for free jobs (`priceMsats` unset or 0). For a paid job
|
|
692
|
-
* it always returns a denomination: `validateQuoteResult` rejects a dynamic
|
|
693
|
-
* amount too small or too large to express in micro-units, and `configureDVM`
|
|
694
|
-
* rejects a static price with the same defect at boot.
|
|
695
|
-
*
|
|
696
|
-
* internal-review: whichever branch answers, the currency is the DVM's declared
|
|
697
|
-
* pricing currency, so a job draws in the denomination `/v1/credit` funded in.
|
|
698
|
-
* Held there by two guards outside this function, which is why neither branch
|
|
699
|
-
* needs to know the DVM's currency to return it — `validateQuoteResult`
|
|
700
|
-
* refuses a quote that disagrees with the declaration, and `configureDVM`
|
|
701
|
-
* refuses a static (USD-literal) price on a DVM that declares anything but
|
|
702
|
-
* `"usd"`. Both refuse rather than convert, deliberately: an fx lookup on this
|
|
703
|
-
* path is the internal-review shape, where a cold-start rate outage took money and
|
|
704
|
-
* recorded no draw.
|
|
687
|
+
* Paid requests pin the validated fiat price without an FX lookup. Free jobs
|
|
688
|
+
* return undefined. configureDVM and validateQuoteResult enforce micro-unit
|
|
689
|
+
* range and the DVM's declared currency before this helper is called; neither
|
|
690
|
+
* guard converts a mismatched currency.
|
|
705
691
|
*/
|
|
706
692
|
declare function resolvePriceFiat(args: {
|
|
707
|
-
/** The capability's static `price` value, if any. */
|
|
708
693
|
price: string | undefined;
|
|
709
|
-
/** The resolved required price in msats (static or dynamic). */
|
|
710
694
|
priceMsats: number | undefined;
|
|
711
695
|
/** Fiat envelope from a dynamic quote, when the price came from `onQuote`. */
|
|
712
696
|
dynamicUpfront?: {
|
|
@@ -715,28 +699,11 @@ declare function resolvePriceFiat(args: {
|
|
|
715
699
|
};
|
|
716
700
|
}): PriceFiat | undefined;
|
|
717
701
|
/**
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
* A caller doing an explicit fund-and-draw has to sign the provider's own
|
|
723
|
-
* valuation of the artifact it attaches (`price_micro × paid_msats /
|
|
724
|
-
* required_msats`), and `price_micro` is `PriceFiat.amountMicro`. Reconstructing
|
|
725
|
-
* that integer from a rendered decimal is lossy the moment any surface between
|
|
726
|
-
* the two rounds — the isolate tier advertises at four decimals, and the CLI's
|
|
727
|
-
* synthetic quote used to re-round a 402 the same way — so `/v1/quote`'s
|
|
728
|
-
* `upfront.amount_micro` and the upfront 402's `price_micro` state the integer
|
|
729
|
-
* itself. Both **must** derive through here rather than recomputing: two
|
|
730
|
-
* independent derivations of one figure is the shape that produced the mismatch
|
|
731
|
-
* these fields exist to remove.
|
|
732
|
-
*
|
|
733
|
-
* The `priceMsats` gate stays on `resolvePriceFiat` alone. An advertisement
|
|
734
|
-
* surface has no msat figure in hand — the quote route states a price before
|
|
735
|
-
* anything has converted it — and a free capability is already excluded there
|
|
736
|
-
* by having no price to pass.
|
|
702
|
+
* Shared exact integer for quote, 402 and ledger valuation. Callers must sign
|
|
703
|
+
* this micro-unit figure rather than reconstruct it from a rounded display.
|
|
704
|
+
* Unlike resolvePriceFiat, advertisement callers need no msat price in hand.
|
|
737
705
|
*/
|
|
738
706
|
declare function priceFiatMicro(args: {
|
|
739
|
-
/** The capability's static `price` value, if any. */
|
|
740
707
|
price?: string | undefined;
|
|
741
708
|
/** Fiat envelope from a dynamic quote, when the price came from `onQuote`. */
|
|
742
709
|
dynamicUpfront?: {
|
|
@@ -744,17 +711,7 @@ declare function priceFiatMicro(args: {
|
|
|
744
711
|
currency: string;
|
|
745
712
|
};
|
|
746
713
|
}): PriceFiat | undefined;
|
|
747
|
-
/**
|
|
748
|
-
* Why a denomination could not be produced (internal-review). Carried out of
|
|
749
|
-
* {@link msatsToFiatMicro} and {@link resolveFxSnapshot} so the skip that
|
|
750
|
-
* follows can name its cause.
|
|
751
|
-
*
|
|
752
|
-
* Every one of these used to collapse into a bare `undefined`, which is how a
|
|
753
|
-
* `credit_topup_skipped` / `denomination_unavailable` line came to be
|
|
754
|
-
* undiagnosable: a rate outage, a currency the snapshot never carried, and an
|
|
755
|
-
* ask too small to express in micro-units all read identically in the log, and
|
|
756
|
-
* the only way to tell them apart was to re-derive the whole path by hand.
|
|
757
|
-
*/
|
|
714
|
+
/** Reasons are kept distinct so a skipped ledger leg identifies its cause. */
|
|
758
715
|
type FiatDenominationFailure =
|
|
759
716
|
/** Nothing to convert — a zero-msat ask. */
|
|
760
717
|
"no_msats"
|
|
@@ -771,7 +728,7 @@ type FiatDenominationFailure =
|
|
|
771
728
|
| "rate_stale_refused";
|
|
772
729
|
/**
|
|
773
730
|
* An fx snapshot plus how fresh it is, or why there is none. `stale` means the
|
|
774
|
-
* live fetch failed and this is the last snapshot the fetcher saw
|
|
731
|
+
* live fetch failed and this is the last snapshot the fetcher saw;
|
|
775
732
|
* `cachedAt` is the Unix ms it landed.
|
|
776
733
|
*/
|
|
777
734
|
type ResolvedFx = {
|
|
@@ -790,7 +747,7 @@ type ResolvedFx = {
|
|
|
790
747
|
* `stale` covers both degradation rungs and `carriedForward` distinguishes
|
|
791
748
|
* them: either the whole snapshot came from `lastKnown()` because the live
|
|
792
749
|
* fetch failed, or the fetch succeeded and only *this* currency was carried
|
|
793
|
-
* over from an earlier one because the body omitted it
|
|
750
|
+
* over from an earlier one because the body omitted it. They read
|
|
794
751
|
* differently to an operator — the rate source down, versus the rate source
|
|
795
752
|
* having quietly narrowed — so the log says which.
|
|
796
753
|
*/
|
|
@@ -806,80 +763,51 @@ type FiatDenomination = {
|
|
|
806
763
|
detail?: string;
|
|
807
764
|
};
|
|
808
765
|
/**
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
* The same ladder `resolveServerBtcUsdRate` walks for the x402/Tempo rails
|
|
813
|
-
* (`btc-rate.ts`), and it exists here because the mid-job top-up's two fx legs
|
|
814
|
-
* — the ask-time pin in `context.ts` and the verify-time hop in
|
|
815
|
-
* {@link msatsToFiatMicro} — were the only ones in the payment layer that
|
|
816
|
-
* called `fetch()` raw and swallowed the throw. `FxFetcher.lastKnown` is
|
|
817
|
-
* documented as "the offline degradation source for the payment rail", so a
|
|
818
|
-
* process holding a perfectly good snapshot was nonetheless dropping the
|
|
819
|
-
* receipt's whole `credit` block over one blip at the rate provider.
|
|
820
|
-
*
|
|
821
|
-
* **A known-stale refusal fails closed and never degrades** (internal-review): the
|
|
822
|
-
* platform decided the snapshot is too old to price against, so serving the
|
|
823
|
-
* last-known one prices off exactly what it refused. Every other failure
|
|
824
|
-
* degrades, which is the internal-review posture.
|
|
825
|
-
*
|
|
826
|
-
* No cool-down latch, unlike `resolveServerBtcUsdRate`. That one guards a
|
|
827
|
-
* per-request hot path; this runs at most twice per mid-job ask, and a latch
|
|
828
|
-
* shared across every DVM in the process would suppress the recovery probe a
|
|
829
|
-
* long-lived host needs.
|
|
766
|
+
* A live fetch failure may use lastKnown(), but a known-stale refusal never
|
|
767
|
+
* does: that would use the very snapshot the platform refused. No shared
|
|
768
|
+
* cooldown suppresses the recovery probe on a later mid-job ask.
|
|
830
769
|
*/
|
|
831
770
|
declare function resolveFxSnapshot(fetcher: FxFetcher): Promise<ResolvedFx>;
|
|
832
771
|
/**
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
* internal-review removed the numeric-msats *price*, so {@link resolvePriceFiat} needs
|
|
838
|
-
* no rate; the mid-job ask is the one surface where a bare msat amount survives
|
|
839
|
-
* (`ctx.requestPayment(50_000, …)`), and the credit it tops up may be
|
|
840
|
-
* denominated in a currency the ask never named. An fx hop is the only way to
|
|
841
|
-
* put either on the ledger.
|
|
842
|
-
*
|
|
843
|
-
* Answers a *reason* rather than `undefined` when it can't denominate — the
|
|
844
|
-
* caller still accepts the money and skips the ledger leg rather than refusing
|
|
845
|
-
* over a rate outage (internal-review), but it can now say which failure it degraded
|
|
846
|
-
* on (internal-review). That fail-soft is why this stays separate from
|
|
847
|
-
* `resolvePriceFiat`, which is now total for every paid request.
|
|
772
|
+
* Bare-msat mid-job asks need an FX conversion into the credit's currency.
|
|
773
|
+
* Failure carries a reason: callers accept the payment and skip its ledger
|
|
774
|
+
* leg rather than refusing over an outage. Upfront prices use resolvePriceFiat
|
|
775
|
+
* and require no rate, so they do not share this fail-soft behavior.
|
|
848
776
|
*/
|
|
849
777
|
declare function msatsToFiatMicro(args: {
|
|
850
778
|
msats: number;
|
|
851
779
|
currency: string;
|
|
852
780
|
fxFetcher: FxFetcher;
|
|
853
781
|
/**
|
|
854
|
-
* A snapshot already in hand
|
|
782
|
+
* A snapshot already in hand. The mid-job ask path fetches one to
|
|
855
783
|
* size a fiat-form ask's msats; converting on that same snapshot costs no
|
|
856
784
|
* second fetch and keeps the two figures arithmetically consistent.
|
|
857
785
|
*/
|
|
858
786
|
snapshot?: FxRateSnapshot;
|
|
859
787
|
}): Promise<FiatDenomination>;
|
|
860
788
|
/**
|
|
861
|
-
* The mid-job ask's fiat pin, in the target currency
|
|
789
|
+
* The mid-job ask's fiat pin, in the target currency — what the cap
|
|
862
790
|
* on this job's cumulative draw growth is summed from, and what the top-up
|
|
863
791
|
* funds against.
|
|
864
792
|
*
|
|
865
793
|
* Two differences from {@link msatsToFiatMicro}, both scoped to the ask:
|
|
866
794
|
*
|
|
867
795
|
* - **A fiat-form ask already in the target currency is taken verbatim**, so a
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
*
|
|
872
|
-
*
|
|
796
|
+
* `ctx.requestPayment({ amount, currency })` keeps the exactness it has and
|
|
797
|
+
* never round-trips through a rate. Any other form converts `msats` on the
|
|
798
|
+
* caller's snapshot — including a fiat-form ask in some *other* currency,
|
|
799
|
+
* which converts on the very snapshot that sized its msats, so the pin and
|
|
800
|
+
* the wire ask can't disagree about the rate.
|
|
873
801
|
* - **Floors at one micro-unit rather than returning `undefined`.**
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
802
|
+
* `ctx.requestPayment(500)` is half a micro at $100k/BTC — a legitimate, if
|
|
803
|
+
* tiny, ask, and refusing to denominate it poisons the job's whole ask total
|
|
804
|
+
* and with it the ceiling. One micro is provably conservative for a ceiling:
|
|
805
|
+
* the ask is worth less than that. `resolvePriceFiat` and `msatsToFiatMicro`
|
|
806
|
+
* keep their sub-micro refusals, which are load-bearing on the upfront path.
|
|
879
807
|
*
|
|
880
808
|
* Throws only what `fxRateFor` throws — an unsupported target currency, or an
|
|
881
809
|
* `fx_rate_unavailable` for one the snapshot never carried. The ask path catches
|
|
882
|
-
* both and degrades to an unpinnable ask
|
|
810
|
+
* both and degrades to an unpinnable ask, naming which it was.
|
|
883
811
|
*/
|
|
884
812
|
declare function pinAskFiat(args: {
|
|
885
813
|
msats: number;
|
|
@@ -892,65 +820,29 @@ declare function pinAskFiat(args: {
|
|
|
892
820
|
}): PriceFiat | undefined;
|
|
893
821
|
/**
|
|
894
822
|
* Fiat micro-units actually funded for a rail payment of `paidMsats` against
|
|
895
|
-
* a job quoted at `requiredMsats` ↔ `priceFiat
|
|
896
|
-
*
|
|
823
|
+
* a job quoted at `requiredMsats` ↔ `priceFiat`. An overpayment funds and draws
|
|
824
|
+
* the full paid amount so ledger value matches money received.
|
|
897
825
|
* Pro-rata at the quote-pinned ratio, so the exact-payment case — what every
|
|
898
826
|
* conformant client sends — is `priceFiat.amountMicro` with zero drift.
|
|
899
827
|
*/
|
|
900
828
|
declare function fundedMicroFor(priceFiat: PriceFiat, paidMsats: number, requiredMsats: number): number;
|
|
901
829
|
/**
|
|
902
|
-
*
|
|
903
|
-
*
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
* stablecoin, and `priceFiat.amountMicro` is already an exact USD micro figure,
|
|
907
|
-
* so the advertisement is the price — no rate in the derivation, and therefore
|
|
908
|
-
* no way for the 402 and the submit to disagree about it.
|
|
909
|
-
*
|
|
910
|
-
* The BTC hop this replaces was not merely lossy, it was *unpinned*. The 402 is
|
|
911
|
-
* stateless, so the figure was recomputed at submit from whatever the fx cache
|
|
912
|
-
* held by then, through two rate-dependent roundings: `resolveStaticPriceMsats`
|
|
913
|
-
* ceils the price to a whole **satoshi**, and `msatsToUsdc` ceils that back to
|
|
914
|
-
* µUSDC. A rate refresh between the 402 and the payment therefore moved the
|
|
915
|
-
* reconstruction by up to a satoshi's worth (~$0.0013 at $100k/BTC) — and when
|
|
916
|
-
* it moved down, the caller's exact payment read as an overpayment, pro-rated
|
|
917
|
-
* above `price_micro`, and was refused `funding_commitment_mismatch` *after*
|
|
918
|
-
* the facilitator had settled. Money moved, no job, no credit. The sat-ceil
|
|
919
|
-
* also overcharged every x402 caller outright: at $0.01 and $97,432/BTC the
|
|
920
|
-
* quote is 11 sats and the old advertisement demanded 10,718 µUSDC — 7% above
|
|
921
|
-
* the price the caller was quoted and the ledger recorded.
|
|
922
|
-
*
|
|
923
|
-
* Every caller gates x402 on a USD-priced DVM before reaching this helper.
|
|
924
|
-
* Keeping the helper USD-only makes a future advertisement call site fail
|
|
925
|
-
* closed instead of quietly restoring the rate-derived non-USD path.
|
|
830
|
+
* USD micro-units are already USDC's six-decimal units. Advertise them verbatim
|
|
831
|
+
* and value settlement against the same figure; a BTC conversion would add
|
|
832
|
+
* rounding and an unpinned rate between challenge and settlement.
|
|
833
|
+
* Non-USD callers fail closed rather than implicitly introducing that FX hop.
|
|
926
834
|
*/
|
|
927
835
|
declare function x402RequiredUsdcMicro(priceFiat: PriceFiat): number;
|
|
928
836
|
/**
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
* msat quote, so the two frames can never disagree about what landed.
|
|
934
|
-
*
|
|
935
|
-
* x402 must NOT reach {@link fundedMicroFor} through `usdcToMsats`. The
|
|
936
|
-
* advertised figure is stated in µUSDC by {@link x402RequiredUsdcMicro} — the
|
|
937
|
-
* price verbatim for a USD job — and the
|
|
938
|
-
* receipt converts back with `usdcToMsats`, a `Math.round`, which is the
|
|
939
|
-
* inverse of neither. The round trip lands at or above `requiredMsats`, which
|
|
940
|
-
* took the pro-rata branch and valued a payment of exactly the advertised
|
|
941
|
-
* amount above `price_micro`. That mismatch used to be refused only after the
|
|
942
|
-
* facilitator had broadcast the transfer, so a conformant caller signing the
|
|
943
|
-
* quoted price lost the payment outright (the gate now also runs pre-settle —
|
|
944
|
-
* internal-review).
|
|
945
|
-
*
|
|
946
|
-
* Valuing in the rail's own units removes the manufactured disagreement and
|
|
947
|
-
* nothing else: a genuine overpayment still pro-rates, and both inputs are on
|
|
948
|
-
* the 402, so the caller can derive the figure it must sign from that alone.
|
|
837
|
+
* Value a settled authorization against the advertised USDC amount, preserving
|
|
838
|
+
* the quote at exact payment and prorating genuine over/underpayment. Use this
|
|
839
|
+
* ratio for both fiat and msat frames: a USDC-to-msat round trip can round an
|
|
840
|
+
* exact payment into a false overpayment and invalidate its signed commitment.
|
|
949
841
|
*/
|
|
950
842
|
declare function x402SettledShare(quoted: number, requiredUsdcMicro: number, settledUsdcMicro: number): number;
|
|
951
843
|
/**
|
|
952
844
|
* SHA-256 hex over the raw funding artifact — the value an explicit
|
|
953
|
-
* fund-and-draw request must sign as `fund.commitment` (
|
|
845
|
+
* fund-and-draw request must sign as `fund.commitment` (the funding-commitment rule).
|
|
954
846
|
* The artifact is the exact header-borne string: the `X-Cashu` header value,
|
|
955
847
|
* the `X-PAYMENT` header value, or the extracted `Payment <…>` mpp scheme
|
|
956
848
|
* string. Hashing the verbatim wire string keeps the commitment computable by
|
|
@@ -964,14 +856,14 @@ declare function fundingCommitment(artifact: string): string;
|
|
|
964
856
|
declare function implicitCreditId(rail: "cashu" | "x402" | "tempo", paymentId: string): string;
|
|
965
857
|
/**
|
|
966
858
|
* Whether a credit id sits in a namespace the server derives — which a caller
|
|
967
|
-
* may **reference** but must never **create
|
|
859
|
+
* may **reference** but must never **create**.
|
|
968
860
|
*
|
|
969
861
|
* Both namespaces are computable by a third party: `imp:` from the rail's
|
|
970
862
|
* payment id, `fnd:` from `(pubkey, fund_id)`. `CreditLedger.fund` INSERTs at
|
|
971
863
|
* whatever id it is handed, so without this rule anyone could pre-open a
|
|
972
864
|
* credit at an id the server would later derive for someone else. The victim's
|
|
973
865
|
* top-up then settles on the rail before `fund` refuses with `caller_mismatch`
|
|
974
|
-
*
|
|
866
|
+
* they pay and hold nothing. Keeping creation server-side means a derived id
|
|
975
867
|
* is only ever owned by the caller it was derived for.
|
|
976
868
|
*
|
|
977
869
|
* Referencing stays open, and has to: the id of a credit opened this way is
|
|
@@ -981,7 +873,7 @@ declare function implicitCreditId(rail: "cashu" | "x402" | "tempo", paymentId: s
|
|
|
981
873
|
declare function isDerivedCreditId(creditId: string): boolean;
|
|
982
874
|
/**
|
|
983
875
|
* Deterministic credit id for a `/v1/credit` top-up that names no credit —
|
|
984
|
-
* the documented "omit `credit_id` to open a new one" flow
|
|
876
|
+
* the documented "omit `credit_id` to open a new one" flow.
|
|
985
877
|
*
|
|
986
878
|
* Minting a fresh random id per attempt would make that flow the one top-up
|
|
987
879
|
* shape that is *not* retry-safe: the second attempt looks up
|
|
@@ -998,8 +890,8 @@ declare function isDerivedCreditId(creditId: string): boolean;
|
|
|
998
890
|
*/
|
|
999
891
|
declare function derivedFundCreditId(callerPubkey: string, fundId: string): string;
|
|
1000
892
|
/**
|
|
1001
|
-
* Settlement reference stamped on the revenue event of an **explicit** draw
|
|
1002
|
-
*
|
|
893
|
+
* Settlement reference stamped on the revenue event of an **explicit** draw;
|
|
894
|
+
* one funding backs N draws, so the funding's own rail reference
|
|
1003
895
|
* cannot key them: `revenue_events` carries a global `UNIQUE (rail, tx_hash)`
|
|
1004
896
|
* that deliberately drops cross-job reuse of a settlement reference, and
|
|
1005
897
|
* reusing it would silently discard every draw but the first.
|
|
@@ -1013,17 +905,17 @@ declare function drawSettlementRef(dvmId: string, creditId: string, drawId: stri
|
|
|
1013
905
|
|
|
1014
906
|
/**
|
|
1015
907
|
* Durable marker for a rail payment that already funded the credit ledger
|
|
1016
|
-
* (
|
|
908
|
+
* (the funding-commitment rule). The x402 and mpp rails have no dvmkit-side
|
|
1017
909
|
* commit point of their own — the money moves at the facilitator / the payer's
|
|
1018
910
|
* mpp server — so the marker row, inserted in the SAME transaction as
|
|
1019
911
|
* `CreditLedger.fund` + `draw`, is what makes a crash-replayed credential
|
|
1020
912
|
* unable to fund twice: the second attempt collides on the primary key and
|
|
1021
913
|
* surfaces as {@link ProcessedPaymentReplayError}, which the verifier routes
|
|
1022
|
-
* into the
|
|
914
|
+
* into the recovery gates instead of a second fund.
|
|
1023
915
|
*
|
|
1024
916
|
* The Cashu rail does not use this store — its accumulator insert is already
|
|
1025
917
|
* the durable commit point, and its replay key (the `X-Cashu-Request-Id`
|
|
1026
|
-
* regime) must stay exactly as-is (dual-idempotency rule,
|
|
918
|
+
* regime) must stay exactly as-is (dual-idempotency rule, the signed credit contract).
|
|
1027
919
|
*/
|
|
1028
920
|
interface ProcessedPaymentStore {
|
|
1029
921
|
/**
|
|
@@ -1086,7 +978,7 @@ declare class PostgresProcessedPaymentStore implements ProcessedPaymentStore {
|
|
|
1086
978
|
withTx<T>(fn: (tx?: ProcessedPaymentQuerier) => Promise<T>): Promise<T>;
|
|
1087
979
|
/** Create the `processed_payments` table if absent. Call once at SDK boot. */
|
|
1088
980
|
init(): Promise<void>;
|
|
1089
|
-
/** The boot DDL itself — always runs under {@link withSdkInitLock}
|
|
981
|
+
/** The boot DDL itself — always runs under {@link withSdkInitLock}. */
|
|
1090
982
|
private createTables;
|
|
1091
983
|
record(args: {
|
|
1092
984
|
dvmId: string;
|
|
@@ -1128,7 +1020,7 @@ declare class MemoryProcessedPaymentStore implements ProcessedPaymentStore {
|
|
|
1128
1020
|
/**
|
|
1129
1021
|
* Machine codes an accumulator-path receive can reject with. Named and exported
|
|
1130
1022
|
* so the payment-error builder in `payment.ts` folds them into `PaymentErrorCode`
|
|
1131
|
-
* and keeps display/hint/retryable copy in lockstep with this union
|
|
1023
|
+
* and keeps display/hint/retryable copy in lockstep with this union.
|
|
1132
1024
|
*/
|
|
1133
1025
|
type AccumulatorRejectionCode = "missing_request_id" | "missing_lock_pubkey" | "not_p2pk_locked" | "wrong_lock_pubkey" | "proof_not_unspent" | "proof_pending" | "replay_rejected" | "mint_unreachable" | "invalid_token";
|
|
1134
1026
|
|
|
@@ -1437,7 +1329,7 @@ interface X402BatchChannelObservation {
|
|
|
1437
1329
|
refundNonce: number;
|
|
1438
1330
|
}
|
|
1439
1331
|
/**
|
|
1440
|
-
* The chain submission behind an accepted voucher did not return success
|
|
1332
|
+
* The chain submission behind an accepted voucher did not return success.
|
|
1441
1333
|
*
|
|
1442
1334
|
* Typed and `retryable` because it is a service-availability fault, not a fault
|
|
1443
1335
|
* in the credential: the voucher verified, the ledger leg rolled back, and the
|
|
@@ -1479,16 +1371,16 @@ interface X402BatchRefusal {
|
|
|
1479
1371
|
* `false` when re-signing cannot fix this: the caller must be answered with a
|
|
1480
1372
|
* terminal error rather than a 402 carrying fresh requirements, because an
|
|
1481
1373
|
* agent reads a corrective 402 as "sign this and resubmit" and lands straight
|
|
1482
|
-
* back on the same refusal
|
|
1374
|
+
* back on the same refusal. Omitted means corrective.
|
|
1483
1375
|
*/
|
|
1484
1376
|
corrective?: boolean;
|
|
1485
1377
|
}
|
|
1486
1378
|
/**
|
|
1487
|
-
* What the chain says about one wedged settlement
|
|
1379
|
+
* What the chain says about one wedged settlement.
|
|
1488
1380
|
*
|
|
1489
1381
|
* The four answers are kept apart because their repairs differ and because an
|
|
1490
1382
|
* operator re-pointing an RPC needs a definitive answer to read as definitive
|
|
1491
|
-
*
|
|
1383
|
+
* the same split `LightningReceive.lookupSettlement` draws between
|
|
1492
1384
|
* `invoice_unknown_to_wallet` and `wallet_unreachable`. `findAppliedSettlement`
|
|
1493
1385
|
* collapses all of them (`undefined` for absent *and* unbookmarked, a throw for
|
|
1494
1386
|
* transport), so this is where they are separated.
|
|
@@ -1563,7 +1455,7 @@ interface X402SettlementRepaired<T> {
|
|
|
1563
1455
|
}
|
|
1564
1456
|
/**
|
|
1565
1457
|
* The operator's exit from a settlement stuck between "chain paid" and "ledger
|
|
1566
|
-
* booked"
|
|
1458
|
+
* booked".
|
|
1567
1459
|
*
|
|
1568
1460
|
* `reconcile` is the ordinary recovery path with the chain's figure substituted
|
|
1569
1461
|
* for the facilitator's: the amount is re-derived from
|
|
@@ -1617,7 +1509,7 @@ interface X402BatchSettlementServer {
|
|
|
1617
1509
|
* Required on `refund`: the credit's unspent balance in the channel token's
|
|
1618
1510
|
* own units. It is the ceiling the on-chain payout is sized to, so that a
|
|
1619
1511
|
* drain returns what the caller is owed and nothing the DVM has earned
|
|
1620
|
-
*
|
|
1512
|
+
* `commit` must re-assert it against the authoritative ledger
|
|
1621
1513
|
* figure, since this one is read before the channel lock is taken.
|
|
1622
1514
|
*/
|
|
1623
1515
|
refundBoundNative?: number;
|
|
@@ -1633,7 +1525,7 @@ interface X402BatchSettlementServer {
|
|
|
1633
1525
|
* required in the *type*, because the payload the payer presents is
|
|
1634
1526
|
* what revokes, settles and pays: a refund voucher naming another
|
|
1635
1527
|
* channel would extinguish this credit's balance out of that channel's
|
|
1636
|
-
* escrow
|
|
1528
|
+
* escrow. The fund path binds in the ledger
|
|
1637
1529
|
* (`credit-ledger.ts`'s `rail_mismatch`) and the Tempo close binds in
|
|
1638
1530
|
* its credential verifier; this is x402's.
|
|
1639
1531
|
*/
|
|
@@ -1648,20 +1540,19 @@ interface X402BatchSettlementServer {
|
|
|
1648
1540
|
claimAndSettle(): Promise<void>;
|
|
1649
1541
|
/**
|
|
1650
1542
|
* Operator repair for a settlement wedged between chain and ledger
|
|
1651
|
-
*
|
|
1543
|
+
* Absent without durable storage: with no settlement rows there
|
|
1652
1544
|
* is no queue and nothing a repair could act on.
|
|
1653
1545
|
*/
|
|
1654
1546
|
repair?: X402SettlementRepair;
|
|
1655
1547
|
/**
|
|
1656
1548
|
* The durable settlement read the credit ledger gates spending on
|
|
1657
|
-
*
|
|
1549
|
+
* Absent without durable storage, on the same reasoning as
|
|
1658
1550
|
* {@link repair}: with no settlement rows nothing can be wedged, so there is
|
|
1659
1551
|
* nothing to gate.
|
|
1660
1552
|
*/
|
|
1661
1553
|
settlementGate?: X402RefundSettlementGate;
|
|
1662
1554
|
stop(): Promise<void>;
|
|
1663
1555
|
}
|
|
1664
|
-
/** Options for constructing one DVM's batch-settlement server. */
|
|
1665
1556
|
interface CreateX402BatchSettlementServerOpts {
|
|
1666
1557
|
network: string;
|
|
1667
1558
|
payTo: string;
|
|
@@ -1677,12 +1568,12 @@ interface CreateX402BatchSettlementServerOpts {
|
|
|
1677
1568
|
rpcUrl?: string;
|
|
1678
1569
|
db?: Pool;
|
|
1679
1570
|
/**
|
|
1680
|
-
* Durable store to adopt instead of building one from {@link db}
|
|
1571
|
+
* Durable store to adopt instead of building one from {@link db}.
|
|
1681
1572
|
*
|
|
1682
1573
|
* `createDVMServer` builds it at ledger construction, because the credit
|
|
1683
1574
|
* ledger's spend gate has to exist whether or not this server is ever
|
|
1684
1575
|
* constructed. Adopting it here keeps one `PostgresX402ChannelStorage` — and
|
|
1685
|
-
* so one settlement pool — per mount, which is what the
|
|
1576
|
+
* so one settlement pool — per mount, which is what the reasoning on
|
|
1686
1577
|
* {@link PostgresX402ChannelStorage} rests on. Ignored when the config
|
|
1687
1578
|
* supplies its own {@link X402BatchSettlementConfig.storage}.
|
|
1688
1579
|
*/
|
|
@@ -1694,7 +1585,7 @@ interface CreateX402BatchSettlementServerOpts {
|
|
|
1694
1585
|
* `undefined` means the channel's credit could not be resolved.
|
|
1695
1586
|
*
|
|
1696
1587
|
* Required, not optional: it is the ceiling every claim is capped at
|
|
1697
|
-
*
|
|
1588
|
+
* and a server that cannot derive one must claim nothing rather
|
|
1698
1589
|
* than fall back to the deposit.
|
|
1699
1590
|
*/
|
|
1700
1591
|
earnedNative: (channelId: string) => Promise<number | undefined>;
|
|
@@ -1705,13 +1596,13 @@ interface CreateX402BatchSettlementServerOpts {
|
|
|
1705
1596
|
/** Authoritative channel-view override for deterministic tests. */
|
|
1706
1597
|
channelStateReader?: (channelId: string) => Promise<X402BatchChannelObservation>;
|
|
1707
1598
|
/**
|
|
1708
|
-
* The payout tracker
|
|
1599
|
+
* The payout tracker. Attached with this server's settle scope
|
|
1709
1600
|
* and routed every claim and settle, so the batch that lands at `pay_to` is
|
|
1710
1601
|
* reported with the value claimed into it, and the pending snapshot can say
|
|
1711
1602
|
* what is still on the channels and whether the settle is wedged.
|
|
1712
1603
|
*
|
|
1713
1604
|
* @internal Wired by `createDVMServer` from the platform reporter; not a
|
|
1714
|
-
* builder surface, and not part of the extractable SDK contract
|
|
1605
|
+
* builder surface, and not part of the extractable SDK contract.
|
|
1715
1606
|
*/
|
|
1716
1607
|
payout?: X402PayoutObserver;
|
|
1717
1608
|
}
|
|
@@ -1728,7 +1619,7 @@ interface PaymentInfo {
|
|
|
1728
1619
|
/**
|
|
1729
1620
|
* Rail value this payment contributes to the job, in millisatoshis.
|
|
1730
1621
|
*
|
|
1731
|
-
* For a credit-drawn job
|
|
1622
|
+
* For a credit-drawn job this is the **draw's** allocated share of
|
|
1732
1623
|
* the credit's rail value, not the amount the caller handed over on this
|
|
1733
1624
|
* request — under an explicit fund-and-draw the caller may have funded twenty
|
|
1734
1625
|
* jobs' worth. That makes it the revenue basis for this job: mid-job top-ups
|
|
@@ -1739,38 +1630,38 @@ interface PaymentInfo {
|
|
|
1739
1630
|
/**
|
|
1740
1631
|
* Rail that satisfied the upfront payment, if any. A {@link FundingMethod}
|
|
1741
1632
|
* rather than a `PaymentMethod`: a draw against a Lightning-funded credit
|
|
1742
|
-
* reports `"lightning"` here
|
|
1633
|
+
* reports `"lightning"` here, which is what lets the revenue
|
|
1743
1634
|
* ledger book it instead of silently dropping the row.
|
|
1744
1635
|
*/
|
|
1745
1636
|
paymentRail?: FundingMethod;
|
|
1746
1637
|
receivedProofs: ProofLike[];
|
|
1747
1638
|
/**
|
|
1748
1639
|
* Settlement reference for the credit. x402 carries the EVM tx hash from
|
|
1749
|
-
* the facilitator
|
|
1750
|
-
* HMAC-bound to the realm so cross-DVM collisions are impossible
|
|
1751
|
-
* cashu accumulator
|
|
1752
|
-
*
|
|
1640
|
+
* the facilitator; mpp carries the credential's `challenge.id`,
|
|
1641
|
+
* HMAC-bound to the realm so cross-DVM collisions are impossible;
|
|
1642
|
+
* cashu accumulator sets it to the `X-Cashu-Request-Id` UUID
|
|
1643
|
+
* Required by the platform revenue ledger for `tempo`/`x402`
|
|
1753
1644
|
* rails; nullable for cashu's `tx_hash` column.
|
|
1754
1645
|
*/
|
|
1755
1646
|
paymentTxHash?: string;
|
|
1756
1647
|
/** Chain transaction returned to a caller recovering this accepted request. */
|
|
1757
1648
|
paymentTransactionHash?: string;
|
|
1758
1649
|
/**
|
|
1759
|
-
* Rail-native payment amount in atomic units
|
|
1650
|
+
* Rail-native payment amount in atomic units. Sats for mpp,
|
|
1760
1651
|
* USDC microunits for x402. Cashu leaves it unset — the existing
|
|
1761
1652
|
* `paidMsats / 1000` derivation in the platform callback covers it.
|
|
1762
1653
|
*/
|
|
1763
1654
|
nativeAmount?: number;
|
|
1764
|
-
/** Native asset tag matching `revenue_events.native_asset
|
|
1655
|
+
/** Native asset tag matching `revenue_events.native_asset`. */
|
|
1765
1656
|
nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
|
|
1766
1657
|
/**
|
|
1767
1658
|
* Discriminator written into `revenue_events.metadata.cashu_flow` so ops
|
|
1768
|
-
* queries can split per-call cashu rows by source path
|
|
1659
|
+
* queries can split per-call cashu rows by source path.
|
|
1769
1660
|
*/
|
|
1770
1661
|
cashuFlow?: "p2pk_accumulator";
|
|
1771
1662
|
/**
|
|
1772
1663
|
* Base64-encoded x402 `SettleResponse` for the `X-PAYMENT-RESPONSE`
|
|
1773
|
-
* response header
|
|
1664
|
+
* response header. Set on the upfront-flow x402 success path;
|
|
1774
1665
|
* `app.ts` attaches it to the 2xx response per x402 spec.
|
|
1775
1666
|
*/
|
|
1776
1667
|
x402SettleResponseHeader?: string;
|
|
@@ -1781,21 +1672,21 @@ interface PaymentInfo {
|
|
|
1781
1672
|
replayed: boolean;
|
|
1782
1673
|
};
|
|
1783
1674
|
/**
|
|
1784
|
-
* Credit-ledger linkage
|
|
1675
|
+
* Credit-ledger linkage. Set whenever the payment funded and/or
|
|
1785
1676
|
* drew the ledger — every paid job with a ledger wired. Persisted on the
|
|
1786
1677
|
* job so the terminal funnel can settle/release the draw and the receipt
|
|
1787
1678
|
* can countersign the `ReceiptCredit` block.
|
|
1788
1679
|
*/
|
|
1789
1680
|
creditId?: string;
|
|
1790
|
-
/** The draw placed for this job on `creditId
|
|
1681
|
+
/** The draw placed for this job on `creditId`. */
|
|
1791
1682
|
drawId?: string;
|
|
1792
|
-
/** The draw's fiat amount, 1e-6 of {@link creditCurrency}
|
|
1683
|
+
/** The draw's fiat amount, 1e-6 of {@link creditCurrency}. */
|
|
1793
1684
|
drawAmountMicro?: number;
|
|
1794
|
-
/** Currency the credit — and therefore the draw — is denominated in
|
|
1685
|
+
/** Currency the credit — and therefore the draw — is denominated in. */
|
|
1795
1686
|
creditCurrency?: string;
|
|
1796
1687
|
/**
|
|
1797
1688
|
* The funding leg, when this request actually moved money onto a credit
|
|
1798
|
-
*
|
|
1689
|
+
* Reported to the platform as a **deposit**: a liability until
|
|
1799
1690
|
* drawn, never summed into revenue. Absent on a pure draw against an
|
|
1800
1691
|
* existing balance.
|
|
1801
1692
|
*/
|
|
@@ -1805,7 +1696,7 @@ interface PaymentInfo {
|
|
|
1805
1696
|
fundingCredit?: CreditSnapshot;
|
|
1806
1697
|
/**
|
|
1807
1698
|
* Fiat micro of {@link creditCurrency} this payment funded that bought the
|
|
1808
|
-
* job **nothing**
|
|
1699
|
+
* job **nothing** — a second payment for an ask the job's draw
|
|
1809
1700
|
* already covers, or the overpaid tail of an oversized one. It is available
|
|
1810
1701
|
* balance on {@link creditId}, drainable at once, and the caller has to be
|
|
1811
1702
|
* told or they will re-send. Absent on every ordinary payment.
|
|
@@ -1813,7 +1704,7 @@ interface PaymentInfo {
|
|
|
1813
1704
|
unappliedMicro?: number;
|
|
1814
1705
|
}
|
|
1815
1706
|
/**
|
|
1816
|
-
* A funding event as the platform books it — one deposit row (
|
|
1707
|
+
* A funding event as the platform books it — one deposit row (spec
|
|
1817
1708
|
* §10). Keyed on `(dvmId, creditId, fundingId)`, so a retried report is a
|
|
1818
1709
|
* no-op rather than a double-count.
|
|
1819
1710
|
*/
|
|
@@ -1845,12 +1736,12 @@ interface CreditFundingReport {
|
|
|
1845
1736
|
channelId: string;
|
|
1846
1737
|
cumulativeAmount: string;
|
|
1847
1738
|
};
|
|
1848
|
-
/** TIP-1034 chain binding, reported so the platform can route a close
|
|
1739
|
+
/** TIP-1034 chain binding, reported so the platform can route a close. */
|
|
1849
1740
|
tempoChannel?: TempoChannelReport;
|
|
1850
1741
|
}
|
|
1851
1742
|
/**
|
|
1852
1743
|
* The chain identity of a TIP-1034 channel, as reported to the platform
|
|
1853
|
-
* alongside its deposit
|
|
1744
|
+
* alongside its deposit. Read from the DVM's own durable channel
|
|
1854
1745
|
* store, never from the caller's credential.
|
|
1855
1746
|
*/
|
|
1856
1747
|
interface TempoChannelReport {
|
|
@@ -1858,7 +1749,6 @@ interface TempoChannelReport {
|
|
|
1858
1749
|
escrow: string;
|
|
1859
1750
|
payee?: string;
|
|
1860
1751
|
}
|
|
1861
|
-
/** Options for verifying an upfront payment on job submission. */
|
|
1862
1752
|
interface UpfrontPaymentOpts {
|
|
1863
1753
|
cashuToken?: string;
|
|
1864
1754
|
requiredMsats?: number;
|
|
@@ -1875,7 +1765,7 @@ interface UpfrontPaymentOpts {
|
|
|
1875
1765
|
/**
|
|
1876
1766
|
* Resource URL the client retried against. Bound into the rebuilt
|
|
1877
1767
|
* `PaymentRequirements` when verifying x402, and surfaced on the 402 body
|
|
1878
|
-
* `accepts` array for x402 dual-serve
|
|
1768
|
+
* `accepts` array for x402 dual-serve.
|
|
1879
1769
|
*/
|
|
1880
1770
|
x402Resource?: string;
|
|
1881
1771
|
/** Either the structured credential object or its serialized `Authorization: Payment <…>` value. */
|
|
@@ -1884,17 +1774,17 @@ interface UpfrontPaymentOpts {
|
|
|
1884
1774
|
/**
|
|
1885
1775
|
* Request path of the resource being paid for (e.g. `/v1/job`). Bound into
|
|
1886
1776
|
* each issued mppx challenge's `meta.path` so a credential can't be replayed
|
|
1887
|
-
* against a different paid endpoint of the same DVM
|
|
1777
|
+
* against a different paid endpoint of the same DVM.
|
|
1888
1778
|
*/
|
|
1889
1779
|
resourcePath?: string;
|
|
1890
1780
|
/**
|
|
1891
1781
|
* Whether `tempo/session` may be issued and newly accepted right now — the
|
|
1892
|
-
* platform close-observer and hosted settlement-readiness gates
|
|
1893
|
-
*
|
|
1782
|
+
* platform close-observer and hosted settlement-readiness gates, resolved per
|
|
1783
|
+
* request by `DVMServer`. Absent means offered: a
|
|
1894
1784
|
* self-hosted DVM operates these responsibilities itself.
|
|
1895
1785
|
*
|
|
1896
1786
|
* `false` refuses a session credential that would open a channel the platform
|
|
1897
|
-
* cannot safely protect or settle
|
|
1787
|
+
* cannot safely protect or settle, and withholds session
|
|
1898
1788
|
* challenges from every 402 this call emits, *except* on a credit already
|
|
1899
1789
|
* bound to a TIP-1034 channel. That deposit is already exposed, so refusing
|
|
1900
1790
|
* its vouchers only strands it; and since a channel-bound credit can be funded
|
|
@@ -1907,11 +1797,11 @@ interface UpfrontPaymentOpts {
|
|
|
1907
1797
|
/**
|
|
1908
1798
|
* Tracks consumed `(realm, challenge.id)` pairs so a credential that was
|
|
1909
1799
|
* already accepted on the upfront path can't be replayed within its
|
|
1910
|
-
* `expires` window
|
|
1800
|
+
* `expires` window. Optional: when absent, the upfront flow has
|
|
1911
1801
|
* no per-process replay protection beyond mppx's own expiry check.
|
|
1912
1802
|
*/
|
|
1913
1803
|
consumedCredentialStore?: ConsumedCredentialStore;
|
|
1914
|
-
/** Cashu receive mode
|
|
1804
|
+
/** Cashu receive mode. Routes the receive path. */
|
|
1915
1805
|
cashuMode?: CashuMode;
|
|
1916
1806
|
/** Per-DVM Postgres pool — required for the `p2pk-accumulator` path. */
|
|
1917
1807
|
accumulatorDb?: AccumulatorPool;
|
|
@@ -1921,7 +1811,7 @@ interface UpfrontPaymentOpts {
|
|
|
1921
1811
|
/**
|
|
1922
1812
|
* Active NUT-11 P2PK lock pubkeys — required for the `p2pk-accumulator`
|
|
1923
1813
|
* path. Includes the current pubkey followed by retired pubkeys still
|
|
1924
|
-
* within the rotation grace window
|
|
1814
|
+
* within the rotation grace window. Caller resolves via
|
|
1925
1815
|
* `loadLockPubkeyState` per-request so rotations propagate without a
|
|
1926
1816
|
* server restart.
|
|
1927
1817
|
*/
|
|
@@ -1929,7 +1819,7 @@ interface UpfrontPaymentOpts {
|
|
|
1929
1819
|
/** UUIDv4 from `X-Cashu-Request-Id` header. */
|
|
1930
1820
|
requestId?: string;
|
|
1931
1821
|
/**
|
|
1932
|
-
* Runtime mint-health tracker
|
|
1822
|
+
* Runtime mint-health tracker. Receive path short-circuits with
|
|
1933
1823
|
* `cashu_mint_sick` when the token's mint is currently flagged sick, so
|
|
1934
1824
|
* agents retry against a healthy alternative without waiting for the
|
|
1935
1825
|
* per-call mint timeout to fire inside `acceptAccumulatorPayment`.
|
|
@@ -1937,14 +1827,14 @@ interface UpfrontPaymentOpts {
|
|
|
1937
1827
|
mintHealthTracker?: MintHealthTracker;
|
|
1938
1828
|
/**
|
|
1939
1829
|
* Host fx fetcher used to price the non-sat rails (x402 USDC, mppx fiat)
|
|
1940
|
-
* through the same BTC/USD source the quote path uses
|
|
1830
|
+
* through the same BTC/USD source the quote path uses. On a hosted
|
|
1941
1831
|
* DVM this is the platform-served snapshot; self-hosted defaults to
|
|
1942
1832
|
* CoinGecko-direct. Optional: a lazy default fetcher stands in when unset, so
|
|
1943
1833
|
* the payment path never reaches `fetchBtcUsdRate`.
|
|
1944
1834
|
*/
|
|
1945
1835
|
fxFetcher?: FxFetcher;
|
|
1946
1836
|
/**
|
|
1947
|
-
* Credit ledger every payment funds and draws through (
|
|
1837
|
+
* Credit ledger every payment funds and draws through (the credit lifecycle contract):
|
|
1948
1838
|
* an attached rail payment is an implicit N=1 funding — `fund` the paid
|
|
1949
1839
|
* amount + `draw` it, atomically with the rail commit. When absent (or
|
|
1950
1840
|
* when the price can't be fiat-denominated), the payment is accepted with
|
|
@@ -1953,21 +1843,21 @@ interface UpfrontPaymentOpts {
|
|
|
1953
1843
|
*/
|
|
1954
1844
|
creditLedger?: CreditLedgerLike;
|
|
1955
1845
|
/**
|
|
1956
|
-
* Durable processed-payment markers for the x402/mpp rails
|
|
1846
|
+
* Durable processed-payment markers for the x402/mpp rails.
|
|
1957
1847
|
* Those rails commit money outside dvmkit, so the marker row — written in
|
|
1958
1848
|
* the same transaction as the ledger fund — is what makes a crash-replayed
|
|
1959
1849
|
* credential unable to fund twice.
|
|
1960
1850
|
*/
|
|
1961
1851
|
processedPayments?: ProcessedPaymentStore;
|
|
1962
1852
|
/**
|
|
1963
|
-
* Reporter-owned transactional outbox enqueue
|
|
1853
|
+
* Reporter-owned transactional outbox enqueue. When present,
|
|
1964
1854
|
* every funding awaits this inside the rail's commit transaction, so money
|
|
1965
1855
|
* and its durable deposit report land together.
|
|
1966
1856
|
*/
|
|
1967
1857
|
enqueueCreditDeposit?: CreditDepositEnqueue;
|
|
1968
1858
|
/**
|
|
1969
1859
|
* The job price's fiat denomination, pinned by the caller from the same
|
|
1970
|
-
* quote surface that produced `requiredMsats
|
|
1860
|
+
* quote surface that produced `requiredMsats`. The ledger is
|
|
1971
1861
|
* fiat-micro denominated; fund = draw = this amount in the exact-payment
|
|
1972
1862
|
* case, so no fresh fx conversion happens on the verify hot path.
|
|
1973
1863
|
*/
|
|
@@ -1983,14 +1873,14 @@ interface UpfrontPaymentOpts {
|
|
|
1983
1873
|
/** Requester id — funder identity fallback for DVMs without descriptor auth. */
|
|
1984
1874
|
requesterId?: string;
|
|
1985
1875
|
/**
|
|
1986
|
-
* Explicit credit fields extracted from the signed body (
|
|
1876
|
+
* Explicit credit fields extracted from the signed body (the signed credit contract). Their
|
|
1987
1877
|
* signature coverage and structural validation happen in `app.ts` before
|
|
1988
1878
|
* payment verification; this layer enforces ownership, the funding
|
|
1989
1879
|
* commitment, and the draw itself.
|
|
1990
1880
|
*/
|
|
1991
1881
|
creditEnvelope?: CreditEnvelope;
|
|
1992
1882
|
/**
|
|
1993
|
-
* Turn this verification into a **fund-only top-up
|
|
1883
|
+
* Turn this verification into a **fund-only top-up**: the rails
|
|
1994
1884
|
* verify and commit exactly as they do for a job, but the ledger leg funds
|
|
1995
1885
|
* without drawing — there is no job to pay for. Set by `POST /v1/credit`;
|
|
1996
1886
|
* `requiredMsats` / `priceFiat` carry the requested top-up amount rather
|
|
@@ -2021,7 +1911,7 @@ interface FundOnlyRequest {
|
|
|
2021
1911
|
/** Client-generated idempotency key for this deposit (`credit_fundings` PK). */
|
|
2022
1912
|
fundId: string;
|
|
2023
1913
|
/**
|
|
2024
|
-
* SHA-256 hex over the raw funding artifact, signed by the caller (
|
|
1914
|
+
* SHA-256 hex over the raw funding artifact, signed by the caller (the signed credit contract
|
|
2025
1915
|
* condition 3). Checked before any rail work: it is what binds the artifact
|
|
2026
1916
|
* presented on the header to the deposit named in the signed body.
|
|
2027
1917
|
*/
|
|
@@ -2033,14 +1923,14 @@ interface PaymentErrorDetail {
|
|
|
2033
1923
|
status: number;
|
|
2034
1924
|
/**
|
|
2035
1925
|
* mppx challenges to advertise via `WWW-Authenticate` headers on the 402
|
|
2036
|
-
* response
|
|
1926
|
+
* response. Populated whenever the DVM has an mppx handle and
|
|
2037
1927
|
* the request is short on payment — including on payment_invalid, since
|
|
2038
1928
|
* draft-ryan-httpauth-payment-01 requires a fresh challenge on rejection.
|
|
2039
1929
|
*/
|
|
2040
1930
|
mppChallenges?: Challenge.Challenge[];
|
|
2041
1931
|
/**
|
|
2042
1932
|
* x402 `PaymentRequirements` to merge into the 402 body's `accepts`
|
|
2043
|
-
* array under the `x402Version` envelope
|
|
1933
|
+
* array under the `x402Version` envelope. Populated when the
|
|
2044
1934
|
* DVM has x402 wired and dual-serve is on.
|
|
2045
1935
|
*/
|
|
2046
1936
|
x402Requirements?: PaymentRequirements[];
|
|
@@ -2049,30 +1939,30 @@ interface PaymentErrorDetail {
|
|
|
2049
1939
|
/**
|
|
2050
1940
|
* Scheme-level error code for the x402 v2 envelope, kept apart from the
|
|
2051
1941
|
* body's human `message` because a scheme client matches on it verbatim to
|
|
2052
|
-
* decide whether a 402 is correctable
|
|
1942
|
+
* decide whether a 402 is correctable.
|
|
2053
1943
|
*/
|
|
2054
1944
|
x402ErrorCode?: string;
|
|
2055
1945
|
/**
|
|
2056
1946
|
* On a `replay_rejected` from the x402/mpp processed-payment marker
|
|
2057
|
-
*
|
|
2058
|
-
* route feeds it to the
|
|
1947
|
+
* the payment id the original funding committed under. The
|
|
1948
|
+
* route feeds it to the recovery gates (jobs persist it as
|
|
2059
1949
|
* `payment_tx_hash`) so a crash-replayed credential can recover its
|
|
2060
1950
|
* original job instead of a bare 409.
|
|
2061
1951
|
*/
|
|
2062
1952
|
replayPaymentId?: string;
|
|
2063
1953
|
/**
|
|
2064
1954
|
* A short payment that was kept under the fail-closed posture and settled
|
|
2065
|
-
* on the spot
|
|
1955
|
+
* on the spot. The route books it as revenue under a synthetic
|
|
2066
1956
|
* job id — no job exists to carry it — tagged so it stays separable from
|
|
2067
1957
|
* per-job service revenue.
|
|
2068
1958
|
*/
|
|
2069
1959
|
forfeit?: ShortPayForfeit;
|
|
2070
1960
|
/**
|
|
2071
|
-
* Money that landed on a credit even though this request failed
|
|
2072
|
-
*
|
|
1961
|
+
* Money that landed on a credit even though this request failed. Set when a
|
|
1962
|
+
* short mid-job pay funds without completing its ask,
|
|
2073
1963
|
* or an explicit upfront fund-and-draw commits the deposit but the combined
|
|
2074
1964
|
* balance cannot cover the draw. In both cases the caller keeps an available
|
|
2075
|
-
* balance they can reclaim under the
|
|
1965
|
+
* balance they can reclaim under the drain. That is a liability, so
|
|
2076
1966
|
* the deposit has to reach the platform on the 402 path exactly as it does on
|
|
2077
1967
|
* the success path, or the outstanding-liability view is short by precisely
|
|
2078
1968
|
* the money we still owe.
|
|
@@ -2085,7 +1975,7 @@ interface PaymentErrorDetail {
|
|
|
2085
1975
|
*/
|
|
2086
1976
|
funding?: CreditFundingReport;
|
|
2087
1977
|
}
|
|
2088
|
-
/** A kept-but-undelivered short payment, ready to be booked as tagged revenue
|
|
1978
|
+
/** A kept-but-undelivered short payment, ready to be booked as tagged revenue. */
|
|
2089
1979
|
interface ShortPayForfeit {
|
|
2090
1980
|
/** Cashu request id — the rail settlement reference. */
|
|
2091
1981
|
paymentId: string;
|
|
@@ -2101,9 +1991,9 @@ type PaymentResult = PaymentInfo & {
|
|
|
2101
1991
|
error?: PaymentErrorDetail;
|
|
2102
1992
|
/**
|
|
2103
1993
|
* The signed body's `(credit_id, draw_id)` matches an already-recorded
|
|
2104
|
-
* draw (
|
|
1994
|
+
* draw (the draw-idempotency rule — the explicit idempotency regime).
|
|
2105
1995
|
* No rail work ran and no second hold was placed; the route applies the
|
|
2106
|
-
*
|
|
1996
|
+
* proof-of-origin gates and re-issues the original job's response.
|
|
2107
1997
|
*/
|
|
2108
1998
|
replayedDraw?: {
|
|
2109
1999
|
creditId: string;
|
|
@@ -2112,7 +2002,7 @@ type PaymentResult = PaymentInfo & {
|
|
|
2112
2002
|
};
|
|
2113
2003
|
};
|
|
2114
2004
|
/**
|
|
2115
|
-
* Whether a dev-mode server skips payment verification outright
|
|
2005
|
+
* Whether a dev-mode server skips payment verification outright.
|
|
2116
2006
|
*
|
|
2117
2007
|
* Both upfront and mid-job acceptance require verification whenever any Cashu,
|
|
2118
2008
|
* x402, or Tempo rail is configured. An unusable configured rail fails closed;
|
|
@@ -2134,7 +2024,7 @@ declare function devModeSkipsPaymentVerification(opts: {
|
|
|
2134
2024
|
*/
|
|
2135
2025
|
declare const PAYMENT_PROOF_KEYS: readonly ["cashu_token", "x402_payment", "tempo_credential"];
|
|
2136
2026
|
/**
|
|
2137
|
-
* Whether a `payment` message carries a proof on any mid-job rail
|
|
2027
|
+
* Whether a `payment` message carries a proof on any mid-job rail.
|
|
2138
2028
|
*
|
|
2139
2029
|
* Deliberately colocated with the rail dispatch it mirrors, because two call
|
|
2140
2030
|
* sites decide on it: `validateMessageContent` (`server/app.ts`) admits a
|
|
@@ -2146,26 +2036,26 @@ declare const PAYMENT_PROOF_KEYS: readonly ["cashu_token", "x402_payment", "temp
|
|
|
2146
2036
|
declare function hasPaymentProof(content: Record<string, unknown>): boolean;
|
|
2147
2037
|
/**
|
|
2148
2038
|
* Issue one mppx Challenge per registered method for the upfront HTTP-transport
|
|
2149
|
-
* 402 path
|
|
2039
|
+
* 402 path. Mirrors the message-based issuance loop in
|
|
2150
2040
|
* `context.ts` but binds challenges via `meta` (resource path + per-request
|
|
2151
|
-
* nonce) instead of the per-job `pendingMppChallengeIds` binding
|
|
2041
|
+
* nonce) instead of the per-job `pendingMppChallengeIds` binding —
|
|
2152
2042
|
* there's no job at upfront challenge-issue time, so the binding has to live
|
|
2153
2043
|
* inside the challenge itself.
|
|
2154
2044
|
*
|
|
2155
2045
|
* `meta.nonce` is unique-per-issue and HMAC-bound; consumed-id tracking on
|
|
2156
|
-
* the verification side is wired via `ConsumedCredentialStore
|
|
2046
|
+
* the verification side is wired via `ConsumedCredentialStore`, so
|
|
2157
2047
|
* a successfully-accepted credential can't be replayed within its `expires`
|
|
2158
2048
|
* window even if it lands on the same paid endpoint.
|
|
2159
2049
|
*
|
|
2160
|
-
* `btcUsdRate` is resolved once per request by the caller
|
|
2050
|
+
* `btcUsdRate` is resolved once per request by the caller rather
|
|
2161
2051
|
* than fetched here, so every rail this request advertises or verifies prices
|
|
2162
2052
|
* off the same number and a rate-provider outage is handled in one place.
|
|
2163
2053
|
*
|
|
2164
|
-
* `sessionIssuable` is the platform close-observer gate
|
|
2054
|
+
* `sessionIssuable` is the platform close-observer gate, threaded in
|
|
2165
2055
|
* by `DVMServer` and defaulting to issuable for a self-hosted DVM that watches
|
|
2166
2056
|
* its own closes. It used to gate advertisement only, so a `/v1/credit` 402
|
|
2167
2057
|
* kept issuing `tempo/session` challenges the DVM had just stopped advertising
|
|
2168
|
-
*
|
|
2058
|
+
* and its own interface doc said "advertised **and** issued". The
|
|
2169
2059
|
* caller resolves it as "the gate is open, **or** this credit is already bound
|
|
2170
2060
|
* to a channel", so a top-up on an existing channel keeps its challenge while
|
|
2171
2061
|
* the gate is closed; `verifyUpfrontPayment` still refuses any *other* channel
|
|
@@ -2179,7 +2069,6 @@ declare function issueUpfrontChallenges(mpp: MppxServer, requiredMsats: number,
|
|
|
2179
2069
|
declare function verifyUpfrontPayment(opts: UpfrontPaymentOpts): Promise<PaymentResult>;
|
|
2180
2070
|
/**
|
|
2181
2071
|
* A management credential signed for a channel other than the one required
|
|
2182
|
-
* (internal-review).
|
|
2183
2072
|
*
|
|
2184
2073
|
* Typed rather than a bare `Error` because the drain route has to tell it apart
|
|
2185
2074
|
* from the transport and chain failures its catch ladder reconciles: this one is
|
|
@@ -2206,7 +2095,7 @@ declare function verifyTempoSessionManagementCredential(args: {
|
|
|
2206
2095
|
*
|
|
2207
2096
|
* The drain route re-asserts its close intent here rather than a line above
|
|
2208
2097
|
* the call so that a credential signed for another channel is refused before
|
|
2209
|
-
* the reservation is touched
|
|
2098
|
+
* the reservation is touched. Only this function knows where that
|
|
2210
2099
|
* seam is, so putting the hook here makes the ordering structural instead of a
|
|
2211
2100
|
* comment the next edit can reorder past. A throw propagates to the caller
|
|
2212
2101
|
* with nothing broadcast.
|
|
@@ -2218,7 +2107,7 @@ declare function verifyTempoSessionManagementCredential(args: {
|
|
|
2218
2107
|
cumulativeAmount?: string;
|
|
2219
2108
|
}>;
|
|
2220
2109
|
/**
|
|
2221
|
-
* Every machine code a flat payment-error body can carry
|
|
2110
|
+
* Every machine code a flat payment-error body can carry — the six
|
|
2222
2111
|
* codes constructed directly in this file plus the agent-wallet cashu rejection
|
|
2223
2112
|
* codes ({@link AccumulatorRejectionCode}), which surface verbatim through the
|
|
2224
2113
|
* accumulator branch of {@link verifyAccumulatorReceipt}. `fx_rate_unavailable`
|
|
@@ -2229,7 +2118,7 @@ declare function verifyTempoSessionManagementCredential(args: {
|
|
|
2229
2118
|
type PaymentErrorCode = "payment_invalid" | "x402_not_offered" | "x402_settlement_ambiguous" | "cashu_unavailable" | "cashu_not_p2pk_locked" | "mint_health_pending" | "cashu_mint_sick" | "payment_insufficient" | "payment_unapplied" | "insufficient_credit" | "credit_expired" | "credit_unbacked" | "credit_not_found" | "tempo_channel_closing" | "settlement_pending" | "draw_conflict" | "funding_commitment_mismatch" | "credit_not_supported" | "one_shot_credit_not_supported" | "credit_below_min" | "below_rail_minimum" | "credit_over_max" | "credit_id_unavailable" | "nothing_to_drain" | "drain_below_dust" | "drain_not_found" | "drain_conflict" | "drain_method_unsupported" | "tempo_channel_exit_required" | "x402_settlement_submission_failed" | X402SettlementReconciliationReason | "lightning_settlement_pending" | "lightning_invoice_expired" | "lightning_unavailable" | AccumulatorRejectionCode;
|
|
2230
2119
|
/**
|
|
2231
2120
|
* Build a flat payment-error body carrying the four agent-facing fields every
|
|
2232
|
-
* payment error must have
|
|
2121
|
+
* payment error must have: the machine `code`, the operator-facing
|
|
2233
2122
|
* `message`, and the `display`/`hint`/`retryable` copy an agent relays to a
|
|
2234
2123
|
* human and branches retries on. `display`, `hint`, and `retryable` default from
|
|
2235
2124
|
* {@link PAYMENT_ERROR_COPY} keyed on `code`; `message` is the per-site operator
|
|
@@ -2241,7 +2130,7 @@ type PaymentErrorCode = "payment_invalid" | "x402_not_offered" | "x402_settlemen
|
|
|
2241
2130
|
*
|
|
2242
2131
|
* `retryable` overrides on the same terms, and for the same reason: a code that
|
|
2243
2132
|
* covers more than one refusal can be terminal for one of them and clear on its
|
|
2244
|
-
* own for another — `drain_conflict` is both
|
|
2133
|
+
* own for another — `drain_conflict` is both. Override it only
|
|
2245
2134
|
* alongside the `hint`, since the two are one answer: a hint that says retry
|
|
2246
2135
|
* under `retryable: false` is the disagreement this exists to prevent.
|
|
2247
2136
|
*
|
|
@@ -2258,14 +2147,14 @@ declare function paymentErrorBody(code: PaymentErrorCode, opts: {
|
|
|
2258
2147
|
/**
|
|
2259
2148
|
* Build a payment-error response carrying a JSON error body plus one
|
|
2260
2149
|
* `WWW-Authenticate: Payment <serialized challenge>` header per mppx challenge
|
|
2261
|
-
* (
|
|
2150
|
+
* (per draft-ryan-httpauth-payment-01). When `challenges` is empty,
|
|
2262
2151
|
* returns a plain JSON response (Cashu/x402-only DVMs).
|
|
2263
2152
|
*
|
|
2264
2153
|
* Status defaults to 402 (Payment Required) but callers can override — e.g.
|
|
2265
|
-
* the
|
|
2154
|
+
* the sick-mint short-circuit returns 503 because the failure is a
|
|
2266
2155
|
* mint-side outage, not a payment problem.
|
|
2267
2156
|
*
|
|
2268
|
-
* When `x402Requirements` is present (
|
|
2157
|
+
* When `x402Requirements` is present (x402 dual-serve), merge the
|
|
2269
2158
|
* x402-spec envelope `{ x402Version, accepts: [...] }` into the body next to
|
|
2270
2159
|
* the existing dvmkit error fields so x402 clients can find their retry
|
|
2271
2160
|
* shape on the same response. The MPP `WWW-Authenticate` header and the
|
|
@@ -2275,7 +2164,6 @@ declare function paymentErrorBody(code: PaymentErrorCode, opts: {
|
|
|
2275
2164
|
* are single-use and shared caches must not return them to other clients.
|
|
2276
2165
|
*/
|
|
2277
2166
|
declare function buildPaymentErrorResponse(body: Record<string, unknown>, challenges?: Challenge.Challenge[], x402Requirements?: PaymentRequirements[], status?: number, x402V2Requirements?: PaymentRequirementsV2[], x402V2Resource?: string, x402ExactVersions?: X402ExactVersionSupport, x402ErrorCode?: string): Response;
|
|
2278
|
-
/** Options for processing an incoming mid-job payment message. */
|
|
2279
2167
|
interface IncomingPaymentOpts {
|
|
2280
2168
|
mints?: string[];
|
|
2281
2169
|
x402Config?: X402Config;
|
|
@@ -2288,7 +2176,7 @@ interface IncomingPaymentOpts {
|
|
|
2288
2176
|
mpp?: MppxServer;
|
|
2289
2177
|
/** Resource URL bound into the rebuilt x402 `PaymentRequirements`. */
|
|
2290
2178
|
resourcePath?: string;
|
|
2291
|
-
/** Cashu receive mode
|
|
2179
|
+
/** Cashu receive mode. Mirrors {@link UpfrontPaymentOpts.cashuMode}. */
|
|
2292
2180
|
cashuMode?: CashuMode;
|
|
2293
2181
|
/** Per-DVM Postgres pool — required for the `p2pk-accumulator` path. */
|
|
2294
2182
|
accumulatorDb?: AccumulatorPool;
|
|
@@ -2297,28 +2185,28 @@ interface IncomingPaymentOpts {
|
|
|
2297
2185
|
dvmId?: string;
|
|
2298
2186
|
/** Active NUT-11 P2PK lock pubkeys (current + retired-in-grace). */
|
|
2299
2187
|
lockPubkeys?: LockPubkey[];
|
|
2300
|
-
/** Runtime mint-health tracker
|
|
2188
|
+
/** Runtime mint-health tracker. */
|
|
2301
2189
|
mintHealthTracker?: MintHealthTracker;
|
|
2302
2190
|
/**
|
|
2303
|
-
* Host fx fetcher for the non-sat rails
|
|
2191
|
+
* Host fx fetcher for the non-sat rails. See
|
|
2304
2192
|
* {@link UpfrontPaymentOpts.fxFetcher}.
|
|
2305
2193
|
*/
|
|
2306
2194
|
fxFetcher?: FxFetcher;
|
|
2307
2195
|
/**
|
|
2308
|
-
* Credit ledger the mid-job top-up funds and grows (
|
|
2196
|
+
* Credit ledger the mid-job top-up funds and grows (the credit lifecycle contract:
|
|
2309
2197
|
* "mid-job `requestPayment` becomes a top-up"). Absent ⇒ the payment is
|
|
2310
2198
|
* accepted with pre-credits semantics, exactly as before.
|
|
2311
2199
|
*/
|
|
2312
2200
|
creditLedger?: CreditLedgerLike;
|
|
2313
2201
|
/**
|
|
2314
|
-
* Durable processed-payment markers
|
|
2202
|
+
* Durable processed-payment markers. x402/mpp money moves outside
|
|
2315
2203
|
* dvmkit, so the marker + `fund` + `growDraw` in one transaction is what
|
|
2316
2204
|
* makes the mid-job ledger leg atomic and crash-replay safe — the cashu path
|
|
2317
2205
|
* gets the same property from the accumulator's own commit transaction.
|
|
2318
2206
|
*/
|
|
2319
2207
|
processedPayments?: ProcessedPaymentStore;
|
|
2320
2208
|
/**
|
|
2321
|
-
* Reporter-owned transactional outbox enqueue
|
|
2209
|
+
* Reporter-owned transactional outbox enqueue. Runs inside the
|
|
2322
2210
|
* mid-job rail transaction after the ledger top-up/growth and before commit.
|
|
2323
2211
|
*/
|
|
2324
2212
|
enqueueCreditDeposit?: CreditDepositEnqueue;
|
|
@@ -2328,7 +2216,7 @@ interface IncomingPaymentOpts {
|
|
|
2328
2216
|
creditTtlMs?: number;
|
|
2329
2217
|
}
|
|
2330
2218
|
/**
|
|
2331
|
-
* Pure verification of an incoming mid-job payment
|
|
2219
|
+
* Pure verification of an incoming mid-job payment. Runs the
|
|
2332
2220
|
* rail-specific verify path and returns the credit shape; does not mutate
|
|
2333
2221
|
* the job. The caller (`JobManager.processPayment`) applies the result
|
|
2334
2222
|
* inside Tx C so verify+credit land atomically.
|
|
@@ -2341,7 +2229,7 @@ interface VerifyIncomingSnapshot {
|
|
|
2341
2229
|
pendingPaymentMsats?: number;
|
|
2342
2230
|
pendingMppChallengeIds?: string[];
|
|
2343
2231
|
/**
|
|
2344
|
-
* Per-job x402 binding
|
|
2232
|
+
* Per-job x402 binding. When set, the incoming `x402_payment`'s
|
|
2345
2233
|
* decoded `authorization.nonce` must match this value or the credential is
|
|
2346
2234
|
* rejected — closes the pre-settlement cross-job replay window. Pre-fix
|
|
2347
2235
|
* DVMs leave this `undefined`; the verify-side check no-ops for them.
|
|
@@ -2350,9 +2238,9 @@ interface VerifyIncomingSnapshot {
|
|
|
2350
2238
|
/** Exact advertised USDC microunits paired with `pendingX402Nonce`. */
|
|
2351
2239
|
pendingX402AmountUsdcMicro?: string;
|
|
2352
2240
|
paymentRail?: FundingMethod;
|
|
2353
|
-
/** The credit the upfront leg funded and drew, when it had one
|
|
2241
|
+
/** The credit the upfront leg funded and drew, when it had one. */
|
|
2354
2242
|
creditId?: string;
|
|
2355
|
-
/** The draw this job holds on {@link creditId}; the top-up grows it
|
|
2243
|
+
/** The draw this job holds on {@link creditId}; the top-up grows it. */
|
|
2356
2244
|
drawId?: string;
|
|
2357
2245
|
/**
|
|
2358
2246
|
* The job's current settlement reference. Used to tell an explicit
|
|
@@ -2361,17 +2249,17 @@ interface VerifyIncomingSnapshot {
|
|
|
2361
2249
|
* upfront leg established.
|
|
2362
2250
|
*/
|
|
2363
2251
|
paymentTxHash?: string;
|
|
2364
|
-
/** Fiat envelope pinned when the ask was emitted
|
|
2252
|
+
/** Fiat envelope pinned when the ask was emitted. See `JobRecord`. */
|
|
2365
2253
|
pendingPaymentFiatMicro?: number;
|
|
2366
2254
|
pendingPaymentFiatCurrency?: string;
|
|
2367
2255
|
/**
|
|
2368
2256
|
* Everything this job has asked for mid-job, summed and never decremented
|
|
2369
|
-
*
|
|
2257
|
+
* the ceiling on its draw growth. See `JobRecord`, including
|
|
2370
2258
|
* why it can't be derived from the outstanding figure above.
|
|
2371
2259
|
*/
|
|
2372
2260
|
askedTopUpMicro?: number;
|
|
2373
2261
|
askedTopUpCurrency?: string;
|
|
2374
|
-
/** Why the total above can't express a ceiling, when it can't
|
|
2262
|
+
/** Why the total above can't express a ceiling, when it can't. */
|
|
2375
2263
|
topUpCapUnenforcedReason?: TopUpCapUnenforcedReason;
|
|
2376
2264
|
/**
|
|
2377
2265
|
* Caller identity for a top-up that has to mint a *fresh* implicit credit
|
|
@@ -2382,7 +2270,6 @@ interface VerifyIncomingSnapshot {
|
|
|
2382
2270
|
requesterPubkey?: string;
|
|
2383
2271
|
requesterId?: string;
|
|
2384
2272
|
}
|
|
2385
|
-
/** Result of `verifyIncomingPayment`. */
|
|
2386
2273
|
interface VerifiedIncomingPayment {
|
|
2387
2274
|
paidMsats: number;
|
|
2388
2275
|
paymentMint?: string;
|
|
@@ -2391,21 +2278,21 @@ interface VerifiedIncomingPayment {
|
|
|
2391
2278
|
nativeAmount?: number;
|
|
2392
2279
|
nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
|
|
2393
2280
|
cashuFlow?: "p2pk_accumulator";
|
|
2394
|
-
/** True when this credit should accumulate onto an existing same-rail native amount
|
|
2281
|
+
/** True when this credit should accumulate onto an existing same-rail native amount. */
|
|
2395
2282
|
sameRail: boolean;
|
|
2396
2283
|
/**
|
|
2397
|
-
* Credit this top-up opened
|
|
2284
|
+
* Credit this top-up opened. Only set when the job had none — a
|
|
2398
2285
|
* free-then-paid job — so Tx C can bind it; a job that already holds a credit
|
|
2399
2286
|
* keeps its binding, because the top-up grew that draw.
|
|
2400
2287
|
*/
|
|
2401
2288
|
creditId?: string;
|
|
2402
2289
|
/** The draw opened alongside {@link creditId}. */
|
|
2403
2290
|
drawId?: string;
|
|
2404
|
-
/** The funding leg, reported to the platform as a deposit
|
|
2291
|
+
/** The funding leg, reported to the platform as a deposit. */
|
|
2405
2292
|
funding?: CreditFundingReport;
|
|
2406
2293
|
/**
|
|
2407
2294
|
* Fiat micro this payment funded that the job's draw did not take
|
|
2408
|
-
*
|
|
2295
|
+
* See `PaymentInfo.unappliedMicro`. When it equals the whole
|
|
2409
2296
|
* funding, the payment moved no job counters at all and Tx C is handed a
|
|
2410
2297
|
* `null` delta.
|
|
2411
2298
|
*/
|
|
@@ -2413,7 +2300,7 @@ interface VerifiedIncomingPayment {
|
|
|
2413
2300
|
}
|
|
2414
2301
|
/**
|
|
2415
2302
|
* Verify an incoming payment without mutating the job. Returns the credit
|
|
2416
|
-
* shape on success or an error envelope on failure.
|
|
2303
|
+
* shape on success or an error envelope on failure.
|
|
2417
2304
|
*
|
|
2418
2305
|
* The rails dispatched on below are the list {@link PAYMENT_PROOF_KEYS}
|
|
2419
2306
|
* carries — add one here and it must be added there in the same commit.
|
|
@@ -2428,27 +2315,27 @@ declare function verifyIncomingPayment(body: {
|
|
|
2428
2315
|
status: number;
|
|
2429
2316
|
/**
|
|
2430
2317
|
* A short mid-job pay that funded a credit before this request failed
|
|
2431
|
-
*
|
|
2318
|
+
* still a deposit, still reported. See `PaymentErrorDetail`.
|
|
2432
2319
|
*/
|
|
2433
2320
|
funding?: CreditFundingReport;
|
|
2434
2321
|
};
|
|
2435
2322
|
}>;
|
|
2436
2323
|
/**
|
|
2437
2324
|
* Apply a verified payment result to the in-memory job. Used by callers that
|
|
2438
|
-
* don't run Tx C (e.g., the legacy isolate-job-manager path).
|
|
2325
|
+
* don't run Tx C (e.g., the legacy isolate-job-manager path).
|
|
2439
2326
|
*/
|
|
2440
2327
|
declare function applyPaymentInfoToJob(job: ServerJob, info: VerifiedIncomingPayment): void;
|
|
2441
2328
|
/**
|
|
2442
2329
|
* Process an incoming payment message. Returns an error body on failure.
|
|
2443
2330
|
*
|
|
2444
|
-
* **The isolate-runtime path, and deliberately ledger-less** (
|
|
2331
|
+
* **The isolate-runtime path, and deliberately ledger-less** (the isolate-runtime rule). Its
|
|
2445
2332
|
* only caller is `platform/isolate-job-manager.ts`; isolate DVMs have no
|
|
2446
2333
|
* Postgres of their own, so they run implicit N=1 with today's semantics
|
|
2447
2334
|
* preserved and never advertise credit. The credits-carrying mid-job path is
|
|
2448
2335
|
* {@link verifyIncomingPayment} + `JobManager.processPayment` (Tx C), where
|
|
2449
|
-
*
|
|
2336
|
+
* wired `fund` + `growDraw`. Wiring a ledger here would need a
|
|
2450
2337
|
* platform-side ledger leg with its own funding-atomicity and receipt-key
|
|
2451
|
-
* story — the trust-surface expansion
|
|
2338
|
+
* story — the trust-surface expansion exists to decide.
|
|
2452
2339
|
*/
|
|
2453
2340
|
declare function processIncomingPayment(job: ServerJob, body: {
|
|
2454
2341
|
type: MessageType;
|
|
@@ -2463,22 +2350,22 @@ interface CommittedFunding {
|
|
|
2463
2350
|
/** Snapshot fixed immediately after this request's funding leg, before any draw. */
|
|
2464
2351
|
fundingCredit?: CreditSnapshot;
|
|
2465
2352
|
/**
|
|
2466
|
-
* Absent for a `/v1/credit` fund-only top-up
|
|
2353
|
+
* Absent for a `/v1/credit` fund-only top-up — money landed on the
|
|
2467
2354
|
* credit with no job to pay for — for a short mid-job pay, which funds
|
|
2468
|
-
* without drawing or growing
|
|
2469
|
-
* whose combined balance cannot cover the draw
|
|
2355
|
+
* without drawing or growing, and for an explicit short deposit
|
|
2356
|
+
* whose combined balance cannot cover the draw. On a mid-job
|
|
2470
2357
|
* top-up this is the **grown** draw, so `amountMicro` is the job's running
|
|
2471
2358
|
* total, not this leg's contribution.
|
|
2472
2359
|
*/
|
|
2473
2360
|
draw?: DrawResult;
|
|
2474
2361
|
/**
|
|
2475
2362
|
* An explicit short deposit committed, but the combined available balance
|
|
2476
|
-
* could not cover the full-price draw
|
|
2363
|
+
* could not cover the full-price draw. Preserved instead of
|
|
2477
2364
|
* thrown so the rail receive and deposit commit atomically with no draw.
|
|
2478
2365
|
*/
|
|
2479
2366
|
drawRefusal?: CreditLedgerError;
|
|
2480
2367
|
/**
|
|
2481
|
-
* What this leg added to an already-placed draw
|
|
2368
|
+
* What this leg added to an already-placed draw. Set only on a
|
|
2482
2369
|
* mid-job top-up; the upfront legs contribute the whole draw, so they leave
|
|
2483
2370
|
* it unset and `draw.railValue` is the same thing.
|
|
2484
2371
|
*/
|
|
@@ -2488,7 +2375,7 @@ interface CommittedFunding {
|
|
|
2488
2375
|
drawNative: number | null;
|
|
2489
2376
|
};
|
|
2490
2377
|
/**
|
|
2491
|
-
* Fiat micro that landed on the credit but grew no draw
|
|
2378
|
+
* Fiat micro that landed on the credit but grew no draw — a
|
|
2492
2379
|
* duplicate payment for an ask already satisfied, the clipped tail of an
|
|
2493
2380
|
* oversized one, or a payment that arrived after the ask was met. It stays
|
|
2494
2381
|
* the caller's available balance; `UnappliedCredit` on the 201 is how they
|
|
@@ -2503,9 +2390,9 @@ interface CommittedFunding {
|
|
|
2503
2390
|
*
|
|
2504
2391
|
* Exported because the builder-supplied `onCreditFunded` override is a second
|
|
2505
2392
|
* door onto the same report, and both must say the same thing: hand-rolling the
|
|
2506
|
-
* mapping there once dropped the
|
|
2393
|
+
* mapping there once dropped the `tempoChannel` block, so a DVM
|
|
2507
2394
|
* reporting through the override registered no channel with the close observer
|
|
2508
|
-
*
|
|
2395
|
+
* One builder, no drift.
|
|
2509
2396
|
*/
|
|
2510
2397
|
declare function creditDepositPayload(dvmId: string, funding: CreditFundingReport): CreditDepositPayload;
|
|
2511
2398
|
/** Finish the local ledger effect retained by a durable exact-settlement intent. */
|
|
@@ -2518,7 +2405,6 @@ declare function repairX402ExactSettlementEffect(args: {
|
|
|
2518
2405
|
tx?: CreditLedgerQuerier;
|
|
2519
2406
|
}): Promise<CommittedFunding | undefined>;
|
|
2520
2407
|
|
|
2521
|
-
/** Everything the issuer needs to sign for one DVM. */
|
|
2522
2408
|
interface ReceiptIssuerOpts {
|
|
2523
2409
|
/** 32-byte hex receipt secret — `DVMKIT_RECEIPT_KEY`, or an ephemeral dev key. */
|
|
2524
2410
|
secret: string;
|
|
@@ -2528,7 +2414,7 @@ interface ReceiptIssuerOpts {
|
|
|
2528
2414
|
dvmId: string;
|
|
2529
2415
|
}
|
|
2530
2416
|
/**
|
|
2531
|
-
* Signs job receipts for one DVM
|
|
2417
|
+
* Signs job receipts for one DVM.
|
|
2532
2418
|
*
|
|
2533
2419
|
* Constructed only when a receipt key is wired, so the presence of an issuer
|
|
2534
2420
|
* *is* the "this DVM emits receipts" signal that `/v1/info` advertises. Pure:
|
|
@@ -2546,19 +2432,23 @@ declare class ReceiptIssuer {
|
|
|
2546
2432
|
constructor(opts: ReceiptIssuerOpts);
|
|
2547
2433
|
/**
|
|
2548
2434
|
* Build and sign the receipt for a terminal job. `seq` comes from
|
|
2549
|
-
* `JobStore.claimReceiptSeq
|
|
2435
|
+
* `JobStore.claimReceiptSeq`; `issuedAt` is epoch seconds and defaults to now.
|
|
2550
2436
|
*
|
|
2551
2437
|
* Free jobs get receipts too, with `paid.msats: 0` — they still consume a
|
|
2552
2438
|
* sequence number and still attest an outcome. Whether feedback is
|
|
2553
2439
|
* paid-only is the feedback layer's call, not this one's.
|
|
2554
2440
|
*
|
|
2555
|
-
*
|
|
2441
|
+
* Timing and attribution come from the committed terminal row, not signing
|
|
2442
|
+
* time or handler input. Legacy rows without those durable inputs omit the
|
|
2443
|
+
* optional blocks rather than inventing historical evidence.
|
|
2444
|
+
*
|
|
2445
|
+
* `credit` is the resolved draw block — present on every job
|
|
2556
2446
|
* whose payment funded/drew the ledger, absent otherwise. Signature-
|
|
2557
2447
|
* compatible either way: `canonicalize` omits absent keys.
|
|
2558
2448
|
*/
|
|
2559
2449
|
issue(record: JobRecord, seq: number, issuedAt?: number, credit?: ReceiptCredit): JobReceipt;
|
|
2560
2450
|
/**
|
|
2561
|
-
* Countersign one reclaim event
|
|
2451
|
+
* Countersign one reclaim event. Unlike job receipts there is
|
|
2562
2452
|
* no store-allocated sequence — the drain's own `ledger_seq` (taken under
|
|
2563
2453
|
* the credit row lock, shared with draws) already orders it in the
|
|
2564
2454
|
* per-credit evidence chain.
|
|
@@ -2650,13 +2540,9 @@ declare class X402FacilitatorHealth implements X402FacilitatorHealthLike {
|
|
|
2650
2540
|
private refreshOnce;
|
|
2651
2541
|
}
|
|
2652
2542
|
|
|
2653
|
-
/** Options for creating a JobManager. */
|
|
2654
2543
|
interface JobManagerOpts {
|
|
2655
|
-
/** Environment variables for ctx.env. */
|
|
2656
2544
|
env: Record<string, string>;
|
|
2657
|
-
/** KV store for ctx.store. */
|
|
2658
2545
|
store: KVStore;
|
|
2659
|
-
/** Cashu mints accepted for payment. */
|
|
2660
2546
|
mints?: string[];
|
|
2661
2547
|
/** Persistent backing store for job records. Default: in-memory. */
|
|
2662
2548
|
jobStore?: JobStore;
|
|
@@ -2664,7 +2550,6 @@ interface JobManagerOpts {
|
|
|
2664
2550
|
devMode?: boolean;
|
|
2665
2551
|
/** Payment methods this DVM accepts. Default: derived by the server from configured rails. */
|
|
2666
2552
|
paymentMethods?: PaymentMethod[];
|
|
2667
|
-
/** x402 stablecoin payment configuration. */
|
|
2668
2553
|
x402?: X402Config;
|
|
2669
2554
|
/** Durable exact-settlement coordinator shared with upfront verification. */
|
|
2670
2555
|
x402ExactSettlement?: X402ExactSettlementServer;
|
|
@@ -2672,16 +2557,16 @@ interface JobManagerOpts {
|
|
|
2672
2557
|
x402FacilitatorHealth?: X402FacilitatorHealthLike;
|
|
2673
2558
|
/** MPP multi-rail payment handle (built via `createMppFromOpts`). */
|
|
2674
2559
|
mpp?: MppxServer;
|
|
2675
|
-
/** Cashu receive mode
|
|
2560
|
+
/** Cashu receive mode. */
|
|
2676
2561
|
cashuMode?: CashuMode;
|
|
2677
|
-
/** Builder's NUT-11 P2PK lock pubkey
|
|
2562
|
+
/** Builder's NUT-11 P2PK lock pubkey. Required for accumulator mode. */
|
|
2678
2563
|
lockPubkey?: string;
|
|
2679
2564
|
/** Test-only atomic Cashu backend, supplied by the development host. */
|
|
2680
2565
|
disposableCashu?: DisposableCashuCommit;
|
|
2681
2566
|
/** Postgres pool for wallet accumulator persistence. */
|
|
2682
2567
|
db?: Pool;
|
|
2683
2568
|
/**
|
|
2684
|
-
* Agent-wallet
|
|
2569
|
+
* Agent-wallet: per-DVM identifier used for the scheduler advisory
|
|
2685
2570
|
* lock and the `(dvm_id, request_id)` replay key. Defaults to `DVMKIT_DVM_ID`
|
|
2686
2571
|
* env at boot in `createDVMHost()`.
|
|
2687
2572
|
*/
|
|
@@ -2690,18 +2575,18 @@ interface JobManagerOpts {
|
|
|
2690
2575
|
authAudience?: SignedRequestAudience;
|
|
2691
2576
|
/**
|
|
2692
2577
|
* Shared fx fetcher used to convert USD-string descriptor prices and
|
|
2693
|
-
* fiat-form `requestPayment` calls into sats
|
|
2578
|
+
* fiat-form `requestPayment` calls into sats. The SDK server owns
|
|
2694
2579
|
* one per process so concurrent quotes share the 60s cache window.
|
|
2695
2580
|
*/
|
|
2696
2581
|
fxFetcher: FxFetcher;
|
|
2697
2582
|
/**
|
|
2698
|
-
* Signs a receipt at every terminal transition
|
|
2583
|
+
* Signs a receipt at every terminal transition. Absent when the
|
|
2699
2584
|
* DVM has no receipt key wired — jobs then terminate exactly as before and
|
|
2700
2585
|
* `/v1/info` doesn't advertise `receipts`.
|
|
2701
2586
|
*/
|
|
2702
2587
|
receiptIssuer?: ReceiptIssuer;
|
|
2703
2588
|
/**
|
|
2704
|
-
* Credit ledger for the terminal funnel
|
|
2589
|
+
* Credit ledger for the terminal funnel: a job carrying a
|
|
2705
2590
|
* `creditId`/`drawId` settles its draw on success and releases it on
|
|
2706
2591
|
* failure/cancel — this is how "no debit on job failure" is mechanically
|
|
2707
2592
|
* real. The resolution runs before receipt issuance so the receipt's
|
|
@@ -2709,31 +2594,31 @@ interface JobManagerOpts {
|
|
|
2709
2594
|
*/
|
|
2710
2595
|
creditLedger?: CreditLedgerLike;
|
|
2711
2596
|
/**
|
|
2712
|
-
* Durable processed-payment markers
|
|
2713
|
-
* mid-job top-up path
|
|
2597
|
+
* Durable processed-payment markers, threaded through to the
|
|
2598
|
+
* mid-job top-up path so an x402/mpp payment's marker, `fund`, and
|
|
2714
2599
|
* `growDraw` commit together.
|
|
2715
2600
|
*/
|
|
2716
2601
|
processedPayments?: ProcessedPaymentStore;
|
|
2717
2602
|
/**
|
|
2718
2603
|
* Reporter-owned transactional deposit enqueue threaded into mid-job rail
|
|
2719
|
-
* commits
|
|
2604
|
+
* commits.
|
|
2720
2605
|
*/
|
|
2721
2606
|
enqueueCreditDeposit?: CreditDepositEnqueue;
|
|
2722
|
-
/** Reporter outbox inserted atomically with a terminal draw release
|
|
2607
|
+
/** Reporter outbox inserted atomically with a terminal draw release. */
|
|
2723
2608
|
enqueueCreditDrawRelease?: CreditDrawReleaseEnqueue;
|
|
2724
2609
|
/** Credit lifetime a top-up mints with, from the DVM's advertised `credit.ttl`. */
|
|
2725
2610
|
creditTtlMs?: number;
|
|
2726
2611
|
/**
|
|
2727
|
-
* The DVM's declared pricing currency
|
|
2612
|
+
* The DVM's declared pricing currency, which is the currency of
|
|
2728
2613
|
* every credit it opens. Threaded into the job context so a mid-job ask on a
|
|
2729
2614
|
* job that holds no credit yet is still pinned in the denomination its first
|
|
2730
|
-
* payment will open the credit in
|
|
2615
|
+
* payment will open the credit in. `DVMServer` passes its resolved
|
|
2731
2616
|
* `pricingCurrency`; a host building a `JobManager` directly falls back to the
|
|
2732
2617
|
* descriptor, and then to `"usd"`.
|
|
2733
2618
|
*/
|
|
2734
2619
|
pricingCurrency?: string;
|
|
2735
2620
|
/**
|
|
2736
|
-
* Callback fired when a mid-job top-up moves money onto a credit
|
|
2621
|
+
* Callback fired when a mid-job top-up moves money onto a credit.
|
|
2737
2622
|
* The platform books it as a **deposit** — a liability until drawn, never
|
|
2738
2623
|
* summed into revenue — so a top-up that skipped this would leave the
|
|
2739
2624
|
* outstanding-liability view short. Mirrors `DVMServer.reportCreditDeposit`,
|
|
@@ -2741,7 +2626,7 @@ interface JobManagerOpts {
|
|
|
2741
2626
|
*/
|
|
2742
2627
|
onCreditFunded?: (info: CreditDepositPayload) => void | Promise<void>;
|
|
2743
2628
|
/**
|
|
2744
|
-
* Callback fired when a paid job completes
|
|
2629
|
+
* Callback fired when a paid job completes. Container-runtime DVMs
|
|
2745
2630
|
* use this to report revenue to the platform via `RevenueReporter`. Mirrors
|
|
2746
2631
|
* the isolate path's `IsolateJobManager.opts.onJobCompleted`.
|
|
2747
2632
|
*/
|
|
@@ -2773,16 +2658,18 @@ interface JobManagerOpts {
|
|
|
2773
2658
|
}) => void | Promise<void>;
|
|
2774
2659
|
/**
|
|
2775
2660
|
* Callback fired when a terminal job declared a builder cost but emitted no
|
|
2776
|
-
* revenue report
|
|
2661
|
+
* revenue report. Container-runtime DVMs use the reporter's
|
|
2777
2662
|
* durable `job-cost` outbox path. The receiver de-duplicates by DVM + job,
|
|
2778
2663
|
* independently of the rail-keyed revenue ledger.
|
|
2779
2664
|
*/
|
|
2780
2665
|
onJobCost?: (info: JobCostReportPayload) => void | Promise<void>;
|
|
2666
|
+
/** SDK-owned hosted terminal outbox callback. */
|
|
2667
|
+
onJobTerminal?: (info: JobTerminalReportPayload) => Promise<void>;
|
|
2781
2668
|
/**
|
|
2782
2669
|
* Callback fired when the stale-job reaper force-fails a *paid* job — its
|
|
2783
2670
|
* pending credit draw is released rather than settled, because only a
|
|
2784
|
-
* completed job settles a draw
|
|
2785
|
-
* to a paid-job-death report → operator `notice
|
|
2671
|
+
* completed job settles a draw. Container-runtime DVMs wire this
|
|
2672
|
+
* to a paid-job-death report → operator `notice`. Never fires for
|
|
2786
2673
|
* free jobs (`paidMsats === 0`). Fired fire-and-forget so a throw or a slow
|
|
2787
2674
|
* report can't wedge the sweep.
|
|
2788
2675
|
*/
|
|
@@ -2799,20 +2686,20 @@ interface JobManagerOpts {
|
|
|
2799
2686
|
}) => void | Promise<void>;
|
|
2800
2687
|
/**
|
|
2801
2688
|
* Callback fired when a settled credit draw cannot book a revenue event
|
|
2802
|
-
* because no usable payment rail is available
|
|
2689
|
+
* because no usable payment rail is available. Container-runtime
|
|
2803
2690
|
* DVMs auto-wire this to the platform's deduped operator notice. Fired
|
|
2804
2691
|
* fire-and-forget so alert delivery cannot block terminal handling or draw
|
|
2805
2692
|
* reconciliation.
|
|
2806
2693
|
*/
|
|
2807
2694
|
onRevenueSkippedNoRail?: (info: RevenueSkippedNoRailPayload) => void | Promise<void>;
|
|
2808
2695
|
/**
|
|
2809
|
-
* Stale-job sweep threshold in ms
|
|
2696
|
+
* Stale-job sweep threshold in ms. Jobs in a non-terminal status
|
|
2810
2697
|
* (`processing` / `working` / `awaiting-input`) whose `lastActivityAt` is
|
|
2811
2698
|
* older than this are force-cancelled with reason `stale_no_terminal_status`,
|
|
2812
2699
|
* so callers always reach a terminal status instead of polling forever after
|
|
2813
2700
|
* a worker crash, OOM, or silent `messageAppender` failure. Default:
|
|
2814
|
-
* `2 × descriptor.idleTimeout + STALE_JOB_WATCHDOG_HEADROOM_MS`,
|
|
2815
|
-
* the
|
|
2701
|
+
* `2 × descriptor.idleTimeout + STALE_JOB_WATCHDOG_HEADROOM_MS`, which keeps
|
|
2702
|
+
* the default beyond scrape's `JOB_TIMEOUT_MS`.
|
|
2816
2703
|
* Builders whose per-handler wall-clock bound exceeds 90s must override
|
|
2817
2704
|
* this — otherwise the watchdog fires on a legitimate in-flight handler.
|
|
2818
2705
|
* Set to `0` to disable (test seam).
|
|
@@ -2824,7 +2711,7 @@ interface JobManagerOpts {
|
|
|
2824
2711
|
*/
|
|
2825
2712
|
staleJobSweepIntervalMs?: number;
|
|
2826
2713
|
/**
|
|
2827
|
-
* Worker-liveness watchdog in ms
|
|
2714
|
+
* Worker-liveness watchdog in ms. Jobs in `processing`/`working`
|
|
2828
2715
|
* whose `lastActivityAt` is older than this are marked `failed` (dead
|
|
2829
2716
|
* worker), separately from the longer `awaiting-input` idle timeout above.
|
|
2830
2717
|
* Default: `descriptor.processingWatchdog * 1000` or
|
|
@@ -2834,15 +2721,15 @@ interface JobManagerOpts {
|
|
|
2834
2721
|
/**
|
|
2835
2722
|
* Interval at which locally-active `processing`/`working` jobs have their
|
|
2836
2723
|
* `lastActivityAt` bumped so the processing watchdog only fires on a truly
|
|
2837
|
-
* dead worker
|
|
2838
|
-
* `0` to disable (test seam). Also floors how far the
|
|
2724
|
+
* dead worker. Default: `DEFAULT_HEARTBEAT_INTERVAL_MS`. Set to
|
|
2725
|
+
* `0` to disable (test seam). Also floors how far the clock guard
|
|
2839
2726
|
* may compensate a backwards step on the processing arm — a cadence close to
|
|
2840
2727
|
* `processingWatchdogMs` leaves it no room and it compensates nothing.
|
|
2841
2728
|
*/
|
|
2842
2729
|
heartbeatIntervalMs?: number;
|
|
2843
2730
|
/**
|
|
2844
2731
|
* Age in ms at which a `pending` credit draw whose `job_id` matches no row in
|
|
2845
|
-
* the job store is released as orphaned
|
|
2732
|
+
* the job store is released as orphaned. The draw commits before
|
|
2846
2733
|
* the job row is persisted, so a crash or a fail-closed refusal in that
|
|
2847
2734
|
* window strands a hold no terminal path can ever reach. Must stay
|
|
2848
2735
|
* comfortably above the submit path's draw→persist latency. Set to `0` to
|
|
@@ -2853,7 +2740,7 @@ interface JobManagerOpts {
|
|
|
2853
2740
|
orphanDrawAgeMs?: number;
|
|
2854
2741
|
/**
|
|
2855
2742
|
* Age in ms at which a `pending` credit draw whose job row **is** terminal is
|
|
2856
|
-
* reconciled against that row's outcome
|
|
2743
|
+
* reconciled against that row's outcome — settled for a
|
|
2857
2744
|
* `completed` job, released for a `failed`/`cancelled` one. Covers the hole
|
|
2858
2745
|
* `orphanDrawAgeMs` cannot: `resolveCreditDraw` throwing on a ledger error
|
|
2859
2746
|
* leaves a terminal job whose hold no later path retries.
|
|
@@ -2861,7 +2748,7 @@ interface JobManagerOpts {
|
|
|
2861
2748
|
* Rides the same scan as the orphan arm, which has two consequences for the
|
|
2862
2749
|
* value you set here. Setting `orphanDrawAgeMs` to `0` disables this arm too.
|
|
2863
2750
|
* And the effective gate is `max(orphanDrawAgeMs, terminalDrawReconcileAgeMs)`
|
|
2864
|
-
*
|
|
2751
|
+
* the scan never returns a row younger than its own cutoff, so anything
|
|
2865
2752
|
* below `orphanDrawAgeMs` is silently clamped up to it. That floor is
|
|
2866
2753
|
* deliberate rather than a wart: honouring a narrower value would mean
|
|
2867
2754
|
* widening the scan, which hands the orphan arm rows younger than
|
|
@@ -2900,7 +2787,7 @@ interface JobManagerOpts {
|
|
|
2900
2787
|
/**
|
|
2901
2788
|
* Grace window in ms a reactivating machine waits for a live original handler
|
|
2902
2789
|
* to win the single-execution claim before replaying an `awaiting-input` job
|
|
2903
|
-
* itself
|
|
2790
|
+
* itself. Default: `DEFAULT_REACTIVATION_CLAIM_GRACE_MS`. Widen if
|
|
2904
2791
|
* cross-machine NOTIFY latency causes spurious double executions; lower in
|
|
2905
2792
|
* tests for speed.
|
|
2906
2793
|
*/
|
|
@@ -2908,7 +2795,7 @@ interface JobManagerOpts {
|
|
|
2908
2795
|
}
|
|
2909
2796
|
type ResolvedInput<I extends ZodLike | undefined> = I extends ZodLike<infer O> ? O : string;
|
|
2910
2797
|
/**
|
|
2911
|
-
* A deposit this payment funded that the job row did **not** take
|
|
2798
|
+
* A deposit this payment funded that the job row did **not** take.
|
|
2912
2799
|
*
|
|
2913
2800
|
* Reached when two mid-job top-ups race on a free-then-paid job: each opens its
|
|
2914
2801
|
* own implicit credit, and `verifyAndCredit`'s set-if-null bind keeps one. The
|
|
@@ -2958,12 +2845,13 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
2958
2845
|
private readonly cleanupTimers;
|
|
2959
2846
|
/** Cost revisions held only by a terminal handler owner until its durable merge succeeds. */
|
|
2960
2847
|
private readonly terminalCostHandoffRetryTimers;
|
|
2961
|
-
/** Per-job NOTIFY unsubscribe handles for the durable-status watch
|
|
2848
|
+
/** Per-job NOTIFY unsubscribe handles for the durable-status watch. */
|
|
2962
2849
|
private readonly statusWatchers;
|
|
2963
|
-
/** Job ids whose durable-status re-read is in flight — coalesces notify storms
|
|
2850
|
+
/** Job ids whose durable-status re-read is in flight — coalesces notify storms. */
|
|
2964
2851
|
private readonly statusChecksInFlight;
|
|
2965
|
-
/** Job ids that were notified mid-re-read and must be re-checked
|
|
2852
|
+
/** Job ids that were notified mid-re-read and must be re-checked. */
|
|
2966
2853
|
private readonly statusChecksQueued;
|
|
2854
|
+
private readonly inputResumeTails;
|
|
2967
2855
|
private readonly sessionId;
|
|
2968
2856
|
private nextJobId;
|
|
2969
2857
|
private accumulatorMonitor?;
|
|
@@ -2971,7 +2859,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
2971
2859
|
private readonly processingWatchdogMs;
|
|
2972
2860
|
private readonly reactivationClaimGraceMs;
|
|
2973
2861
|
/**
|
|
2974
|
-
* Worker-heartbeat cadence
|
|
2862
|
+
* Worker-heartbeat cadence. Also floors the clock
|
|
2975
2863
|
* guard's compensation on the processing arm — see
|
|
2976
2864
|
* {@link staleSweepCompensationCapMs}.
|
|
2977
2865
|
*/
|
|
@@ -2991,7 +2879,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
2991
2879
|
private jobRetentionResumeFrom?;
|
|
2992
2880
|
/**
|
|
2993
2881
|
* A `Date.now()` reading that cannot regress within this manager's lifetime
|
|
2994
|
-
*
|
|
2882
|
+
* Anchored at construction, so a manager already running when
|
|
2995
2883
|
* the clock stepped is covered. The stale-job sweep's two cutoffs, the
|
|
2996
2884
|
* orphan-draw age gate and the reactivation claim grace all read it; see
|
|
2997
2885
|
* {@link createMonotonicClock} for what it costs and where it is clamped.
|
|
@@ -3004,18 +2892,16 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3004
2892
|
private jobRetentionSweepInFlight;
|
|
3005
2893
|
private heartbeatInFlight;
|
|
3006
2894
|
/**
|
|
3007
|
-
* The denomination of every credit this DVM opens
|
|
2895
|
+
* The denomination of every credit this DVM opens. Resolved once,
|
|
3008
2896
|
* with the same boundary fallback `DVMServer` applies for direct JavaScript
|
|
3009
2897
|
* callers that force an incomplete value past the descriptor contract.
|
|
3010
2898
|
*/
|
|
3011
2899
|
private readonly pricingCurrency;
|
|
3012
2900
|
constructor(descriptor: DVMDescriptor<State, InputSchema>, opts: JobManagerOpts);
|
|
3013
|
-
/** Active in-memory jobs. */
|
|
3014
2901
|
get activeJobs(): Map<string, ServerJob>;
|
|
3015
|
-
/** The backing job store. */
|
|
3016
2902
|
get store(): JobStore;
|
|
3017
2903
|
/**
|
|
3018
|
-
* True when this DVM will actually issue receipts
|
|
2904
|
+
* True when this DVM will actually issue receipts — a key *and*
|
|
3019
2905
|
* a store that can allocate sequence numbers and persist bytes write-once.
|
|
3020
2906
|
* `/v1/info#receipts` reads this rather than the key alone, so the flag can
|
|
3021
2907
|
* never promise something `issueReceipt` silently declines to do.
|
|
@@ -3026,7 +2912,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3026
2912
|
/** Names of every capability this descriptor exposes — used for error envelopes. */
|
|
3027
2913
|
capabilityNames(): string[];
|
|
3028
2914
|
/**
|
|
3029
|
-
* Resolve a capability's static `price` to msats
|
|
2915
|
+
* Resolve a capability's static `price` to msats.
|
|
3030
2916
|
*
|
|
3031
2917
|
* `"$X.XX"` is parsed as USD and converted via the SDK's shared fx fetcher.
|
|
3032
2918
|
* Returns `undefined` for dynamic-priced capabilities (those that declare
|
|
@@ -3035,7 +2921,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3035
2921
|
currentPriceMsats(capability: string): Promise<number | undefined>;
|
|
3036
2922
|
/**
|
|
3037
2923
|
* Translate a capability's static `price` into the fiat envelope used by
|
|
3038
|
-
* the per-capability `pricing.max` advertised on `/v1/info
|
|
2924
|
+
* the per-capability `pricing.max` advertised on `/v1/info`.
|
|
3039
2925
|
* Returns `undefined` for dynamic-priced capabilities and for unknown
|
|
3040
2926
|
* names (the route handler validates the name before invoking this).
|
|
3041
2927
|
*/
|
|
@@ -3044,24 +2930,24 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3044
2930
|
currency: string;
|
|
3045
2931
|
} | undefined;
|
|
3046
2932
|
/**
|
|
3047
|
-
* Parse a request body through a capability's input schema
|
|
2933
|
+
* Parse a request body through a capability's input schema.
|
|
3048
2934
|
*
|
|
3049
2935
|
* Resolution order when a capability declares an `input` schema:
|
|
3050
2936
|
* 1. `body.data` is preferred — agents pass structured fields directly
|
|
3051
|
-
*
|
|
2937
|
+
* (CLI builds this from `--param k=v`).
|
|
3052
2938
|
* 2. Fallback: `JSON.parse(body.input)` for clients still on the legacy
|
|
3053
|
-
*
|
|
2939
|
+
* JSON-string contract.
|
|
3054
2940
|
* 3. Neither usable → throw `MissingStructuredInputError` so the route can
|
|
3055
|
-
*
|
|
2941
|
+
* return `invalid_input` with a hint pointing at `/v1/info`.
|
|
3056
2942
|
*
|
|
3057
2943
|
* When the capability has no `input` schema, returns `body.input` raw
|
|
3058
2944
|
* (primitive path).
|
|
3059
2945
|
*
|
|
3060
|
-
* The
|
|
3061
|
-
* branches
|
|
2946
|
+
* The credit envelope is removed before the schema runs, on both
|
|
2947
|
+
* branches — it rides the signed body but is protocol-level, so a
|
|
3062
2948
|
* top-level `.strict()` capability schema would otherwise reject every
|
|
3063
2949
|
* explicit draw as an unknown key, before the envelope was even extracted.
|
|
3064
|
-
* The persisted `record.input` the
|
|
2950
|
+
* The persisted `record.input` the reactivation path re-reads is the
|
|
3065
2951
|
* pre-Zod wire form, so it comes through the second branch carrying them too.
|
|
3066
2952
|
*/
|
|
3067
2953
|
parseInput(body: {
|
|
@@ -3069,7 +2955,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3069
2955
|
data?: unknown;
|
|
3070
2956
|
}, capability: string): unknown;
|
|
3071
2957
|
/**
|
|
3072
|
-
* Look up the per-capability descriptor by name
|
|
2958
|
+
* Look up the per-capability descriptor by name. Throws when the
|
|
3073
2959
|
* name doesn't exist — the route layer validates body.capability first, so
|
|
3074
2960
|
* reaching this with an unknown name is a programmer error.
|
|
3075
2961
|
*/
|
|
@@ -3083,7 +2969,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3083
2969
|
*/
|
|
3084
2970
|
private getCapability;
|
|
3085
2971
|
/**
|
|
3086
|
-
* Allocate the id the next job will be created under
|
|
2972
|
+
* Allocate the id the next job will be created under. The
|
|
3087
2973
|
* submit path calls this BEFORE payment verification so the ledger draw
|
|
3088
2974
|
* commits with its job linkage, then passes the id back through
|
|
3089
2975
|
* `createJob`'s provenance. Ids allocated for requests whose payment is
|
|
@@ -3092,7 +2978,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3092
2978
|
*/
|
|
3093
2979
|
allocateJobId(): string;
|
|
3094
2980
|
/**
|
|
3095
|
-
* Create a new job from a request body. `provenance`
|
|
2981
|
+
* Create a new job from a request body. `provenance` is what the
|
|
3096
2982
|
* idempotent-replay path on `POST /v1/job` reads back: the raw `job_token`
|
|
3097
2983
|
* to re-issue, and the fingerprint + caller pubkey a retry must reproduce
|
|
3098
2984
|
* to be given it.
|
|
@@ -3107,32 +2993,32 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3107
2993
|
requestId?: string;
|
|
3108
2994
|
authRequestPath?: string;
|
|
3109
2995
|
/**
|
|
3110
|
-
* Pre-allocated id from {@link allocateJobId}
|
|
2996
|
+
* Pre-allocated id from {@link allocateJobId} — the submit
|
|
3111
2997
|
* path allocates before payment verification so the ledger draw can
|
|
3112
2998
|
* record the job it pays for.
|
|
3113
2999
|
*/
|
|
3114
3000
|
jobId?: string;
|
|
3115
3001
|
}): ServerJob;
|
|
3116
|
-
/** Build an SDKJobContext and attach it to the job. */
|
|
3117
3002
|
buildAndAttachContext(job: ServerJob, parsedInput: unknown): SDKJobContext<State, ResolvedInput<InputSchema>>;
|
|
3118
|
-
/** Start the handler for a job. */
|
|
3119
3003
|
runHandler(job: ServerJob, sdkCtx: SDKJobContext<unknown, unknown>): void;
|
|
3120
|
-
/** Dispatch a validated incoming message to the appropriate handler or pending resolver. */
|
|
3121
3004
|
dispatchMessage(job: ServerJob, type: MessageType, content: unknown): Promise<void>;
|
|
3005
|
+
private queueInputResume;
|
|
3006
|
+
private claimLocalInput;
|
|
3007
|
+
private dispatchMessageNow;
|
|
3122
3008
|
/**
|
|
3123
3009
|
* Persist a job to the backing store.
|
|
3124
3010
|
*
|
|
3125
|
-
* Awaits the per-job appender tail
|
|
3126
|
-
*
|
|
3127
|
-
*
|
|
3128
|
-
*
|
|
3129
|
-
*
|
|
3130
|
-
*
|
|
3131
|
-
*
|
|
3011
|
+
* Awaits the per-job appender tail so that:
|
|
3012
|
+
* 1. The snapshot's `messages` is consistent with the `job_messages` table
|
|
3013
|
+
* no row is still in flight at snapshot time.
|
|
3014
|
+
* 2. For terminal saves, the `DELETE FROM job_messages` inside `save()`
|
|
3015
|
+
* can't race a still-pending `appendOutgoing` for the final yield
|
|
3016
|
+
* message (which would otherwise wipe the row before an in-flight SSE
|
|
3017
|
+
* subscriber's NOTIFY-driven fetch can see it).
|
|
3132
3018
|
*/
|
|
3133
3019
|
persistJob(job: ServerJob): Promise<void>;
|
|
3134
3020
|
/**
|
|
3135
|
-
* Cross-machine terminal guard
|
|
3021
|
+
* Cross-machine terminal guard. `buildContext`'s guard reads the
|
|
3136
3022
|
* in-memory `job.status`, so on its own it only protects the machine running
|
|
3137
3023
|
* the handler. On a multi-machine DVM a `DELETE /v1/job/:id` routinely lands
|
|
3138
3024
|
* on a machine that isn't running the job — `app.ts` cancels it in the store
|
|
@@ -3144,7 +3030,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3144
3030
|
* fires `job.abort` so in-flight `ctx.fetch` calls tear down. Returns true when
|
|
3145
3031
|
* the durable state won and the caller must skip its save.
|
|
3146
3032
|
*
|
|
3147
|
-
* Two triggers: `watchDurableStatus`'s NOTIFY subscription
|
|
3033
|
+
* Two triggers: `watchDurableStatus`'s NOTIFY subscription fires
|
|
3148
3034
|
* this within a round-trip of the remote cancel committing, which is what
|
|
3149
3035
|
* actually stops the provider spend; `persistJob` calls it again on every save
|
|
3150
3036
|
* as the backstop for the window where a cancel commits between a handler's
|
|
@@ -3163,7 +3049,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3163
3049
|
/** Durably merge a terminal handler owner's latest cost before reporting it. */
|
|
3164
3050
|
private persistAdoptedTerminalCost;
|
|
3165
3051
|
/**
|
|
3166
|
-
* Watch the durable status of a locally-active job
|
|
3052
|
+
* Watch the durable status of a locally-active job.
|
|
3167
3053
|
*
|
|
3168
3054
|
* `ctx.signal` is raised by `dispatchMessage`, which only runs on the machine
|
|
3169
3055
|
* holding the job in `activeJobs`. On a multi-machine DVM the `DELETE` usually
|
|
@@ -3181,30 +3067,30 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3181
3067
|
*/
|
|
3182
3068
|
private watchDurableStatus;
|
|
3183
3069
|
/**
|
|
3184
|
-
* Tear down a job's durable-status subscription
|
|
3070
|
+
* Tear down a job's durable-status subscription. Must be called
|
|
3185
3071
|
* wherever a job leaves `activeJobs` — a leaked subscriber outlives the job on
|
|
3186
3072
|
* the store's shared LISTEN connection. Idempotent.
|
|
3187
3073
|
*/
|
|
3188
3074
|
unwatchDurableStatus(jobId: string): void;
|
|
3189
3075
|
/**
|
|
3190
|
-
* NOTIFY-driven durable-status re-read, coalesced per job
|
|
3076
|
+
* NOTIFY-driven durable-status re-read, coalesced per job.
|
|
3191
3077
|
*
|
|
3192
3078
|
* A notify that arrives while a re-read is in flight is queued rather than
|
|
3193
3079
|
* dropped: the in-flight read may have observed the row a moment *before* the
|
|
3194
|
-
* cancel committed, and dropping its notify would
|
|
3195
|
-
*
|
|
3080
|
+
* cancel committed, and dropping its notify would leave the local handler
|
|
3081
|
+
* running until its next store write instead of aborting promptly.
|
|
3196
3082
|
*/
|
|
3197
3083
|
private checkDurableTerminal;
|
|
3198
3084
|
/**
|
|
3199
3085
|
* Wire the Tx A appender when the store supports streaming.
|
|
3200
3086
|
*
|
|
3201
|
-
* Tx A
|
|
3087
|
+
* Tx A: outgoing message + `pending_payment_msats` bump land in
|
|
3202
3088
|
* the same transaction. The DB allocates the row's seq via
|
|
3203
3089
|
* `UPDATE jobs SET next_seq = next_seq + 1 RETURNING` so concurrent inbound
|
|
3204
3090
|
* traffic on different machines can never collide on the `(job_id, seq)` PK.
|
|
3205
3091
|
* In-memory `job.seq` keeps its own counter for same-machine SSE listeners.
|
|
3206
3092
|
*
|
|
3207
|
-
* Per-job serialisation
|
|
3093
|
+
* Per-job serialisation: chained through `job.messageAppenderTail`
|
|
3208
3094
|
* so two synchronous `providerMessage` calls (e.g. `artifact` followed by
|
|
3209
3095
|
* `complete`) commit their `appendOutgoing` transactions in call order.
|
|
3210
3096
|
* Without this, the two transactions race for the `jobs` row lock and the
|
|
@@ -3219,15 +3105,15 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3219
3105
|
startIdleTimer(job: ServerJob): void;
|
|
3220
3106
|
/** Clear the idle timer for a job. */
|
|
3221
3107
|
clearIdleTimer(id: string): void;
|
|
3222
|
-
/** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard
|
|
3108
|
+
/** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard. */
|
|
3223
3109
|
cancelJobIdle(job: ServerJob): Promise<void>;
|
|
3224
3110
|
/**
|
|
3225
3111
|
* Force-terminate stale non-terminal jobs. Two status-aware arms:
|
|
3226
|
-
*
|
|
3227
|
-
*
|
|
3228
|
-
*
|
|
3229
|
-
*
|
|
3230
|
-
*
|
|
3112
|
+
* • `awaiting-input` past `staleJobTimeoutMs` → `cancelled`
|
|
3113
|
+
* (`stale_no_terminal_status`) — the caller never paid / responded.
|
|
3114
|
+
* • `processing`/`working` past `processingWatchdogMs` → `failed`
|
|
3115
|
+
* (`worker_died_mid_job`) — the worker died mid-job. The heartbeat keeps
|
|
3116
|
+
* live workers fresh, so a stale row here means a dead process.
|
|
3231
3117
|
*
|
|
3232
3118
|
* Fires on a timer (wired in the constructor) and also callable on demand
|
|
3233
3119
|
* (tests, ops). The CAS in `cancelStaleJob` makes this safe to run
|
|
@@ -3236,17 +3122,17 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3236
3122
|
* descriptors — the local activeJobs teardown only makes sense on the
|
|
3237
3123
|
* JobManager that actually hosted the zombie handler.
|
|
3238
3124
|
*
|
|
3239
|
-
* **Both cutoffs float off the wall clock
|
|
3125
|
+
* **Both cutoffs float off the wall clock.** Each is one
|
|
3240
3126
|
* `Date.now()` sample compared against `last_activity_at`, stamped from a
|
|
3241
3127
|
* different sample at a different moment, so a backwards step between the
|
|
3242
3128
|
* two makes every row look more recently active than it is: the query
|
|
3243
3129
|
* matches nothing and both arms go silent for the length of the skew. The
|
|
3244
3130
|
* dead worker's job keeps its `processing` status, its credit hold stays
|
|
3245
|
-
* `pending`, and the
|
|
3246
|
-
* never fires. Same defect, same host class, as
|
|
3131
|
+
* `pending`, and the paid-job-death alert — wired to this reaper —
|
|
3132
|
+
* never fires. Same defect, same host class, as orphan sweep.
|
|
3247
3133
|
*
|
|
3248
3134
|
* **The compensation is capped** — see {@link staleSweepCompensationCapMs}
|
|
3249
|
-
* for where each arm's cap comes from — which
|
|
3135
|
+
* for where each arm's cap comes from — which did not need to do. Its eagerness costs at worst an early release
|
|
3250
3136
|
* of an unclaimed hold, refused outright for any live job row; ours
|
|
3251
3137
|
* force-*fails* a running job. `cancelStaleJob`'s CAS does not cover that:
|
|
3252
3138
|
* `expectedActivityBefore` is the same compensated threshold the query used,
|
|
@@ -3278,7 +3164,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3278
3164
|
}>;
|
|
3279
3165
|
/**
|
|
3280
3166
|
* How far one watchdog arm may lean on {@link monotonicNowMs} past the wall
|
|
3281
|
-
* clock to cover a backwards step
|
|
3167
|
+
* clock to cover a backwards step. The compensation is capped,
|
|
3282
3168
|
* not free: a claimed row must still have been silent for
|
|
3283
3169
|
* `windowMs - cap`, and unlike the orphan sweep, claiming eagerly here
|
|
3284
3170
|
* force-fails a job that may well be alive.
|
|
@@ -3298,7 +3184,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3298
3184
|
* clock stepped. That pair is marginal already; the guard must not make it
|
|
3299
3185
|
* deterministic. The cap collapses to zero there, which is today's
|
|
3300
3186
|
* behaviour: late, never wrong. The `awaiting-input` arm takes no such term
|
|
3301
|
-
*
|
|
3187
|
+
* nothing heartbeats it by design, and its refresh is an inbound caller
|
|
3302
3188
|
* message on no cadence at all.
|
|
3303
3189
|
*
|
|
3304
3190
|
* **Why a magnitude and not a predicate.** The tempting sharper rule is to
|
|
@@ -3335,7 +3221,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3335
3221
|
* Resolve `pending` credit draws the terminal funnel can no longer reach.
|
|
3336
3222
|
* Two arms over one scan, distinguished by whether the draw's job row exists.
|
|
3337
3223
|
*
|
|
3338
|
-
* **No job row
|
|
3224
|
+
* **No job row — release.** allocates the job id and
|
|
3339
3225
|
* commits the ledger draw, with its `job_id` linkage, before `recordReplay`
|
|
3340
3226
|
* and `persistJob` write the job row. A crash, a `draw_conflict` 409, or a
|
|
3341
3227
|
* fail-closed `replay_detected` 401 in that window leaves a committed hold
|
|
@@ -3345,12 +3231,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3345
3231
|
* available balance stays reduced forever. Cosmetic for an implicit N=1
|
|
3346
3232
|
* credit, a silent balance shrink for an explicit N>1 one.
|
|
3347
3233
|
*
|
|
3348
|
-
* **Terminal job row
|
|
3234
|
+
* **Terminal job row — reconcile to its outcome.** The funnel
|
|
3349
3235
|
* demonstrably fails: `resolveCreditDraw` throws on a ledger/DB error,
|
|
3350
3236
|
* `issueReceipt` catches it, logs `credit_resolve_failed` and returns
|
|
3351
3237
|
* nothing, and until now nothing retried. Same stranded hold, reached through
|
|
3352
3238
|
* a different door — and for a `completed` job it strands the platform's
|
|
3353
|
-
* books too, since draws *are* the revenue events
|
|
3239
|
+
* books too, since draws *are* the revenue events: the caller is
|
|
3354
3240
|
* charged nothing, the balance never moves, and outstanding liability
|
|
3355
3241
|
* (`deposits − draw revenue`) overstates forever. So a late settle books, off
|
|
3356
3242
|
* the committed draw row, through the ordinary reporter path.
|
|
@@ -3377,8 +3263,8 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3377
3263
|
reconciled: number;
|
|
3378
3264
|
}>;
|
|
3379
3265
|
/**
|
|
3380
|
-
* The end-of-scan line for a tick that resolved nothing
|
|
3381
|
-
*
|
|
3266
|
+
* The end-of-scan line for a tick that resolved nothing. Two shapes are
|
|
3267
|
+
* worth a warning, and neither is the ordinary
|
|
3382
3268
|
* one — the timer runs every 5 minutes per ledger in production.
|
|
3383
3269
|
*
|
|
3384
3270
|
* `examined > 0`: aged candidates were looked at and none moved. Usually
|
|
@@ -3387,7 +3273,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3387
3273
|
*
|
|
3388
3274
|
* `examined === 0`: the query returned nothing. Ordinarily that means there
|
|
3389
3275
|
* is nothing to do — but it is equally what a blinded scan looks like, which
|
|
3390
|
-
* is the case the
|
|
3276
|
+
* is the case the gate could not reach. Probe for the oldest
|
|
3391
3277
|
* `pending` hold at any age and speak up when one exists the scan should
|
|
3392
3278
|
* have seen and didn't: already past the age gate outright, stamped ahead of
|
|
3393
3279
|
* our clock (some other process's clock is fast), or hidden while our own
|
|
@@ -3400,7 +3286,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3400
3286
|
*/
|
|
3401
3287
|
private reportSweepOutcome;
|
|
3402
3288
|
/**
|
|
3403
|
-
* Resolve one aged `pending` draw whose job row reached terminal
|
|
3289
|
+
* Resolve one aged `pending` draw whose job row reached terminal,
|
|
3404
3290
|
* mirroring what `resolveCreditDraw` would have done in line. Returns true
|
|
3405
3291
|
* when this call is the one that moved the draw — the caller counts it, and
|
|
3406
3292
|
* only it books.
|
|
@@ -3434,7 +3320,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3434
3320
|
/**
|
|
3435
3321
|
* Refresh `lastActivityAt` for jobs this process is actively running
|
|
3436
3322
|
* (`processing`/`working`) so the processing watchdog only fires once the
|
|
3437
|
-
* worker is genuinely dead
|
|
3323
|
+
* worker is genuinely dead. `awaiting-input` jobs are
|
|
3438
3324
|
* deliberately excluded — their idle timeout must still elapse. Fires on the
|
|
3439
3325
|
* heartbeat timer; also callable on demand for tests.
|
|
3440
3326
|
*/
|
|
@@ -3442,15 +3328,15 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3442
3328
|
beat: number;
|
|
3443
3329
|
}>;
|
|
3444
3330
|
/**
|
|
3445
|
-
* Single-execution claim for a reactivating machine
|
|
3331
|
+
* Single-execution claim for a reactivating machine. Before
|
|
3446
3332
|
* replaying an `awaiting-input` job, wait a bounded grace window for a live
|
|
3447
3333
|
* original handler to win the `awaiting-input → processing` CAS via its
|
|
3448
3334
|
* NOTIFY wake. Resolves:
|
|
3449
|
-
*
|
|
3450
|
-
*
|
|
3451
|
-
*
|
|
3452
|
-
*
|
|
3453
|
-
*
|
|
3335
|
+
* • `false` — the row left `awaiting-input` during the window (the original
|
|
3336
|
+
* handler claimed it / drove it terminal). Stand down; do not replay.
|
|
3337
|
+
* • `true` — the window elapsed still `awaiting-input` (the original worker
|
|
3338
|
+
* is gone or has no live resolver) AND this machine won the claim CAS.
|
|
3339
|
+
* Replay for dead-worker recovery.
|
|
3454
3340
|
*
|
|
3455
3341
|
* Biasing the original handler to win kills the double execution (double
|
|
3456
3342
|
* substrate spend, clobbered artifact) without heartbeating `awaiting-input`
|
|
@@ -3459,7 +3345,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3459
3345
|
*/
|
|
3460
3346
|
claimAwaitingInputForReactivation(jobId: string): Promise<boolean>;
|
|
3461
3347
|
/**
|
|
3462
|
-
* Record an inbound client message durably via Tx B
|
|
3348
|
+
* Record an inbound client message durably via Tx B and append
|
|
3463
3349
|
* it to the in-memory `job.messages` array. Returns the DB-allocated seq
|
|
3464
3350
|
* (`null` when the store isn't streamable — non-Postgres test fallback).
|
|
3465
3351
|
*
|
|
@@ -3478,13 +3364,13 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3478
3364
|
* in-memory `ServerJob`, wires the appender, builds the context, starts
|
|
3479
3365
|
* the idle timer. The caller has already recorded the inbound via Tx B
|
|
3480
3366
|
* and run Tx C (verify + credit); reactivation is now decoupled from
|
|
3481
|
-
* credit
|
|
3367
|
+
* credit. The caller decides whether to run the
|
|
3482
3368
|
* handler (Pattern A: status was `awaiting-input`) or to dispatch the
|
|
3483
3369
|
* message to a custom handler (Pattern B).
|
|
3484
3370
|
*
|
|
3485
3371
|
* For DVMs that declare descriptor-level auth, the persisted
|
|
3486
3372
|
* `record.input` carries the original signed envelope. We re-verify the
|
|
3487
|
-
* signature here
|
|
3373
|
+
* signature here so a DB-layer tamper — compromised admin, SQL
|
|
3488
3374
|
* injection, malicious operator with DB access — can't silently feed an
|
|
3489
3375
|
* attacker-supplied pubkey/envelope into the handler. The drift window
|
|
3490
3376
|
* and replay store are deliberately skipped: the persisted timestamp is
|
|
@@ -3498,7 +3384,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3498
3384
|
* What gets re-verified is the persisted row read back through
|
|
3499
3385
|
* {@link signedRequestInput} — `app.ts` stores the pre-Zod wire form, so
|
|
3500
3386
|
* those are the caller's own signed bytes, the same ones `/v1/quote` and the
|
|
3501
|
-
* submit checked
|
|
3387
|
+
* submit checked. That makes the tamper check strictly stronger
|
|
3502
3388
|
* than the parsed form it replaced: an injected key the capability schema
|
|
3503
3389
|
* doesn't declare used to be stripped before the signature was checked, so
|
|
3504
3390
|
* it re-verified clean.
|
|
@@ -3508,7 +3394,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3508
3394
|
sdkCtx: SDKJobContext<unknown, unknown>;
|
|
3509
3395
|
};
|
|
3510
3396
|
/**
|
|
3511
|
-
* Verify an inbound payment and apply the credit via Tx C
|
|
3397
|
+
* Verify an inbound payment and apply the credit via Tx C.
|
|
3512
3398
|
* The inbound message must have been recorded via Tx B already
|
|
3513
3399
|
* (`recordInboundPending` returned `inboundSeq`). For non-streamable
|
|
3514
3400
|
* stores (`inboundSeq === null`), falls back to in-memory mutation via
|
|
@@ -3518,12 +3404,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3518
3404
|
* `pendingPaymentMsats` without external verification. Gated on
|
|
3519
3405
|
* `devModeSkipsPaymentVerification` — the same predicate the upfront path
|
|
3520
3406
|
* reads, so a dev server wired to a mint verifies mid-job payments for real
|
|
3521
|
-
*
|
|
3407
|
+
* plus the explicit `dev_auto` opt-out the dev console uses,
|
|
3522
3408
|
* which is the one caller that has no wallet to pay from. `dev_auto` loses to
|
|
3523
3409
|
* any proof riding the same message: money on the wire always takes the rail,
|
|
3524
3410
|
* so the flag can never leave a real token unspent against a credited job.
|
|
3525
3411
|
*
|
|
3526
|
-
* Tx C is not atomic with the rail commit
|
|
3412
|
+
* Tx C is not atomic with the rail commit. `verifyIncomingPayment`
|
|
3527
3413
|
* commits the proofs *and* the ledger leg in one transaction and returns;
|
|
3528
3414
|
* only then does `verifyAndCredit` write the job row, on its own connection.
|
|
3529
3415
|
* Everything after that call therefore runs with the caller's money already
|
|
@@ -3539,7 +3425,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3539
3425
|
mintHealthTracker?: MintHealthTracker;
|
|
3540
3426
|
}): Promise<ProcessPaymentOutcome>;
|
|
3541
3427
|
/**
|
|
3542
|
-
* Tx C with a bounded retry
|
|
3428
|
+
* Tx C with a bounded retry.
|
|
3543
3429
|
*
|
|
3544
3430
|
* By the time this runs on the verified path, the rail commit and the ledger
|
|
3545
3431
|
* leg have already committed in a transaction this call is not part of — so a
|
|
@@ -3573,7 +3459,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3573
3459
|
private creditInbound;
|
|
3574
3460
|
/**
|
|
3575
3461
|
* Answer a payment whose rail and ledger legs committed but whose job row
|
|
3576
|
-
* could not be written
|
|
3462
|
+
* could not be written — every retry spent, the money real.
|
|
3577
3463
|
*
|
|
3578
3464
|
* The refusal names the credit the money landed on so the caller can act on
|
|
3579
3465
|
* it directly, mirroring the short mid-job pay's 402 (`payment.ts`). It is a
|
|
@@ -3582,7 +3468,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3582
3468
|
* fault an operator should see in their 5xx rate. `retryable` is true because
|
|
3583
3469
|
* the ask is genuinely still outstanding.
|
|
3584
3470
|
*
|
|
3585
|
-
* Nothing is released here. The hold is left to the
|
|
3471
|
+
* Nothing is released here. The hold is left to the reconciler,
|
|
3586
3472
|
* which makes the same decision — unbound draw ⇒ release, never settle — but
|
|
3587
3473
|
* under the credit row's `FOR UPDATE`, after the job is terminal, where it
|
|
3588
3474
|
* cannot race an in-flight COMMIT. Releasing from here would also be
|
|
@@ -3605,23 +3491,23 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3605
3491
|
private reportUnappliedPayment;
|
|
3606
3492
|
/**
|
|
3607
3493
|
* Describe a deposit the job row never took, in the shape both the 201 and
|
|
3608
|
-
* the `payment_unapplied` 500 carry
|
|
3494
|
+
* the `payment_unapplied` 500 carry. `undefined` when the payment
|
|
3609
3495
|
* ran ledger-less (no credit ledger wired, or the top-up preflight skipped) —
|
|
3610
3496
|
* there is no credit to name, so the 201 carries no `credit` block and the
|
|
3611
3497
|
* 500 takes its words from `unappliedCopy` directly.
|
|
3612
3498
|
*/
|
|
3613
3499
|
private unappliedCredit;
|
|
3614
|
-
/** Book a mid-job top-up's funding as a deposit (
|
|
3500
|
+
/** Book a mid-job top-up's funding as a deposit (the deposit-accounting rule). */
|
|
3615
3501
|
private reportCreditDeposit;
|
|
3616
3502
|
/**
|
|
3617
3503
|
* Internal: build the BuildContextOpts shared between `createJob` and
|
|
3618
3504
|
* `reactivateJob`. Centralised so persistence/terminal callbacks and the
|
|
3619
|
-
*
|
|
3505
|
+
* NOTIFY-driven cross-machine wake stay in one place.
|
|
3620
3506
|
*/
|
|
3621
3507
|
private buildContextOpts;
|
|
3622
3508
|
/**
|
|
3623
3509
|
* Internal: what unit `ctx.requestPayment`'s auto-credit gate may measure this
|
|
3624
|
-
* job's remaining pool in
|
|
3510
|
+
* job's remaining pool in.
|
|
3625
3511
|
*
|
|
3626
3512
|
* The regime is read off the DRAW, never off the job. Implicit N=1 is exactly
|
|
3627
3513
|
* `credit_id === imp:<rail>:<draw_id>`, because `fundAndDraw` derives both
|
|
@@ -3639,15 +3525,14 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3639
3525
|
* drew and whose pool this can't read or denominate answers `unpriced`, which
|
|
3640
3526
|
* the gate declines to auto-credit from: the msat figure it would otherwise
|
|
3641
3527
|
* fall back on is a slice of the credit's rail value at a ratio pinned
|
|
3642
|
-
* whenever that credit was funded,
|
|
3643
|
-
* to stop spending against.
|
|
3528
|
+
* whenever that credit was funded, so it cannot safely fund a new ask.
|
|
3644
3529
|
*/
|
|
3645
3530
|
private resolveDrawPool;
|
|
3646
3531
|
/** Internal: structured warn for the one path that can't price a drawn job's pool. */
|
|
3647
3532
|
private warnDrawPool;
|
|
3648
3533
|
/**
|
|
3649
3534
|
* Issue the signed receipt for a job that has reached a terminal status
|
|
3650
|
-
*
|
|
3535
|
+
* Idempotent and safe to call from every terminal path — the
|
|
3651
3536
|
* store owns both the sequence allocation and the write-once persist, so
|
|
3652
3537
|
* two machines racing on the same job converge on identical bytes and burn
|
|
3653
3538
|
* exactly one sequence number.
|
|
@@ -3663,10 +3548,10 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3663
3548
|
/** Build the common settle/release args, adding the hosted release outbox when wired. */
|
|
3664
3549
|
private drawResolutionArgs;
|
|
3665
3550
|
/**
|
|
3666
|
-
* Settle or release a terminal job's credit draw
|
|
3551
|
+
* Settle or release a terminal job's credit draw: `completed`
|
|
3667
3552
|
* settles (the hold becomes a real debit); `failed`/`cancelled` releases
|
|
3668
3553
|
* (the hold evaporates — "no debit on job failure", mechanically
|
|
3669
|
-
* superseding the
|
|
3554
|
+
* superseding the 974 no-op refund for credit-paid jobs, including
|
|
3670
3555
|
* the `ctx.fail` path and the stale-sweeper reap). Idempotent — a replayed
|
|
3671
3556
|
* resolution returns the recorded state. Returns the `ReceiptCredit` block
|
|
3672
3557
|
* for the receipt: `balance_after` is the recorded draw trajectory for a
|
|
@@ -3674,7 +3559,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3674
3559
|
*/
|
|
3675
3560
|
private resolveCreditDraw;
|
|
3676
3561
|
/**
|
|
3677
|
-
* The one place a terminal job's status becomes a ledger verb
|
|
3562
|
+
* The one place a terminal job's status becomes a ledger verb:
|
|
3678
3563
|
* `completed` settles the hold into a real debit, `failed`/`cancelled`
|
|
3679
3564
|
* release it. Read by the in-line funnel (`resolveCreditDraw`) and by the
|
|
3680
3565
|
* late reconciler (`reconcileTerminalDraw`) — two paths that must never
|
|
@@ -3688,32 +3573,26 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3688
3573
|
*/
|
|
3689
3574
|
private attachReceipt;
|
|
3690
3575
|
/**
|
|
3691
|
-
* Read-path repair for a terminal job carrying no receipt
|
|
3576
|
+
* Read-path repair for a terminal job carrying no receipt, for
|
|
3692
3577
|
* either an in-memory job or a store record. Free in the steady state — it
|
|
3693
3578
|
* returns on the `receipt` check without touching the store — so it costs
|
|
3694
3579
|
* only on the cases it exists for:
|
|
3695
3580
|
*
|
|
3696
3581
|
* - **A crash between the two store calls.** `claimReceiptSeq` commits the
|
|
3697
|
-
*
|
|
3698
|
-
*
|
|
3699
|
-
*
|
|
3700
|
-
*
|
|
3701
|
-
*
|
|
3702
|
-
*
|
|
3582
|
+
* sequence number to the job row before `saveReceipt` writes the bytes; a
|
|
3583
|
+
* process death in that window would otherwise strand that number
|
|
3584
|
+
* forever, and a permanent gap is indistinguishable from the deliberate
|
|
3585
|
+
* suppression `seq` exists to expose. Re-issuing here reuses the already
|
|
3586
|
+
* committed number (the claim is idempotent per job) rather than
|
|
3587
|
+
* allocating a second one.
|
|
3703
3588
|
* - **A transient store failure** at the terminal: `issueReceipt` logs
|
|
3704
|
-
*
|
|
3705
|
-
*
|
|
3589
|
+
* `receipt_issue_failed` and returns nothing rather than failing the job,
|
|
3590
|
+
* so the next read retries.
|
|
3706
3591
|
* - **Jobs that terminated before the DVM had a receipt key.** They pick one
|
|
3707
|
-
*
|
|
3592
|
+
* up on first read, with `issued_at` reflecting when it was signed.
|
|
3708
3593
|
*/
|
|
3709
3594
|
ensureReceipt(target: ServerJob | JobRecord): Promise<void>;
|
|
3710
|
-
/**
|
|
3711
|
-
* Close out a terminal job whose caller hasn't already persisted it: sign
|
|
3712
|
-
* the receipt, then save the snapshot. The snapshot save never writes the
|
|
3713
|
-
* receipt column, so ordering only affects how soon a reader sees the
|
|
3714
|
-
* receipt — this way a caller polling immediately after the terminal
|
|
3715
|
-
* already finds it.
|
|
3716
|
-
*/
|
|
3595
|
+
/** Commit the terminal snapshot before resolving money and signing its durable evidence. */
|
|
3717
3596
|
private finalizeTerminal;
|
|
3718
3597
|
/**
|
|
3719
3598
|
* Issue a receipt for a job this process doesn't hold in `activeJobs` — a
|
|
@@ -3724,9 +3603,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3724
3603
|
issueReceiptForStoredJob(jobId: string): Promise<JobReceipt | undefined>;
|
|
3725
3604
|
/**
|
|
3726
3605
|
* Finish accounting for a terminal written directly to the store — the
|
|
3727
|
-
* stale reaper and a cancel handled on a different machine
|
|
3606
|
+
* stale reaper and a cancel handled on a different machine.
|
|
3728
3607
|
*/
|
|
3729
3608
|
finalizeStoredTerminal(jobId: string): Promise<JobReceipt | undefined>;
|
|
3609
|
+
private reportTerminalJob;
|
|
3610
|
+
private terminalReportRecoveryCursor?;
|
|
3611
|
+
private recoverTerminalReports;
|
|
3730
3612
|
/** Handle job reaching terminal state (completed, failed, cancelled). */
|
|
3731
3613
|
private handleJobTerminal;
|
|
3732
3614
|
/**
|
|
@@ -3744,11 +3626,11 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3744
3626
|
/** Report the latest declared-cost state only when no revenue event carried it. */
|
|
3745
3627
|
private reportJobCost;
|
|
3746
3628
|
/**
|
|
3747
|
-
* Report a completed paid job's revenue
|
|
3629
|
+
* Report a completed paid job's revenue under its durable job identity.
|
|
3748
3630
|
* Fire-and-forget — the `RevenueReporter` owns persistence and retry.
|
|
3749
3631
|
*
|
|
3750
3632
|
* For a **credit-backed** job the revenue event is the *settled draw* (spec
|
|
3751
|
-
* §10), and since
|
|
3633
|
+
* §10), and since the behavior was introduced the sats figure corrects the job's mirror of that
|
|
3752
3634
|
* draw against the draw itself: `withDrawBasis` stamps the job when the
|
|
3753
3635
|
* payment lands, but a Bitcoin credit's settle re-prices the draw against
|
|
3754
3636
|
* the funding lots it consumed. The correction is a delta, because a job's
|
|
@@ -3763,7 +3645,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3763
3645
|
* whose price couldn't be fiat-denominated) nothing changes: the pre-credits
|
|
3764
3646
|
* payload is reported verbatim.
|
|
3765
3647
|
*
|
|
3766
|
-
* Takes the fields rather than a `ServerJob` so the
|
|
3648
|
+
* Takes the fields rather than a `ServerJob` so the reconciler — a
|
|
3767
3649
|
* background sweep that holds no in-process job — books through this exact
|
|
3768
3650
|
* path off the row it read. `paymentTxHash` is passed **verbatim**, never
|
|
3769
3651
|
* recomputed as a `drawSettlementRef`: it is the same column the in-line
|
|
@@ -3780,7 +3662,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
|
|
|
3780
3662
|
* DVM with no reporter). Every drop that costs a booking logs first.
|
|
3781
3663
|
*
|
|
3782
3664
|
* The rail check sits **below** the draw re-read, not in the entry guard
|
|
3783
|
-
*
|
|
3665
|
+
* a settled draw the terminal can't report under is a real debit
|
|
3784
3666
|
* with no revenue event, permanently overstating outstanding liability
|
|
3785
3667
|
* (`deposits − draw revenue`), and it used to return here in silence. Placed
|
|
3786
3668
|
* after the re-read, `revenue_skipped_no_rail` can name the draw and its
|
|
@@ -3801,7 +3683,7 @@ interface LightningRailHealth {
|
|
|
3801
3683
|
refresh?(): Promise<void>;
|
|
3802
3684
|
}
|
|
3803
3685
|
|
|
3804
|
-
/** Builder-facing wiring for the Lightning receive leg
|
|
3686
|
+
/** Builder-facing wiring for the Lightning receive leg. */
|
|
3805
3687
|
interface LightningReceiveConfig {
|
|
3806
3688
|
/**
|
|
3807
3689
|
* Receive-only NWC connection URI (`DVMKIT_NWC_RECEIVE_URI`). Validated at
|
|
@@ -3835,27 +3717,26 @@ interface LightningReceiveConfig {
|
|
|
3835
3717
|
declare const DEFAULT_INVOICE_TTL_SECONDS = 900;
|
|
3836
3718
|
/**
|
|
3837
3719
|
* Floor on the invoice lifetime: twice the signed-request drift window, so the
|
|
3838
|
-
* bolt11 always outlives the funding negotiation that produced it
|
|
3839
|
-
* "invoice expiry ≥ the funding-negotiation window"). A shorter one would
|
|
3720
|
+
* bolt11 always outlives the funding negotiation that produced it. A shorter one would
|
|
3840
3721
|
* expire inside the caller's own retry budget and strand them mid-top-up.
|
|
3841
3722
|
*/
|
|
3842
3723
|
declare const MIN_INVOICE_TTL_SECONDS = 600;
|
|
3843
3724
|
/**
|
|
3844
|
-
* The builder-side Lightning receive leg
|
|
3725
|
+
* The builder-side Lightning receive leg for funding prepaid credit.
|
|
3845
3726
|
*
|
|
3846
3727
|
* Issues a bolt11 over a **receive-only** NWC connection while the DVM is
|
|
3847
3728
|
* awake serving the 402, and credits the ledger when a later request observes
|
|
3848
3729
|
* settlement. Two properties are load-bearing:
|
|
3849
3730
|
*
|
|
3850
3731
|
* - **It never holds a send credential.** `pay_invoice` on this connection is
|
|
3851
|
-
*
|
|
3852
|
-
*
|
|
3853
|
-
*
|
|
3732
|
+
* refused at construction, so a compromised DVM can mint invoices and
|
|
3733
|
+
* nothing else. The drain/refund sender is a separate, budgeted
|
|
3734
|
+
* connection by rule.
|
|
3854
3735
|
* - **Crediting is pull-based — there is no settlement watcher.** The caller's
|
|
3855
|
-
*
|
|
3856
|
-
*
|
|
3857
|
-
*
|
|
3858
|
-
*
|
|
3736
|
+
* next request drives `lookup_invoice`, which is what makes this work on a
|
|
3737
|
+
* suspend-to-zero fleet: a machine that is asleep has nothing to miss.
|
|
3738
|
+
* Wallet downtime at that moment delays crediting; it never loses money,
|
|
3739
|
+
* because the invoice→credit binding is a durable row.
|
|
3859
3740
|
*
|
|
3860
3741
|
* Reachability is **cached**, never probed per request: the funding menu is
|
|
3861
3742
|
* assembled on every quote and every 402, so a live NIP-47 round trip there
|
|
@@ -3897,7 +3778,7 @@ declare class LightningReceive {
|
|
|
3897
3778
|
* Issue (or re-issue) the bolt11 funding `(creditId, fundId)`.
|
|
3898
3779
|
*
|
|
3899
3780
|
* The invoice is minted **for** the credit and amount named in the caller's
|
|
3900
|
-
* signed body — that binding is the
|
|
3781
|
+
* signed body — that binding is the condition-3 commitment on this
|
|
3901
3782
|
* rail, and it is why no artifact hash rides the request: there is no
|
|
3902
3783
|
* caller-supplied artifact to hash. A re-poll returns the stored row rather
|
|
3903
3784
|
* than minting again; a second bolt11 for one `fund_id` would leave two
|
|
@@ -3913,7 +3794,7 @@ declare class LightningReceive {
|
|
|
3913
3794
|
description?: string;
|
|
3914
3795
|
}): Promise<CreditInvoiceRecord>;
|
|
3915
3796
|
/**
|
|
3916
|
-
* Ask the wallet whether one payment hash is paid, and when
|
|
3797
|
+
* Ask the wallet whether one payment hash is paid, and when.
|
|
3917
3798
|
*
|
|
3918
3799
|
* The operator's reconcile verb runs this before it credits anything. A
|
|
3919
3800
|
* `blocked` row implies payment — {@link applyOne} returns above the block
|
|
@@ -4019,7 +3900,7 @@ declare class LightningReceive {
|
|
|
4019
3900
|
}
|
|
4020
3901
|
|
|
4021
3902
|
/**
|
|
4022
|
-
* Owner display identity surfaced on `/v1/info#owner
|
|
3903
|
+
* Owner display identity surfaced on `/v1/info#owner`. Personal orgs
|
|
4023
3904
|
* resolve to the owner builder's profile; shared orgs resolve to the org's own
|
|
4024
3905
|
* profile. Container DVMs read from env vars; isolate DVMs resolve from Postgres.
|
|
4025
3906
|
*/
|
|
@@ -4035,16 +3916,16 @@ interface BuilderIdentity {
|
|
|
4035
3916
|
name?: string;
|
|
4036
3917
|
url?: string;
|
|
4037
3918
|
/**
|
|
4038
|
-
* Builder identity x-only secp256k1 pubkey
|
|
3919
|
+
* Builder identity x-only secp256k1 pubkey. When populated, the
|
|
4039
3920
|
* `/v1/info#builder` block also carries `attestation` + `signature` so
|
|
4040
3921
|
* consumers can verify the deploy was signed by the holder of this key.
|
|
4041
3922
|
* Populated at deploy time from `DVMKIT_BUILDER_PUBKEY` (Fly secret); the
|
|
4042
3923
|
* SDK never re-signs at runtime.
|
|
4043
3924
|
*/
|
|
4044
3925
|
pubkey?: string;
|
|
4045
|
-
/** Canonical deploy-time attestation payload
|
|
3926
|
+
/** Canonical deploy-time attestation payload. Served verbatim. */
|
|
4046
3927
|
attestation?: AttestationPayload;
|
|
4047
|
-
/** Schnorr signature over `canonicaliseForSigning(attestation)
|
|
3928
|
+
/** Schnorr signature over `canonicaliseForSigning(attestation)`. */
|
|
4048
3929
|
signature?: string;
|
|
4049
3930
|
}
|
|
4050
3931
|
/** Platform reporter overrides — the host falls back to env when omitted. */
|
|
@@ -4061,21 +3942,24 @@ interface DVMServeResult {
|
|
|
4061
3942
|
/** Boot snapshot for each mount; configured but unusable rails remain payment-required. */
|
|
4062
3943
|
paymentModes: PaymentMode[];
|
|
4063
3944
|
}
|
|
4064
|
-
/** Options for {@link createDVMHost}. */
|
|
4065
3945
|
interface DVMHostOpts {
|
|
4066
3946
|
/**
|
|
4067
3947
|
* Postgres connection string. Defaults to `DATABASE_URL` env.
|
|
4068
|
-
* Required at runtime
|
|
3948
|
+
* Required at runtime. Boot fails when missing unless `jobStore`
|
|
4069
3949
|
* is explicitly supplied or `devMode` is true (test/dev escape hatches).
|
|
4070
3950
|
*/
|
|
4071
3951
|
database?: string;
|
|
4072
3952
|
/** Listen port. Defaults to `PORT` env or 8080. */
|
|
4073
3953
|
port?: number;
|
|
4074
|
-
/**
|
|
3954
|
+
/**
|
|
3955
|
+
* Environment variables. Defaults to `process.env` when omitted. A supplied
|
|
3956
|
+
* object is the complete host configuration: absent fields do not fall back
|
|
3957
|
+
* to ambient process variables.
|
|
3958
|
+
*/
|
|
4075
3959
|
env?: Record<string, string>;
|
|
4076
3960
|
/** Builder identity for `/v1/info` (forward-compatible). */
|
|
4077
3961
|
builder?: BuilderIdentity;
|
|
4078
|
-
/** Owner display identity for `/v1/info#owner
|
|
3962
|
+
/** Owner display identity for `/v1/info#owner`. Falls back to env vars. */
|
|
4079
3963
|
owner?: OwnerDisplay;
|
|
4080
3964
|
/** Platform reporter overrides. Defaults to env-derived values. */
|
|
4081
3965
|
platformReporter?: PlatformReporterOpts;
|
|
@@ -4093,7 +3977,7 @@ interface DVMHostOpts {
|
|
|
4093
3977
|
jobStore?: JobStore;
|
|
4094
3978
|
/**
|
|
4095
3979
|
* Existing Postgres pool to reuse instead of opening one from `database`
|
|
4096
|
-
* (
|
|
3980
|
+
* (also useful as a test seam). When set, the host builds its JobStore / KVStore /
|
|
4097
3981
|
* cashu accumulator on this pool and leaves it open at shutdown — the caller
|
|
4098
3982
|
* owns it. Lets the e2e harness run DVMs in `p2pk-accumulator` mode against
|
|
4099
3983
|
* its single shared pool rather than spawning a pool per DVM.
|
|
@@ -4110,7 +3994,7 @@ interface DVMHostOpts {
|
|
|
4110
3994
|
/** Payment methods override. Defaults to derived from configured rails. */
|
|
4111
3995
|
paymentMethods?: PaymentMethod[];
|
|
4112
3996
|
/**
|
|
4113
|
-
* Lightning receive leg for credit funding
|
|
3997
|
+
* Lightning receive leg for credit funding. Defaults to the
|
|
4114
3998
|
* env-resolved `DVMKIT_NWC_RECEIVE_URI` /
|
|
4115
3999
|
* `DVMKIT_NWC_RECEIVE_INVOICE_TTL_SECONDS`. Pass `backend` to inject a
|
|
4116
4000
|
* wallet directly — a test seam that skips the connect-time probe, so
|
|
@@ -4144,7 +4028,7 @@ interface MountOpts {
|
|
|
4144
4028
|
/** Path prefix for this DVM's protocol routes (e.g. `/delete-feed`). */
|
|
4145
4029
|
prefix?: string;
|
|
4146
4030
|
}
|
|
4147
|
-
/** Live DVM host. Single wiring locus for the SDK
|
|
4031
|
+
/** Live DVM host. Single wiring locus for the SDK. */
|
|
4148
4032
|
interface DVMHost {
|
|
4149
4033
|
/**
|
|
4150
4034
|
* Underlying Hono app for custom non-protocol routes that belong to the
|
|
@@ -4155,15 +4039,15 @@ interface DVMHost {
|
|
|
4155
4039
|
/**
|
|
4156
4040
|
* Resolved Postgres pool — undefined before `host.serve()` completes
|
|
4157
4041
|
* database resolution, set thereafter. Exposed so DVM-local Postgres
|
|
4158
|
-
* consumers (e.g. scrape's `ScrapeDb` per-fetch event store
|
|
4042
|
+
* consumers (e.g. scrape's `ScrapeDb` per-fetch event store)
|
|
4159
4043
|
* can reuse the host's pool rather than constructing their own. Wire
|
|
4160
4044
|
* inside descriptor `onBoot` (fires after pool init, before listener opens).
|
|
4161
4045
|
*/
|
|
4162
4046
|
readonly pool: Pool | undefined;
|
|
4163
4047
|
/**
|
|
4164
|
-
* Money-safe credit ledger
|
|
4048
|
+
* Money-safe credit ledger — undefined before `host.serve()`
|
|
4165
4049
|
* resolves stores, set thereafter. Postgres-backed when a pool exists;
|
|
4166
|
-
* pool-less hosts get a shared in-memory ledger so the
|
|
4050
|
+
* pool-less hosts get a shared in-memory ledger so the fund+draw
|
|
4167
4051
|
* semantics hold everywhere. Same wiring window as `pool` (descriptor
|
|
4168
4052
|
* `onBoot` fires after init, before the listener opens).
|
|
4169
4053
|
*/
|
|
@@ -4181,7 +4065,7 @@ interface DVMHost {
|
|
|
4181
4065
|
shutdown(): Promise<void>;
|
|
4182
4066
|
}
|
|
4183
4067
|
/**
|
|
4184
|
-
* Single wiring locus for SDK-side construction
|
|
4068
|
+
* Single wiring locus for SDK-side construction. Replaces both the
|
|
4185
4069
|
* `serve()` wrapper and the `createDVMServer` direct path. Reads env once,
|
|
4186
4070
|
* resolves payment rails, owns the Postgres pool, and mounts each DVM
|
|
4187
4071
|
* descriptor as a Hono sub-app on `host.app`.
|
|
@@ -4190,13 +4074,12 @@ declare function createDVMHost(opts?: DVMHostOpts): DVMHost;
|
|
|
4190
4074
|
|
|
4191
4075
|
/**
|
|
4192
4076
|
* Cached liveness signal for the platform's central Tempo close observer
|
|
4193
|
-
* (internal-review).
|
|
4194
4077
|
*
|
|
4195
4078
|
* A TIP-1034 session channel is only safe to accept while something is
|
|
4196
4079
|
* watching the chain for its close: without that, a caller can consume value,
|
|
4197
4080
|
* request a close, wait out the 900-second grace period and withdraw. So this
|
|
4198
4081
|
* gate is **fail-closed**, which is the opposite of `lightning-rail-health.ts`
|
|
4199
|
-
*
|
|
4082
|
+
* there, withholding a rail over an outage costs a sale; here, advertising
|
|
4200
4083
|
* one costs the money. `tempo/charge` is untouched either way: a charge is an
|
|
4201
4084
|
* ordinary payment with no channel and no close to watch.
|
|
4202
4085
|
*/
|
|
@@ -4219,7 +4102,7 @@ interface TempoObserverHealth {
|
|
|
4219
4102
|
refresh?(): Promise<void>;
|
|
4220
4103
|
/**
|
|
4221
4104
|
* Register a mount's `tempo/session` **capability** so the poll can carry it
|
|
4222
|
-
* back to the platform
|
|
4105
|
+
* back to the platform.
|
|
4223
4106
|
*
|
|
4224
4107
|
* The platform's `dvms.tempo_session_advertised_at` — the only filter on the
|
|
4225
4108
|
* close observer's payee-fallback rung 2 — was written once per container
|
|
@@ -4230,7 +4113,7 @@ interface TempoObserverHealth {
|
|
|
4230
4113
|
*
|
|
4231
4114
|
* The reader must answer from **configuration**, never from
|
|
4232
4115
|
* {@link TempoObserverHealth.available} — a fail-closed gate blip must not
|
|
4233
|
-
* de-stamp the DVM, which is the
|
|
4116
|
+
* de-stamp the DVM, which is the failure through a new door.
|
|
4234
4117
|
*
|
|
4235
4118
|
* Returns a disposer the mount calls on shutdown, so a torn-down capability
|
|
4236
4119
|
* stops voting in the OR below.
|
|
@@ -4352,7 +4235,7 @@ declare class TempoSettlementReadiness {
|
|
|
4352
4235
|
}
|
|
4353
4236
|
|
|
4354
4237
|
/**
|
|
4355
|
-
* Options for the revenue reporter readiness assertion
|
|
4238
|
+
* Options for the revenue reporter readiness assertion.
|
|
4356
4239
|
*
|
|
4357
4240
|
* The check branches on `platformUrl` presence — that's the unambiguous
|
|
4358
4241
|
* "platform-hosted" signal. `failFast` stays on the opts as a general SDK
|
|
@@ -4361,13 +4244,13 @@ declare class TempoSettlementReadiness {
|
|
|
4361
4244
|
interface RevenueBootCheckOpts {
|
|
4362
4245
|
/**
|
|
4363
4246
|
* General SDK strict-mode flag, sourced from `DVMKIT_FAIL_FAST` at the host
|
|
4364
|
-
* boundary
|
|
4365
|
-
* reporter check itself ignores it
|
|
4247
|
+
* boundary. Reserved for future warning-vs-fatal SDK checks; the
|
|
4248
|
+
* reporter check itself ignores it, hence optional.
|
|
4366
4249
|
*/
|
|
4367
4250
|
failFast?: boolean;
|
|
4368
4251
|
/**
|
|
4369
4252
|
* The SDK's canonical "this is a dev/test run" signal. Exempts the boot from
|
|
4370
|
-
* the platform-hosted wiring requirement
|
|
4253
|
+
* the platform-hosted wiring requirement — see below.
|
|
4371
4254
|
*/
|
|
4372
4255
|
devMode?: boolean;
|
|
4373
4256
|
platformToken: string | undefined;
|
|
@@ -4376,63 +4259,38 @@ interface RevenueBootCheckOpts {
|
|
|
4376
4259
|
hasPgPool: boolean;
|
|
4377
4260
|
}
|
|
4378
4261
|
/**
|
|
4379
|
-
*
|
|
4380
|
-
*
|
|
4381
|
-
*
|
|
4382
|
-
*
|
|
4383
|
-
* Partial wiring is always fatal — there's no legitimate "platform set my URL
|
|
4384
|
-
* but didn't finish wiring" case, and `DVMKIT_FAIL_FAST` has no effect here.
|
|
4385
|
-
*
|
|
4386
|
-
* Self-hosted DVMs (no `DVMKIT_PLATFORM_URL`) have no platform reporter
|
|
4387
|
-
* contract to enforce — the SDK skips validation entirely.
|
|
4388
|
-
*
|
|
4389
|
-
* `devMode` boots are exempt (internal-review), mirroring the test-mint guard's
|
|
4390
|
-
* exemption in `createDVMHost` (internal-review, `host.ts`). `devMode` is set by
|
|
4391
|
-
* `dvmctl dev`, `standalone development tooling`, the money-loop, and the e2e/smoke
|
|
4392
|
-
* harnesses — all of which spread `...process.env`, so a builder whose shell
|
|
4393
|
-
* exports `DVMKIT_PLATFORM_URL` for their `dvmctl` platform commands would
|
|
4394
|
-
* otherwise hit this FATAL on a dev server that reports no revenue anyway (no
|
|
4395
|
-
* payment, no auth, no DB). Production boots never set it: first-party
|
|
4396
|
-
* container entrypoints and `dvmctl serve` (`buildServeOpts`) both leave it
|
|
4397
|
-
* unset — asserted in
|
|
4398
|
-
* `__tests__/revenue-reporter-boot.test.ts`, not just claimed here.
|
|
4262
|
+
* Hosted boots (a platform URL is set) require a token, DVM ID and database;
|
|
4263
|
+
* partial wiring is fatal regardless of `failFast`. Self-hosted and `devMode`
|
|
4264
|
+
* boots skip this assertion. The exemption does not disable an already wired
|
|
4265
|
+
* reporter; the banner separately describes whether reporting is active.
|
|
4399
4266
|
*/
|
|
4400
4267
|
declare function assertRevenueReporterReady(opts: RevenueBootCheckOpts): void;
|
|
4401
4268
|
/** What the host's boot banner knows about the revenue reporter's wiring. */
|
|
4402
4269
|
interface ReporterBannerOpts {
|
|
4403
4270
|
/** `DVMKIT_PLATFORM_URL` — the platform-hosted signal. */
|
|
4404
4271
|
platformUrl: string | undefined;
|
|
4405
|
-
/** Whether `DVMKIT_PLATFORM_TOKEN` resolved — never the token itself
|
|
4272
|
+
/** Whether `DVMKIT_PLATFORM_TOKEN` resolved — never the token itself. */
|
|
4406
4273
|
hasPlatformToken: boolean;
|
|
4407
4274
|
/** Whether `DVMKIT_DVM_ID` resolved. */
|
|
4408
4275
|
hasDvmId: boolean;
|
|
4409
|
-
/** Whether a Postgres pool is wired. */
|
|
4410
4276
|
databaseConnected: boolean;
|
|
4411
|
-
/** Dev/test boot — exempt from the wiring assertion above (internal-review). */
|
|
4412
4277
|
devMode: boolean;
|
|
4413
4278
|
}
|
|
4414
4279
|
/**
|
|
4415
|
-
* Render the boot banner's `Platform rep.` line
|
|
4280
|
+
* Render the boot banner's `Platform rep.` line.
|
|
4416
4281
|
*
|
|
4417
4282
|
* Lives here, next to the assertion, because `active` has to mirror the
|
|
4418
4283
|
* reporter's own wiring gate in `createDVMServer` below — token **and** dvmId
|
|
4419
4284
|
* **and** database. Miss one and the banner claims a reporter that was never
|
|
4420
4285
|
* constructed.
|
|
4421
4286
|
*
|
|
4422
|
-
*
|
|
4423
|
-
*
|
|
4424
|
-
* assertion (internal-review), making it the one path where partial wiring reaches
|
|
4425
|
-
* this line — say so plainly rather than shouting `misconfigured` at a builder
|
|
4426
|
-
* whose shell merely exports the platform URL.
|
|
4287
|
+
* Partial dev wiring reaches the banner because the boot assertion is exempt;
|
|
4288
|
+
* it reports a dev status rather than a production configuration failure.
|
|
4427
4289
|
*/
|
|
4428
4290
|
declare function revenueReporterBannerState(s: ReporterBannerOpts): string;
|
|
4429
|
-
/** Options for creating the SDK server app. */
|
|
4430
4291
|
interface SDKServerOpts {
|
|
4431
|
-
/** Environment variables for ctx.env. */
|
|
4432
4292
|
env: Record<string, string>;
|
|
4433
|
-
/** KV store for ctx.store. */
|
|
4434
4293
|
store: KVStore;
|
|
4435
|
-
/** Cashu mints accepted for payment. */
|
|
4436
4294
|
mints?: string[];
|
|
4437
4295
|
/** Persistent backing store for job records. Default: in-memory. */
|
|
4438
4296
|
jobStore?: JobStore;
|
|
@@ -4440,7 +4298,6 @@ interface SDKServerOpts {
|
|
|
4440
4298
|
devMode?: boolean;
|
|
4441
4299
|
/** Payment methods this DVM accepts. Default: derived from configured rails — adds "cashu" when `mints` is set, "x402" when `x402` is set, "tempo" when `tempo` is set. */
|
|
4442
4300
|
paymentMethods?: PaymentMethod[];
|
|
4443
|
-
/** x402 stablecoin payment configuration. */
|
|
4444
4301
|
x402?: X402Config;
|
|
4445
4302
|
/** Live facilitator capability gate. Hosts wire this after probing `/supported`. */
|
|
4446
4303
|
x402FacilitatorHealth?: X402FacilitatorHealthLike;
|
|
@@ -4449,13 +4306,13 @@ interface SDKServerOpts {
|
|
|
4449
4306
|
/**
|
|
4450
4307
|
* Tracks already-consumed upfront-flow MPP credentials so the same
|
|
4451
4308
|
* credential can't satisfy `POST /v1/job` twice within its `expires`
|
|
4452
|
-
* window
|
|
4309
|
+
* window. Default: in-memory, bounded, per-process. Swap for a
|
|
4453
4310
|
* shared backend before deploying multiple replicas.
|
|
4454
4311
|
*/
|
|
4455
4312
|
consumedCredentialStore?: ConsumedCredentialStore;
|
|
4456
4313
|
/**
|
|
4457
4314
|
* Tracks already-consumed admin-endpoint nonces so a signed P2PK challenge
|
|
4458
|
-
* can't be replayed within the 5-minute sliding window
|
|
4315
|
+
* can't be replayed within the 5-minute sliding window. Default:
|
|
4459
4316
|
* in-memory, bounded, per-process. Swap for a shared backend before
|
|
4460
4317
|
* deploying multiple replicas.
|
|
4461
4318
|
*/
|
|
@@ -4469,14 +4326,14 @@ interface SDKServerOpts {
|
|
|
4469
4326
|
* registration order.
|
|
4470
4327
|
*/
|
|
4471
4328
|
healthHandler?: (c: Context<AppEnv>) => Response | Promise<Response>;
|
|
4472
|
-
/** Cashu receive mode
|
|
4329
|
+
/** Cashu receive mode. */
|
|
4473
4330
|
cashuMode?: CashuMode;
|
|
4474
4331
|
/**
|
|
4475
|
-
* Builder's NUT-11 P2PK lock pubkey
|
|
4332
|
+
* Builder's NUT-11 P2PK lock pubkey. Used as the seed pubkey for
|
|
4476
4333
|
* `seedLockPubkeyState` on first boot, as the admin-auth identity, and as
|
|
4477
4334
|
* part of the gate for whether `/v1/info` advertises `cashu`. After first
|
|
4478
4335
|
* boot, the authoritative `[current, ...retired]` state lives in the DVM's
|
|
4479
|
-
* Postgres via `dvm_lock_pubkeys`; rotations
|
|
4336
|
+
* Postgres via `dvm_lock_pubkeys`; rotations mutate that store
|
|
4480
4337
|
* directly.
|
|
4481
4338
|
*/
|
|
4482
4339
|
lockPubkey?: string;
|
|
@@ -4485,12 +4342,11 @@ interface SDKServerOpts {
|
|
|
4485
4342
|
/**
|
|
4486
4343
|
* Rotation grace window (seconds) — retired pubkeys remain advertised on
|
|
4487
4344
|
* `cashu.lock_pubkeys` and accepted by the receive path until they age out.
|
|
4488
|
-
*
|
|
4489
|
-
* `BUILDER_LOCK_PUBKEY_GRACE_SECONDS` env (internal-review).
|
|
4345
|
+
* Defaults to 24h.
|
|
4490
4346
|
*/
|
|
4491
4347
|
lockPubkeyGraceSeconds?: number;
|
|
4492
4348
|
/**
|
|
4493
|
-
* Per-DVM canonical identifier
|
|
4349
|
+
* Per-DVM canonical identifier. Advertised on `/v1/info` as
|
|
4494
4350
|
* `cashu.canonical_id` so agents can derive the matching per-DVM refund
|
|
4495
4351
|
* subkey at `m/1789'/2'/hashed(canonical_dvm_id)'`. Defaults to `dvmId`
|
|
4496
4352
|
* (the platform's DVM UUID) when unset — keeps `/v1/info` self-consistent
|
|
@@ -4500,30 +4356,25 @@ interface SDKServerOpts {
|
|
|
4500
4356
|
/** Postgres pool for builder admin state and wallet accumulator persistence. */
|
|
4501
4357
|
db?: Pool;
|
|
4502
4358
|
/**
|
|
4503
|
-
* General SDK strict-mode flag
|
|
4359
|
+
* General SDK strict-mode flag, sourced from `DVMKIT_FAIL_FAST` at
|
|
4504
4360
|
* the host boundary. Reserved for future warning-vs-fatal SDK checks; the
|
|
4505
4361
|
* revenue-reporter boot check is gated on platform context, not on this
|
|
4506
|
-
* flag
|
|
4362
|
+
* flag.
|
|
4507
4363
|
*/
|
|
4508
4364
|
failFast?: boolean;
|
|
4509
|
-
/** Agent-wallet
|
|
4365
|
+
/** Agent-wallet: per-DVM identifier — drives the monitor advisory lock + replay key. */
|
|
4510
4366
|
dvmId?: string;
|
|
4511
4367
|
/**
|
|
4512
|
-
* Runtime mint-health tracker
|
|
4513
|
-
* fresh one whenever `mints?.length > 0`.
|
|
4514
|
-
* `/v1/info
|
|
4515
|
-
*
|
|
4368
|
+
* Runtime mint-health tracker. When omitted, the server builds a
|
|
4369
|
+
* fresh one whenever `mints?.length > 0`. Healthy mints are preferred in
|
|
4370
|
+
* `/v1/info`; when none are healthy, transiently sick mints remain advertised.
|
|
4371
|
+
* NUT-incompatible mints are excluded even from that fallback.
|
|
4516
4372
|
*/
|
|
4517
4373
|
mintHealthTracker?: MintHealthTracker;
|
|
4518
|
-
/**
|
|
4519
|
-
* Shared fx fetcher used to translate USD descriptor prices and fiat-form
|
|
4520
|
-
* `ctx.requestPayment` calls into sats (internal-review). When omitted, the server
|
|
4521
|
-
* builds one from `env.DVMKIT_FX_SOURCE` (or the bundled CoinGecko default).
|
|
4522
|
-
* Tests pass a deterministic stub.
|
|
4523
|
-
*/
|
|
4374
|
+
/** Shared upfront/mid-job fiat conversion fetcher. Defaults to `DVMKIT_FX_SOURCE` or CoinGecko. */
|
|
4524
4375
|
fxFetcher?: FxFetcher;
|
|
4525
4376
|
/**
|
|
4526
|
-
* Platform API token for revenue reporting
|
|
4377
|
+
* Platform API token for revenue reporting. Read from
|
|
4527
4378
|
* `opts.env.DVMKIT_PLATFORM_TOKEN` when not set explicitly. When both
|
|
4528
4379
|
* `platformToken` and `platformUrl` are present alongside `db` and `dvmId`,
|
|
4529
4380
|
* the SDK auto-constructs a `RevenueReporter` and wires it into the
|
|
@@ -4531,35 +4382,32 @@ interface SDKServerOpts {
|
|
|
4531
4382
|
*/
|
|
4532
4383
|
platformToken?: string;
|
|
4533
4384
|
/**
|
|
4534
|
-
* Platform API URL for revenue reporting
|
|
4385
|
+
* Platform API URL for revenue reporting. Read from
|
|
4535
4386
|
* `opts.env.DVMKIT_PLATFORM_URL` when not set explicitly.
|
|
4536
4387
|
*/
|
|
4537
4388
|
platformUrl?: string;
|
|
4538
|
-
/**
|
|
4539
|
-
* Advanced override: callback fired when a paid job completes (internal-review).
|
|
4540
|
-
* When set, bypasses the SDK's automatic `RevenueReporter` wiring — the
|
|
4541
|
-
* caller assumes full responsibility for revenue tracking. Intended for
|
|
4542
|
-
* tests and advanced integrations only.
|
|
4543
|
-
*/
|
|
4389
|
+
/** Observes completed jobs with a positive settled draw; failures are logged and swallowed. */
|
|
4544
4390
|
onPaidJobCompleted?: (event: PaidJobCompletion) => void | Promise<void>;
|
|
4545
4391
|
paymentMode?: PaymentMode;
|
|
4392
|
+
/** Overrides automatic RevenueReporter wiring; the caller owns revenue delivery. */
|
|
4546
4393
|
onJobCompleted?: JobManagerOpts["onJobCompleted"];
|
|
4547
4394
|
/**
|
|
4548
4395
|
* Advanced override for a declared cost on a terminal job that emitted no
|
|
4549
|
-
* revenue report
|
|
4396
|
+
* revenue report. Auto-wired to the reporter's durable
|
|
4550
4397
|
* job-cost path; intended for tests and advanced integrations only.
|
|
4551
4398
|
*/
|
|
4552
4399
|
onJobCost?: JobManagerOpts["onJobCost"];
|
|
4400
|
+
onJobTerminal?: JobManagerOpts["onJobTerminal"];
|
|
4553
4401
|
/**
|
|
4554
4402
|
* Advanced override: callback fired when the stale-job reaper force-fails a
|
|
4555
|
-
* *paid* job
|
|
4403
|
+
* *paid* job. Auto-wired to the `RevenueReporter`'s paid-job-death
|
|
4556
4404
|
* report alongside `onJobCompleted`; set explicitly only for tests / advanced
|
|
4557
4405
|
* integrations that own their own reporting.
|
|
4558
4406
|
*/
|
|
4559
4407
|
onPaidJobDeath?: JobManagerOpts["onPaidJobDeath"];
|
|
4560
4408
|
/**
|
|
4561
4409
|
* Advanced override: callback fired when a settled credit draw could not
|
|
4562
|
-
* book revenue because its rail was unavailable
|
|
4410
|
+
* book revenue because its rail was unavailable. Auto-wired to
|
|
4563
4411
|
* the platform reporter alongside the completion and paid-job-death paths.
|
|
4564
4412
|
*/
|
|
4565
4413
|
onRevenueSkippedNoRail?: (info: RevenueSkippedNoRailPayload) => void | Promise<void>;
|
|
@@ -4568,22 +4416,22 @@ interface SDKServerOpts {
|
|
|
4568
4416
|
/** Advanced clear-on-recovery callback for clean job-retention passes. */
|
|
4569
4417
|
onJobRetentionSweepRecovered?: JobManagerOpts["onJobRetentionSweepRecovered"];
|
|
4570
4418
|
/**
|
|
4571
|
-
* Advanced override: callback fired when a payment funds a credit
|
|
4419
|
+
* Advanced override: callback fired when a payment funds a credit.
|
|
4572
4420
|
* Auto-wired to the `RevenueReporter`'s deposit report alongside
|
|
4573
4421
|
* `onJobCompleted`; set explicitly only for tests / advanced integrations
|
|
4574
4422
|
* that own their own reporting. Fundings are deposits — a liability until
|
|
4575
|
-
* drawn — reported separately from the draws that become revenue (
|
|
4423
|
+
* drawn — reported separately from the draws that become revenue (the deposit-accounting rule).
|
|
4576
4424
|
*/
|
|
4577
4425
|
onCreditFunded?: (info: CreditDepositPayload) => void | Promise<void>;
|
|
4578
4426
|
/**
|
|
4579
4427
|
* Grace window (ms) a reactivating machine waits for a live original handler
|
|
4580
4428
|
* to win the single-execution claim before replaying an `awaiting-input` job
|
|
4581
|
-
*
|
|
4429
|
+
* Defaults to the JobManager's built-in value; exposed as an ops
|
|
4582
4430
|
* override and a test seam.
|
|
4583
4431
|
*/
|
|
4584
4432
|
reactivationClaimGraceMs?: JobManagerOpts["reactivationClaimGraceMs"];
|
|
4585
4433
|
/**
|
|
4586
|
-
* Builder identity for `/v1/info#builder
|
|
4434
|
+
* Builder identity for `/v1/info#builder`. Inline alternative
|
|
4587
4435
|
* to the `DVMKIT_BUILDER_PUBKEY` / `_ATTESTATION` / `_SIGNATURE` env vars
|
|
4588
4436
|
* the platform's deploy pipeline injects as Fly secrets. When set,
|
|
4589
4437
|
* `builder.{pubkey, attestation, signature}` take per-field precedence
|
|
@@ -4591,10 +4439,10 @@ interface SDKServerOpts {
|
|
|
4591
4439
|
* `/v1/info#builder` block to be emitted.
|
|
4592
4440
|
*/
|
|
4593
4441
|
builder?: BuilderIdentity;
|
|
4594
|
-
/** Owner display identity for `/v1/info#owner
|
|
4442
|
+
/** Owner display identity for `/v1/info#owner`. Falls back to env vars. */
|
|
4595
4443
|
owner?: OwnerDisplay;
|
|
4596
4444
|
/**
|
|
4597
|
-
* Per-DVM receipt-signing secret, 32 bytes hex
|
|
4445
|
+
* Per-DVM receipt-signing secret, 32 bytes hex. Resolved at the
|
|
4598
4446
|
* host boundary from `DVMKIT_RECEIPT_KEY` — the deploy pipeline provisions
|
|
4599
4447
|
* it as a Fly secret, derived one-way from the builder identity key and
|
|
4600
4448
|
* bound into the deploy attestation's `receipt_pubkey`.
|
|
@@ -4605,25 +4453,25 @@ interface SDKServerOpts {
|
|
|
4605
4453
|
*/
|
|
4606
4454
|
receiptKey?: string;
|
|
4607
4455
|
/**
|
|
4608
|
-
* Credit ledger every payment funds and draws through
|
|
4456
|
+
* Credit ledger every payment funds and draws through. The host
|
|
4609
4457
|
* passes the Postgres `CreditLedger` when a pool exists; standalone /
|
|
4610
4458
|
* pool-less setups default to a per-server `MemoryCreditLedger` so the
|
|
4611
4459
|
* fund+draw semantics (and receipts' credit block) hold everywhere.
|
|
4612
4460
|
*/
|
|
4613
4461
|
creditLedger?: CreditLedgerLike;
|
|
4614
4462
|
/**
|
|
4615
|
-
* Durable x402/mpp processed-payment markers
|
|
4463
|
+
* Durable x402/mpp processed-payment markers. Defaults to the
|
|
4616
4464
|
* in-memory store; the host passes the Postgres one alongside the ledger.
|
|
4617
4465
|
*/
|
|
4618
4466
|
processedPayments?: ProcessedPaymentStore;
|
|
4619
4467
|
/**
|
|
4620
|
-
* Lightning credit-funding receive leg
|
|
4468
|
+
* Lightning credit-funding receive leg, when the host resolved a
|
|
4621
4469
|
* receive-only NWC connection. Absent ⇒ `lightning` never appears on the
|
|
4622
4470
|
* funding menu and `POST /v1/credit` refuses the method.
|
|
4623
4471
|
*/
|
|
4624
4472
|
lightningReceive?: LightningReceive;
|
|
4625
4473
|
/**
|
|
4626
|
-
* Liveness of the platform's Tempo close observer
|
|
4474
|
+
* Liveness of the platform's Tempo close observer. Absent ⇒ this
|
|
4627
4475
|
* DVM is self-hosted and answers for its own close watching, so nothing is
|
|
4628
4476
|
* withheld. Present and unhealthy ⇒ `tempo/session` is withheld while
|
|
4629
4477
|
* `tempo/charge` stays available.
|
|
@@ -4632,20 +4480,16 @@ interface SDKServerOpts {
|
|
|
4632
4480
|
/** Platform-hosted operator fee-token readiness; absent outside hosted sessions. */
|
|
4633
4481
|
tempoSettlementReadiness?: TempoSettlementReadiness;
|
|
4634
4482
|
/**
|
|
4635
|
-
* Tuning for the credit-expiry sweep
|
|
4483
|
+
* Tuning for the credit-expiry sweep. Omitted, the sweep runs on
|
|
4636
4484
|
* its own defaults whenever the ledger supports it; tests pass `intervalMs: 0`
|
|
4637
4485
|
* and `bootDelayMs: 0` to hold the clock still and drive `runOnce` by hand.
|
|
4638
4486
|
*/
|
|
4639
4487
|
creditExpirySweep?: CreditExpirySweepOpts;
|
|
4640
4488
|
}
|
|
4641
4489
|
/**
|
|
4642
|
-
* Per-descriptor server construction — internal to the SDK after host
|
|
4643
|
-
* consolidation (internal-review). Public callers use `createDVMHost` instead; this
|
|
4644
|
-
* function is what the host invokes per `host.mount(dvm)`.
|
|
4645
|
-
*
|
|
4646
4490
|
* Builds a Hono sub-app for one DVM descriptor, wires payment verification,
|
|
4647
4491
|
* auto-wires `RevenueReporter` when platform env + db + dvmId are present, and
|
|
4648
|
-
* runs the platform-hosted partial-wiring assertion
|
|
4492
|
+
* runs the platform-hosted partial-wiring assertion — gated on
|
|
4649
4493
|
* `DVMKIT_PLATFORM_URL` presence, independent of `DVMKIT_FAIL_FAST`.
|
|
4650
4494
|
*/
|
|
4651
4495
|
declare function createDVMServer<State, InputSchema extends ZodLike | undefined>(descriptor: DVMDescriptor<State, InputSchema>, opts: SDKServerOpts): Promise<{
|
|
@@ -4654,13 +4498,13 @@ declare function createDVMServer<State, InputSchema extends ZodLike | undefined>
|
|
|
4654
4498
|
shutdown: () => Promise<void>;
|
|
4655
4499
|
jobManager: JobManager<State, InputSchema>;
|
|
4656
4500
|
revenueReporterActive: boolean;
|
|
4657
|
-
/** The payout reporter
|
|
4501
|
+
/** The payout reporter, for the host to attach its Tempo readers to. */
|
|
4658
4502
|
payoutReporter?: PayoutReporter;
|
|
4659
4503
|
}>;
|
|
4660
4504
|
/**
|
|
4661
4505
|
* Hono env for the SDK's DVM app. `unknownRoute` is set by
|
|
4662
4506
|
* {@link unknownRouteNotFound} so the `dvm.request` span can classify a 404 as
|
|
4663
|
-
* scanner-probe noise vs a registered route's own 404
|
|
4507
|
+
* scanner-probe noise vs a registered route's own 404.
|
|
4664
4508
|
*
|
|
4665
4509
|
* Exported as a type only, for route modules split out of this file
|
|
4666
4510
|
* (`credit-routes.ts`) — a type-only import erases at compile time, so it
|
|
@@ -4676,7 +4520,7 @@ interface AppEnv {
|
|
|
4676
4520
|
/**
|
|
4677
4521
|
* `notFound` handler for paths that match no registered route — scanner probes
|
|
4678
4522
|
* (`GET /robots.txt`, `/.env`, …). Tags the request context so the `dvm.request`
|
|
4679
|
-
* span downgrades the 404 to `warn`-level probe noise
|
|
4523
|
+
* span downgrades the 404 to `warn`-level probe noise instead of
|
|
4680
4524
|
* crowding out real errors in a `--level error` sweep, and returns a
|
|
4681
4525
|
* machine-parseable JSON 404 (Hono's default is plain text). Registered on both
|
|
4682
4526
|
* the DVM app (standalone `createDVMServer`) and the host app (mounted path):
|
|
@@ -4731,21 +4575,21 @@ interface CreditTerms {
|
|
|
4731
4575
|
lightning_min_micro?: number;
|
|
4732
4576
|
}
|
|
4733
4577
|
/**
|
|
4734
|
-
* The **funding menu** a DVM advertises on `/v1/quote` and in every 402
|
|
4735
|
-
*
|
|
4578
|
+
* The **funding menu** a DVM advertises on `/v1/quote` and in every 402;
|
|
4579
|
+
* this is the snake_case wire shape.
|
|
4736
4580
|
*
|
|
4737
4581
|
* `min_micro` / `max_micro` / `ttl_ms` are the DVM's sizing terms — `max_micro`
|
|
4738
4582
|
* binds the **residual balance**, not a funding amount, so a job priced above
|
|
4739
4583
|
* it still clears as fund-and-immediately-draw. `funding` is the rail list a
|
|
4740
4584
|
* top-up may arrive on — a {@link FundingMethod}, not a `PaymentMethod`, since
|
|
4741
|
-
* `lightning` funds a credit without ever being an attached-proof wire method
|
|
4742
|
-
*
|
|
4743
|
-
*
|
|
4585
|
+
* `lightning` funds a credit without ever being an attached-proof wire method.
|
|
4586
|
+
* A rail whose backing wallet is unreachable drops out and the sale survives
|
|
4587
|
+
* on the others.
|
|
4744
4588
|
*
|
|
4745
4589
|
* The `credit_id` / `balance_micro` / `remaining_micro` / `expiry_ms` echo
|
|
4746
4590
|
* appears only for an authenticated caller who already holds a live credit —
|
|
4747
4591
|
* so one round trip answers "what does this cost, and what do I have left?"
|
|
4748
|
-
*
|
|
4592
|
+
* through the signed credit endpoint.
|
|
4749
4593
|
*/
|
|
4750
4594
|
interface CreditMenu extends CreditTerms {
|
|
4751
4595
|
/**
|
|
@@ -4753,7 +4597,7 @@ interface CreditMenu extends CreditTerms {
|
|
|
4753
4597
|
* available.
|
|
4754
4598
|
*
|
|
4755
4599
|
* `withheld` names an instrument this DVM is configured for and is not
|
|
4756
|
-
* offering at this instant, with the reason
|
|
4600
|
+
* offering at this instant, with the reason. Two audiences read
|
|
4757
4601
|
* it. An agent gets to relay *why* `tempo/session` vanished instead of
|
|
4758
4602
|
* inferring the DVM never had it. And the platform's deploy probe reads
|
|
4759
4603
|
* capability from `methods ∪ withheld`, so a deploy landing during an
|
|
@@ -4764,14 +4608,14 @@ interface CreditMenu extends CreditTerms {
|
|
|
4764
4608
|
*/
|
|
4765
4609
|
/**
|
|
4766
4610
|
* Which x402 flavours a top-up may arrive on, and the chain each rides
|
|
4767
|
-
*
|
|
4611
|
+
* Present exactly when `funding` lists `x402`. Reusable
|
|
4768
4612
|
* `batch-settlement` is admitted by default; one-payment `exact` appears
|
|
4769
4613
|
* only when the builder explicitly accepts its manual refund obligation.
|
|
4770
4614
|
*/
|
|
4771
4615
|
/**
|
|
4772
4616
|
* Smallest funding the `lightning` rail accepts, in 1e-6 units of
|
|
4773
4617
|
* `currency`. Present only when `funding` lists `lightning`. This is the
|
|
4774
|
-
* deployment's sats-physical receive floor
|
|
4618
|
+
* deployment's sats-physical receive floor rendered into fiat
|
|
4775
4619
|
* with a ×{@link LIGHTNING_FLOOR_FX_MARGIN} margin absorbing rate drift
|
|
4776
4620
|
* between menu fetch and funding — issuance enforces the *raw* floor at
|
|
4777
4621
|
* request-time rates, so funding exactly this figure always clears. Rails
|
|
@@ -4798,7 +4642,7 @@ interface BuildCreditMenuArgs {
|
|
|
4798
4642
|
/**
|
|
4799
4643
|
* Currency the menu — and the credit it funds — denominates in: the DVM's
|
|
4800
4644
|
* declared `DVMConfig.currency`, which every call site reads off the single
|
|
4801
|
-
* `DVMServer.creditCurrency()
|
|
4645
|
+
* `DVMServer.creditCurrency()`. Deliberately *not* the currency of
|
|
4802
4646
|
* the response this menu rides on: a quote priced elsewhere is refused before
|
|
4803
4647
|
* it reaches here, so the two agree, and sourcing it from the response is
|
|
4804
4648
|
* what left `POST /v1/credit` — which has no response to read — funding in
|
|
@@ -4819,7 +4663,7 @@ interface BuildCreditMenuArgs {
|
|
|
4819
4663
|
reason: string;
|
|
4820
4664
|
}[];
|
|
4821
4665
|
/**
|
|
4822
|
-
* x402 flavours this DVM can actually accept a top-up on
|
|
4666
|
+
* x402 flavours this DVM can actually accept a top-up on — one
|
|
4823
4667
|
* server read, so what the sub-block advertises is what `/v1/credit` will
|
|
4824
4668
|
* answer a 402 with.
|
|
4825
4669
|
*/
|
|
@@ -4837,7 +4681,7 @@ interface BuildCreditMenuArgs {
|
|
|
4837
4681
|
lightningFundingMinSats?: number;
|
|
4838
4682
|
/**
|
|
4839
4683
|
* Whether this DVM has at least one accepted Cashu mint configured
|
|
4840
|
-
*
|
|
4684
|
+
* Without one, no Bitcoin funding rail may be advertised — see
|
|
4841
4685
|
* the filter in {@link buildCreditMenu}.
|
|
4842
4686
|
*
|
|
4843
4687
|
* Read off the **configured** mint list, never the health-filtered
|
|
@@ -4859,23 +4703,23 @@ interface BuildCreditMenuArgs {
|
|
|
4859
4703
|
* advertise credit. **The refusal ladder is the feature**; each rung exists
|
|
4860
4704
|
* because advertising anyway would misadvertise:
|
|
4861
4705
|
*
|
|
4862
|
-
* 1. **Isolate runtime** (
|
|
4863
|
-
*
|
|
4864
|
-
*
|
|
4865
|
-
*
|
|
4866
|
-
*
|
|
4867
|
-
*
|
|
4706
|
+
* 1. **Isolate runtime** (the isolate-runtime rule) — checked first and unconditionally,
|
|
4707
|
+
* before config is even read. Isolate DVMs have no Postgres of their own;
|
|
4708
|
+
* their only DVM-side persistence is the last-write-wins callback KV, which
|
|
4709
|
+
* the durable-ledger requirement prohibits for credit state. Until isolate
|
|
4710
|
+
* runtimes have durable credit storage, they run implicit N=1 only and their quote never offers
|
|
4711
|
+
* credit so nothing is misadvertised.
|
|
4868
4712
|
* 2. **Not opted in** — no `credit` block on `configureDVM`.
|
|
4869
4713
|
* 3. **No descriptor auth** — a credit belongs to a verified secp256k1 pubkey.
|
|
4870
|
-
*
|
|
4871
|
-
*
|
|
4872
|
-
*
|
|
4873
|
-
*
|
|
4714
|
+
* Without auth the ledger's funder identity degrades to the requester id or
|
|
4715
|
+
* `"anonymous"` (see `resolveLedgerContext`), which would pool every
|
|
4716
|
+
* anonymous caller's money into one balance. Advertising a per-caller
|
|
4717
|
+
* balance on such a DVM would be a lie at best and a disclosure at worst.
|
|
4874
4718
|
* 4. **Non-durable ledger outside devMode** — a `MemoryCreditLedger` is
|
|
4875
|
-
*
|
|
4876
|
-
*
|
|
4719
|
+
* per-process, so a caller funding on machine A and drawing on machine B
|
|
4720
|
+
* gets `credit_not_found`. Advertise only what survives the fleet.
|
|
4877
4721
|
* 5. **Bounds not expressible in `currency`** — an fx outage, or a currency the
|
|
4878
|
-
*
|
|
4722
|
+
* snapshot doesn't carry. Omit rather than quote a number we can't state.
|
|
4879
4723
|
*
|
|
4880
4724
|
* The balance echo is added only when a verified `callerPubkey` holds a live,
|
|
4881
4725
|
* same-currency credit.
|
|
@@ -4892,7 +4736,7 @@ declare function buildCreditMenu(args: BuildCreditMenuArgs): Promise<CreditMenu
|
|
|
4892
4736
|
* Live, same-currency credits with something left in them; largest spendable
|
|
4893
4737
|
* balance wins, newest breaks a tie. That is the credit a caller would in fact
|
|
4894
4738
|
* draw against next. A failed job's released hold leaves a positive implicit
|
|
4895
|
-
* balance, which is real caller money (
|
|
4739
|
+
* balance, which is real caller money (the credit lifecycle contract, no debit on failure) and is
|
|
4896
4740
|
* echoed like any other.
|
|
4897
4741
|
*
|
|
4898
4742
|
* **Requires a verified pubkey.** Never call this with a requester id or
|