A self-hosted, cross-machine archive of your AI coding-agent history.
A per-machine sync daemon pushes Claude Code, Codex CLI, Copilot, Pi, OpenCode, Gemini CLI and 20 more into a central Rust/Postgres hub with a search API and a web UI. Your history survives each tool's local retention window, and it's searchable from anywhere on your tailnet.
This is a hard fork, and it is server-first.
jhlee0409/claude-code-history-vieweris an excellent cross-provider desktop viewer. This fork keeps its parsers and its UI, but re-points the product at a different problem: a durable, central, multi-machine archive. We ship no desktop app — no.dmg, no.exe, no.AppImage, no auto-updater, no Homebrew cask. What we ship is the archive stack and a static web UI. See Relationship to upstream.
AI coding agents delete local history on a fixed window — Claude Code keeps roughly 30 days — and every tool keeps its own history in its own format, on whichever machine you happened to be sitting at. So the conversation where you worked out why the migration had to run in two passes is gone, or it's on the other laptop.
This fork fixes that with three properties:
- Cumulative. The daemon only ever ingests. Deleting a local file never deletes anything from the hub. Once a message lands, it stays.
- Cross-machine. Every machine pushes to one hub. One search covers all of them.
- Cross-provider. 26 agents, one normalized schema, one search box.
each machine: sync-daemon ──(HTTPS over Tailscale, bearer token)──▶ hub ──▶ Postgres
│ │
└─ reads ~/.claude, ~/.codex, ~/.pi, … └─ /v1/ingest /v1/search
via the shared history-core parser /v1/projects /v1/journal …
(the ONLY component
with DB credentials)
│
static archive webapp ────┘
(served by the hub at /)
| Crate | Role |
|---|---|
crates/history-core |
Tauri-free extraction and normalization of every provider's history. The shared parser — the daemon, the hub and the local viewer all use it. |
crates/sync-daemon |
Per-machine daemon. Backfills, then incrementally pushes local history to the hub. Crash-safe checkpoints; holds only a hub URL + token. |
crates/hub |
axum + sqlx service. The only component with DB credentials. Bearer-authed /v1/ingest, plus the read API: full-text + semantic search, browse, journal, analytics. |
crates/protocol |
The wire types shared by daemon and hub. |
dist-archive/ |
The static archive webapp (just archive-web-build) — backend-free, served by the hub itself or any static host. |
Transport security is Tailscale (WireGuard); a bearer token gates the data
endpoints. Read access can additionally be gated on Tailscale identity
(trust_tailscale_identity).
Full instructions — including systemd/launchd units, TLS, secrets, and the
homelab-specific bits — are in docs/archive/deployment.md.
The short version:
1. Postgres. 12 or newer (the schema uses generated stored columns). The hub applies its own migrations on startup, so there is no manual migration step, and no extensions are required.
createdb cchv_archive
psql -d cchv_archive -c "CREATE ROLE cchv LOGIN PASSWORD 'CHANGE_ME';"
psql -d cchv_archive -c "GRANT ALL ON DATABASE cchv_archive TO cchv;"2. Hub, on your always-on node. Either build it (cargo build --release -p hub)
or grab cchv-hub-<version>-aarch64-apple-darwin from
Releases.
# /etc/cchv/hub.toml — one [[tokens]] entry per machine
database_url = "postgres://cchv:CHANGE_ME@localhost/cchv_archive"
bind_addr = "0.0.0.0:8787" # reachable over the tailnet
static_dir = "/path/to/dist-archive" # optional: serve the web UI at /
[[tokens]]
token = "GENERATE_A_LONG_RANDOM_SECRET"
machine_id = "11111111-1111-1111-1111-111111111111"
label = "laptop"HUB_CONFIG=/etc/cchv/hub.toml ./target/release/hub
curl http://<host>:8787/v1/healthz # {"status":"ok","db":"up"}3. Sync daemon, on every machine you code from
(cargo build --release -p sync-daemon):
# ~/.config/cchv/daemon.toml
hub_url = "http://<tailnet-host>:8787"
hub_token = "GENERATE_A_LONG_RANDOM_SECRET" # this machine's token
scan_interval_secs = 3600The daemon persists a stable machine id at ~/.claude-history-sync/machine_id on
first run and prints it — put that id in the hub's hub.toml alongside this
machine's token.
4. Web UI. just archive-web-build produces dist-archive/. Point the hub's
static_dir (or HUB_STATIC_DIR) at it and the hub serves the UI and the API from
one origin — no CORS, no mixed content. /v1/* always wins over static files;
static assets are served without auth, the bearer token still gates all data.
The webapp is also published as cchv-webapp.tar.gz on every release, if you'd
rather not build it.
| Capability | What it does |
|---|---|
| Journal | Day-grouped distilled summaries of what each session was actually about, so you can skim weeks at a glance instead of scrolling transcripts. Deep-linkable. |
| Search | Postgres full-text with prefix matching, plus hub-local semantic embeddings — mode=hybrid fuses both and degrades to keyword if the embedder is unavailable. Results land you on the exact message, in context. |
| Browse | Projects → sessions → messages, with provider badges, role/timestamp gutters, and worktree/clone grouping. |
| Project identity | Moved, cloned and worktree copies of the same repo are grouped by git fingerprint rather than by path, so a ~/dev/foo → ~/work/foo move doesn't fork your history. |
| Analytics | Token and cost breakdowns across every machine and provider, not just the one you're sitting at. |
| Time Machine backfill | just tm-backfill recovers history older than the local retention window from Time Machine backups — including from retired machines, via their backup disk. See docs/archive/timemachine-backfill.md. |
Twenty-six assistants, one archive. Every provider below is parsed by
history-core, so it is browsable locally and ingested into the hub.
| Provider | Data Location | What You Get |
|---|---|---|
| Claude Code | ~/.claude/projects/ |
Full conversation history, tool use, thinking, costs |
| GitHub Copilot | ~/.copilot/session-state/ (CLI & Desktop), VS Code workspaceStorage/.../chatSessions/ |
Copilot CLI, Copilot Desktop, and VS Code Copilot Chat history (read-only, WSL-aware) |
| Codex CLI | ~/.codex/sessions/ |
Session rollouts with agent responses |
| Pi | ~/.pi/agent/sessions/ |
badlogic's pi / Pi SDK agent — JSONL transcripts per working directory |
| OpenCode | ~/.local/share/opencode/ |
Conversation sessions and tool results |
| Gemini CLI | ~/.gemini/history/ |
Conversation history with tool calls |
| Antigravity | ~/.gemini/antigravity/ |
Conversation state under brain/ plus token monitor data under .token-monitor/rpc-cache/v1/ |
| Cline (incl. Roo Code, Kilo Code) | VS Code globalStorage/<ext>/tasks/ |
Task-based history across the Cline family |
| Cursor | ~/.cursor/ |
Composer and chat conversations |
| Cursor Agent | ~/.cursor/projects/.../agent-transcripts/ |
Agent transcripts, distinct from the Cursor IDE source |
| Aider | Project directories | Chat history and edit logs |
| ForgeCode | ~/.forge/.forge.db |
Conversation history from SQLite database |
| CodeBuddy Code | ~/.codebuddy/projects/ |
Conversation history with tool calls (Claude Code fork format) |
| Kimi | ~/.kimi/ |
Session history with kimi -r resume |
| Kiro | kiro-cli/data.sqlite3 |
SQLite-backed conversation history |
| Amazon Q CLI | …/amazon-q/data.sqlite3 |
SQLite conversations store (shares format with the Kiro CLI provider) |
| Continue.dev | ~/.continue/sessions/*.json |
Per-session JSON, grouped by workspace (honors CONTINUE_GLOBAL_DIR) |
| PearAI | ~/.pearai/sessions/ |
Continue fork — same session format |
| Goose | …/goose/sessions/sessions.db |
Block's agent — SQLite sessions + messages |
| Crush | per-project ./.crush/crush.db |
Charm's TUI — SQLite, discovered across common code roots |
| llm | …/io.datasette.llm/logs.db |
Simon Willison's CLI — SQLite conversations/responses with token counts |
| Open Interpreter | ~/.openinterpreter/sessions/ |
Codex-format rollouts (reuses the Codex parser; INTERPRETER_HOME override) |
| Qwen Code | ~/.qwen/projects/.../chats/ |
Per-session JSONL transcripts (tool calls, thinking, token usage) |
| Zed | …/Zed/threads/threads.db |
Agent Panel threads — SQLite + Zstd-compressed JSON |
| OpenHands | ~/.openhands/sessions/ |
Classic event-store conversations |
| Trae | …/Trae/User/workspaceStorage/.../state.vscdb |
Per-workspace chat (icube store; experimental, reverse-engineered) |
Adding a provider means adding one module under
crates/history-core/src/providers/ and
registering it in mod.rs — the daemon, hub and viewer pick it up for free.
src-tauri is no longer a shipped artifact, but it is not dead code: it's the
local tool you build from source when you want to read this machine's history
without a hub, or export a session.
# Headless HTTP server — browse this machine's local history in a browser
just serve-build-run # → http://localhost:3727 (token printed to stderr)
# Headless export — no GUI, no webview; writes and exits
cargo run -p claude-code-history-viewer -- \
--export <session-id|/abs/path.jsonl> --format html --output report.html| Flag | Description |
|---|---|
--serve |
Start the HTTP server instead of the GUI. Takes --port, --host, --dist, --token, --no-auth. Requires a --features webui-server build. |
--export <id|path> |
Render one session to HTML or JSON (--format, --output) and exit. Session ids resolve under ~/.claude/projects; an unambiguous prefix is accepted. |
--session <uuid|prefix> |
Launch the GUI pre-focused on a session. |
Server-mode details (Docker, systemd, reverse proxies, auth) are in the Server Mode Guide (한국어).
git clone https://github.com/colangelo/claude-code-history-viewer.git
cd claude-code-history-viewer
just setup # install deps, configure the build environment
just archive-web-build # static archive webapp → dist-archive/
cargo build --release -p hub -p sync-daemonRequirements: Node.js 18+, pnpm, Rust 1.80+ (the hub's embedder dependency
graph sets the floor; the other crates are fine at 1.77.2). Building src-tauri
additionally needs the platform webview toolchain — on Debian/Ubuntu,
libgtk-3-dev and libwebkit2gtk-4.1-dev.
Common recipes (just --list for the rest):
| Recipe | What it does |
|---|---|
just archive-web-build |
Static archive webapp → dist-archive/ |
just serve-build-run |
Build + run the local WebUI server |
just rust-check-all |
fmt --check + clippy + tests |
just test-run |
Frontend tests, once, verbose |
just lint |
ESLint |
just sync-version |
Propagate package.json version → Cargo workspace + Tauri config |
just tm-backfill |
Recover old history from Time Machine backups |
Tests run single-threaded on the Rust side (cargo test -- --test-threads=1) —
the settings tests set HOME process-globally.
Self-hosted, no third parties. Nothing leaves the infrastructure you run. The daemon talks only to the hub you configured; the hub talks only to your Postgres. No analytics, no tracking, no telemetry, no cloud service.
The archive is, by design, a durable copy of conversations that would otherwise expire — treat the hub and its database as sensitive, and put them behind a private network.
Upstream is this fork's parser supply chain. Each sync ports
jhlee0409's provider
fixes and new providers into crates/history-core, and fixes that belong upstream
go back as PRs against its develop branch.
The two projects have diverged in product, not in parsing:
Upstream (jhlee0409) |
This fork | |
|---|---|---|
| Shape | Desktop app (macOS/Windows/Linux) + optional headless server | Archive stack: daemon → hub → Postgres → static web UI |
| Scope | One machine's local files | Every machine, centrally, cumulatively |
| Distribution | .dmg / .exe / .AppImage, Homebrew cask, auto-updater |
Hub binary + cchv-webapp.tar.gz on each release; build the rest from source |
| Versions | v1.x |
cchv-vX.Y.Z (0.x — pre-stable, dogfood tier) |
Upstream's v1.x tags are fetched for the parser supply chain but are not ours;
CHANGELOG.md is upstream's release history. For this fork's
history, see git tag -n 'cchv-v*' and the
Releases page.
The desktop distribution is retired here; the desktop dependency is not.
src-tauri still compiles the full webview stack and its GUI path still runs —
see AGENTS.md for exactly what that does and does not mean before you act on it.
This fork is primarily a personal, dogfooded archive, so the useful contribution paths are:
- Provider parsers and parser fixes → upstream. They benefit everyone, and they flow back here on the next sync.
- Archive stack (hub, daemon, protocol, webapp) → here. Non-trivial changes
are specced through OpenSpec first (
openspec/changes/<name>/) — seeAGENTS.md.
Before committing:
pnpm tsc --build . && pnpm vitest run && pnpm lint && pnpm run i18n:validate
just rust-check-allMIT — free for personal and commercial use. Copyright for the original work remains with JaeHyeok Lee; fork changes are MIT under the same terms.

