A polyglot client SDK for tamper-evident, SQL-queryable audit logging.
@smooai/audit gives every service — in any language — one shared way to emit
audit events that are:
- Canonical — a single
AuditEventschema, serialized to byte-identical canonical JSON regardless of language, so events are comparable and hashable everywhere. - Tamper-evident — each event is chained into a per-org-per-day SHA-256 hash chain: every event's hash covers the previous event's hash, so any retroactive edit or deletion breaks every subsequent link.
- SQL-queryable — events land in a structured store you can query with plain SQL.
It ships as a native package for five languages — TypeScript, Python, Rust, Go, and .NET — with identical semantics. All implementations are verified byte-for-byte against a shared parity corpus, so a hash computed in Go matches one computed in Rust or TypeScript for the same event.
Status: all five language implementations are complete and verified byte-for-byte against the shared parity corpus (
spec/parity-corpus.json).
Every language exposes the same four things:
| Concept | What it does |
|---|---|
AuditEvent |
The canonical event schema (id, organizationId, timestamp, actorType, actorId, action, resource?, metadata?, hashPrevious?, hashCurrent?). |
canonicalJson(event) |
Deterministic, byte-identical JSON serialization. |
computeEventHash(event) / buildHashChain(events) |
The per-org-per-day SHA-256 hash chain. |
AuditClient / emit(event) |
POSTs an event to a configurable ingest endpoint with a bearer token. |
TypeScript / Node
pnpm add @smooai/auditPython
uv add smooai-audit # or: pip install smooai-auditRust
cargo add smooai-auditGo
go get github.com/SmooAI/audit/go.NET
dotnet add package SmooAI.AuditTypeScript
import { AuditClient, type AuditEvent } from "@smooai/audit";
const client = new AuditClient({
endpoint: process.env.AUDIT_ENDPOINT!,
token: process.env.AUDIT_TOKEN!,
});
const event: AuditEvent = {
id: crypto.randomUUID(),
orgId: "org_123",
timestamp: new Date().toISOString(),
actor: "user_abc",
action: "record.delete",
resource: "contact:xyz",
metadata: { reason: "gdpr" },
};
await client.emit(event);Python
from smooai_audit import AuditClient, AuditClientOptions, AuditEvent
client = AuditClient(AuditClientOptions(endpoint=endpoint, token=token))
client.emit(AuditEvent(id=..., org_id="org_123", timestamp=..., actor="user_abc", action="record.delete"))Rust
use smooai_audit::{AuditClient, AuditClientOptions, AuditEvent};
let client = AuditClient::new(AuditClientOptions { endpoint, token });
client.emit(&event)?;Go
import audit "github.com/SmooAI/audit/go"
client := audit.NewClient(audit.ClientOptions{Endpoint: endpoint, Token: token})
err := client.Emit(event).NET
using SmooAI.Audit;
var client = new AuditClient(new AuditClientOptions { Endpoint = endpoint, Token = token });
await client.EmitAsync(evt);Audit trails are only trustworthy if a hash means the same thing everywhere. The canonical serializer and hash chain are held to a shared parity corpus: a set of fixed input events with expected canonical JSON and expected hashes. Every language's test suite runs the corpus and asserts byte-for-byte equality, so a chain written by a Go service verifies cleanly in a Rust or TypeScript reader.
This is a single repo housing all five language implementations. See
CLAUDE.md / AGENTS.md for build, test, and
release commands. The short version:
pnpm install
pnpm check-all # typecheck + lint + test + build across all languagesMIT © SmooAI