Idempotency
A caller-supplied idempotency key identifies one intended operation; the execution store prevents that operation from running twice.
Every execution request carries a caller-supplied idempotencyKey. Kaji
never generates one itself — the caller decides what “one intended
operation” means, and Kaji makes sure that operation only ever runs once.
Claiming
Before authorizing or executing anything, the executor claims the
idempotency key in the configured ExecutionStore:
- No existing claim: the executor proceeds to authorization.
- An existing claim with the same input fingerprint: the caller (or a
concurrent duplicate) receives the same recorded or in-flight outcome —
executedoes not run again. - An existing claim with a different input, capability, or principal:
the request settles
failedas a conflict. One key can’t represent two different actions.
Claiming happens before authorization deliberately: it’s bookkeeping, not the side effect the authorization boundary protects. That way a denied or rejected request still occupies and settles its idempotency key, so a caller retrying a denied request with the same key sees the same denial instead of racing a fresh claim.
Store Contract
type ExecutionClaim = {
capability: string;
principalId: string;
idempotencyKey: string;
inputFingerprint: string;
};
type ClaimResult =
| { status: "claimed"; executionId: string }
| { status: "existing"; outcome: Promise<StoredExecution> }
| { status: "conflict"; executionId: string };
type ExecutionStore = {
claim(claim: ExecutionClaim): Promise<ClaimResult>;
record(execution: StoredExecution): Promise<void>;
};
claim() must be atomic enough that concurrent claims for the same
identity produce exactly one "claimed" result — a check-then-create
sequence can let two callers both believe they claimed first, which is
exactly the race this contract exists to prevent. record() persists the
terminal outcome once it’s known.
memoryStore()
import { memoryStore } from "@irogane/kaji";
const kaji = createKaji({ store: memoryStore() });
memoryStore() is a process-local, in-memory ExecutionStore. It honors
the full claim/record contract, which makes it useful for local
development, tests, and examples — but it has no external persistence and
no durability guarantee. Nothing it records survives a process restart, and
it does not coordinate across processes. Kaji v0 does not ship a database
store, distributed lock, or recovery worker; a production deployment
supplies its own durable ExecutionStore implementation.
Evidence
Every settled execution carries minimal ExecutionEvidence:
type ExecutionEvidence = {
executionId: string;
capability: string;
principalId: string;
idempotencyKey: string;
inputFingerprint: string;
};
This is deliberately not a general audit or event-sourcing record — it’s only what’s needed to identify an execution and detect conflicting idempotency key reuse. Kaji does not store arbitrary application events, conversation transcripts, or full payload history.