Audkit is the append-only record of everything your product does. Every event is sequenced, hash-linked to the one before it, and signed — so the entire history can be re-verified on demand, down to the first record.
npm i audkitmutation endpoints — the API cannot edit or delete
links every event to the one before it
customer-held keys sign events before they leave your infra
signed verdict for every verify()
01 — seal
prev 00000000One FIFO lane per project. Order is enforced by the queue, not hoped for.
A monotonic counter assigns the event its place. The next number is never in doubt.
prevHash — SHA-256 of the previous event — welds it to the record so far.
HMAC-SHA256 over sequence, timestamp, and both hashes. The seal.
Safe aggregates flow to analytics. Payloads never leave Postgres.
The dashboard sees the event the moment it lands.
the sealed fields
02 — verify
prev 9759cdf8There is no edit endpoint to abuse — forging history means writing to the database directly. Even that shows: the altered row no longer matches the hash sealed into its successor. verify() re-walks the chain from sequence 1 and returns a signed receipt. When something is wrong, it names the exact sequence.
const receipt = await audit.verify();
receipt.valid; // false — someone touched row 88,412
receipt.checkedCount; // 184,201 events re-walked, from sequence 1
receipt.firstBreak; // { sequence: 88412, reason: "hash_mismatch" }
receipt.signature; // the receipt itself is signed. check it.what a forgery looks like
Not just “something is wrong” — the exact sequence where the chain no longer holds.
Every verdict is signed by the platform. The proof of integrity can't be forged either.
Bring your own Ed25519 key. The SDK signs each event before it leaves your infrastructure, and only you hold the private key — a history not even the platform can forge.
Email digests carry the signed chain head. Truncate the record and the last anchor disagrees.
The platform's own control-plane actions land in project-audit — and verify the same way.
03 — ship
prev 37549f8ePersonal or org-owned. The chain starts at sequence 1.
log:write · log:read · log:export · log:verify. Shown once, revocable always.
One call. A 202 comes back before the seal work starts — your request path never waits.
import { Audkit } from "audkit";
const audit = new Audkit({ apiKey: process.env.AUDKIT_API_KEY! });
await audit.log({
action: "role.changed",
actor: { type: "user", id: "usr_9f27", display: "dana@acme.dev" },
target: { type: "member", id: "mem_512", display: "kai@acme.dev" },
metadata: { from: "member", to: "admin" },
});
// 202 — accepted. sequenced, linked, signed. permanent.04 — govern
prev 5f7de154When events pass your retention window — 90 days by default, or never — Audkit deletes the payloads outright and the storage actually shrinks. Sequence, hashes, and signatures stay in the chain, so every old record still verifies end to end. Minimize data. Keep proof.
Build a hold from a query — actor, action, time range. Matching events are committed to a Merkle root anchored into the platform's own audit chain, with inclusion proofs on demand. Deletion is blocked for as long as the hold stands.
AI tools act in production now. auditedTool() wraps any AI-SDK tool with risk labels, required reasons, and an authorize gate — every call enters the record as pending, then success, denied, or failed.
const refund = auditedTool({
client: audit,
name: "refund_payment",
risk: "high",
requireReason: true,
authorize: ({ input }) => input.amountCents <= 50_00,
handler: ({ paymentId }) => payments.refund(paymentId),
});05 — index
prev ef154ce6end of record — you've reached the head
Create a project, mint a key, send an event. What lands in the chain, stays in the chain.