New:Socket for Asana Is Now Available.Learn more
Get Started

once-kernel

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

once-kernel - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+80
dist/audit.d.ts
/**
* Find duplicate effects that ALREADY happened.
*
* Every other part of this library prevents a future loss, which means asking
* someone who has never been burned to restructure their code against a bug
* they cannot see. This module inverts that: point it at records you already
* have — a payments table, a send log, a CSV export — and it tells you what
* fired twice last month.
*
* It requires adopting nothing. There is no store to install and no call site
* to change. That is deliberate: the answer is worth money before the fix is.
*
* The hard part is not finding equal rows. It is deciding what "the same
* operation" means when nobody was tracking one — and being honest about the
* cases where we are guessing. Every result carries the reason it was flagged
* and a confidence, because a wrong duplicate accusation sends someone chasing
* a refund that never happened.
*/
export interface EffectRecord {
/** Anything that identifies the row for a human: an id, a URL, a row number. */
id: string;
/** When it happened. Epoch ms, or anything Date can parse. */
at: number | string | Date;
/**
* The fields that define WHAT was done. Two records with the same subject are
* the same operation — so leave out timestamps, trace ids, retry counters and
* anything else that differs between attempts of one operation.
*/
subject: Record<string, unknown>;
/** Optional: what it cost. Used to total the exposure. */
amount?: number;
/** Optional: an idempotency key, if the system had one. */
key?: string;
}
export type DuplicateReason = "same-key" | "identical-subject-within-window" | "identical-subject";
export interface DuplicateGroup {
reason: DuplicateReason;
/** How much to trust it. Explained in `note`. */
confidence: "high" | "medium" | "low";
note: string;
subjectHash: string;
records: EffectRecord[];
/** Extra occurrences beyond the first — the ones that should not exist. */
duplicateCount: number;
/** Cost of those extra occurrences, if amounts were supplied. */
duplicateAmount?: number;
/** Milliseconds between first and last occurrence. */
spanMs: number;
}
export interface AuditReport {
recordsExamined: number;
groups: DuplicateGroup[];
totalDuplicates: number;
totalDuplicateAmount?: number;
/** Stated in the report itself so a reader cannot mistake it for certainty. */
caveats: string[];
}
export interface AuditOptions {
/**
* Two identical subjects further apart than this are treated as a deliberate
* repeat rather than a duplicate. Default 24h.
*
* This is the judgement call of the whole module. A subscription charged on
* the 1st of each month is identical and legitimate; the same charge twice in
* ten seconds is not. Outside the window we still report, at low confidence,
* clearly labelled — because sometimes it IS a duplicate, and silently hiding
* it would be the worse error.
*/
windowMs?: number;
/** Ignore groups whose duplicates are worth less than this. */
minAmount?: number;
}
/**
* Group records by what they did, and report which groups happened more than
* once. Pure — no I/O, no store, nothing to install.
*/
export declare function findDuplicates(records: EffectRecord[], opts?: AuditOptions): AuditReport;
/** Render a report for a human. Money first, uncertainty stated. */
export declare function formatAuditReport(r: AuditReport): string;
//# sourceMappingURL=audit.d.ts.map
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,iCAAiC,GACjC,mBAAmB,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,iDAAiD;IACjD,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EAAE,EACvB,IAAI,GAAE,YAAiB,GACtB,WAAW,CAoIb;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAyBxD"}
/**
* Find duplicate effects that ALREADY happened.
*
* Every other part of this library prevents a future loss, which means asking
* someone who has never been burned to restructure their code against a bug
* they cannot see. This module inverts that: point it at records you already
* have — a payments table, a send log, a CSV export — and it tells you what
* fired twice last month.
*
* It requires adopting nothing. There is no store to install and no call site
* to change. That is deliberate: the answer is worth money before the fix is.
*
* The hard part is not finding equal rows. It is deciding what "the same
* operation" means when nobody was tracking one — and being honest about the
* cases where we are guessing. Every result carries the reason it was flagged
* and a confidence, because a wrong duplicate accusation sends someone chasing
* a refund that never happened.
*/
import { payloadHashHex } from "./canonical.js";
const ms = (v) => v instanceof Date ? v.getTime() : typeof v === "number" ? v : Date.parse(v);
/**
* Group records by what they did, and report which groups happened more than
* once. Pure — no I/O, no store, nothing to install.
*/
export function findDuplicates(records, opts = {}) {
const windowMs = opts.windowMs ?? 24 * 60 * 60 * 1000;
// An explicit key, where one exists, beats any inference we could make.
const byKey = new Map();
const bySubject = new Map();
for (const r of records) {
if (r.key) {
const g = byKey.get(r.key) ?? [];
g.push(r);
byKey.set(r.key, g);
}
else {
const h = payloadHashHex(r.subject);
const g = bySubject.get(h) ?? [];
g.push(r);
bySubject.set(h, g);
}
}
const groups = [];
const make = (recs, reason, confidence, note, hash) => {
const sorted = [...recs].sort((a, b) => ms(a.at) - ms(b.at));
const extra = sorted.slice(1);
const amounts = extra.map((r) => r.amount).filter((a) => typeof a === "number");
return {
reason,
confidence,
note,
subjectHash: hash,
records: sorted,
duplicateCount: extra.length,
duplicateAmount: amounts.length ? amounts.reduce((a, b) => a + b, 0) : undefined,
spanMs: ms(sorted[sorted.length - 1].at) - ms(sorted[0].at),
};
};
for (const [key, recs] of byKey) {
if (recs.length < 2)
continue;
groups.push(make(recs, "same-key", "high", `The system's own idempotency key ${JSON.stringify(key)} appears on ` +
`${recs.length} separate effects. If the key means what it says, this is a duplicate.`, key));
}
for (const [hash, recs] of bySubject) {
if (recs.length < 2)
continue;
const sorted = [...recs].sort((a, b) => ms(a.at) - ms(b.at));
// Split the group at gaps larger than the window, so a monthly charge
// becomes twelve legitimate singles rather than one twelve-fold duplicate.
let run = [sorted[0]];
const runs = [];
for (let i = 1; i < sorted.length; i++) {
if (ms(sorted[i].at) - ms(sorted[i - 1].at) <= windowMs)
run.push(sorted[i]);
else {
runs.push(run);
run = [sorted[i]];
}
}
runs.push(run);
const clustered = runs.filter((r) => r.length >= 2);
for (const r of clustered) {
groups.push(make(r, "identical-subject-within-window", "medium", `${r.length} effects with an identical subject inside ${Math.round(windowMs / 3600000)}h. ` +
`No idempotency key was recorded, so this is inferred from the payload — ` +
`check that nothing distinguishing was left out of \`subject\`.`, hash));
}
// Repeats spread beyond the window: reported, but flagged low, because a
// recurring charge looks exactly like this and usually is not a bug.
if (clustered.length === 0 && runs.length >= 2) {
groups.push(make(sorted, "identical-subject", "low", `${sorted.length} identical effects spread over time. This is what a legitimate ` +
`recurring charge looks like — reported so you can rule it out, not because it is wrong.`, hash));
}
}
const filtered = opts.minAmount === undefined
? groups
: groups.filter((g) => (g.duplicateAmount ?? 0) >= opts.minAmount);
const rank = { high: 0, medium: 1, low: 2 };
filtered.sort((a, b) => rank[a.confidence] - rank[b.confidence] ||
(b.duplicateAmount ?? 0) - (a.duplicateAmount ?? 0) ||
b.duplicateCount - a.duplicateCount);
const amounts = filtered
.map((g) => g.duplicateAmount)
.filter((a) => typeof a === "number");
return {
recordsExamined: records.length,
groups: filtered,
totalDuplicates: filtered.reduce((n, g) => n + g.duplicateCount, 0),
totalDuplicateAmount: amounts.length ? amounts.reduce((a, b) => a + b, 0) : undefined,
caveats: [
"A duplicate here means two records described the same operation — not that money definitely moved twice. Confirm against the provider before acting.",
"Anything left out of `subject` makes two different operations look identical. Anything varying that was left IN hides real duplicates.",
"Low-confidence groups are usually legitimate recurring activity. They are shown so you can rule them out.",
],
};
}
/** Render a report for a human. Money first, uncertainty stated. */
export function formatAuditReport(r) {
const out = [];
out.push(`Examined ${r.recordsExamined} records.`);
if (r.groups.length === 0) {
out.push("No duplicate effects found.");
out.push("That is this check finding nothing — not proof the system is safe.");
return out.join("\n");
}
out.push(`Found ${r.totalDuplicates} duplicate occurrence(s) across ${r.groups.length} group(s)` +
(r.totalDuplicateAmount !== undefined
? `, worth ${r.totalDuplicateAmount} in repeated effects.`
: "."));
out.push("");
for (const g of r.groups.slice(0, 20)) {
const amt = g.duplicateAmount !== undefined ? ` (${g.duplicateAmount})` : "";
out.push(`[${g.confidence}] ${g.duplicateCount} extra${amt} — ${g.reason}`);
out.push(` ${g.note}`);
out.push(` ids: ${g.records.map((x) => x.id).join(", ")}`);
out.push("");
}
out.push("Before acting:");
for (const c of r.caveats)
out.push(` · ${c}`);
return out.join("\n");
}
//# sourceMappingURL=audit.js.map
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
/**
* A spending ceiling at the same choke point as the fence.
*
* If you are already intercepting every money-moving call in order to
* deduplicate it, you are one line away from bounding what it may spend. The
* insertion point is identical; only the question differs. That matters
* commercially as much as technically: it is a second reason to install, aimed
* at exactly the people who care about the first.
*
* The failure this prevents is not a duplicate. It is an agent doing something
* individually reasonable, repeatedly, until the money is gone. No single call
* looks wrong — the ceiling is the only thing that sees the pattern.
*
* Two decisions worth stating outright:
*
* 1. **It refuses rather than warns.** The fence's guard warns because
* blocking an effect you cannot prove is unsafe would be worse than the
* bug. A ceiling is different: the caller set the number, so enforcing it
* is doing what they asked.
*
* 2. **It reserves before the call and settles after.** Checking a total
* afterwards is a time-of-check-to-time-of-use race — ten concurrent calls
* each see room and every one proceeds. Reserve first, and the tenth is
* refused while the ninth is still in flight.
*/
export interface BudgetWindow {
/** Ceiling for this window, in whatever unit you spend in. */
limit: number;
/** Window length in ms. Rolling, not calendar-aligned. */
windowMs: number;
}
export interface SpendAttempt {
/** What is being spent on. Surfaced in the refusal. */
what: string;
amount: number;
context?: Record<string, string | number>;
}
export declare class BudgetExceeded extends Error {
readonly limit: number;
readonly spent: number;
readonly attempted: number;
readonly windowMs: number;
readonly resetsInMs: number;
constructor(a: {
what: string;
limit: number;
spent: number;
attempted: number;
windowMs: number;
resetsInMs: number;
});
}
export declare class SpendLimiter {
private readonly limit;
private readonly windowMs;
private entries;
constructor(w: BudgetWindow);
private prune;
/** Committed spend in the current window — reservations included. */
spent(now?: number): number;
remaining(now?: number): number;
/**
* Reserve headroom BEFORE the call. Throws if it would breach the ceiling.
*
* Returns a handle: `settle()` if it went through, `release()` if it did not.
* A reservation that is never resolved simply expires with the window, so a
* crash costs you conservatism rather than a stuck ceiling.
*/
reserve(a: SpendAttempt): {
settle: () => void;
release: () => void;
};
/**
* Run `fn` only if it fits under the ceiling. Releases the reservation if it
* throws, so a failed call does not consume budget it never spent.
*/
run<T>(a: SpendAttempt, fn: () => T | Promise<T>): Promise<T>;
/** Test-only: forget all spend. */
reset(): void;
}
//# sourceMappingURL=budget.d.ts.map
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB;CAeF;AAQD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,OAAO,CAAe;gBAElB,CAAC,EAAE,YAAY;IAO3B,OAAO,CAAC,KAAK;IAKb,qEAAqE;IACrE,KAAK,CAAC,GAAG,SAAa,GAAG,MAAM;IAK/B,SAAS,CAAC,GAAG,SAAa,GAAG,MAAM;IAInC;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,EAAE,YAAY,GAAG;QAAE,MAAM,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE;IA+BrE;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAYnE,mCAAmC;IACnC,KAAK,IAAI,IAAI;CAGd"}
/**
* A spending ceiling at the same choke point as the fence.
*
* If you are already intercepting every money-moving call in order to
* deduplicate it, you are one line away from bounding what it may spend. The
* insertion point is identical; only the question differs. That matters
* commercially as much as technically: it is a second reason to install, aimed
* at exactly the people who care about the first.
*
* The failure this prevents is not a duplicate. It is an agent doing something
* individually reasonable, repeatedly, until the money is gone. No single call
* looks wrong — the ceiling is the only thing that sees the pattern.
*
* Two decisions worth stating outright:
*
* 1. **It refuses rather than warns.** The fence's guard warns because
* blocking an effect you cannot prove is unsafe would be worse than the
* bug. A ceiling is different: the caller set the number, so enforcing it
* is doing what they asked.
*
* 2. **It reserves before the call and settles after.** Checking a total
* afterwards is a time-of-check-to-time-of-use race — ten concurrent calls
* each see room and every one proceeds. Reserve first, and the tenth is
* refused while the ninth is still in flight.
*/
export class BudgetExceeded extends Error {
limit;
spent;
attempted;
windowMs;
resetsInMs;
constructor(a) {
super(`refusing "${a.what}": ${a.attempted} would take spending to ` +
`${a.spent + a.attempted} against a ceiling of ${a.limit} per ` +
`${Math.round(a.windowMs / 60000)} minutes. ` +
`${a.spent} already committed; the window frees up in ` +
`${Math.round(a.resetsInMs / 1000)}s.`);
this.name = "BudgetExceeded";
this.limit = a.limit;
this.spent = a.spent;
this.attempted = a.attempted;
this.windowMs = a.windowMs;
this.resetsInMs = a.resetsInMs;
}
}
export class SpendLimiter {
limit;
windowMs;
entries = [];
constructor(w) {
if (!(w.limit > 0))
throw new Error("budget limit must be positive");
if (!(w.windowMs > 0))
throw new Error("budget window must be positive");
this.limit = w.limit;
this.windowMs = w.windowMs;
}
prune(now = Date.now()) {
const cutoff = now - this.windowMs;
this.entries = this.entries.filter((e) => e.at > cutoff);
}
/** Committed spend in the current window — reservations included. */
spent(now = Date.now()) {
this.prune(now);
return this.entries.reduce((n, e) => n + e.amount, 0);
}
remaining(now = Date.now()) {
return Math.max(0, this.limit - this.spent(now));
}
/**
* Reserve headroom BEFORE the call. Throws if it would breach the ceiling.
*
* Returns a handle: `settle()` if it went through, `release()` if it did not.
* A reservation that is never resolved simply expires with the window, so a
* crash costs you conservatism rather than a stuck ceiling.
*/
reserve(a) {
if (!(a.amount >= 0))
throw new Error("spend amount must be >= 0");
const now = Date.now();
const spent = this.spent(now);
if (spent + a.amount > this.limit) {
const oldest = this.entries[0]?.at ?? now;
throw new BudgetExceeded({
what: a.what,
limit: this.limit,
spent,
attempted: a.amount,
windowMs: this.windowMs,
resetsInMs: Math.max(0, oldest + this.windowMs - now),
});
}
const entry = { at: now, amount: a.amount, settled: false };
this.entries.push(entry);
return {
settle: () => {
entry.settled = true;
},
release: () => {
const i = this.entries.indexOf(entry);
if (i >= 0 && !entry.settled)
this.entries.splice(i, 1);
},
};
}
/**
* Run `fn` only if it fits under the ceiling. Releases the reservation if it
* throws, so a failed call does not consume budget it never spent.
*/
async run(a, fn) {
const h = this.reserve(a);
try {
const out = await fn();
h.settle();
return out;
}
catch (e) {
h.release();
throw e;
}
}
/** Test-only: forget all spend. */
reset() {
this.entries = [];
}
}
//# sourceMappingURL=budget.js.map
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"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"}
/**
* `guardEffect` — shout at the moment an irreversible effect is about to run
* without a dedup key.
*
* The idea came from a maintainer reviewing one of our findings, and it is the
* sharpest thing anyone has said about this problem: a caller-addressable
* idempotency key only protects you if the *other side* honours it, and today a
* caller has no way to know whether it does. So the safe default for any client
* is to assume retries are unsafe — and almost nobody does, because nothing
* tells them.
*
* Every other part of this library helps someone who already knows they have
* the problem. This part is for the far larger group who do not. It is the
* difference between a tool you reach for and a tool that reaches you.
*
* It deliberately does NOT prevent the call. A library that silently blocked
* effects would be worse than the bug: teams would rip it out. It raises a
* flag at exactly the moment a supervisor wants one, and gets out of the way.
*/
export type Reversibility = "irreversible" | "reversible" | "unknown";
export interface EffectDescriptor {
/** What this does, in the caller's words. Used in the warning. */
what: string;
/** Whether undoing it is possible. Money and messages are irreversible. */
reversibility?: Reversibility;
/** The dedup key, if the caller has one. Its ABSENCE is the whole point. */
idempotencyKey?: string;
/**
* Whether the endpoint has *stated* that it deduplicates.
*
* Not whether you believe it does — whether it said so. An x402 challenge
* carrying an `idempotency` block, a documented `Idempotency-Key` header, a
* provider that publishes the semantics. If you are guessing, this is
* `"unknown"`, and that is the case worth warning about.
*/
endpointDeduplicates?: "declared" | "declared-absent" | "unknown";
/** Free-form, surfaced in the warning: endpoint, amount, recipient. */
context?: Record<string, string | number>;
}
export interface GuardOptions {
/** Where warnings go. Defaults to console.warn. */
onWarn?: (warning: EffectWarning) => void;
/**
* Throw instead of warning. Off by default and it should stay off in most
* systems — see the note at the top about libraries that block effects.
*/
strict?: boolean;
/** Warn at most once per distinct reason+what. Default true. */
dedupeWarnings?: boolean;
}
export interface EffectWarning {
reason: "no-key-no-disclosure" | "no-key" | "no-disclosure";
what: string;
message: string;
context?: Record<string, string | number>;
}
export declare class UnguardedEffectError extends Error {
readonly warning: EffectWarning;
constructor(w: EffectWarning);
}
/**
* Check an effect before running it. Returns the warning it raised, or null.
*
* ```ts
* guardEffect({
* what: "charge card",
* reversibility: "irreversible",
* idempotencyKey: paymentId, // undefined is the case that warns
* endpointDeduplicates: "unknown",
* context: { amount: "49.00", to: "acct_123" },
* });
* ```
*/
export declare function guardEffect(d: EffectDescriptor, opts?: GuardOptions): EffectWarning | null;
/** Wrap a function so the check runs immediately before it. */
export declare function withGuard<A extends unknown[], R>(d: EffectDescriptor, fn: (...args: A) => R, opts?: GuardOptions): (...args: A) => R;
/** Test-only: forget which warnings have already been shown. */
export declare function resetGuardWarnings(): void;
//# sourceMappingURL=guard.d.ts.map
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,YAAY,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,UAAU,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAClE,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,sBAAsB,GAAG,QAAQ,GAAG,eAAe,CAAC;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;gBACpB,CAAC,EAAE,aAAa;CAK7B;AA+CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,gBAAgB,EACnB,IAAI,GAAE,YAAiB,GACtB,aAAa,GAAG,IAAI,CAatB;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAC9C,CAAC,EAAE,gBAAgB,EACnB,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,EACrB,IAAI,CAAC,EAAE,YAAY,GAClB,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAKnB;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
/**
* `guardEffect` — shout at the moment an irreversible effect is about to run
* without a dedup key.
*
* The idea came from a maintainer reviewing one of our findings, and it is the
* sharpest thing anyone has said about this problem: a caller-addressable
* idempotency key only protects you if the *other side* honours it, and today a
* caller has no way to know whether it does. So the safe default for any client
* is to assume retries are unsafe — and almost nobody does, because nothing
* tells them.
*
* Every other part of this library helps someone who already knows they have
* the problem. This part is for the far larger group who do not. It is the
* difference between a tool you reach for and a tool that reaches you.
*
* It deliberately does NOT prevent the call. A library that silently blocked
* effects would be worse than the bug: teams would rip it out. It raises a
* flag at exactly the moment a supervisor wants one, and gets out of the way.
*/
export class UnguardedEffectError extends Error {
warning;
constructor(w) {
super(w.message);
this.name = "UnguardedEffectError";
this.warning = w;
}
}
const seen = new Set();
function build(d) {
const irreversible = (d.reversibility ?? "unknown") !== "reversible";
if (!irreversible)
return null;
const hasKey = typeof d.idempotencyKey === "string" && d.idempotencyKey.length > 0;
const disclosure = d.endpointDeduplicates ?? "unknown";
const endpointSaysYes = disclosure === "declared";
// Both present: the caller controls dedup and the endpoint honours it.
if (hasKey && endpointSaysYes)
return null;
// Nothing to say if the caller has a key AND we simply don't know about the
// endpoint — that is the normal, reasonable case and warning on it would
// make this noise, which is how warnings get switched off.
if (hasKey && disclosure === "unknown")
return null;
const reason = !hasKey && !endpointSaysYes ? "no-key-no-disclosure" : !hasKey ? "no-key" : "no-disclosure";
const lines = [
`About to run an irreversible effect without a dedup guarantee: ${d.what}`,
];
if (!hasKey) {
lines.push(` · no idempotency key was supplied, so a retry cannot be recognised as the same operation`);
}
if (disclosure === "declared-absent") {
lines.push(` · the endpoint has stated that it does NOT deduplicate`);
}
else if (!endpointSaysYes) {
lines.push(` · the endpoint has not stated whether it deduplicates, so assume it does not`);
}
lines.push(` If this call is retried after a timeout, the effect may happen twice and look like one success.`);
if (d.context) {
for (const [k, v] of Object.entries(d.context))
lines.push(` ${k}: ${v}`);
}
return { reason, what: d.what, message: lines.join("\n"), context: d.context };
}
/**
* Check an effect before running it. Returns the warning it raised, or null.
*
* ```ts
* guardEffect({
* what: "charge card",
* reversibility: "irreversible",
* idempotencyKey: paymentId, // undefined is the case that warns
* endpointDeduplicates: "unknown",
* context: { amount: "49.00", to: "acct_123" },
* });
* ```
*/
export function guardEffect(d, opts = {}) {
const w = build(d);
if (!w)
return null;
if (opts.dedupeWarnings !== false) {
const k = `${w.reason}:${w.what}`;
if (seen.has(k))
return w;
seen.add(k);
}
if (opts.strict)
throw new UnguardedEffectError(w);
(opts.onWarn ?? ((x) => console.warn(`[once] ${x.message}`)))(w);
return w;
}
/** Wrap a function so the check runs immediately before it. */
export function withGuard(d, fn, opts) {
return (...args) => {
guardEffect(d, opts);
return fn(...args);
};
}
/** Test-only: forget which warnings have already been shown. */
export function resetGuardWarnings() {
seen.clear();
}
//# sourceMappingURL=guard.js.map
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
+39
-0

