AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Self-hosted history for your Claude Code sessions. A hook logs every session to your own CouchDB + S3; a web API, web UI, and CLI read it back. Nothing leaves your network.
Developer toolsTypeScript · HTMLGitHub @vredchenkoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Claude Code Transcripts - self-hosted, lossless ground-truth archive of your Claude Code sessions
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Self-hosted history for your Claude Code sessions. A Claude Code hook logs every session — events, an end-of-session summary (counts, tool usage, token usage), and the full transcript — to your own CouchDB + S3-compatible storage. A web API serves it back; a web UI and a CLI (and AI agents) read it.
Everything runs on your own infrastructure. Nothing leaves your network.
⚠️ Early — a preview, not something to depend on
There are tagged releases with published binaries and container images, and the install and upgrade paths have been exercised against a real instance carrying real history. What that does not mean: it has not been validated on a clean machine other than the author's, breaking changes land without notice, and stored data may still have to be discarded between revisions.
There is no auth and no security model — Tier 1 assumes a single user on a single trusted machine, so don't expose it to a network or point it at anything you can't afford to lose. Issues and feedback are welcome.
Status: early rebuild. Tier 1 (single machine, single user) first — retention,
browse/search, and programmatic access, no auth. The design lives in
docs/, also published as a
project site.
One command. It fetches the release binary for your platform, verifies its checksum,
and hands over to claude-transcripts install — which generates this instance's
secrets and ports, starts the backing services, provisions the stores, starts the app,
sets up search, and registers the hook with Claude Code:
curl -fsSL https://raw.githubusercontent.com/vredchenko/claude-transcripts/main/install.sh | sh
Needs Docker and Claude Code. Not Bun, not a clone. Then:
claude-transcripts doctor # write → read → search, end to end
claude-transcripts sessions # what's been recorded
The UI is at http://127.0.0.1:<WEBAPI_PORT>/app — install prints the address, and
picks a free port block per instance rather than assuming one. Upgrading later is the
same command again; it's idempotent and keeps your history.
Details, including what it writes where: installation.md.
The rest of this section is the from-source path — what you want if you're changing the code. If you just want to record sessions, use the installer above.
Everything runs on your own machine, bound to 127.0.0.1, with no auth. The
7650–7661 range is only a default: every port is an .env variable
(WEBAPI_PORT, COUCHDB_PORT, GARAGE_S3_PORT, …) and that one file feeds both
Docker Compose and the host-run app, so changing a number moves the port
everywhere at once — pick whatever is free on your box.
Two ways to provide the backing services — CouchDB (source of truth), S3-compatible object storage (transcript blobs), and optionally Meilisearch (a derived search index):
A. All local — the bundled stack. Supported; this is the path the steps
below walk. The deploy/ Docker Compose stack brings up CouchDB +
Garage (S3) + Meilisearch and their admin UIs from public images, and the app
runs on the host via Bun (or as a container built from this repo). Nothing needs
to be built or published to try it.
B. External backing services. Designed for, not yet verified — TODO.
Only the app runs locally; .env points at services you already operate. How far
it goes today:
S3_ENDPOINT is a full URL, so any S3-compatible
store works (Garage, MinIO, Cloudflare R2, AWS S3) with S3_ACCESS_KEY /
S3_SECRET_KEY. You create the bucket and key yourself — bun run bootstrap:garage (step 4) only targets the bundled Garage.MEILI_HOST + MEILI_API_KEY), so a remote or hosted instance is fine. It's
also fully optional: features.meilisearch: false simply drops the search
index.COUCHDB_URL to the full base URL (plus
COUCHDB_USER / COUCHDB_PASSWORD); HTTPS and a path prefix are both
accepted, e.g. https://couch.example.com/couchdb. It wins over
COUCHDB_HOST / COUCHDB_PORT, which stay as the bundled-stack shorthand.The plumbing is there for all three, but no external deployment has been exercised end to end — hence the TODO.
See backend topology in the configuration docs.
Prerequisites: Docker (with Compose),
Bun ≥ 1.4, git, openssl, and
Claude Code (to record sessions).
git clone [email protected]:vredchenko/claude-transcripts.git
cd claude-transcripts
bun install
cp .env.template .env
In .env: leave IMAGE_NS blank (we use public images). COUCHDB_USER /
COUCHDB_PASSWORD default to admin/admin — CouchDB 3 refuses to start
without an admin, so the bundled stack ships one; change both if this box isn't
just yours. Generate Garage's internal cluster secrets and paste them in:
for k in GARAGE_RPC_SECRET GARAGE_ADMIN_TOKEN GARAGE_METRICS_TOKEN; do
echo "$k=$(openssl rand -hex 32)"
done
Leave S3_ACCESS_KEY / S3_SECRET_KEY empty for now — you fill them in step 4.
App config (DB/bucket names, ports) comes from
config/config.template.json automatically; you
don't need to copy it.
bun run stack:up:upstream # CouchDB, Garage, Meilisearch + admin UIs
bun run scripts/stack.ts ps --upstream
CouchDB → :7652 (Fauxton at /_utils/), Garage S3 → :7653 (web UI :7655),
Meilisearch → :7656. State lives under deploy/data/ (delete it to reset).
S3 always signs requests, so a bucket and key must exist. One command does it —
it assigns the layout, creates the bucket + app key, grants access, and writes
S3_ACCESS_KEY / S3_SECRET_KEY into .env (idempotent):
bun run bootstrap:garage
G="docker exec claude-transcripts-garage /garage"
$G status # note this node's ID
$G layout assign -z dc1 -c 1G <NODE_ID> # <NODE_ID> from the line above
$G layout apply --version 1
$G bucket create claude-transcripts-sessions
$G key create claude-transcripts-app # prints a Key ID + Secret — copy both
$G bucket allow --read --write claude-transcripts-sessions --key claude-transcripts-app
Then paste the Key ID → S3_ACCESS_KEY and the Secret → S3_SECRET_KEY in .env
(see the Garage quick-start).
On the host (fast iteration — edit + reload, no image build):
bun run dev:webapi # http://127.0.0.1:7650 — creates the CouchDB DBs + views on boot
# in a second terminal:
bun run dev:webui # http://127.0.0.1:7651/app/
With an installed instance also running, set WEBAPI_PORT=7650 in .env so the dev
webui and bun run cli use this webapi.
Or as a container (the full stack, app image built locally from this repo):
bun run stack:up:local # = stack up --build --upstream: builds + runs the app container
# webapi + webui served at http://127.0.0.1:7650/app/
bun run cli doctor
Expect all checks ✓ — it writes one synthetic session through the webapi, reads
it back (verifying CouchDB + S3 are wired), then deletes it again. To see it in
bun run cli sessions afterwards, run bun run cli doctor --keep.
bun run cli setup # verify later with: bun run cli setup --check
This writes ~/.config/claude-transcripts/config.json, ensures the CouchDB
databases, probes the Garage bucket, and registers the logging hook in
~/.claude/settings.json for all session events. Now run Claude Code anywhere —
each session is logged; browse them at http://127.0.0.1:<WEBAPI_PORT>/app (7650 by
default) — the same address install prints.
From a source checkout the hook runs
bun run <repo>/packages/cli/src/cli.tsx hook run, so keep this clone in place andbunon yourPATH; an installed binary registers itself instead and needs neither. Either way it never blocks a session — if the stack is down, events are dropped rather than surfaced. It writes to CouchDB and S3 directly for that reason (ADR 0016).
bun run cli backfill --dry-run # preview what would be adopted
bun run cli backfill # adopt on-disk ~/.claude transcripts as history
Non-secret deployment-wide settings live in config/ (copy
config/config.template.json → config/config.json);
secrets/endpoints in a local .env (copy .env.template). The
bundled dev stack runs on ports 7650–7661 with no auth on localhost.
Everything below is for working on Claude Transcripts, not just running it.
| Component | Path | Role |
|---|---|---|
| hooks | hooks/ | Claude Code plugin: a thin shim that pipes each hook event to claude-transcripts hook run. Installs per machine. |
| webapi | packages/webapi/ | Bun + Hono gateway: the single I/O surface; serves the SPA in prod. |
| webui | packages/webui/ | React + MUI SPA (optional). |
| cli | packages/cli/ | Bun + Ink user-facing tool + admin utility, and the writer (hook run records sessions). |
| shared | packages/shared/ | The blueprint (central state, incl. the CLI spec) + migrations framework + cross-cutting types + token accounting. |
| scripts | scripts/ | Dev-only automation (client gen, image mirroring, release). |
| deploy | deploy/ | Docker Compose: CouchDB + Garage + Fossil + Meilisearch + admin UIs; deploy/k8s/ kustomize base. |
| tests | tests/ | End-to-end + Playwright browser suites, and a mock Claude Code. |
For a container deploy (rather than running on the host), the combined app
image (claude-transcripts-app) is published to GHCR by the publish-image workflow
— :vX.Y.Z + :latest per release, :main per push to main
(ADR 0023). The
default (non---upstream) stack mode pulls mirrored backing images from your own
${IMAGE_NS} registry; --upstream uses public upstream images instead.
docs/ — organised into
getting started,
development,
operations, reference,
and design (with the
ADRs nested under it). Published at
vredchenko.github.io/claude-transcripts/docs.CHANGELOG.md — what shipped in each release (lockstep-versioned;
see releasing.md).CLAUDE.md — build conventions and repo invariants for agents.bun run lint, bun run typecheck, bun run build.