UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

100 lines (99 loc) • 5.05 kB
/** * Receipts, and the reciprocal netting built on them. * * **The problem receipts solve.** Until now the lender's word was the only * record of what a borrowed request cost. The lender settles from usage the * borrower never sees a ledger of, and a complete-mode borrower self-reports * spend the lender cannot see at all. Each side simply believed the other. * * A receipt is the lender's signed statement that one request was settled, for * how much, and against what usage. It travels with the usage block, so the * borrower does not have to accept the coin figure — it recomputes the charge * from the response it actually received and compares. `sequence` is contiguous * per grant, so a receipt withheld to hide a charge leaves a hole. * * What it cannot do: an HMAC proves authorship only to the holder of the key, so * a receipt settles a dispute *between the two parties to it* and is worth * nothing to a third. That is the deliberate trade — see `shareSigning.ts`. * * **Netting.** Two nodes that lend to each other otherwise accumulate two * one-way debts that never meet. Netting forgives the overlap: if a borrower has * consumed 300 coins of mine and I have consumed 500 of theirs, 300 cancels on * both sides. It is expressed in *cumulative* totals rather than deltas, so a * replayed or duplicated round nets zero rather than paying twice. * * @module proxy/shareReceipts */ import type { ProxyShareNettingResult, ProxyShareReceipt, ProxyShareStatement, ProxyShareUsage } from "../types/index.js"; /** * Receipts kept per grant. * * Enough that a borrower checking in daily never misses one, bounded so a busy * grant cannot grow the file without limit. A borrower that falls further behind * than this sees a gap, which is the honest answer — the alternative is silently * dropping the evidence and reporting a clean run. */ export declare const RECEIPT_HISTORY_LIMIT = 500; export declare function initShareReceipts(filePath: string): void; /** * Record and sign a settlement. * * Never throws: a receipt that could not be written must not turn a response the * borrower already has into an error. A grant with no receipt secret — one * issued before receipts existed — is skipped rather than signed with nothing. */ export declare function issueShareReceipt(args: { grantId: string; coins: number; usage: ProxyShareUsage; model?: string; balanceAfter: number | null; settledAt?: number; }): Promise<ProxyShareReceipt | undefined>; /** Receipts a borrower has not collected yet. */ export declare function listShareReceipts(grantId: string, since?: number): Promise<ProxyShareReceipt[]>; /** * Check a collected run of receipts against the secret and against itself. * * Three independent questions, because they have different answers: * a signature that does not verify means the receipt did not come from this * lender; a coin figure that disagrees with its own usage block means the * lender's arithmetic (or its price table) differs from ours; a missing * sequence means a charge was never shown to us at all. */ export declare function auditShareReceipts(grantId: string, collected: readonly ProxyShareReceipt[], secret: string | undefined): ProxyShareStatement; /** Cumulative coins a grant has consumed, as its receipts record it. */ export declare function totalReceiptedCoins(grantId: string): Promise<number>; /** Cumulative coins already forgiven on a grant by netting. */ export declare function nettedCoinsFor(grantId: string): Promise<number>; /** * Settle one round of reciprocal netting, from the lender's side. * * Netting forgives the same amount on both sides, so the cumulative total * forgiven is one number the two nodes hold a copy of each. The round is * therefore the overlap that has not been forgiven yet: * * ``` * forgivable = min(coins they consumed of mine, coins I consumed of theirs) * alreadyForgiven = max(my record, their record) * round = max(0, forgivable - alreadyForgiven) * ``` * * **Why the totals and not a delta.** A delta is a number the caller chooses, * and a replayed round would pay it out again. Deriving the round from * cumulative positions makes a replay free by construction: the second call * subtracts the first one's forgiveness and lands on zero. * * **Why `max` over the two records.** They should agree. When they do not — a * round applied on one side and lost on the other — taking the larger forgives * less, which is the direction that cannot hand out coins twice. */ export declare function applyReciprocalNetting(args: { grantId: string; /** Cumulative coins this node has consumed under the *peer's* grant to it. */ consumedFromPeer: number; /** Cumulative coins the peer says it has already forgiven on its side. */ peerAlreadyNetted: number; }): Promise<ProxyShareNettingResult>; /** Drop a grant's receipts and netting position. Used when the grant goes. */ export declare function clearShareReceipts(grantId: string): Promise<void>;