The Problem
Teams extracting text from documents face a fragmented toolchain: separate libraries for PDFs, Office files, and images, each with its own API and output format. Kreuzberg consolidates this into a single extraction pipeline with one interface across 97+ formats, plus code intelligence for 305 programming languages.
What This Does
The Rust core (in crates/kreuzberg/) handles extraction, OCR, layout analysis, and chunking. It exposes bindings for 14 languages, a CLI (crates/kreuzberg-cli/src/main.rs), a REST API, and an MCP server. The packages/ directory contains the language-specific wrappers, e2e/ holds cross-language integration tests, and test_documents/ provides 2,405 sample files.
A plugin system (crates/kreuzberg/src/plugins/registry/processor.rs) supports custom OCR backends and post-processors. The .ai-rulez/ directory documents architecture decisions and domain-specific rules for extraction, OCR, and plugin development.
How It Is Wired
Execution starts at main in crates/kreuzberg-cli/src/main.rs, which reaches 262 functions. The shortest path to external effect is main -> parse with a filesystem read via reader.read_bytes. For server mode, serve_command in crates/kreuzberg-cli/src/commands/server.rs starts a REST API that reaches 120 functions.
The call graph shows 4,799 resolved edges. extract_file_sync (called 124 times) and extract_bytes (119 times) are the primary public APIs. is_empty is called from 207 places—the hottest function in the codebase. Key routing hubs:
crates/kreuzberg/src/core/extractor/bytes.rs— single functionextract_bytes, called from 28 files, calls into 19 otherscrates/kreuzberg/src/core/extractor/sync.rs— sync wrappers, called from 22 filescrates/kreuzberg/src/core/config/formats.rs— format dispatch, called from 84 filescrates/kreuzberg/src/utils/string_utils.rs— string helpers, called from 42 files
The counter.rs in keywords/yake/ is the most connected module (called from 109 files) despite being small—changing it has wide blast radius. The pool.rs utility manages resource pools with hit-rate tracking.
How To Use It
Setup: Install the Rust core and bindings via your package manager. The repo uses pnpm for Node packages. For Python:
pip install kreuzberg
Configuration: No mandatory environment variables. Settings live in .ai-rulez/config.yaml for development. OCR backends (PaddleOCR, PDFium) are optional plugins.
Running it: The CLI is the simplest entry point:
cargo run --bin kreuzberg -- --input document.pdf --output extracted.txt
Or use the REST API via serve_command, or the Python API:
from kreuzberg import extract_file
result = extract_file("document.pdf")
Real-World Use
A document processing pipeline for a legal firm: ingest PDFs, scanned images, and DOCX files, extract text and metadata, run OCR on scanned pages, then chunk results for embedding into a vector database. The unified API means one code path handles all formats, with language bindings for the team's stack.
Code Health & Issues
Static analysis found 45 findings (1 high, 17 medium, 27 low). The high-severity issue is duplicated code blocks across 171 files (44 repeated 6-line blocks), particularly in scripts/ and docs/snippets/. Medium issues include deep nesting in stopword language files and high branching density in check-release-assets.sh and hwp/mod.rs.
Security audit flagged 3 high-severity issues in CI/CD:
- High — GitHub Actions unpinned (
@v1,@v4tags) in.github/workflows/; tags can be moved - High —
publish.yamldiscards exit codes at lines 1949-1950, masking failures - High — automated push to default branch in
publish.yaml, bypassing PR review
Medium issues: mutable Docker base images (alpine:3.21), no dependency vulnerability scan in CI, 13 blobs over 5MB in test_documents/ (largest 59.2MB), persist-credentials not disabled on checkout, and no non-root user in the Dockerfile.
The Bottom Line
Kreuzberg is a serious, well-engineered document extraction framework with exceptional language coverage and a clean plugin architecture. The CI/CD security issues are fixable but should be addressed before production use. This is a strong choice for teams needing one extraction API across many formats and languages, though the 7,803-file repository is substantial to navigate.