
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
@usekaval/kaval
Advanced tools
Fact verification for AI agents: before an agent acts, Kaval verifies the facts the action relies on and returns a time-bounded, Ed25519-signed proof your policy can enforce.
Before an AI agent acts, Kaval verifies the facts the action relies on and returns a time-bounded
signed proof your policy can enforce — ALLOW, REVIEW, or BLOCK.
Policy engines decide whether an action is permitted under the rules; Kaval verifies whether the facts those rules depend on are still true.
npm install @usekaval/kaval
This package is ESM-first ("type": "module"). Use import in ESM projects, or dynamic import
in CommonJS:
const { Kaval } = await import("@usekaval/kaval");
CJS require("@usekaval/kaval") needs Node ≥20.19 or ≥22.12 (Node’s native
require(esm) support). On Node 18, use import / await import() instead — engines.node is
>=18 for ESM + fetch, not for CJS require.
audit() builds the proof — the expensive research path. gate() applies it at act time with no
search, parsing, or model call.
import { Kaval, ProofNotFoundError } from "@usekaval/kaval";
const kaval = new Kaval({ apiKey: process.env.KAVAL_API_KEY });
// 1. Build, sign, and persist a complete action-bound proof packet.
const proof = await kaval.audit({
text: "Acme is eligible for a $12,000 refund",
as_of: new Date().toISOString(),
intended_action: "Issue Acme a $12,000 refund",
materiality: "critical",
reversibility: "irreversible",
false_allow_cost_usd: 12_000,
record: { system: "billing", table: "refunds", id: "acme-2026" },
});
// 2. At the exact action boundary, apply the durable proof — cheap and research-free.
try {
const gate = await kaval.gate({
proof_id: proof.proof_id,
material_claim_ids: proof.action_decision.material_claim_ids,
threshold: proof.action_decision.threshold,
action: proof.research_contract.action,
});
if (gate.state !== "current" || gate.decision.decision !== "ALLOW") {
throw new Error("Kaval did not allow the action"); // fail closed
}
} catch (error) {
if (error instanceof ProofNotFoundError) {
// No durable proof matches this proof_id/proof_key — build one with audit() first.
}
throw error;
}
audit() returns the complete typed ProofPacket: atomic claims, policy bindings, immutable source
versions, exact evidence spans, lineage families, claim assessments, calibrated/withheld risk,
provenance, expiry, and an Ed25519 signature (signature.algorithm: "Ed25519", key id like
proof-ed25519-2026-07).
gate() returns { proofId, state, decision, billingClass, proofReused, researchPerformed: false, latencyMs }. state is one of current, not_yet_valid, expired, invalidated,
dependency_changed, integrity_failed, policy_mismatch, or operational_failure. A missing
proof is never a 200 state: the server returns HTTP 404 proof_not_found, which this client throws
as the typed ProofNotFoundError (a KavalError subclass with code: "proof_not_found").
gateAction() remains as an alias for gate().
verify() checks one load-bearing conclusion against its evidence references and returns
valid, invalidated, or could_not_verify plus a signed proof receipt. Production actions
should build proof with audit() and enforce it with gate().
const { status, receipt } = await kaval.verify({
conclusion: "The 2024 International Building Code is the current IBC edition.",
evidence_refs: ["https://codes.iccsafe.org/content/IBC2024V2.0"],
});
status; // "valid" | "invalidated" | "could_not_verify"
receipt.decision; // "ALLOW" | "BLOCK" | "REVIEW"
receipt.reason; // e.g. "All material claims verified against current evidence."
receipt.share_endpoint; // "/v1/proofs/<id>/share"
receipt.packet; // the full signed ProofPacket
receipt.packet.action_decision.expires_at; // expiry lives here, not on the receipt
Each item in evidence_refs (1–20 entries) is either a plain https URL string or a strict
{ url, document_id } object; document_id values must be unique per request. A bare { url }
object without document_id is invalid — pass the plain string instead. The client rejects these
wire-invalid shapes locally before spending a request.
Receipts are Ed25519-signed. Anyone can verify one offline with the open verifier
(@kaval/receipt-verifier in the main Kaval repo) against the published JWK at
GET /v1/proof-verification-keys/:kid — no Kaval account required.
Demo results carry no organizational authority. A production ALLOW requires a customer-bound
action policy and applicable empirical calibration; REVIEW is never permission.
Every billable call automatically sends a fresh UUID Idempotency-Key. If the connection fails
without a trustworthy response, or the API says the operation is still being finalized, the client
retries once with the same key. It does not retry ordinary API errors, rate limits, or terminal 5xx
responses.
Pass your own key when an outer job/retry system needs to keep one logical operation stable:
const operationId = crypto.randomUUID();
const proof = await kaval.audit(
{ text: "Acme is eligible for a $12,000 refund", as_of: new Date().toISOString() },
{ idempotencyKey: operationId },
);
Reuse a key only after an ambiguous/no-response failure. After receiving a terminal response, start
a new key for any new attempt. reportOutcome() and health() are not billable and do not send this
header. If both bounded attempts remain ambiguous, the thrown KavalError or transport error exposes
the generated key as error.idempotencyKey; pass it back explicitly after your own delay to resume
the same operation instead of starting and billing a new one.
All billable methods accept { idempotencyKey?, signal?, timeoutMs? }. The constructor defaults to
a 30-second deadline; override per call or set timeoutMs: null to disable it. Cancellation and
timeout errors retain error.idempotencyKey, because an interrupted billable request can be
ambiguous.
The original currentness API remains available under legacy names — the server still accepts a
belief-freshness body on the same /v1/verify route:
const decision = await kaval.verifyBelief("Acme's CEO is Jane Doe");
if (!decision.act) {
// stale / contradicted — re-fetch before relying on it
}
verifyBelief() returns the verdict plus act — true only when the belief is current and
confident (≥ 0.7 by default; override with minConfidence). mode selects a speed/depth tier
(instant | fast | auto | deep); the deep tier adds a cited explanation. The related legacy
surfaces also still work: check, extractAndCheck, scanStore, monitor, kaval, kavalBatch,
and reportOutcome.
const report = await kaval.scanStore({
beliefs: ["Acme is on the Enterprise plan", "Jane Doe is VP Eng at Acme"],
});
report.riskiest.forEach((r) => console.log(r.belief, "→", r.status));
// …or get pushed the newly-stale ones:
await kaval.monitor({ beliefs, webhook: "https://your-app.com/hooks/stale" });
audit · gate (gateAction alias) · verify · verifyBelief · check · extractAndCheck ·
scanStore · monitor · reportOutcome · kaval · kavalBatch · health. Billable methods
accept a final { idempotencyKey?, signal?, timeoutMs? } request-options argument (kavalBatch
includes it alongside concurrency). Construct with { apiKey, baseUrl?, fetch?, timeoutMs? } —
baseUrl defaults to https://api.usekaval.com. Works in Node 18+, browsers, and edge runtimes
(uses the global fetch).
Env vars: this package does not read KAVAL_BASE_URL from the environment — pass
baseUrl in the constructor (Python SDK and MCP use KAVAL_BASE_URL; the marketing-site proxy
uses KAVAL_API_URL). See the clients README.
The Python client mirrors this surface: pip install kaval.
FAQs
Before an AI agent acts, Kaval verifies the facts the action depends on and returns ALLOW, REVIEW, or BLOCK with an Ed25519-signed receipt.
The npm package @usekaval/kaval receives a total of 216 weekly downloads. As such, @usekaval/kaval popularity was classified as not popular.
We found that @usekaval/kaval demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.