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. These docs describe the intended design as much as the current state.

Releasing & publishing

Everything is lockstep-versioned (ADR 0023): one vMAJOR.MINOR.PATCH git tag drives every published artifact. Push the tag and CI does the rest. What each release contained is recorded in CHANGELOG.md.

What a vX.Y.Z tag publishes

ArtifactWhereWorkflow
App image (claude-transcripts-app — webapi + webui SPA + docs + bundled CLI)GHCR: ghcr.io/<owner>/claude-transcripts-apppublish-image.yml
Mirrored backing images (CouchDB, Garage, Meilisearch + admin UIs)GHCR: ghcr.io/<owner>/claude-transcripts-*mirror-images.yml (also on-demand)
CLI binaries (Linux + macOS, x64 + arm64) + SHA-256 sumsGitHub Release assetsrelease-cli.yml
CLI on npm (@claude-transcripts/cli, a bun-runnable bundle)npmjs.orgrelease-cli.ymlskipped with a warning until NPM_TOKEN is set (below)

The CLI needs bun at runtime (it uses Bun APIs). So: the compiled binaries are the zero-dependency option (curl the one for your platform from the Release); the npm package is for bun users (bunx @claude-transcripts/cli, or a global install with bun on PATH).

App image tags

TagMeansPushed by
vX.Y.Zthat exact releasea v*.*.* tag
latestthe newest releasea v*.*.* tag
mainthe tip of main, rebuilt on every mergea push to main
<sha>one specific commitany of the above, and manual dispatch

latest deliberately tracks releases, not maininstall uses it, and pointing it at unreleased code would hand users something untested. But that leaves a gap the main tag fills: between releases there would otherwise be no image of the current code at all. After 0.0.1 the app image sat at schema v5 while main reached v7, so an install could only pair a current CLI with a months-old app — which breaks the lockstep-versioning invariant (ADR 0023) silently, since everything starts and only some later read misbehaves.

Accordingly install pins the app image to the CLI's own version when the CLI is a release, and to main when it isn't, then reports the version the running app actually announces so a mismatch is visible rather than inferred.

Building on every merge to main also means the image vulnerability scans (grype + trivy, failing on HIGH) now run per-merge instead of only at release — earlier warning, at the cost of a noisier signal when a base image picks up a CVE.

Cutting a release

bun run scripts/release.ts 0.1.0   # stamp the lockstep version into every manifest
git commit -am "chore(release): 0.1.0" && git push   # via a PR, per branching.md
git tag v0.1.0 && git push origin v0.1.0             # from main, once merged

scripts/release.ts <semver> stamps the version into the root package.json, each packages/*/package.json, and the hook's .claude-plugin/plugin.json (ADR 0023); --check verifies they all match without writing. The tag is what CI reacts to — the stamped manifests just keep the tree honest about which release it is.

CI then builds + publishes all of the above. A manual release-cli / mirror-images dispatch (Actions tab → Run workflow) is available for testing — release-cli on dispatch uploads the binaries as workflow artifacts without publishing.

One-time setup ("click-admin")

CI uses the built-in GITHUB_TOKEN to push to GHCR — no secret needed for the images. The manual bits, once:

  1. Make the GHCR packages public (so anyone can docker pull without auth): after the first publish, each package appears under your profile/org → open it → Package settings → Change visibility → Public. Do this for claude-transcripts-app and each mirrored claude-transcripts-*. (Repo → Settings → Actions → General → Workflow permissions should allow read/write.)
  2. npm:
    • Own the scope: create the claude-transcripts org on npmjs (the package is @claude-transcripts/cli, published with --access public).
    • Create an npm Automation access token (npmjs → Access Tokens) and add it as the repo secret NPM_TOKEN (Settings → Secrets and variables → Actions).
  3. (Optional) a protected release environment (Settings → Environments) if you want a manual approval gate before publishing.

After releasing: pull from your own registry

Point the stack at your mirrored images instead of external registries (so an unmaintained/unverified upstream can never ship you a surprise):

# in .env
IMAGE_NS=ghcr.io/<owner>

Then bun run stack:up (without --upstream) pulls couchdb, garage, meilisearch, the admin UIs, and the app image from ghcr.io/<owner>/… only. Re-run mirror-images (or tag a release) whenever you bump a pinned upstream tag in scripts/mirror-images.ts + .env.template.

Notes