claudeers.
// Developer Tools

recensa

Self-hosted web viewer for Claude Code session transcripts — read, search, replay, and audit every session you have ever run

// Developer Tools[ cli ][ api ][ desktop ][ web ][ mobile ][ claude ]#claude#agents#ai-agents#anthropic#chat-history#claude-code#conversation-history#developer-tools#devtoolsMIT$open-sourceupdated about 1 month ago
Actively maintained
100/100
last commit about 1 month ago
last release about 1 month ago
releases 3
open issues 0
// star history1 this week

Install with your AI

Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.

Install and set up recensa (git-clone project) into my current project.
Found on https://claudeers.com/recensa
Repo: https://github.com/S40911120/recensa
Homepage/docs: https://hub.docker.com/r/s40911120/recensa
Detected install method: git-clone → git clone https://github.com/S40911120/recensa
Category: devtools. Platforms: cli, api, desktop, web, mobile.
Read the repo's README for exact setup and env vars, then install it and wire it into my project.

Claudeers Health Verdict:
active; community-verified: false. Confirm the source before running anything.
// or clone
git clone https://github.com/S40911120/recensa

// compatibility

Platformscli, api, desktop, web, mobile
Operating systems
AI compatibilityclaude
LicenseMIT
Pricingopen-source
LanguageTypeScript

Recensa

Recensa

See what your agents actually did.
A self-hosted viewer for your Claude Code session transcripts — read, search, replay, and audit every session you have ever run. Your transcripts stay on your machine; nothing is uploaded anywhere.

Quick start · Tour · Why · Themes · Security · Architecture · Contributing

Every Claude Code session you run is written to disk as raw JSONL under ~/.claude/projects/ — the full conversation, every tool call, the model's thinking, every subagent it spawned. Then it just sits there, unreadable. Want to know how you fixed that bug last month? What that 40-minute agent run actually changed? Which of your prompts sent it down a dead end? Good luck grepping.

Recensa indexes those transcripts into a local SQLite database and serves a three-pane reader at http://localhost:7788. Reading never modifies your transcripts, and the index is a disposable cache you can rebuild at any time.

Recensa's three-pane reader: a session list on the left, a virtualized conversation stream in the middle, and a session info panel on the right

Quick start

You need Docker (Docker Desktop on Windows/macOS, Docker Engine on Linux) and a ~/.claude directory with some sessions in it.

Try it — run the prebuilt image straight from Docker Hub:

# macOS / Linux
docker run -d --name recensa -p 127.0.0.1:7788:7788 \
  -v "$HOME/.claude:/data/claude" \
  -v recensa-data:/app/backend/data \
  -e RECENSA_PROJECTS_DIR=/data/claude/projects \
  s40911120/recensa
# Windows — use PowerShell, not Git Bash (MSYS rewrites the /data/... paths)
docker run -d --name recensa -p 127.0.0.1:7788:7788 `
  -v "$env:USERPROFILE\.claude:/data/claude" `
  -v recensa-data:/app/backend/data `
  -e RECENSA_PROJECTS_DIR=/data/claude/projects `
  s40911120/recensa

Or run it from source with Compose:

git clone https://github.com/S40911120/recensa.git && cd recensa
cp .env.example .env             # set CLAUDE_HOME to your .claude path
docker compose up -d --build

Then open http://localhost:7788. The first index runs in a background worker and can take minutes to tens of minutes depending on how many sessions you have — the UI is usable while it runs, and progress shows in a floating pill in the corner.

A tour

Search all of it, not the last five. Claude Code's resume picker shows a handful of recent sessions. Recensa's full-text search (SQLite FTS5, CJK-aware) covers every session you have ever run — "how did I fix that S3 retry bug?" is one query away.

Full-text search across all sessions, with highlighted snippet matches

Follow the conversation, not the noise. One session can hold thousands of tool calls. God-view folds tools, thinking, and system noise into landmark rows you can expand on demand. Async tool results are paired back to their call with a shared colored rail, so a tool_result landing twelve messages later still reads as one step.

Tool calls and their asynchronous results linked by a shared colored rail, with the result row echoing the call summary

Or cut the noise down two ways. The Main toggle (top right) folds every tool call and thinking block away, leaving only your prompts and the model's replies. Focus mode (the tab at the bottom) hides the app chrome itself — panels, header, breadcrumb — for full-width, distraction-free reading. Esc exits.

Focus mode: the side panels and header are hidden, leaving the full-width transcript with the Focus On tab at the bottom

