@dvmkit/sdk 0.1.2-rc.7 → 0.1.4-rc.7
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 +121 -8
- package/dist/chunk-BIFLRKMO.js +87 -0
- package/dist/chunk-BQ2NMWKE.js +160 -0
- package/dist/{chunk-LDTWX7JW.js → chunk-BTZY7VPH.js} +13 -1
- package/dist/{chunk-FJDCFHW5.js → chunk-C6JHBLMW.js} +3 -81
- package/dist/{chunk-EXHBXA4U.js → chunk-DBCLBYHP.js} +13 -1
- package/dist/chunk-E4EVGPDX.js +391 -0
- package/dist/chunk-EDDYHZ6W.js +1010 -0
- package/dist/{chunk-2ABMGUDS.js → chunk-EVBK675R.js} +88 -10
- package/dist/{chunk-P4RUVDU7.js → chunk-H2MEFVH6.js} +12 -141
- package/dist/chunk-KXZUCCEY.js +142 -0
- package/dist/{chunk-LWUR4CGG.js → chunk-MLRCSJYX.js} +11 -3
- package/dist/{chunk-N4VTG3KH.js → chunk-QK3VJNCK.js} +930 -3011
- package/dist/chunk-RW5LP57K.js +44 -0
- package/dist/{chunk-6JZIX5WW.js → chunk-SSSZUVWM.js} +178 -8
- package/dist/{chunk-TKA6ZP4M.js → chunk-U6M3ATSG.js} +56 -426
- package/dist/chunk-VRQDX5P4.js +1742 -0
- package/dist/{chunk-JGGI65I3.js → chunk-Z4BNLUZF.js} +1 -150
- package/dist/{credit-ledger-ED6JXKVD.js → credit-ledger-2DFQHNLB.js} +2 -2
- package/dist/{credit-menu-BM4qCD5U.d.ts → credit-menu-C1ezIFlJ.d.ts} +1699 -1941
- package/dist/{fx-C-liI3oY.d.ts → fx-C6dl2LVI.d.ts} +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +8 -4
- package/dist/internal/caller.d.ts +10441 -0
- package/dist/internal/caller.js +10078 -0
- package/dist/internal/index.d.ts +2 -10816
- package/dist/internal/index.js +2 -10130
- package/dist/internal/server.d.ts +404 -0
- package/dist/internal/server.js +202 -0
- package/dist/job-store-DHnW4Cg_.d.ts +591 -0
- package/dist/lightning-backend-Ci1nogk_.d.ts +367 -0
- package/dist/{memory-credit-ledger-XJ5VQEVP.js → memory-credit-ledger-OP24Z2KO.js} +3 -3
- package/dist/{postgres-job-store-J5F4GUWU.js → postgres-job-store-3RAXMNSY.js} +1 -1
- package/dist/{revenue-reporter-JIKUPXOK.js → revenue-reporter-ASZ7SHHH.js} +1 -1
- package/dist/server/index.d.ts +41 -11
- package/dist/server/index.js +93 -69
- package/dist/{job-store-C53VQ5uu.d.ts → step-cache-BLPZNizw.d.ts} +176 -585
- package/dist/{tempo-session-store-DALMRIWN.js → tempo-session-store-2JNOKJGX.js} +2 -2
- package/dist/testing/index.d.ts +25 -3
- package/dist/testing/index.js +36 -3
- package/dist/{usd-gLcJB1ps.d.ts → usd-BgOfZlk6.d.ts} +1 -1
- package/dist/wallet-CJC8lwxx.d.ts +29 -0
- package/dist/{x402-FTG2GRAQ.js → x402-T2C5MX3T.js} +6 -3
- package/package.json +11 -5
- package/dist/{chunk-RU7SXHLO.js → chunk-UP2F5RRT.js} +3 -3
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { FacilitatorConfig } from '@x402/core/server';
|
|
2
|
+
import { aj as PaymentPayload, ap as PaymentRequirementsV1, aq as PaymentRequirementsV2, aE as X402Config, av as SettleResponse, aC as VerifyResponse, bi as X402ExactVersionSupport, bj as X402Receipt, bk as TransactionalPayoutHook, bl as CashuMeltCompleted } from '../step-cache-BLPZNizw.js';
|
|
3
|
+
export { bm as CreditDepositPayload, bn as CreditDrainPayload, bo as FundingLot, bp as LotDebit, bq as LotDepletion, br as MppxServer, bs as NON_CHANNEL_BITCOIN_RAILS, bt as NonChannelBitcoinRail, bu as PayoutReporter, bv as PostgresTempoSessionStore, bw as RevenueReporter, bx as SIGNED_ENVELOPE_FIELDS, by as SIGNED_ENVELOPE_TYPES, bz as X402TrackedChannel, bA as _testing, bB as createDefaultReplayStore, bC as depleteLots, bD as fifoOrder, bE as inKindDrawMsats, bF as isInKindDepletion, bG as isNonChannelBitcoinRail, bH as lotOwedSats, bI as netOwedSats, bJ as wrapMppx } from '../step-cache-BLPZNizw.js';
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
import { Env, BlankSchema } from 'hono/types';
|
|
6
|
+
import { Pool } from 'pg';
|
|
7
|
+
export { Pool } from 'pg';
|
|
8
|
+
import { g as getWallet } from '../wallet-CJC8lwxx.js';
|
|
9
|
+
import { A as AccumulatorPool, a as AccumulatorQuerier, C as ConsumedCredentialStore } from '../credit-menu-C1ezIFlJ.js';
|
|
10
|
+
export { b as AppEnv, c as CreditFundingReport, F as FiatDenomination, d as FiatDenominationFailure, e as FundOnlyRequest, I as IncomingPaymentOpts, J as JobCancelledError, M as MintHealthTracker, P as PAYMENT_PROOF_KEYS, f as PaymentErrorCode, g as PaymentErrorDetail, h as PaymentInfo, R as ReporterBannerOpts, i as ResolvedFx, j as RevenueBootCheckOpts, S as SDKServerOpts, k as ServerJob, l as ShortPayForfeit, T as TempoChannelReport, m as TempoObserverHealth, n as TempoSessionChannelMismatchError, U as UpfrontPaymentOpts, V as VerifiedIncomingPayment, o as VerifyIncomingSnapshot, X as X402BatchChannelObservation, p as X402FacilitatorHealth, q as X402SettlementEvidenceOutcome, r as X402SettlementRepair, s as X402SettlementRepairRefusal, t as X402SettlementRepaired, u as X402WedgedSettlement, v as X402WedgedSettlementPage, w as X402_BATCH_SETTLEMENT_MAINNET_NETWORK, x as abortJob, y as applyPaymentInfoToJob, z as assertRevenueReporterReady, B as buildPaymentErrorResponse, D as clearAccumulatorForDvm, E as createDVMServer, G as creditDepositPayload, H as derivedFundCreditId, K as devModeSkipsPaymentVerification, L as fromJobRecord, N as fundedMicroFor, O as hasPaymentProof, Q as hashLockKey, W as implicitCreditId, Y as initWalletAccumulatorTable, Z as insertAccumulatorRows, _ as isDerivedCreditId, $ as isTerminal, a0 as isYieldMessage, a1 as issueUpfrontChallenges, a2 as msatsToFiatMicro, a3 as paymentErrorBody, a4 as pinAskFiat, a5 as priceFiatMicro, a6 as processIncomingPayment, a7 as providerMessage, a8 as repairX402ExactSettlementEffect, a9 as resolveFxSnapshot, aa as resolvePriceFiat, ab as revenueReporterBannerState, ac as toCreditTerms, ad as toJobRecord, ae as unknownRouteNotFound, af as verifyIncomingPayment, ag as verifyTempoSessionManagementCredential, ah as verifyUpfrontPayment, ai as x402RequiredUsdcMicro, aj as x402SettledShare } from '../credit-menu-C1ezIFlJ.js';
|
|
11
|
+
import { g as LockPubkey } from '../lightning-backend-Ci1nogk_.js';
|
|
12
|
+
export { t as lockPubkeyStoreToLockPubkey } from '../lightning-backend-Ci1nogk_.js';
|
|
13
|
+
export { O as OutgoingMessage, S as StaleJobReapable, i as isStaleJobReapable } from '../job-store-DHnW4Cg_.js';
|
|
14
|
+
import { Store } from 'mppx';
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import '@x402/evm/batch-settlement/server';
|
|
17
|
+
import 'viem';
|
|
18
|
+
import '@x402/core/types';
|
|
19
|
+
import '@cashu/cashu-ts';
|
|
20
|
+
import '../fx-C6dl2LVI.js';
|
|
21
|
+
|
|
22
|
+
/** POST a verify request to the configured facilitator. */
|
|
23
|
+
declare function verifyWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<VerifyResponse>;
|
|
24
|
+
/** POST a settlement request to the configured facilitator. */
|
|
25
|
+
declare function settleWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<SettleResponse>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Verify an x402 `X-PAYMENT` header per the x402 spec (internal-review).
|
|
29
|
+
*
|
|
30
|
+
* Flow:
|
|
31
|
+
* 1. Decode the base64 `PaymentPayload`
|
|
32
|
+
* 2. Rebuild the `PaymentRequirements` the server advertised on the 402 — the
|
|
33
|
+
* facilitator deep-equals these against the payload's authorization, so a
|
|
34
|
+
* credential bought against any other set fails verification.
|
|
35
|
+
* 3. Call the facilitator's `/verify`
|
|
36
|
+
* 4. On valid, call the facilitator's `/settle` to broadcast the EIP-3009
|
|
37
|
+
* `transferWithAuthorization`. The on-chain tx hash flows through to the
|
|
38
|
+
* receipt and into `X-PAYMENT-RESPONSE` for the client.
|
|
39
|
+
*
|
|
40
|
+
* A positive `btcUsdRate` converts the settled USDC back to msats. Zero means
|
|
41
|
+
* the requirement was already stated exactly in USD and no rate was needed;
|
|
42
|
+
* in that case `amountMsats` degrades to zero while `amountUsdcMicro` remains
|
|
43
|
+
* authoritative. Upfront accounting values that native amount against the
|
|
44
|
+
* advertised requirement, so it never books the degraded field.
|
|
45
|
+
*/
|
|
46
|
+
declare function verifyX402Payment(paymentHeader: string, config: X402Config, requiredUsdcMicro: bigint, resource: string, btcUsdRate: number, exactVersions?: X402ExactVersionSupport): Promise<X402Receipt>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Snapshot of a DVM's published lock-pubkey state at a point in time. `current`
|
|
50
|
+
* is what the SDK advertises as `cashu.lock_pubkeys[0]`; `retired` are the
|
|
51
|
+
* pubkeys that have been rotated out but still fall inside the rotation grace
|
|
52
|
+
* window (`cashu.lock_pubkey_grace_seconds`). Both halves are needed by the
|
|
53
|
+
* receive path (accept proofs locked to any entry) and the admin auth verifier
|
|
54
|
+
* (accept signatures from any entry during grace).
|
|
55
|
+
*/
|
|
56
|
+
interface LockPubkeyState {
|
|
57
|
+
current: LockPubkey;
|
|
58
|
+
retired: {
|
|
59
|
+
pubkey: LockPubkey;
|
|
60
|
+
retiredAtMs: number;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Lazy accessor for the DVM's lock-pubkey state. Threaded through the SDK
|
|
65
|
+
* server so call sites (info handler, accept flow, admin auth) read the fresh
|
|
66
|
+
* state on every invocation — rotation propagation has to be visible without
|
|
67
|
+
* a process restart.
|
|
68
|
+
*/
|
|
69
|
+
type LockPubkeyStateLoader = () => Promise<LockPubkeyState>;
|
|
70
|
+
/**
|
|
71
|
+
* Subset of pg.Pool used by the lock-pubkey store. Sharing `AccumulatorPool`
|
|
72
|
+
* keeps the SDK's "pass me a Postgres pool" surface uniform across stores.
|
|
73
|
+
*/
|
|
74
|
+
type LockPubkeyPool = AccumulatorPool;
|
|
75
|
+
/**
|
|
76
|
+
* Create the `dvm_lock_pubkeys` table if absent. Each row is one entry in a
|
|
77
|
+
* DVM's lock-pubkey lifecycle: `retired_at_ms IS NULL` is the current pubkey,
|
|
78
|
+
* any non-null value is a retired pubkey that the rotation walker can still
|
|
79
|
+
* derive privkeys for. One current row per `dvm_id` is enforced via a partial
|
|
80
|
+
* unique index — keeps `rotateLockPubkey` honest under concurrent calls.
|
|
81
|
+
*/
|
|
82
|
+
declare function initLockPubkeyTable(db: LockPubkeyPool): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Idempotent first-boot seed. Inserts `pubkey` as the current row for `dvmId`
|
|
85
|
+
* only when no current row exists. Reseeding with a different pubkey is a
|
|
86
|
+
* no-op — once the DB has state, `rotateLockPubkey` is the only sanctioned
|
|
87
|
+
* mutator, so a stale `DVMKIT_CASHU_LOCK_PUBKEY` env on a redeploy can't
|
|
88
|
+
* silently reset the DVM's view of "what's current".
|
|
89
|
+
*
|
|
90
|
+
* Also backfills any distinct `lock_pubkey` values found in
|
|
91
|
+
* `wallet_accumulator` for this DVM that aren't yet in `dvm_lock_pubkeys`,
|
|
92
|
+
* inserting them as retired rows with `retired_at_ms = 0` — the
|
|
93
|
+
* "long-retired-still-meltable" sentinel. This handles pre-internal-review batches
|
|
94
|
+
* that would otherwise trip the monitor's `orphan_lock_pubkey` alarm every
|
|
95
|
+
* tick. Sentinel-0 rows are exempt from grace-window filtering and
|
|
96
|
+
* `pruneRetired` so they stay visible until the operator melts the legacy
|
|
97
|
+
* batches and the receive path keeps accepting in-flight tokens locked to
|
|
98
|
+
* them.
|
|
99
|
+
*
|
|
100
|
+
* Two simultaneous cold boots can both pass the `WHERE NOT EXISTS` guard, and
|
|
101
|
+
* the loser then takes a 23505 on `dvm_lock_pubkeys_current_uidx` — the shape
|
|
102
|
+
* internal-review fixed for `mpp_secret_state`, reached here through the same
|
|
103
|
+
* first-boot window. Closed the same way, with `ON CONFLICT DO NOTHING` on
|
|
104
|
+
* that partial index: the boot lock (internal-review) serializes the cross-machine
|
|
105
|
+
* half, but two callers on one pool share its acquisition (a fire-and-forget
|
|
106
|
+
* hydrate overlapping boot's), so the guard has to hold without it.
|
|
107
|
+
*/
|
|
108
|
+
declare function seedLockPubkeyState(db: LockPubkeyPool, dvmId: string, pubkey: string, now?: number): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Read the active lock-pubkey state for a DVM. Retired rows older than the
|
|
111
|
+
* grace window are filtered out; aged-out rows linger physically until
|
|
112
|
+
* `pruneRetired` deletes them on a monitor tick. Sentinel-0 rows (the
|
|
113
|
+
* `retired_at_ms = 0` legacy backfill from `seedLockPubkeyState`) are always
|
|
114
|
+
* included regardless of grace. Throws when no current row exists — the SDK's
|
|
115
|
+
* accept/info/admin paths must not run before `seedLockPubkeyState` has been
|
|
116
|
+
* called.
|
|
117
|
+
*/
|
|
118
|
+
declare function loadLockPubkeyState(db: AccumulatorQuerier, dvmId: string, graceMs: number, now?: number): Promise<LockPubkeyState>;
|
|
119
|
+
/**
|
|
120
|
+
* Atomically demote the current pubkey to retired (stamping `retired_at_ms`)
|
|
121
|
+
* and insert `newPubkey` as the new current row. Wrapped in a transaction so
|
|
122
|
+
* the partial unique index never has two current rows visible mid-rotation.
|
|
123
|
+
* Returns the post-rotation state filtered by the supplied grace window.
|
|
124
|
+
*/
|
|
125
|
+
declare function rotateLockPubkey(db: LockPubkeyPool, dvmId: string, newPubkey: string, graceMs: number, now?: number): Promise<LockPubkeyState>;
|
|
126
|
+
/**
|
|
127
|
+
* Delete retired rows whose `retired_at_ms` is older than `now - graceMs`.
|
|
128
|
+
* Called from the monitor tick so aged-out entries don't accumulate.
|
|
129
|
+
* `wallet_accumulator` rows pointing at those pubkeys remain meltable (the
|
|
130
|
+
* builder can still derive the privkey from the mnemonic + rotation_index) —
|
|
131
|
+
* pruning only affects what the DVM advertises and accepts.
|
|
132
|
+
*
|
|
133
|
+
* Sentinel-0 rows (the legacy backfill from `seedLockPubkeyState`) are
|
|
134
|
+
* exempted from pruning — they exist precisely so the monitor keeps treating
|
|
135
|
+
* legacy `wallet_accumulator` batches as retired-still-meltable rather than
|
|
136
|
+
* orphan.
|
|
137
|
+
*/
|
|
138
|
+
declare function pruneRetired(db: AccumulatorQuerier, dvmId: string, graceMs: number, now?: number): Promise<number>;
|
|
139
|
+
/**
|
|
140
|
+
* Default rotation grace window — 24h, per §4.5 of the cashu-integration
|
|
141
|
+
* design note. Operators tune via `BUILDER_LOCK_PUBKEY_GRACE_SECONDS`.
|
|
142
|
+
*/
|
|
143
|
+
declare const DEFAULT_LOCK_PUBKEY_GRACE_SECONDS = 86400;
|
|
144
|
+
/**
|
|
145
|
+
* Lift a single pubkey + grace-filtered retired list into the flat array the
|
|
146
|
+
* accept path checks against. Convenience for call sites that don't care
|
|
147
|
+
* about the retired-row structure.
|
|
148
|
+
*/
|
|
149
|
+
declare function activePubkeySet(state: LockPubkeyState): LockPubkey[];
|
|
150
|
+
|
|
151
|
+
/** Options for `installAdminCashuRoutes`. */
|
|
152
|
+
interface InstallAdminCashuRoutesOpts {
|
|
153
|
+
/** Pool against the SDK's accumulator DB. */
|
|
154
|
+
db: Pool;
|
|
155
|
+
/** Identifier of this DVM — bound into every signed admin challenge. */
|
|
156
|
+
dvmId: string;
|
|
157
|
+
/**
|
|
158
|
+
* Live lock-pubkey state loader. Auth accepts a signature from the current
|
|
159
|
+
* pubkey or any retired pubkey still within the rotation grace window
|
|
160
|
+
* (internal-review). The `/admin/cashu/rotate-lock-pubkey` route narrows that to
|
|
161
|
+
* current-only.
|
|
162
|
+
*/
|
|
163
|
+
getLockPubkeyState: LockPubkeyStateLoader;
|
|
164
|
+
/** Rotation grace window in seconds — threaded into `rotateLockPubkey`. */
|
|
165
|
+
graceSeconds: number;
|
|
166
|
+
/** Replay-protection store keyed by nonce. */
|
|
167
|
+
nonceStore: ConsumedCredentialStore;
|
|
168
|
+
/**
|
|
169
|
+
* Runs inside the transaction that marks the rows melted (internal-review): the
|
|
170
|
+
* proofs are SPENT and the Lightning payment reached the builder, so the
|
|
171
|
+
* melt is a landed payout, and its report commits with the mark or not at
|
|
172
|
+
* all. `enqueue` throwing rolls the mark back and fails the call — the CLI's
|
|
173
|
+
* recovery re-posts `mark-melted` on its next run, where a melt marked with
|
|
174
|
+
* no row would understate paid-out forever. `committed` runs after.
|
|
175
|
+
*/
|
|
176
|
+
onMelted?: TransactionalPayoutHook<CashuMeltCompleted>;
|
|
177
|
+
/** Override for cashu-ts wallet construction (tests). */
|
|
178
|
+
walletProvider?: typeof getWallet;
|
|
179
|
+
/** Override for the current time (tests). Returns ms since epoch. */
|
|
180
|
+
now?: () => number;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Register the four admin endpoints on the supplied Hono app. Idempotent at
|
|
184
|
+
* the call site — `app.ts` only invokes this when accumulator config is
|
|
185
|
+
* complete (`cashuMode === "p2pk-accumulator"`, `db`, `dvmId`, `lockPubkey`).
|
|
186
|
+
*/
|
|
187
|
+
declare function installAdminCashuRoutes<E extends Env = Env, S extends BlankSchema = BlankSchema>(app: Hono<E, S>, opts: InstallAdminCashuRoutesOpts): void;
|
|
188
|
+
/**
|
|
189
|
+
* Convenience constructor for the in-memory nonce store — exported so callers
|
|
190
|
+
* outside `app.ts` (tests, custom wiring) can mint a store with the same caps
|
|
191
|
+
* the SDK uses by default.
|
|
192
|
+
*/
|
|
193
|
+
declare function createAdminCashuNonceStore(): ConsumedCredentialStore;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The `fx_rate_unavailable` payment-error detail, shaped like every other flat
|
|
197
|
+
* payment error (internal-review): a flat machine `error` code plus `message` and the
|
|
198
|
+
* `display`/`hint`/`retryable` fields an agent relays verbatim and branches
|
|
199
|
+
* retries on — the shape the `paymentErrorBody` builder enforces for the sibling
|
|
200
|
+
* 503s (`mint_health_pending`, `cashu_mint_sick`) and the 402 family alike. This
|
|
201
|
+
* one is built by hand rather than through the builder because its copy already
|
|
202
|
+
* lives on `FxRateUnavailableError`, shared with the quote path's nested
|
|
203
|
+
* envelope. 503 rather than 402 — the caller's payment isn't the problem, our
|
|
204
|
+
* pricing is.
|
|
205
|
+
*/
|
|
206
|
+
declare function rateUnavailablePaymentError(): {
|
|
207
|
+
body: Record<string, unknown>;
|
|
208
|
+
status: number;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Create a fetch wrapper that emits `dvm.fetch` spans via `loggers.isolate`.
|
|
213
|
+
*
|
|
214
|
+
* Attributes include the redacted URL, HTTP method, and (post-response)
|
|
215
|
+
* status code. When observability is disabled, `loggers.isolate` is the no-op
|
|
216
|
+
* logger — `span()` calls `fn()` directly with zero overhead.
|
|
217
|
+
*
|
|
218
|
+
* `signal` (internal-review) is the job's cancellation signal. When supplied, every
|
|
219
|
+
* request made through this fetch aborts on job cancel — that is what stops
|
|
220
|
+
* provider spend mid-job. It is *merged* with any caller-supplied
|
|
221
|
+
* `init.signal` rather than replacing it, so a per-request timeout (scrapfly,
|
|
222
|
+
* cast's ranged GET, …) keeps working and either signal can abort the call.
|
|
223
|
+
*/
|
|
224
|
+
declare function createInstrumentedFetch(signal?: AbortSignal): typeof globalThis.fetch;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The subset of Hono's request object these helpers need. Structural rather
|
|
228
|
+
* than `HonoRequest` so both runtimes' `Context` generics pass without either
|
|
229
|
+
* side importing the other's env type.
|
|
230
|
+
*/
|
|
231
|
+
interface JobCredentialRequest {
|
|
232
|
+
header(name: string): string | undefined;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* internal-review: the credential gate on a runtime's `/v1/job/:id/*` routes — the
|
|
236
|
+
* "did this request present anything?" predicate and the 401 body that answers
|
|
237
|
+
* when it didn't, built together so the copy can never describe a credential
|
|
238
|
+
* the predicate doesn't accept.
|
|
239
|
+
*/
|
|
240
|
+
interface JobCredentialGate {
|
|
241
|
+
/** Did this request present *any* credential this runtime accepts? */
|
|
242
|
+
presents(req: JobCredentialRequest): boolean;
|
|
243
|
+
/** The flat 401 body for a request that presented none. */
|
|
244
|
+
body(): Record<string, unknown>;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* internal-review: build the credential gate for one runtime.
|
|
248
|
+
*
|
|
249
|
+
* `acceptsBearer` is what separates them. The isolate tier derives
|
|
250
|
+
* `requesterId` from `Authorization: Bearer`, so a Bearer token really is a
|
|
251
|
+
* job credential there. The SDK server does not — its auth middleware pins
|
|
252
|
+
* `requesterId` to `anonymous`/`dev-anonymous`, so every SDK job is gated on
|
|
253
|
+
* the internal-review `X-Job-Token` and a Bearer header proves nothing. Counting it
|
|
254
|
+
* anyway would send a stale client that happens to send one back to the
|
|
255
|
+
* opaque 404 this exists to eliminate.
|
|
256
|
+
*
|
|
257
|
+
* A request carrying no accepted credential can be refused before the job is
|
|
258
|
+
* ever looked up, which is what makes {@link JobCredentialGate.body} safe to
|
|
259
|
+
* be specific about — the 404-not-403 choice on a *failed* ownership check is
|
|
260
|
+
* deliberate (internal-review: a wrong credential must never confirm a job id exists),
|
|
261
|
+
* and that property is untouched here because this body is returned before any
|
|
262
|
+
* store lookup, identical whether or not the job exists.
|
|
263
|
+
*
|
|
264
|
+
* Shared by `src/sdk/server/app.ts` and the isolate routes so the two runtimes
|
|
265
|
+
* answer identically by construction rather than by comment.
|
|
266
|
+
*/
|
|
267
|
+
declare function jobCredentialGate(opts: {
|
|
268
|
+
acceptsBearer: boolean;
|
|
269
|
+
}): JobCredentialGate;
|
|
270
|
+
|
|
271
|
+
/** Options for `PostgresTempoChargeStore`. */
|
|
272
|
+
interface PostgresTempoChargeStoreOpts {
|
|
273
|
+
/** Table name. Defaults to `"mpp_tempo_charge_replays"`. Must match `/^[a-zA-Z_][a-zA-Z0-9_]*$/`. */
|
|
274
|
+
tableName?: string;
|
|
275
|
+
/**
|
|
276
|
+
* How long a row survives its last write, in milliseconds. Defaults to one
|
|
277
|
+
* hour — see the class doc for why that is both generous and safe.
|
|
278
|
+
*/
|
|
279
|
+
retentionMs?: number;
|
|
280
|
+
/**
|
|
281
|
+
* Probability (per write) of firing a fire-and-forget GC sweep that deletes
|
|
282
|
+
* expired rows. Defaults to 0.01 — every ~100 writes runs one DELETE. Set to
|
|
283
|
+
* 0 to disable GC entirely (rely on an external sweeper).
|
|
284
|
+
*/
|
|
285
|
+
gcSampleRate?: number;
|
|
286
|
+
/** Clock source (ms). Defaults to `Date.now`. Test seam for deterministic TTL. */
|
|
287
|
+
now?: () => number;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Postgres implementation of the atomic store mppx's `tempo.charge` uses for
|
|
291
|
+
* replay protection (internal-review).
|
|
292
|
+
*
|
|
293
|
+
* mppx refuses a charge credential whose transaction hash it has already taken
|
|
294
|
+
* (`markHashUsed`), and refuses a zero-dollar proof credential whose challenge
|
|
295
|
+
* id it has already taken — but only against the store it is handed. Omitting
|
|
296
|
+
* one leaves it on `Store.memory()`: per-process, lost on restart, invisible to
|
|
297
|
+
* sibling machines. A replayed credential then reaches a broadcast the chain
|
|
298
|
+
* rejects instead of the cheap `Transaction hash has already been used`.
|
|
299
|
+
*
|
|
300
|
+
* {@link PostgresTempoSessionStore} cannot serve this keyspace — its `update`
|
|
301
|
+
* asserts a monotonic *channel-state object*, and a replay marker is the number
|
|
302
|
+
* `Date.now()`. Values here are deliberately opaque JSON: mppx writes a number
|
|
303
|
+
* for a replay marker and a `SponsorBudget.State` object for the fee-sponsor
|
|
304
|
+
* budget, which shares this store unprefixed so aggregate in-flight fee
|
|
305
|
+
* exposure is sponsor-wide rather than per-process.
|
|
306
|
+
*
|
|
307
|
+
* **Retention is ours — mppx has no TTL at any layer of `Store`.** Every row
|
|
308
|
+
* carries an `expires_at` refreshed on each write, swept by a sampled in-line
|
|
309
|
+
* DELETE. The window is uniform rather than per-keyspace because it is safe for
|
|
310
|
+
* both kinds of row: a replay marker guards a credential whose transaction is
|
|
311
|
+
* settleable for about twenty-five seconds and whose challenge expires in
|
|
312
|
+
* minutes, and a sponsor-budget reservation is bounded by that same expiring
|
|
313
|
+
* nonce (plus a thirty-second lease while it is `prepared`), so an hour-idle
|
|
314
|
+
* budget row holds only reservations `SponsorBudget.reconcile` would release on
|
|
315
|
+
* sight. Sweeping such a row is equivalent to reconciling it, and the next
|
|
316
|
+
* reservation recreates it. Keeping the rule uniform is what lets this store
|
|
317
|
+
* stay ignorant of mppx's key naming across version bumps.
|
|
318
|
+
*
|
|
319
|
+
* `expires_at` is a GC marker only: reads do not filter on it, so protection
|
|
320
|
+
* lapses when a row is swept and never before.
|
|
321
|
+
*/
|
|
322
|
+
declare class PostgresTempoChargeStore implements Store.AtomicStore {
|
|
323
|
+
private readonly pool;
|
|
324
|
+
private readonly tableName;
|
|
325
|
+
private readonly retentionMs;
|
|
326
|
+
private readonly gcSampleRate;
|
|
327
|
+
private readonly now;
|
|
328
|
+
constructor(pool: Pool, opts?: PostgresTempoChargeStoreOpts);
|
|
329
|
+
/** Run the CREATE TABLE migration. Idempotent — safe to call repeatedly. */
|
|
330
|
+
init(): Promise<void>;
|
|
331
|
+
/** Read one stored value, or `null` when absent — mppx's miss sentinel. */
|
|
332
|
+
get(key: string): Promise<unknown>;
|
|
333
|
+
/** Replace one stored value. */
|
|
334
|
+
put(key: string, value: unknown): Promise<void>;
|
|
335
|
+
/** Delete one stored value — mppx's compensating release on a failed broadcast. */
|
|
336
|
+
delete(key: string): Promise<void>;
|
|
337
|
+
/**
|
|
338
|
+
* Atomic read-modify-write, linearized across every machine sharing the pool.
|
|
339
|
+
*
|
|
340
|
+
* The advisory lock is taken before the row lock because a key's *first*
|
|
341
|
+
* write is the one that matters here: `FOR UPDATE` locks nothing when no row
|
|
342
|
+
* exists, so two machines marking one hash for the first time would both read
|
|
343
|
+
* `null` and both believe they won.
|
|
344
|
+
*
|
|
345
|
+
* Replay keys are unique per credential, so they never contend. The contended
|
|
346
|
+
* key is the fee-sponsor budget — one per `(chain, sponsor)`, read-modified
|
|
347
|
+
* several times per sponsored charge — and serializing it across machines is
|
|
348
|
+
* the point of sharing the store at all. The critical section is a SELECT and
|
|
349
|
+
* an upsert, so a queue of waiters clears in tens of milliseconds; a pooled
|
|
350
|
+
* connection is held for exactly that long and never across a chain call.
|
|
351
|
+
*/
|
|
352
|
+
update<result>(key: string, fn: (current: unknown) => Store.Change<unknown, result>): Promise<result>;
|
|
353
|
+
/** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
|
|
354
|
+
private createTables;
|
|
355
|
+
private maybeSweep;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* `topics[0]` of a `CloseRequested` log.
|
|
360
|
+
*
|
|
361
|
+
* Hard-coded rather than derived, because this module is imported by every
|
|
362
|
+
* DVM server while `viem` is an *optional* peer dependency — computing the
|
|
363
|
+
* selector at import time would make an EVM library mandatory for a DVM that
|
|
364
|
+
* takes no on-chain payments at all. `tempo-close-event.test.ts` pins the
|
|
365
|
+
* constant against viem's own `toEventSelector`, so it cannot drift.
|
|
366
|
+
*/
|
|
367
|
+
declare const TEMPO_CLOSE_REQUESTED_TOPIC = "0xf5a36fc00a96cbb9cf1f8f59299165e1d8ffffe94396d82904b4da524d16bbce";
|
|
368
|
+
/**
|
|
369
|
+
* Maximum wall-clock budget for replacing stale Postgres sockets during one
|
|
370
|
+
* close delivery. The platform wake request allows 30 seconds; retaining a
|
|
371
|
+
* fifteen-second margin leaves receipt verification and the settlement submission
|
|
372
|
+
* room to finish instead of letting database recovery consume the whole wake.
|
|
373
|
+
*/
|
|
374
|
+
declare const TEMPO_CLOSE_DB_RECOVERY_BUDGET_MS = 15000;
|
|
375
|
+
|
|
376
|
+
/** Humanised `{ message, hint }` derived from a ZodError for the `invalid_input`/`invalid_quote_data` envelope. */
|
|
377
|
+
interface HumanizedZodError {
|
|
378
|
+
message: string;
|
|
379
|
+
hint: string;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Render a `ZodError` into the structured `{ message, hint }` fields used by
|
|
383
|
+
* `/v1/job` and `/v1/quote` error envelopes.
|
|
384
|
+
*
|
|
385
|
+
* `message` is a single human-readable line (path: reason; …) that an agent
|
|
386
|
+
* can relay to its caller, capped at {@link MAX_REPORTED_CLAUSES} clauses with
|
|
387
|
+
* the remainder counted rather than printed. `hint` is the most actionable fix
|
|
388
|
+
* derived from the issues — oversized string → chunking guidance, empty
|
|
389
|
+
* required string → "provide non-empty text", everything else → a pointer at
|
|
390
|
+
* `/v1/info`. The hint is picked over **every** issue, not just the clauses
|
|
391
|
+
* that fit: a chunking hint is the one output that tells a caller what to
|
|
392
|
+
* actually do, and losing it because the oversized field sorted fourth is the
|
|
393
|
+
* same class of unhelpful answer this whole path exists to avoid.
|
|
394
|
+
*
|
|
395
|
+
* The `ZodError.message` field is itself a JSON-stringified array of issues
|
|
396
|
+
* (per Zod's default Error contract). Using it raw — as the SDK previously
|
|
397
|
+
* did — leaks the unstructured array into the agent-facing response, which
|
|
398
|
+
* is what internal-review fixes.
|
|
399
|
+
*/
|
|
400
|
+
declare function humanizeZodError(err: z.ZodError, opts: {
|
|
401
|
+
schemaUrl: string;
|
|
402
|
+
}): HumanizedZodError;
|
|
403
|
+
|
|
404
|
+
export { AccumulatorPool, DEFAULT_LOCK_PUBKEY_GRACE_SECONDS, type LockPubkeyPool, type LockPubkeyState, type LockPubkeyStateLoader, LockPubkey as LockPubkeyStoreLockPubkey, PostgresTempoChargeStore, TEMPO_CLOSE_DB_RECOVERY_BUDGET_MS, TEMPO_CLOSE_REQUESTED_TOPIC, activePubkeySet, createAdminCashuNonceStore, createInstrumentedFetch, humanizeZodError, initLockPubkeyTable, installAdminCashuRoutes, jobCredentialGate, loadLockPubkeyState, pruneRetired, rateUnavailablePaymentError, rotateLockPubkey, seedLockPubkeyState, settleWithFacilitator, verifyWithFacilitator, verifyX402Payment };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PostgresTempoSessionStore
|
|
3
|
+
} from "../chunk-UP2F5RRT.js";
|
|
4
|
+
import {
|
|
5
|
+
verifyX402Payment
|
|
6
|
+
} from "../chunk-BIFLRKMO.js";
|
|
7
|
+
import {
|
|
8
|
+
RevenueReporter
|
|
9
|
+
} from "../chunk-EVBK675R.js";
|
|
10
|
+
import {
|
|
11
|
+
PayoutReporter
|
|
12
|
+
} from "../chunk-6BQM7TOW.js";
|
|
13
|
+
import {
|
|
14
|
+
PostgresTempoChargeStore
|
|
15
|
+
} from "../chunk-JZWELPFH.js";
|
|
16
|
+
import "../chunk-YG7G4DPZ.js";
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_LOCK_PUBKEY_GRACE_SECONDS,
|
|
19
|
+
MintHealthTracker,
|
|
20
|
+
PAYMENT_PROOF_KEYS,
|
|
21
|
+
TEMPO_CLOSE_DB_RECOVERY_BUDGET_MS,
|
|
22
|
+
TEMPO_CLOSE_REQUESTED_TOPIC,
|
|
23
|
+
TempoSessionChannelMismatchError,
|
|
24
|
+
X402FacilitatorHealth,
|
|
25
|
+
X402_BATCH_SETTLEMENT_MAINNET_NETWORK,
|
|
26
|
+
activePubkeySet,
|
|
27
|
+
applyPaymentInfoToJob,
|
|
28
|
+
assertRevenueReporterReady,
|
|
29
|
+
buildPaymentErrorResponse,
|
|
30
|
+
clearAccumulatorForDvm,
|
|
31
|
+
createAdminCashuNonceStore,
|
|
32
|
+
createDVMServer,
|
|
33
|
+
creditDepositPayload,
|
|
34
|
+
derivedFundCreditId,
|
|
35
|
+
devModeSkipsPaymentVerification,
|
|
36
|
+
fundedMicroFor,
|
|
37
|
+
hasPaymentProof,
|
|
38
|
+
hashLockKey,
|
|
39
|
+
implicitCreditId,
|
|
40
|
+
initLockPubkeyTable,
|
|
41
|
+
initWalletAccumulatorTable,
|
|
42
|
+
insertAccumulatorRows,
|
|
43
|
+
installAdminCashuRoutes,
|
|
44
|
+
isDerivedCreditId,
|
|
45
|
+
issueUpfrontChallenges,
|
|
46
|
+
jobCredentialGate,
|
|
47
|
+
loadLockPubkeyState,
|
|
48
|
+
msatsToFiatMicro,
|
|
49
|
+
paymentErrorBody,
|
|
50
|
+
pinAskFiat,
|
|
51
|
+
priceFiatMicro,
|
|
52
|
+
processIncomingPayment,
|
|
53
|
+
pruneRetired,
|
|
54
|
+
rateUnavailablePaymentError,
|
|
55
|
+
repairX402ExactSettlementEffect,
|
|
56
|
+
resolveFxSnapshot,
|
|
57
|
+
resolvePriceFiat,
|
|
58
|
+
revenueReporterBannerState,
|
|
59
|
+
rotateLockPubkey,
|
|
60
|
+
seedLockPubkeyState,
|
|
61
|
+
toCreditTerms,
|
|
62
|
+
unknownRouteNotFound,
|
|
63
|
+
verifyIncomingPayment,
|
|
64
|
+
verifyTempoSessionManagementCredential,
|
|
65
|
+
verifyUpfrontPayment,
|
|
66
|
+
x402RequiredUsdcMicro,
|
|
67
|
+
x402SettledShare
|
|
68
|
+
} from "../chunk-QK3VJNCK.js";
|
|
69
|
+
import "../chunk-DMNLFNTW.js";
|
|
70
|
+
import {
|
|
71
|
+
settleWithFacilitator,
|
|
72
|
+
verifyWithFacilitator
|
|
73
|
+
} from "../chunk-BQ2NMWKE.js";
|
|
74
|
+
import "../chunk-DBCLBYHP.js";
|
|
75
|
+
import {
|
|
76
|
+
SIGNED_ENVELOPE_FIELDS,
|
|
77
|
+
SIGNED_ENVELOPE_TYPES,
|
|
78
|
+
createDefaultReplayStore,
|
|
79
|
+
humanizeZodError
|
|
80
|
+
} from "../chunk-H2MEFVH6.js";
|
|
81
|
+
import {
|
|
82
|
+
JobCancelledError,
|
|
83
|
+
abortJob,
|
|
84
|
+
createInstrumentedFetch,
|
|
85
|
+
fromJobRecord,
|
|
86
|
+
isStaleJobReapable,
|
|
87
|
+
isTerminal,
|
|
88
|
+
isYieldMessage,
|
|
89
|
+
providerMessage,
|
|
90
|
+
toJobRecord
|
|
91
|
+
} from "../chunk-U6M3ATSG.js";
|
|
92
|
+
import {
|
|
93
|
+
toLockPubkey
|
|
94
|
+
} from "../chunk-VRQDX5P4.js";
|
|
95
|
+
import "../chunk-Z4BNLUZF.js";
|
|
96
|
+
import "../chunk-5URG56JJ.js";
|
|
97
|
+
import "../chunk-MKI6OVW4.js";
|
|
98
|
+
import "../chunk-KXZUCCEY.js";
|
|
99
|
+
import "../chunk-E4EVGPDX.js";
|
|
100
|
+
import "../chunk-66HGCPBU.js";
|
|
101
|
+
import "../chunk-FUJ36YDV.js";
|
|
102
|
+
import "../chunk-27V2ILSR.js";
|
|
103
|
+
import "../chunk-AT6V3SY7.js";
|
|
104
|
+
import {
|
|
105
|
+
NON_CHANNEL_BITCOIN_RAILS,
|
|
106
|
+
depleteLots,
|
|
107
|
+
fifoOrder,
|
|
108
|
+
inKindDrawMsats,
|
|
109
|
+
isInKindDepletion,
|
|
110
|
+
isNonChannelBitcoinRail,
|
|
111
|
+
lotOwedSats,
|
|
112
|
+
netOwedSats
|
|
113
|
+
} from "../chunk-MLRCSJYX.js";
|
|
114
|
+
import "../chunk-S3XAHZQY.js";
|
|
115
|
+
import "../chunk-C3MTFLC6.js";
|
|
116
|
+
import {
|
|
117
|
+
_testing,
|
|
118
|
+
wrapMppx
|
|
119
|
+
} from "../chunk-5GFED3GJ.js";
|
|
120
|
+
export {
|
|
121
|
+
DEFAULT_LOCK_PUBKEY_GRACE_SECONDS,
|
|
122
|
+
JobCancelledError,
|
|
123
|
+
MintHealthTracker,
|
|
124
|
+
NON_CHANNEL_BITCOIN_RAILS,
|
|
125
|
+
PAYMENT_PROOF_KEYS,
|
|
126
|
+
PayoutReporter,
|
|
127
|
+
PostgresTempoChargeStore,
|
|
128
|
+
PostgresTempoSessionStore,
|
|
129
|
+
RevenueReporter,
|
|
130
|
+
SIGNED_ENVELOPE_FIELDS,
|
|
131
|
+
SIGNED_ENVELOPE_TYPES,
|
|
132
|
+
TEMPO_CLOSE_DB_RECOVERY_BUDGET_MS,
|
|
133
|
+
TEMPO_CLOSE_REQUESTED_TOPIC,
|
|
134
|
+
TempoSessionChannelMismatchError,
|
|
135
|
+
X402FacilitatorHealth,
|
|
136
|
+
X402_BATCH_SETTLEMENT_MAINNET_NETWORK,
|
|
137
|
+
_testing,
|
|
138
|
+
abortJob,
|
|
139
|
+
activePubkeySet,
|
|
140
|
+
applyPaymentInfoToJob,
|
|
141
|
+
assertRevenueReporterReady,
|
|
142
|
+
buildPaymentErrorResponse,
|
|
143
|
+
clearAccumulatorForDvm,
|
|
144
|
+
createAdminCashuNonceStore,
|
|
145
|
+
createDVMServer,
|
|
146
|
+
createDefaultReplayStore,
|
|
147
|
+
createInstrumentedFetch,
|
|
148
|
+
creditDepositPayload,
|
|
149
|
+
depleteLots,
|
|
150
|
+
derivedFundCreditId,
|
|
151
|
+
devModeSkipsPaymentVerification,
|
|
152
|
+
fifoOrder,
|
|
153
|
+
fromJobRecord,
|
|
154
|
+
fundedMicroFor,
|
|
155
|
+
hasPaymentProof,
|
|
156
|
+
hashLockKey,
|
|
157
|
+
humanizeZodError,
|
|
158
|
+
implicitCreditId,
|
|
159
|
+
inKindDrawMsats,
|
|
160
|
+
initLockPubkeyTable,
|
|
161
|
+
initWalletAccumulatorTable,
|
|
162
|
+
insertAccumulatorRows,
|
|
163
|
+
installAdminCashuRoutes,
|
|
164
|
+
isDerivedCreditId,
|
|
165
|
+
isInKindDepletion,
|
|
166
|
+
isNonChannelBitcoinRail,
|
|
167
|
+
isStaleJobReapable,
|
|
168
|
+
isTerminal,
|
|
169
|
+
isYieldMessage,
|
|
170
|
+
issueUpfrontChallenges,
|
|
171
|
+
jobCredentialGate,
|
|
172
|
+
loadLockPubkeyState,
|
|
173
|
+
toLockPubkey as lockPubkeyStoreToLockPubkey,
|
|
174
|
+
lotOwedSats,
|
|
175
|
+
msatsToFiatMicro,
|
|
176
|
+
netOwedSats,
|
|
177
|
+
paymentErrorBody,
|
|
178
|
+
pinAskFiat,
|
|
179
|
+
priceFiatMicro,
|
|
180
|
+
processIncomingPayment,
|
|
181
|
+
providerMessage,
|
|
182
|
+
pruneRetired,
|
|
183
|
+
rateUnavailablePaymentError,
|
|
184
|
+
repairX402ExactSettlementEffect,
|
|
185
|
+
resolveFxSnapshot,
|
|
186
|
+
resolvePriceFiat,
|
|
187
|
+
revenueReporterBannerState,
|
|
188
|
+
rotateLockPubkey,
|
|
189
|
+
seedLockPubkeyState,
|
|
190
|
+
settleWithFacilitator,
|
|
191
|
+
toCreditTerms,
|
|
192
|
+
toJobRecord,
|
|
193
|
+
unknownRouteNotFound,
|
|
194
|
+
verifyIncomingPayment,
|
|
195
|
+
verifyTempoSessionManagementCredential,
|
|
196
|
+
verifyUpfrontPayment,
|
|
197
|
+
verifyWithFacilitator,
|
|
198
|
+
verifyX402Payment,
|
|
199
|
+
wrapMppx,
|
|
200
|
+
x402RequiredUsdcMicro,
|
|
201
|
+
x402SettledShare
|
|
202
|
+
};
|