Audkit

Quickstart

Create a project, mint a scoped key, install the SDK, send your first event, and pull a signed verification receipt.

Five minutes from nothing to a verifiable trail.

Create a project

Sign up at audkit.dev and create a project from /dashboard. A project is the isolation unit: its own API keys, members, retention policy, and its own Merkle tree, which starts at sequence 1.

Each project actually keeps two append-only streams, each with its own tree:

  • events — the audit log your application writes.
  • project_audit — Audkit's own control-plane record for that project (key creation and revocation, member changes, retention changes, legal holds). Reachable as stream=project-audit in the API.

Mint a scoped API key

Open Project → Settings → API Keys, create a key, and copy it — it is shown once.

Scopes are enforced per endpoint, so give each service the narrowest key that works:

ScopeGrants
log:writePOST /api/v1/log — ingest events
log:readGET /api/v1/events, legal-hold proofs
log:exportGET /api/v1/export
log:verifyGET /api/v1/verify, /root, /proof/*, /anchors

Set it in your environment:

.env
AUDKIT_API_KEY="..."
# Optional. Defaults to https://audkit.dev
AUDKIT_BASE_URL="https://audkit.dev"

Install the SDK

npm install audkit

The package ships four entrypoints — audkit (the client), audkit/nextjs, audkit/ai, and audkit/protocol (the raw canonicalization, Merkle, and Ed25519 primitives) — plus an npx audkit CLI for offline verification and key generation.

Send your first event

audit.ts
import { Audkit } from "audkit";

export const audit = new Audkit({ apiKey: process.env.AUDKIT_API_KEY! });
app.ts
import { audit } from "./audit";

const { id, status } = await audit.log({
  action: "invoice.approved",
  actor: { type: "user", id: user.id, display: user.email },
  target: { type: "invoice", id: invoice.id },
  risk: "medium",
  metadata: { amountCents: invoice.amountCents },
});

console.log(id, status); // evt_… sealed

status is always "sealed": the event was sequenced and committed to the Merkle tree before the server responded. action and actor are the only required fields — see LogInput for the rest.

Read it back:

const { events } = await audit.query({ action: "invoice.approved", limit: 50 });

Or drill into an entity — what it did (outgoing) versus what happened to it (incoming):

const { events } = await audit.query({
  entityType: "user",
  entityId: user.id,
  direction: "outgoing",
});

Prefer to wrap code you already have instead of calling log() by hand? Pick the wrapper that matches where the mutation lives:

Where the action happensImportWrapper
Next.js route handleraudkit/nextjswithAuditLogging()
Next.js Server Actionaudkit/nextjswithAuditAction()
AI agent tool callaudkit/aiauditedTool()
Anywhere elseaudkitaudit.log()

Verify

Ask the platform to rebuild the tree from row content and hand back a service-signed receipt:

const receipt = await audit.verify();

receipt.valid;        // true — every leaf rebuilt from sequence 1
receipt.checkedCount; // leaves rebuilt
receipt.treeSize;     // size of the tree they reproduce
receipt.rootHash;     // the root they reproduce
receipt.firstBreak;   // null, or { sequence, reason, detail }
receipt.signature;    // Ed25519 over the receipt, by the service key

A platform grading itself proves little on its own, so the client also checks the platform's work without asking. checkConsistency() fetches the current signed tree head and proves — with local hash arithmetic, no trust in the response — that it still contains the head this client last pinned:

await audit.checkConsistency(); // throws AudkitTamperError if it doesn't

This runs automatically (throttled) after log() and query(). Keep going in Verification for receipts, proofs, customer-held signing keys, and the fully offline npx audkit verify full rebuild.

Next steps

On this page