See exactly what changed. Every Edit in a session renders as a real diff — before in red, after in green, per-file, with timestamps. Audit an agent's changes without leaving the reader.

Per-file edit history rendered as unified diffs with red before and green after blocks

Know what a session cost. Per-session statistics break down tool usage, files created / edited / deleted, model mix, token flow, and an estimated dollar cost.

Session statistics: tool distribution donut and bars, file change counts, model usage ranking, token totals and estimated cost

Audit multi-agent runs. When a session spawns subagents or teammates, Recensa rebuilds the roster from the transcripts: every agent with its type, message count, and model in the info panel, a crosshair that jumps to the exact call site where it was spawned, and an "Open subagent transcript" deep-link into the agent's own conversation — and back.

A multi-agent review session: the info panel lists four teammates and two subagents, each with agent type, message count, model, and a jump-to-call-site control; the conversation shows deep-link buttons into each spawned agent's transcript

Navigate compaction like it never happened. When Claude Code compacts a long session, the pre-compaction text is gone from the working context — but not from the transcript chain. Recensa marks every compaction point in the stream, lists them in a Compaction History panel so you can jump straight to any boundary, tracks your read position within the current compaction segment, and can reconstruct the full pre-compaction conversation, merged across the fork/resume chain, as a read-only view.

A twice-compacted session: a compaction-point landmark in the stream, the Compaction History panel with jump links to both boundaries, a Reconstruct-full-conversation button, and the breadcrumb showing the read position within the current compaction segment

The reconstructed view: the full pre-compaction conversation merged along the fork/resume chain, presented as a read-only snapshot

Keep your reading state. Pin, tag, rename, and annotate sessions. Your curation lives in its own database (flags.db) that index rebuilds never touch. Live sessions update in real time over WebSocket while Claude Code is still writing them.

The reader in GitHub Light: pinned and tagged sessions in the list, notes and stats in the info panel

Reads fine on a phone. The layout is responsive: on narrow screens the session list and the info panel become swipe-in drawers around the conversation, so checking what an agent did from the couch actually works.

Recensa on a phone: the session list drawer on the left, the conversation in the middle, and the info panel drawer on the right

Everything else

The smaller things you notice after a day of use:

  • Advanced search filters — narrow a query by session-id prefix, model, or a date window; filter the session list by project or to pinned-only.
  • Copy the resume command — one button in the info panel yields the exact claude --resume invocation for the session, ready to paste into a terminal.
  • Rich tool rendering — TodoWrite checklists, AskUserQuestion choices, and Bash output render as structured blocks, not JSON dumps; Markdown and syntax highlighting throughout.
  • Position that survives navigation — jumping into a subagent and back restores your exact scroll position; every session remembers how far you have read.
  • Keyboard-friendlyj/k walk messages, / focuses search, g/Shift+G jump to the ends, ? shows the full palette; skip-links cover the reader and info panel.
  • Copy anything — per-message and per-code-block copy buttons.
  • Custom titles, reading font size — rename cryptic sessions; tune the reader.
  • Indexing that stays out of the way — first index runs in a worker with a progress pill; the UI stays responsive while it fills in.

Why Recensa

Claude Code gives you /context, /resume, and /compact; everything behind them stays a black box — the raw JSONL, the token breakdown, the tool-call chains, the point where a run looped or failed. A real run also spawns a team of subagents, and none of that is visible in Claude Code or Claude Desktop. Recensa rebuilds the full picture from the transcripts already on your disk:

  • Raw JSONL is unreadable. Recensa renders it as a conversation you can follow.
  • The resume picker only shows the last few. Full-text search covers all of them.
  • Noise buries the signal. God-view folds thousands of tool calls into landmark rows and keeps the main line readable.
  • You can't tell what a run cost. Stats break down tools, files, models, tokens.
  • Fork and resume chains fragment history. Recensa reconstructs the full pre-compaction conversation across merged transcript files.
  • One bad structural edit can brick a transcript (a permanent API 400). Recensa reads without side effects; structural repair is a separate, guarded tool (@recensa/claude-session).

Non-goals

  • Not an editor. It never rewrites transcript content. The only write path to your transcripts is the explicit, guarded session delete described in Security and your data.
  • Not a cloud service. No accounts, no telemetry, no external network calls.
  • Not a Claude Code replacement. It reads the transcripts Claude Code already writes to disk.
  • Not a general log viewer. Built for Claude Code session JSONL, not arbitrary logs.

