pnpm workspace monorepo using TypeScript. Each package manages its own dependencies.
A cryptographically signed receipt system for every AI interaction. Every prompt/response pair gets a SHA-256 hash-chained receipt that can be verified, replayed, and checked against policy-as-code rules.
- Mint Receipts: Submit AI interactions to create cryptographically signed, hash-chained receipts
- Hash Chain: Each receipt links to the previous via chain hash (tamper-evident append-only log)
- Verify: Cryptographic verification of prompt/response/chain hashes
- Replay: One-click replay of any interaction with output diff comparison
- Policies: Policy-as-code rules (JS expressions) evaluated on every new receipt
- Dashboard: Live stats, chain health, recent activity
promptHash = sha256("prompt:" + prompt)responseHash = sha256("response:" + response)chainHash = sha256("chain:" + promptHash + ":" + responseHash + ":" + prevHash|GENESIS)
- Monorepo tool: pnpm workspaces
- Node.js version: 24
- Package manager: pnpm
- TypeScript version: 5.9
- API framework: Express 5
- Database: PostgreSQL + Drizzle ORM
- Frontend: React + Vite + Tailwind CSS + shadcn/ui
- Validation: Zod (
zod/v4),drizzle-zod - API codegen: Orval (from OpenAPI spec)
- Build: esbuild (CJS bundle)
pnpm run typecheck— full typecheck across all packagespnpm run build— typecheck + build all packagespnpm --filter @workspace/api-spec run codegen— regenerate API hooks and Zod schemas from OpenAPI specpnpm --filter @workspace/db run push— push DB schema changes (dev only)pnpm --filter @workspace/api-server run dev— run API server locally
interactions— AI interaction receipts with crypto hashespolicies— Policy-as-code rules (JS expressions)activity_log— Audit trail of all events. Hash-chained vialog_hash/prev_log_hash. The 0001 migration left pre-migration rows with NULL hashes that the verification walker silently skipped. Run the backfill (below) once per environment to repair the chain end-to-end.
Closes the legacy NULL-hash gap and pins the deferred NOT-NULL trigger to
seq > 0 (covers every row going forward). Idempotent — safe to re-run.
# Dry-run first (always): reports counts + first/last seq it would touch.
pnpm --filter @workspace/scripts run backfill:audit-log -- --dry-run
# Apply: rewrites drifted rows in a single advisory-locked transaction
# and lowers the trigger cutoff to 0.
pnpm --filter @workspace/scripts run backfill:audit-logSuccess looks like: applied: true, nullHashRows: 0 on a second pass,
and GET /api/audit/chain-status returning intact: true with tampered: 0
and total === hashableEntries.
Implementation: lib/db/src/audit-log-backfill.ts (the function) and
scripts/src/audit-log-backfill.ts (the CLI). buildLogHash is exported
from @workspace/db so the runtime insert path and the backfill share one
canonical formula.
By design the backfill performs a full-chain canonical rewrite — it
walks every row from seq = 0 and re-computes prev_log_hash / log_hash
under the current canonical formula, not just the legacy NULL-hash rows.
That is intentional: it lets a single run normalize any post-migration
drift (e.g. rows written before a hash-formula fix, or rows with stale
prev_log_hash after an out-of-band repair) without operators needing to
classify each anomaly. The advisory lock (0x4C4F4748) ensures only one
rewrite runs at a time and the trigger cutoff is only lowered after a
successful apply.
See the pnpm-workspace skill for workspace structure, TypeScript setup, and package details.