12 KiB
lean-ctx vs Headroom
Last updated: July 2026 | Both expose a drop-in
compress(messages, model), but that shared surface hides the real difference: Headroom is a stateless compression library; lean-ctx is a stateful context-engineering layer that remembers, proves, and (next) replays the context it shapes.
Overview
| lean-ctx | Headroom | |
|---|---|---|
| Approach | Local context-engineering layer with a deterministic compression funnel | Compression library + proxy with optional ML compression |
| Drop-in API | compress(messages, model) (Py + TS) |
compress(messages, model) (Py + TS) |
| Runtime | Single Rust binary + loopback daemon | Python package (headroom-ai) / Node package |
| License | Apache-2.0 | Apache-2.0 |
| Determinism | Byte-stable output, prompt-cache safe (#498) | Not a stated contract |
| Locality | 100% local, no telemetry by default | Local library; proxy/ML modes optional |
| Beyond compress() | 80 MCP tools, session memory, code intelligence | Cross-agent memory, headroom learn, ML compression |
The core difference
Headroom is a compression library first: compress() inline, a transparent
proxy for zero-code integration, an MCP server, and an optional ML compressor
("Kompress", requires torch). Its reach comes from a broad set of framework
wrappers (LiteLLM, LangChain, Agno, Strands) and agent-wrap commands.
lean-ctx is a context-engineering layer. The same compress() contract is
one surface of a Rust daemon that also does cached file reads (10 modes), 95+
shell-output compression patterns, hybrid semantic search, call-graph/impact
analysis, and a temporal knowledge graph — all 100% local, behind 29 published
stability contracts. Its /v1/compress is deterministic by contract: the
same (messages, model) produces byte-identical output, so Anthropic (90%) and
OpenAI (50%) prompt-cache discounts survive compression.
The gap is structural, not cosmetic. A compress() call is stateless by design:
it sees one message list and returns a shorter one. lean-ctx keeps a stateful
record around that call — a Context Ledger of why each item was kept or dropped
(with Φ-scores), a signed Context Proof of what the model saw, plus session
memory and a temporal knowledge graph that persist across runs. That state is the
foundation for the Context Time Machine (direction; see
docs/concepts/context-time-machine.md): a
git-anchored, signed snapshot you can rewind, reproduce, resume, or share. A
stateless library has nothing to anchor such a timeline to.
Feature comparison
| Feature | lean-ctx | Headroom |
|---|---|---|
Drop-in compress() (Py + TS) |
Yes | Yes |
| Transparent proxy | Yes (multi-provider) | Yes |
| MCP server | 80 tools | headroom_compress/retrieve/stats |
| Reversible (reference retrieval) | CCR (#482/#493) + /v1/references/{id}, ctx_expand/ctx_retrieve |
headroom_retrieve store |
| Deterministic / prompt-cache safe | Yes (#498, CI-guarded) | Not stated |
| Vercel AI SDK middleware | leanCtxMiddleware / withLeanCtx |
headroomMiddleware / withHeadroom |
| LiteLLM hook | LeanCtxLiteLLMHandler |
HeadroomCallback |
LiteLLM proxy guardrail (pre_call sidecar) |
Yes — /v1/compress speaks the guardrail wire contract (#700) |
Yes — native guardrail: headroom (July 2026) |
LiteLLM CCR agentic loop (hash= markers + /v1/retrieve/{hash}) |
Yes — regex-locked contract test (#702) | Yes (native) |
| LangChain | compress_messages + retriever |
wrap model |
| ML / learned compression | No (deterministic by design) | Yes (Kompress, torch) |
| JSON array crusher (row dedup) | json_crush — lossless + opt-in lossy with CCR, deterministic (#935) |
Smart Crusher (statistical) |
| Active prompt-cache breakpoints | Anthropic cache_control injection, opt-in (#939) |
Cache aligner |
| Volatile-field cache alignment | Detector (#940) + opt-in tail-relocate (#974), deterministic | Cache aligner (relocate) |
| Compression-learning loop | Retrieve-coupled session backoff, deterministic (#941) | CCR learning |
| Cross-agent shared memory | Knowledge graph + handoff | SharedContext |
| File-read compression | 10 modes, ~13-token cached re-reads | — |
| Shell-output compression | 95+ patterns | — |
| Semantic search / call graph | Hybrid BM25+vector / multi-hop | — |
| Single binary | Rust | Python / Node |
Compression: deterministic funnel vs ML
lean-ctx's /v1/compress runs every text payload through a deterministic funnel
(dedup, structural prose squeeze, tool-output patterns). Because it is rule-based
it is reproducible and cache-stable, but it does not learn — highly novel
prose compresses modestly, while repetitive tool output, logs and RAG dumps
compress heavily (the same engine reaches up to ~99% on file reads and powers 95+
shell patterns).
For the array-of-objects JSON that dominates API responses, DB dumps and RAG
chunks, lean-ctx ships json_crush (#935) — the deterministic counterpart to
Headroom's statistical Smart Crusher. It hoists every key shared across all rows
to a single _defaults block and keeps only per-row deviations, so the lossless
stage is exactly reconstructible; an opt-in lossy stage drops near-unique
high-entropy columns (timestamps, UUIDs) only behind a content-addressed
ctx_expand handle, so a dropped datum is never irrecoverable. Unlike a
statistical crusher, the output is a pure function of the input (no mean/stddev
sampling), so it stays byte-stable and cache-safe. The accuracy floor is
CI-guarded: a model-free A/B gate (Condition::JsonCrush, #942) proves the crush
keeps every gold answer while cutting tokens on a redundant payload.
Headroom additionally offers an ML compressor (Kompress) for prose, at the
cost of a torch dependency and non-deterministic output.
Rule of thumb: choose lean-ctx when the payload is code, tool output, logs or RAG context and you need local, deterministic, cache-preserving output; consider Headroom's ML mode when you specifically need learned prose compression.
Reversibility
Compression is only safe if the model can get the original bytes back when it needs them. lean-ctx never throws content away — it moves it to a content-addressed store and leaves a deterministic handle. There are five recovery paths, so reversibility holds whether you drive lean-ctx as a proxy, an SDK or an MCP server:
- Archive +
ctx_expand— any truncated tool output keeps an archive id; the agent callsctx_expand(id, …)(orhead/tail/grep) to stream the rest. ctx_retrieve— fetches the verbatim original for a stored reference id.- Proxy CCR (#482) — when the proxy prunes an old
tool_result, it persists the verbatim original to the shared tee store and embeds the file path as the retrieval handle, recoverable with the agent's native file read (no MCP needed). The handle is a pure function of the content hash, so it never breaks the prompt cache (#448). - In-band CCR (#493) — for a remote proxy with no shared filesystem, the stub
advertises a compact
<lc_expand:HASH>marker; when the model echoes it, the proxy splices the verbatim original back inline next turn. GET /v1/references/{id}— an HTTP endpoint that resolves a reference id to its original, for SDK/HTTP clients.
All five are deterministic and content-addressed (see
rust/src/proxy/ccr.rs). Headroom is also
reversible — via its headroom_retrieve store — so any third-party table that
lists lean-ctx as "Reversible: No" is simply out of date.
Benchmark (reproduce it yourself)
Numbers depend entirely on the corpus, so we ship a harness instead of cherry-
picked figures. It runs both libraries over the same files with the same
tokenizer and emits JSON (ratio + latency). A tool that is not installed is
reported available: false — never estimated.
# Optional head-to-head + accurate tokens:
pip install headroom-ai tiktoken
# lean-ctx side needs the daemon with /v1/compress:
lean-ctx dev-install
python bench/compress/benchmark.py --corpus docs/ --model gpt-4o --out report.json
A daemon-free lean-ctx data point (deterministic funnel, o200k_base) over this
repo's 27 docs/reference/*.md files, via
cargo test -p lean-ctx --lib proxy::compress_api::tests::bench_real_corpus_o200k -- --ignored --nocapture:
{ "files": 27, "original_tokens": 69594, "compressed_tokens": 57615,
"tokens_saved": 11979, "saved_pct": 17.2, "tokenizer": "o200k_base" }
Prose docs are a conservative corpus; tool-output / log payloads — the common
agent case — compress far more. See bench/compress/.
Where Headroom leads
- Momentum & mindshare — a fast-moving, popular library with broad adoption,
amplified by the native LiteLLM guardrail (July 2026):
guardrail: headroomships in LiteLLM ≥ v1.92, giving Headroom first-mover distribution on every LiteLLM gateway. (lean-ctx speaks the same sidecar wire contract — see below — so the channel is open to both; the mindshare is theirs.) - Learned compression — the ML (Kompress) path can beat rule-based squeezing on free-form prose.
- More framework wrappers out of the box — Agno, Strands, agent-wrap commands.
- Single-language install — pure
pip install headroom-ai, no separate daemon for the inline library path.
Where lean-ctx leads
- Determinism & prompt-cache safety — byte-stable output is a CI-guarded contract (#498); compression never breaks Anthropic/OpenAI cache discounts.
- Deterministic equivalents of Headroom's adaptive stages — the JSON crusher (#935), active Anthropic cache-breakpoint injection (#939), volatile-field cache-aligner relocate (#974) and retrieve-coupled compression-learning loop (#941) deliver Smart-Crusher / cache-aligner / CCR-learning behaviour without sampling, ML weights or non-deterministic output.
- It's a whole layer — compression is 1 of 80 MCP tools alongside cached reads, shell compression, semantic search, code intelligence and memory.
- Stateful, with a temporal axis — a Context Ledger, signed proofs, session
memory and a temporal knowledge graph wrap every compression, and are composing
into a git-anchored Context Time Machine (rewind / reproduce / resume /
share). A stateless
compress()library has no equivalent. - 100% local, single Rust binary — no Python runtime, no telemetry by default.
- Stability contracts — 29 published contracts, frozen surfaces SHA-256-locked in CI; integrations can't silently break.
When to use which
Choose Headroom if you...
- Want a pure-Python (or Node) library with no separate daemon for the inline path
- Need learned/ML prose compression and accept a
torchdependency - Use Agno / Strands or want the widest set of prebuilt framework wrappers
Running a LiteLLM gateway is not by itself a reason to pick either: the
guardrail's api_base can point at a lean-ctx daemon just as well (deterministic,
prompt-cache-safe output — see the
compress() SDK cookbook),
which makes a one-URL A/B between the two trivially easy.
Choose lean-ctx if you...
- Need deterministic, prompt-cache-preserving compression
- Want compression and cached reads, shell compression, search, and memory
- Require 100% local operation in a single binary with stability guarantees
- Are compressing code, tool output, logs or RAG context
Migration (Headroom → lean-ctx)
The contracts line up, so migration is mostly imports:
# Headroom
from headroom import compress
result = compress(messages, model="gpt-4o")
messages = result.messages
# lean-ctx
from lean_ctx import ProxyClient
result = ProxyClient().compress(messages, model="gpt-4o")
messages = result.messages # result.saved_tokens / result.saved_pct
// Headroom → lean-ctx (Vercel AI SDK)
- middleware: headroomMiddleware()
+ middleware: leanCtxMiddleware({ model: "gpt-4o" })
See the compress() SDK cookbook for full recipes.
Both projects are open source (Apache-2.0). Star counts and ML results move fast — run the benchmark on your own corpus and choose what fits.