Execution
createKaji() builds the one canonical entry point that validates, authorizes, approves, executes, and records every governed capability call.
createKaji() creates the Kaji executor — the one entry point that runs a
capability safely.
import { createKaji, memoryStore } from "@irogane/kaji";
const kaji = createKaji({
store: memoryStore(),
approve: async (request) => requestHumanApproval(request),
timeoutMs: 30_000,
});
const result = await kaji.execute(refund, {
input: { paymentId: "pay_123", amount: 750 },
principalId: user.id,
idempotencyKey: request.id,
signal: request.signal,
});
Options
storeis the requiredExecutionStorethe executor uses to claim idempotency keys and record outcomes. See Idempotency.approveis required only if any capability can setapprovaltotrue. It receives the capability name, principal ID, validated input, and idempotency key, and returns (or throws before returning) an approval decision. See Approval.timeoutMsbounds how long Kaji waits for an execution. It does not roll back a side effect or prove that a remote operation didn’t occur — Kaji simply aborts its execution signal when the timeout elapses. See Timeout.
Request & Context
type ExecutionRequest = {
input: unknown;
principalId: string;
idempotencyKey: string;
signal?: AbortSignal;
};
type ExecutionContext = {
principalId: string;
idempotencyKey: string;
signal: AbortSignal;
};
principalId and idempotencyKey must be non-empty strings, and the caller
always supplies both — Kaji never infers either from process state. The
context signal a capability’s execute receives combines the caller’s
signal with Kaji’s optional timeoutMs, using native AbortSignal
semantics.
Results
execute() resolves with an explicit result for every governed execution.
It does not throw for expected outcomes — only when Kaji cannot establish
the execution boundary at all, such as an invalid executor configuration or
an unrecoverable store error before any execution record exists.
type ExecutionResult<Result> =
| { status: "succeeded"; result: Result; evidence: ExecutionEvidence }
| {
status: "denied" | "rejected" | "failed" | "cancelled";
error: unknown;
evidence: ExecutionEvidence;
}
| { status: "unknown"; error: unknown; evidence: ExecutionEvidence };
| Status | Meaning |
|---|---|
succeeded |
execute returned successfully. |
denied |
Authorization did not allow the action. |
rejected |
Required approval was unavailable or not granted. |
failed |
Kaji knows the action did not start or did not succeed. |
cancelled |
Cancellation prevented side-effect execution. |
unknown |
A side effect may have happened but Kaji cannot prove completion. |
Kaji treats a thrown or rejected execute call as unknown by default —
only application code knows whether its own side effect committed before an
error surfaced. knownFailure(cause) is the one explicit exception: a
capability throws it to assert that a specific failure is definitively
known, so Kaji settles failed instead.
import { knownFailure } from "@irogane/kaji";
execute: async (input, context) => {
try {
return await provider.charge(input);
} catch (cause) {
if (isDefinitelyDeclined(cause)) throw knownFailure(cause);
throw cause; // stays unknown: the side effect may have committed
}
},
Kaji never infers this classification from an error’s class, message, or
status code — only application code that can prove no side effect
committed may throw knownFailure().