Themes

Fourteen themes — four light, ten dark — as color-scheme homages to classic editor palettes: Dracula, Tokyo Night, Nord, Gruvbox, One Dark Pro, Monokai, Catppuccin (Latte and Mocha), Solarized (Light and Dark), Rosé Pine, GitHub (Light and Dark), and the warm-light Classic. The active theme is injected server-side into the HTML, so there is no flash of the wrong theme on load.

Tokyo NightDraculaNord
Tokyo NightDraculaNord
Catppuccin MochaSolarized LightGitHub Light
Catppuccin MochaSolarized LightGitHub Light

Troubleshooting

The reader loads but the session list is empty, or the logs show an empty mount. CLAUDE_HOME did not resolve to your .claude directory. On Windows, Compose's ${HOME} fallback is usually unset under PowerShell and cmd, so the bind mount comes up empty. Set CLAUDE_HOME explicitly in .env to an absolute path with forward slashes (C:/Users/you/.claude), then rebuild with node backend/scripts/ops.js up. If you used the docker run one-liner from Git Bash instead of PowerShell, MSYS path rewriting is the likely culprit — rerun it from PowerShell.

docker compose up fails with "port is already allocated", or 7788 never responds. Something else on the host already holds 7788. Publish on a free port by setting RECENSA_HOST_PORT in .env, then rebuild:

RECENSA_HOST_PORT=7799

The container always listens on 7788 internally; only the host-side port changes, so open http://localhost:7799. Running from source instead of Docker? Set SESSION_VIEWER_PORT — that is the port the server process itself binds.

Custom hostname (optional). To reach it at http://sessions.local:7788, add a line to your hosts file (as administrator): 127.0.0.1 sessions.local.

Operations

All operations go through one entry point:

node backend/scripts/ops.js <command>
CommandWhat it does
upBuild the image and start the container (docker compose up -d --build)
downStop and remove the container
restartRestart the running container
logsFollow container logs
statusContainer state and health
smokeEnd-to-end smoke test (health / list / paging / host allowlist)
checkType-check, lint, and unit tests, backend and frontend
backfill-chainRecompute derived columns after a schema change (runs in the container)
vacuumShrink the DB file after large deletes or an FTS migration (runs in the container)

Architecture

Recensa architecture: the host's ~/.claude/projects JSONL, read-only bind-mounted into a single Docker container — ingestion (a live-tail watcher and an incremental index worker) into a JSONL parser, SQLite storage (viewer.db rebuildable cache plus flags.db user data), a Node.js/Express backend (repositories, services, routes behind a Host/Origin allowlist) that delegates to the @recensa/claude-session npm engine, and a React frontend served to the browser over HTTP and WebSocket

Two databases are kept deliberately separate. viewer.db is fully derived from the JSONL and can be deleted and rebuilt at any time. flags.db holds your pins, tags, and notes, which cannot be recomputed, so index rebuilds never touch it.

Session-JSONL parsing, verification, and reconstruction are delegated to @recensa/claude-session, a zero-dependency npm package that also works standalone as a CLI.

Data integrity is checked mechanically. An independent recompute of msg_count, tokens, tool calls, and the model list against the raw JSONL matches the index across sampled sessions, and the msg_count alignment invariant is locked by unit tests.

Configuration

Set these two in .env before the first run (Compose path):

Env varDefaultMeaning
CLAUDE_HOME(required)Absolute host path to your .claude directory. Its projects/ subdirectory holds the session JSONL. Bind-mounted into the container. On Windows use forward slashes (C:/Users/you/.claude).
RECENSA_HOST_PORT7788Host port to publish. Override only if 7788 is already taken. The container always listens on 7788 internally.

Advanced overrides (for running from source or remote self-hosting):

Env varDefaultMeaning
SESSION_VIEWER_PORT7788HTTP/WS port the server listens on.
SESSION_VIEWER_HOST127.0.0.1Bind address. The container sets 0.0.0.0 so the published loopback port can reach it.
RECENSA_PROJECTS_DIR~/.claude/projectsRoot of the session JSONL tree (shared with the recensa-session CLI). The container sets /data/claude/projects.
RECENSA_ALLOWED_HOSTS(none)Extra comma-separated hostnames added to the Host/Origin allowlist. Only relevant behind your own reverse proxy (see Security).
RECENSA_WATCH_MODE(auto)Watch strategy: events (native fs events) or tick (polling). Auto-selects tick inside Docker, where bind-mount fs events are unreliable.

