Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

267 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Decant

CI OpenSSF Scorecard License: Apache 2.0

Your coding agent sessions contain a lot of useful information. What tokens got spent on, how full the context window got, which files an agent worked with, and what a run cost. Decant gives you insight into what's on disk, right now.

Built by Dosu for developers who want their agents to learn from real work.

It reads the JSONL logs Claude Code and Codex already write (~/.claude/projects/*.jsonl, ~/.codex/sessions/rollout-*.jsonl), normalizes both formats into one WAL + FTS5 SQLite archive, and gives you full-text search, token economics, context-window occupancy, and phase breakdowns from a CLI or a local web UI. Your transcripts never leave your machine.

The current Decant Analytics UI showing the command palette, report and share controls, session and token totals, activity economics, and daily charts. Rendered from the repository's synthetic fixtures.

Features

  • One archive for Claude Code and Codex sessions.
  • Full-text search across messages and tool calls.
  • Usage, estimated cost, tool, MCP, file-hotspot, and activity analytics.
  • Deterministic distill artifacts from real command history: scripts, replays, and skill/AGENTS snippets.
  • Persisted recommendations with implemented-state tracking.
  • Local React UI served by the same Bun process: decant serve.
  • Watch mode with native filesystem events plus a periodic sweep.
  • Scriptable JSON output, shell completions, and stable exit codes.

Quick Start

Run Decant with npx—no Bun install or global package required:

npx @dosu/decant@latest serve

Open http://127.0.0.1:3000. The first run syncs the Claude Code and Codex logs already on your machine, then keeps watching them for changes. npx downloads the launcher and matching native binary for this run without installing a persistent decant command.

Use the same package for one-off CLI commands:

npx @dosu/decant@latest sync
npx @dosu/decant@latest ls
npx @dosu/decant@latest search "auth bug"

For a persistent installation:

npm install --global @dosu/decant@latest
decant serve

Install it with Homebrew:

brew tap dosu-ai/dosu && brew install decant
decant serve

Install it without Node, straight from the release assets:

curl -fsSL https://raw.githubusercontent.com/dosu-ai/decant/main/install.sh | sh
decant serve

Run the GHCR image:

docker run --rm \
  -p 127.0.0.1:3000:3000 \
  -v decant-data:/var/lib/decant \
  -v "$HOME/.claude/projects:/sources/claude:ro" \
  -v "$HOME/.codex:/sources/codex:ro" \
  ghcr.io/dosu-ai/decant:latest

To run from source instead, install Bun 1.3+ and use bun run dev. It performs a frozen dependency install, starts decant serve, runs the startup sync, and keeps watching your source logs. See Install Matrix for the full set of options.

Keep the 127.0.0.1: host prefix on the Docker port publish. Publishing as -p 3000:3000 exposes the archive port on every host interface. The container binds 0.0.0.0 only inside its own network namespace so Docker's host loopback publish can reach it.

The API is unauthenticated, so the peer's source address is the boundary. The image ships no peer allowlist. It sets DECANT_TRUST_DEFAULT_GATEWAY=1, which trusts exactly one address: the container's own bridge gateway, which is where Docker's port publisher forwards -p traffic from. It does that only when Decant can prove the default route is a container veth to an on-link gateway inside 172.16.0.0/12. A sibling container on the same bridge keeps its own source address and gets 403 forbidden remote.

Shapes that cannot be proven trust nobody beyond loopback and need one env var: --network host, macvlan/ipvlan, multi-homed hosts, and bridges outside 172.16.0.0/12 (Podman's 10.88.0.0/16, a custom --subnet, Kubernetes). If the UI answers 403 forbidden remote, set DECANT_TRUSTED_PEERS to the exact address requests arrive from, for example -e DECANT_TRUSTED_PEERS=192.168.65.1. That replaces the gateway default entirely; DECANT_TRUST_DEFAULT_GATEWAY=0 turns it off without naming a peer.

CLI

decant sync
decant ls
decant show 1
decant search "auth bug"
decant stats --by model
decant files --group ext
decant tool stats
decant mcp stats
decant distill script
decant distill replay 1
decant distill skill --kind agents
decant recommendations ls
decant export 1 > session.md
decant export 1 --as trajectory > session.trajectory.json
decant completion zsh

All read commands support --json. Use --db /path/to/decant.db or DECANT_DB for an alternate archive. Use decant sync --path /path/to/log-or-dir to ingest only specific source files or a temporary source tree, including raw Claude stream-json logs; pass --path more than once to ingest multiple paths.

Configuration

  • DECANT_DB: archive path, default ~/.decant/decant.db.
  • DECANT_CLAUDE_DIR: Claude projects directory, default ~/.claude/projects.
  • DECANT_CODEX_DIR: Codex home directory, default ~/.codex.
  • DECANT_CONFIG_DIR: settings directory, default ~/.config/decant.
  • DECANT_LOG_LEVEL: minimum structured operational log level written as JSON Lines to stderr. Defaults to info; accepts trace, debug, info, warn (or warning), error, fatal, and off (or silent).
  • DECANT_NO_SYNC: set to any value to skip the sync-on-read that read commands perform, and to run decant serve without its source watcher so it serves the archive as-is and ingests nothing. The --no-sync flag does the same. The "Sync now" button and POST /api/sync still work; this only suppresses the syncing Decant starts on its own.
  • DECANT_TRUSTED_PEERS: comma-separated peer IPs or IPv4 CIDRs allowed through the local API guard when serve is bound to a non-loopback host. Unset by default. Keep it as narrow as the deployment allows: every listed address, and every address inside a listed CIDR, reads and writes the whole archive without a credential.
  • DECANT_TRUST_DEFAULT_GATEWAY: set to 1 to trust this container's own bridge gateway, resolved once at startup from /proc/net/route and /sys/class/net. Off by default and off for any other value, including 0. The derived peer is a single address, and only when the default route is a container veth pointing at an on-link gateway inside 172.16.0.0/12; every other shape resolves to no peers. The container image turns this on so the documented docker run recipe works without a peer CIDR.

Precedence is replace, not merge: --trusted-peer wins over DECANT_TRUSTED_PEERS, which wins over DECANT_TRUST_DEFAULT_GATEWAY. Setting either peer source, including DECANT_TRUSTED_PEERS= with an empty value, means no gateway is derived.

decant serve binds 127.0.0.1:3000 by default. Override with --host/--port. There is no authentication, so bind loopback unless you deliberately want other hosts in: the Host header check is not an access control for non-browser clients, which can send Host: localhost freely.

Archives older than schema v8 are rebuild-only. v8 through v20 archives migrate forward to v21 on open; the next decant sync backfills persisted economics vectors and context-window rollups for unchanged sessions. Older archives should be deleted and rebuilt with decant sync. Source logs remain the source of truth, so nothing is lost by rebuilding.

Integrating with Decant

decant serve exposes a documented local API at http://127.0.0.1:3000/api. It has no authentication on the default loopback listener, so any local process can read or mutate the archive. Keep the listener on loopback unless you have deliberately configured the trusted-peer boundary described under Configuration.

The source OpenAPI 3.1 contract is docs/api/openapi.yaml. A running server exposes the same contract as JSON at http://127.0.0.1:3000/api/openapi.json.

For example, read the archive summary:

curl --fail --silent --show-error \
  http://127.0.0.1:3000/api/stats/summary

See docs/api/routes.md for local access-control semantics, write-request requirements, and Server-Sent Event names.

How It Works

~/.claude + ~/.codex
        |
        v
 Bun + TypeScript Decant process
 parse -> enrich -> ingest + economics vectors -> SQLite WAL + FTS5
        |
        +--> CLI reads / JSON
        +--> local React UI + JSON routes + SSE

There is no background daemon or separate service process. The old Rust/Phoenix/Swift implementation is preserved in the signed pre-typescript tag.

The local API contract lives in docs/api/openapi.yaml, with operational semantics in docs/api/routes.md. Operational log fields and privacy rules live in docs/logging.md. Distribution notes live in docs/distribution.md, and the tag-driven release pipeline is documented in docs/releasing.md.

Install Matrix

Ordered from the most streamlined to the most manual. Everything except "build from source" is available from v0.1.0, once the Release workflow has published a version. See docs/distribution.md for build details and per-artifact verification.

  1. npx / npm — zero persistent install, or a global one:

    npx @dosu/decant@latest serve
    npm install --global @dosu/decant@latest   # persistent, then use `decant`
  2. Homebrew — via the dosu-ai/dosu tap:

    brew tap dosu-ai/dosu && brew install decant
    brew install dosu-ai/dosu/decant   # equivalent one-liner
  3. Install script — for machines without Node:

    curl -fsSL https://raw.githubusercontent.com/dosu-ai/decant/main/install.sh | sh

    Detects your platform, downloads the matching release tarball, verifies it against SHA256SUMS, and installs to ${DECANT_INSTALL_DIR:-~/.local/bin}. Pin a version with DECANT_VERSION, skip the PATH edit with DECANT_NO_MODIFY_PATH=1, or point DECANT_BASE_URL at a mirror — see docs/distribution.md for the full knob list.

  4. Docker — the ghcr.io/dosu-ai/decant image, per the docker run recipe in Quick Start. Keep the 127.0.0.1: host prefix on the port publish.

  5. Build from source — the contributor path, and the only one that works before v0.1.0:

    bun run dev

macOS, tarball downloads only. v0.1.0 binaries are ad-hoc signed but not notarized, so a tarball downloaded through a browser carries com.apple.quarantine and Gatekeeper blocks the first run. Clear it with xattr -d com.apple.quarantine ./decant. brew install, npx @dosu/decant, and the install script are unaffected, because none of them set the quarantine attribute. Integrity comes from SLSA build provenance (published with each release as an offline-verifiable decant-<version>.sigstore.json bundle), SHA256SUMS, and the gh attestation verify check install.sh runs.

Upgrading. npx @dosu/decant@latest always resolves the newest published version. For a persistent install, run npm i -g @dosu/decant@latest, brew upgrade decant, re-run the install script, or docker pull ghcr.io/dosu-ai/decant:latest. A source checkout reports git describe --tags --always --dirty from decant --version (for example, v0.1.0-8-ge9a0c00-dirty); a non-Git unstamped build falls back to dev. Released artifacts carry their injected release version.

Development

Run the local UI and watcher:

bun run dev

Run quality gates:

bun test
bunx tsc --noEmit
bunx biome check .
just check

Build distribution artifacts:

bun run scripts/build-binaries.ts --target native
bun run scripts/build-npm.ts --target native --no-build
docker build --platform linux/amd64 -t decant:local .

See AGENTS.md for the full command list, conventions, and project invariants.

Security and Privacy

Decant is local-first and offline at runtime. It reads files already on disk and makes no outbound runtime network calls. Do not commit real session data, personal archives, tokens, keys, or .env files. To report a vulnerability, see SECURITY.md.

License

Licensed under the Apache License 2.0.

About

Local-first analysis of Claude Code and Codex sessions: token spend, context windows, files touched, and cost.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages