@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
TypeScript
/**
* 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>;