Security and your data

Recensa is built for single-machine, local use. It ships no authentication.

  • Loopback bind is the boundary. By default the server binds 127.0.0.1 and Docker Compose publishes the port to 127.0.0.1 only. On one machine, that loopback bind is what actually keeps the service private.
  • The Host/Origin allowlist does two jobs, and neither is a login. It is primarily DNS-rebinding defense: it stops a malicious web page from using your browser to reach the loopback server. It additionally gates cross-origin writes (next bullet). It is still not access control: a non-browser client that can reach the port can forge the Host and Origin headers and walk past it.
  • Mutating requests require an allowlisted Origin (CSRF write-gate). Every state-changing method (POST/PUT/DELETE/PATCH) must send an Origin header that is present and on the allowlist; GET/HEAD keep it optional. A browser always attaches Origin to a same-origin mutating fetch and cannot forge it cross-site, so another site open in your browser cannot drive writes against your local server. This closes the cross-origin write path; it does not replace auth.
  • Do not expose Recensa on 0.0.0.0 or a public interface without your own auth and a reverse proxy (TLS) in front of it. Adding names via RECENSA_ALLOWED_HOSTS widens the rebinding allowlist; it does not add access control.

Your transcripts:

  • Reading never writes. The indexer, parser, and reconstruction paths only read your JSONL.
  • One write path exists: deleting a session. DELETE /api/session/:id physically removes the session's JSONL file and its subagents directory from disk. This is irreversible. It is guarded: pinned sessions, sessions that belong to a fork/resume chain, and individual subagents are all refused. Nothing else writes to your transcripts.
  • Your curation is safe. Pins, tags, notes, and titles live in a separate flags.db that index rebuilds never touch. The viewer.db index is a disposable cache.

No secrets, no telemetry, no external network calls.

Uninstall

Your pins, tags, notes, and titles live in flags.db, inside the Docker named volume recensa-data. That data is not derived from the transcripts and cannot be recomputed, so back it up before removing anything you want to keep:

# with the container still present, copy flags.db out of the volume
docker cp recensa:/app/backend/data/flags.db ./flags.db.backup

Then stop and remove the container, and optionally the image:

node backend/scripts/ops.js down        # stop and remove the container
docker image rm s40911120/recensa       # optional: drop the pulled image

down leaves the recensa-data volume in place, so a later rebuild keeps your curation. docker compose down -v also deletes that volume — which destroys flags.db permanently. The viewer.db index in the same volume is a disposable cache that rebuilds from your JSONL, so losing it only costs the next reindex. Your transcripts under ~/.claude are never touched by uninstalling.

Contributing

Contributions are welcome. To get a dev environment running:

docker compose up -d --build          # build and start the container
node backend/scripts/ops.js check     # type-check, lint, and unit tests (must pass)

The container serves the built frontend and runs the server; rebuild with node backend/scripts/ops.js up after changes. Keep pull requests focused, follow the existing Conventional Commits style, and make sure ops.js check passes before opening a PR. See CONTRIBUTING.md for details.

License

MIT. See LICENSE. Author: Justin Chen.

// faq

What is recensa?

Self-hosted web viewer for Claude Code session transcripts — read, search, replay, and audit every session you have ever run. It is open-source on GitHub.

Is recensa free to use?

recensa is open-source under the MIT license, so it is free to use.

What category does recensa belong to?

recensa is listed under devtools in the Claudeers registry of Claude-compatible tools.

1 views
69 stars
unclaimed
updated about 1 month ago

// embed badge

recensa on Claudeers
[![Claudeers](https://claudeers.com/api/badge/recensa.svg)](https://claudeers.com/recensa)

// retro hit counter

recensa hit counter
[![Hits](https://claudeers.com/api/counter/recensa.svg)](https://claudeers.com/recensa)

// reviews

// guestbook

0/500

// related in Developer Tools

🔓

The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Curs…

// devtoolsaffaan-m/JavaScript240,305MIT[ claude ]
🔓

Use Garry Tan's exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA

// devtoolsgarrytan/TypeScript128,278MIT[ claude ]
🔓

AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more). Turn any folder of code, SQL schemas, R scripts, shell scripts, docs,…

// devtoolssafishamsi/Python109,277MIT[ claude ]
🔓

Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote.

// devtoolsDietrichGebert/JavaScript104,762MIT[ claude ]
→ see how recensa connects across the ecosystem