Claude Transcripts docs GitHub
Work in progressUnder active development — not tested as ready for use. Breaking changes land without notice, stored data may need to be discarded between revisions, and there is no auth or security model.

Configuration

Two layers:

LayerFileHolds
Settingsconfig/config.json in a checkout (falls back to the committed config/config.template.json); ~/.config/claude-transcripts/app.json on an installed instanceNon-secret: store names, feature flags, tunables, service-menu URLs, recall policy
Secrets and endpoints.env in a checkout (.env.template); ~/.config/claude-transcripts/instance.env on an installed instanceHosts, ports, credentials, S3 keys, image tags

Both live files are gitignored. New knobs go into config/, not a third source.

After changing settings, re-run install (or setup in a checkout). The hook does not read config/ or app.json; it reads a runtime config at ~/.config/claude-transcripts/config.json that install/setup generate from them.

Settings

The committed template:

{
  "app": { "name": "claude-transcripts" },
  "system": {
    "logging": { "chunk": { "maxEntriesPerChunk": 200, "flushIntervalMs": 15000 } },
    "sessions": { "liveWindowMs": 86400000, "idleThresholdMs": 300000 }
  },
  "couchdb": {
    "databases": {
      "sessions": "claude-transcripts-sessions",
      "appLogs":  "claude-transcripts-app-logs"
    }
  },
  "s3": { "buckets": { "sessions": "claude-transcripts-sessions" } },
  "meilisearch": {
    "indexes": {
      "sessions": "claude-transcripts-sessions",
      "turns":    "claude-transcripts-turns"
    }
  },
  "fossil": { "repositories": { "sessions": "claude-transcripts-sessions" } },
  "features": {
    "s3Blobs": true,
    "midFlightChunking": true,
    "couchFullContentChunks": true,
    "meilisearch": true,
    "secretsMasking": false
  },
  "servicesMenu": {},
  "userSettings": {
    "sessionListPageSize": 100,
    "transcriptPageSize": 100,
    "transcriptAutoLoadMax": 2000
  },
  "recall": {
    "mode": "auto",
    "scope": "project",
    "maxResults": 5,
    "maxSnippetChars": 400,
    "triggers": { "priorWorkQuestion": true, "repeatedError": true, "beforeRederiving": true },
    "excludeCwdGlobs": [],
    "primer": { "onSessionStart": true, "maxTokens": 200 }
  }
}
KeyMeaning
system.logging.chunkMid-session chunk flush: after this many transcript entries or this many ms, whichever first (mid-flight-chunking.md).
system.sessions.liveWindowMsA session with no SessionEnd counts as running for this long after its last event (24 h), then incomplete. A recency heuristic; there is no heartbeat.
system.sessions.idleThresholdMsGaps between events longer than this (5 min) don't count towards a session's active time.
couchdb.databases, s3.buckets, meilisearch.indexes, fossil.repositoriesKeyed maps: code refers to a store by logical key (sessions, appLogs, turns), never by its deployed name. The claude-transcripts- prefix keeps them from colliding with anything else on a shared server. Fossil repository names must be letters, digits, - and _; the Fossil container creates each named repository on start, and the default sessions entry is always kept (like the Meilisearch indexes).
features.s3BlobsUpload the transcript and a summary.json copy to S3. Off: no byte-exact transcript is kept anywhere; CouchDB still has the pruned per-turn content if full-content chunks are on.
features.midFlightChunkingTail the transcript into CouchDB chunk docs during the session.
features.couchFullContentChunksPut the parsed turns in those chunks (ADR 0027). Off: a live session's transcript can't be read until it ends, the speaker-split views are empty, and conversation content isn't searchable.
features.meilisearchFull-text search. Off: no search, nothing else changes.
features.secretsMaskingPlaceholder; nothing is masked yet.
servicesMenuAdmin-UI links in the webui (keys couchdbFauxton, garageWebui, meilisearch, meilisearchUi, fossil; any other key is an extra link). Unset keys are derived as http://127.0.0.1:<host port>; set one when the dashboards live elsewhere, e.g. { "couchdbFauxton": "https://couch.example.org/_utils/" }.
userSettingsHow much the webui fetches: page sizes per request, and how many transcript entries load before the viewer offers a "load the rest" button (the list isn't virtualised). Values above a per-key ceiling are capped; missing, non-numeric or below-1 values use the default.
recallWhen a live session consults its own history (ADR 0029). mode: off/suggest/auto; scope: project/host/all. Keep scope: project while secretsMasking is off. The plugin's recall_mode, recall_scope and max_results options override it per user.

Omit a section to get its defaults.

Who reads what

Other variables: CT_HOME relocates a whole install (see installation.md); the app image sets CT_STATIC_DIR, CT_DOCS_DIR, CT_CLI_BIN and CT_VERSION.

Backend topology — bundled or external

The app finds its backends purely through the environment, so the same image runs either way.

Bundled (default, the tested path): the deploy/ Compose stack runs CouchDB, Garage and Meilisearch on localhost. Meilisearch has no master key unless you ask for one; Garage's app key is minted by install (or bun run bootstrap:garage) and written to the env file; CouchDB gets a fixed admin/admin because CouchDB 3 will not start without an admin (ADR 0020).

External: point the app at your own services.

BackendVariables
CouchDBCOUCHDB_URL (full base URL, https and a path prefix allowed; wins over COUCHDB_HOST/COUCHDB_PORT), COUCHDB_USER, COUCHDB_PASSWORD
S3S3_ENDPOINT, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY (Garage, MinIO, R2, AWS)
MeilisearchMEILI_HOST, MEILI_API_KEY

Not verified end to end. Create the bucket and key yourself (bootstrap:garage only targets the bundled Garage; the app never creates buckets), and a CouchDB path prefix is untested.

An external Meilisearch

Read ADR 0028 first. The turns index holds conversation text, so an external Meilisearch is the one configuration where recorded content leaves the machine. And Meilisearch is a derived index the app owns: reindex clears an index before refilling it, which is safe only because the index names are ours. If you rename them, keep them distinct from anything else on that engine.

Search is otherwise local: the webapi follows CouchDB's change feed and writes to Meilisearch; the hook never touches it. Every index can be rebuilt from CouchDB with claude-transcripts reindex.