@@ -62,2 +62,15 @@ /**

export declare function normalizeKey(key: string): string;
/** Evidence that an operation ran, and what it ran on. */
export interface Receipt {
key: string;
status: Status;
/** SHA-256 of the RFC 8785 canonical payload — proves WHAT ran. */
payloadHash: string;
/** 1 normally. Higher means a worker died mid-flight and another took over. */
generation: number;
firstSeenAt: string;
settledAt: string;
result?: unknown;
error?: string;
}
export interface Outcome {

@@ -122,2 +135,28 @@ /** True for exactly one caller: the one that must run the effect. */

/**
* Has this operation already happened? Answers WITHOUT running anything.
*
* Until now the only way to find out was to attempt the operation, which is
* useless during reconciliation: "did we already refund order 4471?" is a
* question ops and finance ask daily, and attempting a refund to find out is
* not an acceptable way to answer it.
*
* Returns `"unknown"` when there is no record — which is genuinely different
* from "it did not happen". A record can expire, or the operation may predate
* the fence. Saying "no" there would be a lie.
*/
status(key: string): Promise<{
state: "completed" | "failed" | "in_progress" | "unknown";
record?: Record_;
}>;
/**
* A receipt proving what happened for this key, or null if nothing is known.
*
* For anyone in payments or regulated work, evidence is worth more than
* prevention: "show me this refund was issued exactly once" is an audit
* question with money attached to the answer. The payload hash makes the
* receipt content-addressed — you can prove WHAT ran, not merely that
* something did.
*/
receipt(key: string): Promise<Receipt | null>;
/**
* begin → fn() → complete/fail, with concurrent callers coalesced onto the

@@ -124,0 +163,0 @@ * single execution rather than racing or erroring.

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"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"}

@@ -233,2 +233,45 @@ /**

/**
* Has this operation already happened? Answers WITHOUT running anything.
*
* Until now the only way to find out was to attempt the operation, which is
* useless during reconciliation: "did we already refund order 4471?" is a
* question ops and finance ask daily, and attempting a refund to find out is
* not an acceptable way to answer it.
*
* Returns `"unknown"` when there is no record — which is genuinely different
* from "it did not happen". A record can expire, or the operation may predate
* the fence. Saying "no" there would be a lie.
*/
async status(key) {
const rec = await this.store.get(normalizeKey(key));
if (!rec)
return { state: "unknown" };
return { state: rec.status, record: rec };
}
/**
* A receipt proving what happened for this key, or null if nothing is known.
*
* For anyone in payments or regulated work, evidence is worth more than
* prevention: "show me this refund was issued exactly once" is an audit
* question with money attached to the answer. The payload hash makes the
* receipt content-addressed — you can prove WHAT ran, not merely that
* something did.
*/
async receipt(key) {
const rec = await this.store.get(normalizeKey(key));
if (!rec)
return null;
return {
key: rec.key,
status: rec.status,
payloadHash: rec.payloadHash,
/** Increments on every reclaim; >1 means a worker died and was replaced. */
generation: rec.generation,
firstSeenAt: new Date(rec.createdAt * 1000).toISOString(),
settledAt: new Date(rec.updatedAt * 1000).toISOString(),
result: rec.result,
error: rec.error,
};
}
/**
* begin → fn() → complete/fail, with concurrent callers coalesced onto the

@@ -235,0 +278,0 @@ * single execution rather than racing or erroring.

@@ -1,1 +0,1 @@

{"version":3,"file":"kernel.js","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"kernel.js","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"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"}
{
"name": "once-kernel",
"version": "0.1.0",
"description": "Idempotency kernel for side-effecting operations. 1,000 racing callers, exactly one execution — proven, not asserted.",
"version": "0.2.0",
"description": "Idempotency kernel for side-effecting operations. 1,000 racing callers, exactly one execution \u2014 proven, not asserted.",
"keywords": [

@@ -38,2 +38,14 @@ "idempotency",

"default": "./dist/stores/sqlite.js"
},
"./guard": {
"types": "./dist/guard.d.ts",
"default": "./dist/guard.js"
},
"./audit": {
"types": "./dist/audit.d.ts",
"default": "./dist/audit.js"
},
"./budget": {
"types": "./dist/budget.d.ts",
"default": "./dist/budget.js"
}

@@ -51,3 +63,3 @@ },

"clean": "rm -rf dist",
"test": "node --experimental-strip-types --no-warnings --test test/canonical.test.ts test/kernel.test.ts test/store-conformance.test.ts",
"test": "node --experimental-strip-types --no-warnings --test test/canonical.test.ts test/kernel.test.ts test/store-conformance.test.ts test/guard.test.ts test/capabilities.test.ts test/readme.test.ts",
"test:storm": "node --experimental-strip-types --no-warnings --test test/storm.test.ts",

@@ -54,0 +66,0 @@ "test:all": "npm run test && npm run test:storm",

+194
-66
# once-kernel
**Idempotency for side effects that cost money. 1,000 racing callers, exactly one execution — proven on every commit, not asserted in a README.**
**Stop side effects happening twice — and find the ones that already did.**

@@ -9,2 +9,25 @@ ```bash

Zero runtime dependencies. No build step. Node 22.5+.
---
## The problem
An agent retries a call after a timeout. It doesn't know whether the first
attempt landed — the request reached the server, the work happened, the response
never came back.
Nothing failed loudly. It succeeded twice.
That's the easy half. The hard half is the worker that dies **between** reserving
a key and finishing the effect. An in-memory `Set` leaves that key claimed
forever and the effect never runs at all. Restart the process and the `Set` is
empty, so it runs twice.
---
## Four things this does
### 1. Run something exactly once
```ts

@@ -16,6 +39,5 @@ import { Once } from "once-kernel";

// Called once, twice, or by fifty racing workers — the card is charged once.
const receipt = await once.run(
`charge:${orderId}`, // the key
{ amount: 4900, currency: "usd" }, // the payload — part of the identity
`charge:${orderId}`, // the key
{ amount: 4900, currency: "usd" }, // the payload is part of the identity
() => stripe.charges.create({ amount: 4900, currency: "usd" }),

@@ -25,63 +47,135 @@ );

Every caller gets the same `receipt`. The charge happens once.
Fifty racing callers, one charge. Every caller gets the same `receipt`.
### 2. Ask whether something already happened — without doing it
```ts
const { state } = await once.status(`refund:${orderId}`);
// "completed" | "failed" | "in_progress" | "unknown"
```
"Did we already refund order 4471?" is a question ops asks daily. Until now the
only way to answer it was to attempt the refund.
`"unknown"` means *no record*, which is **not** the same as "it didn't happen" —
records expire, and operations can predate the fence. Saying "no" there would be
a lie somebody refunds a customer twice on.
### 3. Prove what ran
```ts
const r = await once.receipt(`charge:${orderId}`);
// { status, payloadHash, generation, firstSeenAt, settledAt, result }
```
`payloadHash` is a SHA-256 of the canonical payload, so a receipt proves **what**
ran, not merely that something did. `generation > 1` means a worker died
mid-flight and another took over — the visible trace of a crash you'd otherwise
never see.
For regulated or payments work, evidence is usually worth more than prevention.
### 4. Find duplicates that already happened
No adoption required. Point it at records you already have.
```ts
import { findDuplicates, formatAuditReport } from "once-kernel/audit";
const report = findDuplicates(
rows.map((r) => ({
id: r.id,
at: r.created_at,
subject: { customer: r.customer_id, amount: r.amount }, // what was DONE
amount: r.amount,
key: r.idempotency_key, // if the system had one
})),
);
console.log(formatAuditReport(report));
```
```
Examined 12,480 records.
Found 3 duplicate occurrence(s) across 2 group(s), worth 348 in repeated effects.
[high] 1 extra (49) — same-key
The system's own idempotency key "idem_88f2" appears on 2 separate effects.
ids: ch_4471, ch_4472
```
**`subject` is the whole game.** It should contain what makes two operations
*different* and nothing that varies between attempts of the same one — no
timestamps, no trace ids, no retry counters. Leave something out and unrelated
operations look identical; leave something varying in and real duplicates hide.
Results carry a confidence and the reason they were flagged. A monthly
subscription looks exactly like a duplicate, so spread-out repeats are reported
at **low** confidence and clearly labelled, rather than sent to someone as a
finding.
---
## Why this exists
## Also included
The bug is not two simultaneous calls. That one is easy and everybody's in-memory
`Set` handles it.
### Warn before an unguarded effect
The bug is the worker that dies **between** reserving the key and finishing the
effect. A `Set` leaves that key claimed forever and the effect never runs at all.
Or the process restarts, the `Set` is empty, and the effect runs twice.
```ts
import { guardEffect } from "once-kernel/guard";
Retries are the other half. An agent whose request times out will re-send it. The
server already did the work; the response just never arrived. Nothing failed
loudly — it succeeded twice.
guardEffect({
what: "charge card",
reversibility: "irreversible",
idempotencyKey: paymentId, // undefined is the case that warns
endpointDeduplicates: "unknown", // has the endpoint SAID it dedupes?
});
```
`once` handles both: a durable record, a lease that expires if you crash, and a
fence token so the worker that stalled cannot overwrite the one that replaced it.
Warns when you're about to cause something irreversible with no dedup key
against an endpoint that has never claimed to deduplicate. It **does not block
the call** — a library that silently blocked effects would get ripped out. It
raises a flag at the moment a supervisor wants one.
## What it guarantees
### Cap what can be spent
- **Exactly one execution per key.** Verified by 1,000 racers across real OS
threads, released simultaneously by an `Atomics` barrier, against one shared
database. The execution count is read from a *separate table*, not
self-reported. Ten cold runs, ten times one execution.
- **Every caller gets the winner's result** — not an error, not `undefined`.
- **A crash cannot strand a key.** The lease expires and the work proceeds, with
the `generation` counter advancing so downstream systems can fence the dead worker.
- **Same key + different payload is a conflict, not a dedupe.** Guessing which
body wins is how money moves twice.
- **Key order in your JSON is irrelevant.** `{a,b}` and `{b,a}` are one operation —
which matters when an LLM reformats its arguments between retries.
```ts
import { SpendLimiter } from "once-kernel/budget";
## What it does *not* do
const budget = new SpendLimiter({ limit: 500, windowMs: 24 * 60 * 60 * 1000 });
await budget.run({ what: "pay supplier", amount: 49 }, () => pay(49));
```
- It cannot make a non-idempotent remote API idempotent. If your provider charges
twice for two distinct requests, `once` stops the second request from being
*sent* — it cannot un-charge one that was.
- The default `MemoryStore` is single-process and dies with your program. That is
fine for tests and wrong for production. Use `SqliteStore`, or implement the
four-method `Store` interface against Postgres.
- It is not a queue, a scheduler, or a retry library.
The failure this catches isn't a duplicate — it's an agent doing something
individually reasonable, repeatedly, until the money is gone. Headroom is
**reserved before the call** and settled after, because checking a running total
afterwards is a race where ten concurrent calls all see room and all proceed.
## Cross-language compatibility
---
This is a port of [`once-kernel` on PyPI](https://pypi.org/project/once-kernel/),
and it hashes payloads **identically**: both use RFC 8785 (JSON Canonicalization
Scheme). A Python service and a Node service can key the same operation and agree
about whether it already ran.
## What it guarantees
That claim is tested, not hoped for — `test/vectors/python-jcs-vectors.json` is
*generated by the Python implementation* and asserted against on every commit.
Hand-written expectations would only prove this file agrees with itself.
- **Exactly one execution per key.** 1,000 racers across real OS threads,
released simultaneously by an `Atomics` barrier, against one shared database.
The count is read from a *separate table*, not self-reported. Ten cold runs,
ten times one execution — and it runs on every commit in CI.
- **Every caller gets the winner's result.** Not an error, not `undefined`.
- **A crash cannot strand a key.** The lease expires, work proceeds, and
`generation` advances so downstream systems can fence the dead worker.
- **Same key + different payload is a conflict, not a dedupe.** Guessing which
body wins is how money moves twice.
- **Key order in your JSON is irrelevant.** `{a,b}` and `{b,a}` are one
operation — which matters when an LLM reformats its arguments between retries.
### Large integers
## What it does not do
JavaScript has one number type. By the time `once` sees `1234567890123456789`,
the parser has already rounded it to `1234567890123456800` — the precision is
gone before this library is called. **Pass large identifiers as strings.**
- It cannot make a non-idempotent remote API idempotent. It stops the second
request being *sent*; it cannot un-charge one that was.
- Not exactly-once *delivery*. That's physically impossible and anyone claiming
it is selling you something. This is exactly-once **execution**.
- The default `MemoryStore` is single-process and dies with your program — fine
for tests, wrong for production. Use `SqliteStore`.
- Not a queue, a scheduler, or a retry library.
---
## API

@@ -93,3 +187,3 @@

defaultTtlSec, // how long a completed record is remembered
defaultLeaseSec = 30, // how long before a crashed worker's key is reclaimed
defaultLeaseSec = 30, // before a crashed worker's key is reclaimed
maxResultBytes = 65536,

@@ -99,9 +193,11 @@ });

await once.run(key, payload, fn, { ttlSec, leaseSec, waitTimeoutMs, pollMs });
await once.status(key);
await once.receipt(key);
```
Lower-level, if you need to control the boundary yourself:
Lower level, if you need the boundary yourself:
```ts
const { execute, record } = await once.begin(key, payload);
if (!execute) return record.result; // someone else already did it
if (!execute) return record.result; // someone else already did it
try {

@@ -123,28 +219,60 @@ const result = await doTheThing();

|---|---|
| `IdempotencyConflict` | Same key, different payload. A caller bug — do not retry blindly. |
| `InProgressError` | Another caller holds the key right now. `run()` waits for you. |
| `IdempotencyConflict` | Same key, different payload. A caller bug — don't retry blindly. |
| `InProgressError` | Another caller holds the key. `run()` waits for you. |
| `WaitTimeout` | Waited past `waitTimeoutMs` for an in-flight call to settle. |
| `ResultTooLarge` | Result exceeds `maxResultBytes`. Store it elsewhere, keep a reference. |
| `CanonicalizationError` | Payload contains something JSON cannot represent (`NaN`, `undefined`, `Date`, `BigInt`). Rejected rather than silently coerced — coercion is how two payloads collide into one hash. |
| `ResultTooLarge` | Over `maxResultBytes`. Store it elsewhere, keep a reference. |
| `CanonicalizationError` | Payload holds something JSON can't represent (`NaN`, `undefined`, `Date`, `BigInt`). Rejected rather than coerced — coercion is how two payloads collide into one hash. |
| `BudgetExceeded` | The spend ceiling refused the call. Says what, how much, and when it frees up. |
### Entry points
| Import | For |
|---|---|
| `once-kernel` | `Once`, errors, `MemoryStore` |
| `once-kernel/sqlite` | `SqliteStore` — durable, use this in production |
| `once-kernel/audit` | `findDuplicates`, `formatAuditReport` |
| `once-kernel/budget` | `SpendLimiter` |
| `once-kernel/guard` | `guardEffect`, `withGuard` |
## Storage
`SqliteStore` uses Node's built-in `node:sqlite`. **This package has zero runtime
dependencies.**
`SqliteStore` uses Node's built-in `node:sqlite`. Every mutation is a single SQL
statement with its guard in the `WHERE` clause, so two processes racing the same
key resolve inside the database engine — reading then writing in JavaScript
would be the exact time-of-check-to-time-of-use race this library exists to
prevent.
Every mutation is a single SQL statement with its guard in the `WHERE` clause, so
two processes racing the same key resolve inside the database engine. Reading and
then writing in JavaScript would be a time-of-check-to-time-of-use race — exactly
the bug this library exists to prevent.
To use Postgres or Redis, implement `Store`: `get`, `createInProgress`,
For Postgres or Redis, implement `Store`: `get`, `createInProgress`,
`casComplete`, `casFail`, `reclaimIfLeaseDead`, `heartbeat`. Run
`test/store-conformance.test.ts` against it; that suite is the contract.
`test/store-conformance.test.ts` against it — that suite *is* the contract.
## Requirements
## Cross-language
Node 22.5+ (for `node:sqlite`). No build step, no native modules, no dependencies.
A port of [`once-kernel` on PyPI](https://pypi.org/project/once-kernel/), hashing
payloads **identically** via RFC 8785. A Python service and a Node service can
key the same operation and agree about whether it already ran.
Tested, not hoped for: `test/vectors/python-jcs-vectors.json` is *generated by
the Python implementation* and asserted against on every commit. Hand-written
expectations would only prove this file agrees with itself.
**Large integers:** JavaScript has one number type, and by the time `once` sees
`1234567890123456789` the parser has already rounded it. Pass large identifiers
as strings.
## Related
[`fencescan`](https://www.npmjs.com/package/fencescan) — `npx fencescan` finds
tool calls in a codebase that could fire twice.
[`effectfence`](https://github.com/aurumflux20/effectfence) — the same guarantee
as an MCP server.
## Commercial support
Free and Apache-2.0, staying that way. If you want help applying it to a codebase
that already moves money, email **hello@aurumflux.co** —
[the Fence Audit](https://github.com/aurumflux20/effectfence/blob/main/SUPPORT.md).
## Licence
Apache-2.0