What is Audkit?
Tamper-evident audit logging for TypeScript apps and AI agents — every event sequenced and committed to an RFC 6962 Merkle tree before the API answers.
Audkit is tamper-evident audit logging for TypeScript apps and AI agents.
Every event is sequenced and committed as a leaf of your project's
RFC 6962 Merkle tree, whose root is
Ed25519-signed and published hourly to a public transparency log. There is no API to
edit or delete a record — and if anyone rewrites history out-of-band (a rogue DBA, a
doctored backup), verify() rebuilds the tree and names the exact
sequence where it breaks.
Agents act; someone has to be able to prove what they did
An AI agent that can refund a payment, change a role, or delete a record is a principal in your system with no HR file and no memory. When it goes wrong, "the model decided to" is not an answer a regulator, a customer, or your own incident review will accept.
Wrap the tool and every call an agent makes enters the record — with the risk label, the model-supplied reason, and whether your authorization gate let it through:
import { auditedTool } from "audkit/ai";
const refund = auditedTool({
name: "refund_payment",
inputSchema: refundSchema,
risk: "high",
requireReason: true,
authorize: ({ input }) => input.amountCents <= 50_00,
handler: ({ paymentId }) => payments.refund(paymentId),
});A call that succeeds logs success. A call the gate refuses logs denied — and then
throws. A call that arrives without a reason logs failed before the error reaches the
model. The denial is evidence, not a silent no.
Not just agents
Audkit is a general audit log. auditedTool() is one of four entry points, alongside
withAuditLogging() and withAuditAction() for Next.js, and
audit.log() for everything else.
Sealed before the ack
Most audit pipelines answer 202 Accepted and then queue the write. If the queue drops
the message, reorders it, or the worker crashes, the "accepted" event was never a fact.
Audkit seals inline. By the time log() resolves, the event has already been:
- assigned a per-project sequence under the project row lock,
- encrypted, with a blinding nonce sealed inside the payload blob,
- committed as an RFC 6962 leaf appended to the project's Merkle tree in Postgres.
const { id, status } = await audit.log({ action: "invoice.approved", actor });
// status === "sealed"status: "sealed" is a promise about durable state, not about a queue. Only
order-independent mirror work — analytics counts, realtime broadcast, alert-rule
evaluation, usage metering — runs after the response, and none of it can change the
sequence or the tree.
What makes it evidence
| Property | How |
|---|---|
| Append-only | No edit or delete endpoint exists. Verification rebuilds the tree from row content. |
| Order-fixed | Sequence assigned under a row lock before the response, so arrival order is sequence order. |
| Third-party checkable | Receipts and tree heads are Ed25519-signed by the service key, not HMAC'd. |
| Checkable without trusting us | Clients verify RFC 6962 inclusion/consistency proofs with local hash arithmetic. |
| Unforgeable by the platform | With customer-held signing keys, events are signed before they leave your infrastructure. |
| Externally anchored | Signed tree heads are published hourly to Sigstore Rekor, which Audkit cannot edit. |
| Privacy-compatible | Retention crypto-shreds expired payloads; the leaf skeleton and its proofs still verify. |
Tamper-evidence is deliberately out-of-band: nothing in the API lets history be
rewritten, so what verify() catches is a direct-database forgery — and what an anchored
root catches is the platform itself.
Where to go next
Quickstart
Project, key, first event, first receipt — in five minutes.
AI agent tools
auditedTool(): risk, required reasons, authorization gates, AI SDK v4 and v5.
Next.js
withAuditLogging() for route handlers, withAuditAction() for Server Actions.
Verification
Receipts, proofs, BYOK signing, and the offline CLI verifier.
REST API
Every /api/v1 endpoint, with auth and required scopes.
SDK reference
Client options, every method, and the error types.