once-kernel
Advanced tools
| #!/usr/bin/env node | ||
| /** | ||
| * once-audit — find duplicate effects in records you already have. | ||
| * | ||
| * npx once-kernel-audit records.json --at=created_at --subject=customer,amount --amount=amount --key=idempotency_key | ||
| * | ||
| * No code, no adoption, no store to install. Reads a JSON array (or JSONL) of | ||
| * records exported from wherever you already keep them — a payments table, a | ||
| * send log — and reports which look like the same operation happening more | ||
| * than once. | ||
| * | ||
| * JSON only, deliberately. A CSV parser has quoting and encoding edge cases | ||
| * that are easy to get subtly wrong, and a silent misparse here is the worst | ||
| * possible failure mode: a tool whose whole job is finding money that moved | ||
| * twice must not itself introduce an error nobody notices. Export to JSON | ||
| * first (a spreadsheet's "Export as JSON," or `jq -s .` on JSONL) rather than | ||
| * trust an unaudited CSV reader with financial records. | ||
| * | ||
| * Field mapping is EXPLICIT, never guessed. Auto-detecting which columns | ||
| * mean "the same operation" would be guessing at the one judgment call this | ||
| * whole library exists to get right — get it wrong and you either miss real | ||
| * duplicates or flag legitimate repeats (a recurring subscription looks | ||
| * exactly like a duplicate). You choose the fields; the tool does the | ||
| * comparison honestly once you have. | ||
| */ | ||
| import { readFileSync } from "node:fs"; | ||
| import { findDuplicates, formatAuditReport } from "../dist/audit.js"; | ||
| function parseArgs(argv) { | ||
| const out = { _: [], windowMs: undefined, minAmount: undefined, json: false }; | ||
| const fields = { id: undefined, at: undefined, subject: undefined, amount: undefined, key: undefined }; | ||
| for (const a of argv) { | ||
| if (a === "--json") { out.json = true; continue; } | ||
| if (a === "--help" || a === "-h") { out.help = true; continue; } | ||
| const m = /^--([a-z-]+)=(.*)$/.exec(a); | ||
| if (!m) { out._.push(a); continue; } | ||
| const [, k, v] = m; | ||
| if (k === "id") fields.id = v; | ||
| else if (k === "at") fields.at = v; | ||
| else if (k === "subject") fields.subject = v.split(",").map((s) => s.trim()).filter(Boolean); | ||
| else if (k === "amount") fields.amount = v; | ||
| else if (k === "key") fields.key = v; | ||
| else if (k === "window-hours") out.windowMs = Number(v) * 3600_000; | ||
| else if (k === "min-amount") out.minAmount = Number(v); | ||
| else out._.push(a); | ||
| } | ||
| return { out, fields }; | ||
| } | ||
| const HELP = `once-audit — find duplicate effects in records you already have. | ||
| Usage: | ||
| npx once-kernel-audit <file.json> --at=<field> --subject=<field1,field2,...> [options] | ||
| Required: | ||
| --at=FIELD which field holds the timestamp | ||
| --subject=FIELDS comma-separated fields that define WHAT was done | ||
| (e.g. --subject=customer_id,amount). Leave out | ||
| anything that varies between retries of the SAME | ||
| operation (timestamps, trace ids, retry counters) -- | ||
| leave IN anything that makes two operations | ||
| genuinely different (payee, currency, plan). | ||
| Optional: | ||
| --id=FIELD a human-readable row identifier (default: row index) | ||
| --amount=FIELD numeric field, used to total duplicate exposure | ||
| --key=FIELD an existing idempotency key column, if you have one | ||
| (checked before --subject, and reported at high | ||
| confidence when it matches) | ||
| --window-hours=N treat repeats further apart than this as probably | ||
| legitimate (default 24). A subscription charged | ||
| monthly must not be reported as a duplicate. | ||
| --min-amount=N ignore duplicate groups worth less than this | ||
| --json machine-readable output | ||
| Input file: a JSON array of objects, or JSONL (one object per line). | ||
| Example: | ||
| npx once-kernel-audit payments.json --at=created_at --subject=customer,plan --amount=amount --key=idem_key | ||
| `; | ||
| function loadRecords(path) { | ||
| const raw = readFileSync(path, "utf8"); | ||
| const trimmed = raw.trim(); | ||
| if (!trimmed) throw new Error(`${path} is empty`); | ||
| if (trimmed.startsWith("[")) { | ||
| const parsed = JSON.parse(trimmed); | ||
| if (!Array.isArray(parsed)) throw new Error(`${path} does not contain a JSON array`); | ||
| return parsed; | ||
| } | ||
| // JSONL: one JSON object per non-empty line. | ||
| return trimmed | ||
| .split("\n") | ||
| .map((l) => l.trim()) | ||
| .filter(Boolean) | ||
| .map((line, i) => { | ||
| try { | ||
| return JSON.parse(line); | ||
| } catch (e) { | ||
| throw new Error(`${path}:${i + 1} is not valid JSON (${e.message})`); | ||
| } | ||
| }); | ||
| } | ||
| function main() { | ||
| const { out, fields } = parseArgs(process.argv.slice(2)); | ||
| if (out.help || out._.length === 0) { | ||
| console.log(HELP); | ||
| process.exit(out.help ? 0 : 2); | ||
| } | ||
| const path = out._[0]; | ||
| if (!fields.at || !fields.subject) { | ||
| console.error( | ||
| "once-audit: --at and --subject are required -- guessing which fields identify " + | ||
| "\"the same operation\" is exactly the judgment call this tool exists to get right, " + | ||
| "so it will not auto-detect them.\n\nRun with --help for the field reference.", | ||
| ); | ||
| process.exit(2); | ||
| } | ||
| let raw; | ||
| try { | ||
| raw = loadRecords(path); | ||
| } catch (e) { | ||
| console.error(`once-audit: ${e.message}`); | ||
| process.exit(2); | ||
| } | ||
| const missing = []; | ||
| const records = raw.map((row, i) => { | ||
| if (!(fields.at in row)) missing.push(`row ${i}: missing "${fields.at}"`); | ||
| const subject = {}; | ||
| for (const f of fields.subject) { | ||
| if (!(f in row)) missing.push(`row ${i}: missing subject field "${f}"`); | ||
| subject[f] = row[f]; | ||
| } | ||
| return { | ||
| id: fields.id && row[fields.id] !== undefined ? String(row[fields.id]) : String(i), | ||
| at: row[fields.at], | ||
| subject, | ||
| amount: fields.amount ? Number(row[fields.amount]) : undefined, | ||
| key: fields.key ? row[fields.key] : undefined, | ||
| }; | ||
| }); | ||
| if (missing.length) { | ||
| console.error( | ||
| `once-audit: ${missing.length} row(s) are missing a mapped field -- refusing to guess ` + | ||
| `a value and silently under- or over-count duplicates.\n` + | ||
| missing.slice(0, 10).join("\n") + | ||
| (missing.length > 10 ? `\n...and ${missing.length - 10} more` : ""), | ||
| ); | ||
| process.exit(2); | ||
| } | ||
| const report = findDuplicates(records, { | ||
| windowMs: out.windowMs, | ||
| minAmount: out.minAmount, | ||
| }); | ||
| if (out.json) { | ||
| console.log(JSON.stringify(report, null, 2)); | ||
| } else { | ||
| console.log(formatAuditReport(report)); | ||
| } | ||
| process.exit(0); | ||
| } | ||
| main(); |
+101
| /** | ||
| * One Door — every irreversible action passes a single gate that answers | ||
| * three questions before anything fires: | ||
| * | ||
| * 1. Did this already happen? (once lease — no duplicates) | ||
| * 2. Is there budget left for it? (spend ceiling — no runaway loops) | ||
| * 3. May it run without a human? (clearance policy — no surprises) | ||
| * | ||
| * The three checks share one choke point on purpose. Installed separately, | ||
| * a rate limiter cannot deduplicate, an idempotency layer cannot budget, | ||
| * and a policy engine can do neither atomically. Composed here, the once | ||
| * lease provides the atomicity and the other two ride inside it. | ||
| * | ||
| * Ordering is load-bearing: | ||
| * | ||
| * - Clearance runs FIRST and is pure: an action that may not run alone | ||
| * must not consume budget or claim the key. | ||
| * - The lease runs SECOND: a replay returns the stored result WITHOUT | ||
| * touching the budget — a retry of yesterday's charge costs nothing. | ||
| * - Budget reserves THIRD, before the effect: ten concurrent calls each | ||
| * seeing headroom is the race `SpendLimiter` exists to close. | ||
| * - A failed effect releases both the reservation and the key, so a | ||
| * transient error never strands budget or blocks a legitimate retry. | ||
| */ | ||
| import { Once } from "./kernel.ts"; | ||
| import { SpendLimiter } from "./budget.ts"; | ||
| /** Decides whether an effect may run without a human in the loop. */ | ||
| export interface ClearancePolicy { | ||
| allows(req: DoorRequest): { | ||
| allowed: boolean; | ||
| reason?: string; | ||
| }; | ||
| } | ||
| /** Clears everything. The default — One Door without a policy is dedup+budget. */ | ||
| export declare class AllowAll implements ClearancePolicy { | ||
| allows(): { | ||
| allowed: boolean; | ||
| }; | ||
| } | ||
| /** | ||
| * Clears only listed tools. The deny message names the list so an agent | ||
| * reading the refusal knows the block is policy, not failure. | ||
| */ | ||
| export declare class AllowList implements ClearancePolicy { | ||
| private readonly cleared; | ||
| constructor(cleared: Iterable<string>); | ||
| allows(req: DoorRequest): { | ||
| allowed: boolean; | ||
| reason?: string; | ||
| }; | ||
| } | ||
| export interface DoorRequest { | ||
| /** Idempotency key — what makes two attempts "the same action". */ | ||
| key: string; | ||
| /** Payload fingerprinted for conflict detection (RFC 8785). */ | ||
| payload: unknown; | ||
| /** Spend this action represents. Omit (or 0) for non-metered effects. */ | ||
| amount?: number; | ||
| /** Tool/action name — what clearance policies decide on. */ | ||
| tool?: string; | ||
| /** Human-readable label for refusal messages. Defaults to tool or key. */ | ||
| what?: string; | ||
| } | ||
| export type DoorRefusal = { | ||
| passed: false; | ||
| reason: "not_cleared" | "over_budget" | "conflict"; | ||
| detail: string; | ||
| }; | ||
| export type DoorPass<T> = { | ||
| passed: true; | ||
| /** True when this call did NOT execute — the stored result was returned. */ | ||
| replay: boolean; | ||
| result: T; | ||
| }; | ||
| export type DoorOutcome<T> = DoorPass<T> | DoorRefusal; | ||
| export interface OneDoorOptions { | ||
| once?: Once; | ||
| budget?: SpendLimiter; | ||
| policy?: ClearancePolicy; | ||
| /** How long a concurrent caller waits for the executor's result. */ | ||
| waitTimeoutMs?: number; | ||
| pollMs?: number; | ||
| } | ||
| export declare class OneDoor { | ||
| private readonly once; | ||
| private readonly budget?; | ||
| private readonly policy; | ||
| private readonly waitTimeoutMs; | ||
| private readonly pollMs; | ||
| constructor(opts?: OneDoorOptions); | ||
| /** begin(), but a concurrent in-flight holder is waited out, not thrown. */ | ||
| private claim; | ||
| /** | ||
| * Pass an effect through the door. Exactly one concurrent caller with the | ||
| * same key executes; the rest wait and receive the same result as a replay. | ||
| * Refusals are values, not exceptions — an agent can read the reason and | ||
| * choose its next move. Only the effect's own error is rethrown. | ||
| */ | ||
| pass<T>(req: DoorRequest, effect: () => Promise<T>): Promise<DoorOutcome<T>>; | ||
| } | ||
| //# sourceMappingURL=door.d.ts.map |
| {"version":3,"file":"door.d.ts","sourceRoot":"","sources":["../src/door.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAGL,IAAI,EAIL,MAAM,aAAa,CAAC;AACrB,OAAO,EAAkB,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3D,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAED,kFAAkF;AAClF,qBAAa,QAAS,YAAW,eAAe;IAC9C,MAAM,IAAI;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE;CAG/B;AAED;;;GAGG;AACH,qBAAa,SAAU,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;gBAC1B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC;IAGrC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAQhE;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,KAAK,CAAC;IACd,MAAM,EAAE,aAAa,GAAG,aAAa,GAAG,UAAU,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IACxB,MAAM,EAAE,IAAI,CAAC;IACb,4EAA4E;IAC5E,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,IAAI,GAAE,cAAmB;IAQrC,4EAA4E;YAC9D,KAAK;IAgBnB;;;;;OAKG;IACG,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CAkFnF"} |
+167
| /** | ||
| * One Door — every irreversible action passes a single gate that answers | ||
| * three questions before anything fires: | ||
| * | ||
| * 1. Did this already happen? (once lease — no duplicates) | ||
| * 2. Is there budget left for it? (spend ceiling — no runaway loops) | ||
| * 3. May it run without a human? (clearance policy — no surprises) | ||
| * | ||
| * The three checks share one choke point on purpose. Installed separately, | ||
| * a rate limiter cannot deduplicate, an idempotency layer cannot budget, | ||
| * and a policy engine can do neither atomically. Composed here, the once | ||
| * lease provides the atomicity and the other two ride inside it. | ||
| * | ||
| * Ordering is load-bearing: | ||
| * | ||
| * - Clearance runs FIRST and is pure: an action that may not run alone | ||
| * must not consume budget or claim the key. | ||
| * - The lease runs SECOND: a replay returns the stored result WITHOUT | ||
| * touching the budget — a retry of yesterday's charge costs nothing. | ||
| * - Budget reserves THIRD, before the effect: ten concurrent calls each | ||
| * seeing headroom is the race `SpendLimiter` exists to close. | ||
| * - A failed effect releases both the reservation and the key, so a | ||
| * transient error never strands budget or blocks a legitimate retry. | ||
| */ | ||
| import { IdempotencyConflict, InProgressError, Once, ResultTooLarge, WaitTimeout, } from "./kernel.js"; | ||
| import { BudgetExceeded, SpendLimiter } from "./budget.js"; | ||
| /** Clears everything. The default — One Door without a policy is dedup+budget. */ | ||
| export class AllowAll { | ||
| allows() { | ||
| return { allowed: true }; | ||
| } | ||
| } | ||
| /** | ||
| * Clears only listed tools. The deny message names the list so an agent | ||
| * reading the refusal knows the block is policy, not failure. | ||
| */ | ||
| export class AllowList { | ||
| cleared; | ||
| constructor(cleared) { | ||
| this.cleared = new Set(cleared); | ||
| } | ||
| allows(req) { | ||
| const tool = req.tool ?? ""; | ||
| if (this.cleared.has(tool)) | ||
| return { allowed: true }; | ||
| return { | ||
| allowed: false, | ||
| reason: `tool "${tool || "(unnamed)"}" is not on the clearance list`, | ||
| }; | ||
| } | ||
| } | ||
| export class OneDoor { | ||
| once; | ||
| budget; | ||
| policy; | ||
| waitTimeoutMs; | ||
| pollMs; | ||
| constructor(opts = {}) { | ||
| this.once = opts.once ?? new Once(); | ||
| this.budget = opts.budget; | ||
| this.policy = opts.policy ?? new AllowAll(); | ||
| this.waitTimeoutMs = opts.waitTimeoutMs ?? 30_000; | ||
| this.pollMs = opts.pollMs ?? 50; | ||
| } | ||
| /** begin(), but a concurrent in-flight holder is waited out, not thrown. */ | ||
| async claim(key, payload) { | ||
| const deadline = Date.now() + this.waitTimeoutMs; | ||
| for (;;) { | ||
| try { | ||
| return await this.once.begin(key, payload); | ||
| } | ||
| catch (err) { | ||
| if (err instanceof InProgressError) { | ||
| if (Date.now() >= deadline) | ||
| throw new WaitTimeout(key, this.waitTimeoutMs); | ||
| await new Promise((r) => setTimeout(r, this.pollMs)); | ||
| continue; | ||
| } | ||
| throw err; | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Pass an effect through the door. Exactly one concurrent caller with the | ||
| * same key executes; the rest wait and receive the same result as a replay. | ||
| * Refusals are values, not exceptions — an agent can read the reason and | ||
| * choose its next move. Only the effect's own error is rethrown. | ||
| */ | ||
| async pass(req, effect) { | ||
| const label = req.what ?? req.tool ?? req.key; | ||
| // 1 · Clearance — pure, costs nothing, consumes nothing. | ||
| const verdict = this.policy.allows(req); | ||
| if (!verdict.allowed) { | ||
| return { | ||
| passed: false, | ||
| reason: "not_cleared", | ||
| detail: verdict.reason ?? `"${label}" is not cleared to run without a human`, | ||
| }; | ||
| } | ||
| // 2 · The lease — the atomic heart. A replay never reaches the budget. | ||
| let out; | ||
| try { | ||
| out = await this.claim(req.key, req.payload); | ||
| } | ||
| catch (err) { | ||
| if (err instanceof IdempotencyConflict) { | ||
| return { | ||
| passed: false, | ||
| reason: "conflict", | ||
| detail: `key "${req.key}" was already used with a different payload — refusing to guess which is right`, | ||
| }; | ||
| } | ||
| throw err; | ||
| } | ||
| if (!out.execute) { | ||
| if (out.record.status === "failed") { | ||
| // A hard failure (allowRetry=false) is permanent by the caller's own | ||
| // choice — surfacing it as success would be a lie. | ||
| throw new Error(out.record.error || `"${label}" previously failed permanently`); | ||
| } | ||
| return { passed: true, replay: true, result: out.record.result }; | ||
| } | ||
| const token = out.record.fenceToken; | ||
| // 3 · Budget — reserve BEFORE the effect; free the key if refused. | ||
| let reservation; | ||
| if (this.budget && (req.amount ?? 0) > 0) { | ||
| try { | ||
| reservation = this.budget.reserve({ what: label, amount: req.amount }); | ||
| } | ||
| catch (err) { | ||
| await this.once.fail(req.key, token, `over budget: ${label}`, true); | ||
| if (err instanceof BudgetExceeded) { | ||
| return { passed: false, reason: "over_budget", detail: err.message }; | ||
| } | ||
| throw err; | ||
| } | ||
| } | ||
| // 4 · The effect itself. Its failure is the caller's error, rethrown — | ||
| // but never before both the reservation and the key are freed. | ||
| let result; | ||
| try { | ||
| result = await effect(); | ||
| } | ||
| catch (err) { | ||
| reservation?.release(); | ||
| await this.once.fail(req.key, token, err instanceof Error ? err.message : String(err), true); | ||
| throw err; | ||
| } | ||
| // 5 · Settle: spend committed, result recorded, replays served forever. | ||
| reservation?.settle(); | ||
| try { | ||
| await this.once.complete(req.key, token, result); | ||
| } | ||
| catch (err) { | ||
| if (err instanceof ResultTooLarge) { | ||
| await this.once.complete(req.key, token, { | ||
| __door: "result_too_large_to_store", | ||
| }); | ||
| } | ||
| else { | ||
| throw err; | ||
| } | ||
| } | ||
| return { passed: true, replay: false, result }; | ||
| } | ||
| } | ||
| //# sourceMappingURL=door.js.map |
| {"version":3,"file":"door.js","sourceRoot":"","sources":["../src/door.ts"],"names":[],"mappings":"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"} |
+13
-5
| { | ||
| "name": "once-kernel", | ||
| "version": "0.2.0", | ||
| "description": "Idempotency kernel for side-effecting operations. 1,000 racing callers, exactly one execution \u2014 proven, not asserted.", | ||
| "version": "0.4.0", | ||
| "description": "Idempotency kernel for side-effecting operations. 1,000 racing callers, exactly one execution — proven, not asserted.", | ||
| "keywords": [ | ||
@@ -47,2 +47,6 @@ "idempotency", | ||
| }, | ||
| "./door": { | ||
| "types": "./dist/door.d.ts", | ||
| "default": "./dist/door.js" | ||
| }, | ||
| "./budget": { | ||
@@ -57,4 +61,5 @@ "types": "./dist/budget.d.ts", | ||
| "dist", | ||
| "README.md", | ||
| "LICENSE" | ||
| "LICENSE", | ||
| "bin", | ||
| "README.md" | ||
| ], | ||
@@ -64,3 +69,3 @@ "scripts": { | ||
| "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/guard.test.ts test/capabilities.test.ts test/readme.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/once-audit-cli.test.ts test/door.test.ts", | ||
| "test:storm": "node --experimental-strip-types --no-warnings --test test/storm.test.ts", | ||
@@ -73,3 +78,6 @@ "test:all": "npm run test && npm run test:storm", | ||
| "typescript": "^5.7.0" | ||
| }, | ||
| "bin": { | ||
| "once-kernel-audit": "bin/once-audit.js" | ||
| } | ||
| } |
+48
-0
@@ -109,2 +109,20 @@ # once-kernel | ||
| **No code at all, if you'd rather not write any:** | ||
| ```bash | ||
| npx once-kernel-audit payments.json --at=created_at --subject=customer,plan --amount=amount --key=idem_key | ||
| ``` | ||
| Same function, run against a JSON export straight from your terminal — a | ||
| payments table dumped to JSON, a send log, anything with records in it. | ||
| JSON and JSONL only, deliberately: a CSV parser has quoting and encoding | ||
| edge cases that are easy to get subtly wrong, and a silent misparse here is | ||
| the worst possible failure mode for a tool whose whole job is finding money | ||
| that moved twice. Export to JSON first. | ||
| `--at` and `--subject` are required and never guessed — auto-detecting which | ||
| fields mean "the same operation" would be guessing at the one judgment call | ||
| this tool exists to get right. Run `npx once-kernel-audit --help` for the | ||
| full flag reference, or see `bin/once-audit.js`. | ||
| Results carry a confidence and the reason they were flagged. A monthly | ||
@@ -119,2 +137,32 @@ subscription looks exactly like a duplicate, so spread-out repeats are reported | ||
| ### One Door — three checks, one atomic gate | ||
| Every irreversible action passes a single gate that answers three questions | ||
| before anything fires: **did this already happen? is there budget left? may | ||
| it run without a human?** A rate limiter can't deduplicate, an idempotency | ||
| layer can't budget, a policy engine can't do either atomically — One Door | ||
| composes all three at the same choke point, and a replay never consumes | ||
| budget. | ||
| ```ts | ||
| import { OneDoor, AllowList } from "once-kernel/door"; | ||
| import { SpendLimiter } from "once-kernel/budget"; | ||
| const door = new OneDoor({ | ||
| budget: new SpendLimiter({ limit: 50, windowMs: 86_400_000 }), // $50/day | ||
| policy: new AllowList(["send_email", "create_invoice"]), | ||
| }); | ||
| const out = await door.pass( | ||
| { key: `invoice:${orderId}`, payload: order, amount: 12.5, tool: "create_invoice" }, | ||
| () => stripe.invoices.create(...), | ||
| ); | ||
| // out.passed === true → ran exactly once (or replayed with the same result) | ||
| // out.passed === false → out.reason: "not_cleared" | "over_budget" | "conflict" | ||
| ``` | ||
| Refusals are values, not exceptions — an agent reads the reason and chooses | ||
| its next move. Storm-tested: 50 concurrent passes, one execution, budget | ||
| charged once. | ||
| ### Warn before an unguarded effect | ||
@@ -121,0 +169,0 @@ |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
151530
20.14%32
18.52%1891
28.55%321
17.58%2
Infinity%