litellm-trufflehog
Fast trufflehog secret scanning for LiteLLM. trufflehog's detector engine is compiled into a Go shared library and called from Python via ctypes.
Install
uv pip install litellm-trufflehog
Wheels are py3-none-manylinux_2_28_x86_64: platform-specific, but ABI-independent, so one wheel
works on every CPython 3.10+. Building from source needs Go 1.25+ and a C compiler (cgo).
Use in LiteLLM
guardrails:
- guardrail_name: trufflehog
litellm_params:
guardrail: litellm_trufflehog.TrufflehogGuardrail
mode: pre_call # required for redaction
default_on: true
on_detection: block
profile: core
Full example: examples/config.yaml. A blocked request gets HTTP 400 whose
body names detectors and counts only, never the secret.
| Option | Default | Meaning |
|---|---|---|
on_detection |
block |
block, redact or log |
profile |
core |
minimal (~34 detectors), core (~128), all (~858) or paranoid (~859) |
detectors |
– | Extra detectors, trufflehog selector syntax (AWS, Github.v2, 1-10), plus HighEntropy |
exclude_detectors |
– | Detectors to drop |
verify |
false |
Live-verify credentials: transmits candidates to third parties |
max_bytes |
1 MiB |
Truncation limit per text |
block_on_truncation |
true |
Block when input exceeded max_bytes |
block_on_scan_error |
true |
Block when a detector failed |
filter_entropy |
0 |
Drop unverified results below this Shannon entropy |
filter_unverified |
false |
Keep only the first unverified result per detector |
drop_wordlist_fps |
true |
Apply trufflehog's wordlist false-positive filter |
scan_entire_chunk |
false |
Pass whole input to detectors instead of a keyword window |
stream_holdback_chars |
4096 |
Trailing characters LiteLLM withholds while streaming |
Notes:
- Fails closed. A truncated input or a failed detector blocks by default, because an empty
report can also mean "we did not manage to look".
redactfalls back to blocking when a secret cannot be located. - Catch-alls are opt-in, being the largest source of false positives, and their matches can
swallow a neighbouring character, so prefer
blockorlogoverredact.HighEntropyflags high-entropy values nearpass/token/cred/secret/key, and is whatprofile: paranoidadds. trufflehog's ownGenericis available unchanged asdetectors: ["Generic"]. - Streaming is handled by the
stream_holdback_charstail, so a secret cannot be split across chunks. Usemode: pre_callfor prompts, where blocking is absolute.
Performance
Median Scanner.scan() latency measured from Python (just bench-py), core profile,
i7-1270P. End-to-end: ctypes call, scan, JSON decode, object construction.
| Input | Latency | Throughput |
|---|---|---|
| Chat turn (256 B) | 9 µs | — |
| Prompt (2 KiB) | 15 µs | 103 MB/s |
| RAG context (16 KiB) | 90 µs | 157 MB/s |
| Large paste (128 KiB) | 818 µs | 149 MB/s |
| 16 KiB mentioning credentials | 544 µs | 28 MB/s |
| 16 KiB containing a credential | 210 µs | 69 MB/s |
| Scanner construction | 1.5 ms (all: 6 ms) |
one-off per worker |
An Aho-Corasick keyword prefilter means clean text runs no regexes at all. Prose that merely
talks about API keys defeats it and costs ~6x more. all over core costs only ~30% on clean
text.
Python API
from litellm_trufflehog import Scanner, StreamScanner, get_scanner
scanner = get_scanner(profile="core") # process-wide cache; construction is the costly part
report = scanner.scan(text) # or: await scanner.scan_async(text)
bool(report) # anything detected?
report.detector_types # ('AWS', 'Github') - safe to log
report.summary() # log/response-safe dict, no secret material
report.degraded # a detector failed; an empty result is not trustworthy
report.trustworthy # complete scan, no failures
report.fully_redactable # every finding could be located
for finding in report:
finding.detector_type, finding.secret_sha256, finding.spans, finding.verified
masked, report = scanner.redact("id=AKIA… secret=wJalr… again=AKIA…")
# 'id=[REDACTED:AWS:9f2c1a04] secret=[REDACTED:AWS:be70d3f1] again=[REDACTED:AWS:9f2c1a04]'
Spans are UTF-8 byte offsets; findings carry secret_sha256, not the secret. scan_async offloads
to a thread, and ctypes releases the GIL, so concurrent scans run in parallel.
Redaction masks every occurrence of a value, and each placeholder ends in a per-process keyed
fingerprint, so equal tags mean the same credential twice. Pass template= to redact() to change
the format ({detector} and {fingerprint}, both optional).
StreamScanner covers the async_post_call_streaming_iterator_hook path, but prefer the holdback
path: its cost tracks chunk count, so a 2 KiB response costs 15 µs scanned once but 1.3 ms as
20-character deltas.
Development
Requires just, uv, Go 1.25+
and a C compiler for cgo (gcc/clang; on Windows use mingw-w64 —
winget install BrechtSanders.WinLibs.POSIX.UCRT — MSVC does not work with cgo).
just build # compile the Go shared library into the package
just sync # create the venv from uv.lock
just check # go vet + ruff + ty + all tests
just bench # both benchmark suites (bench-py / bench-go individually)
just wheel # release manylinux wheel in Docker -> ./dist
go/scanner/ detector selection, scanning, span/offset logic (pure Go, unit tested)
go/cbind/ C shared-library bridge (handles, JSON in/out)
src/litellm_trufflehog/
_lib.py ctypes binding
scanner.py Scanner, ScanReport, Finding, redaction
stream.py overlapping-window scanner for streams
guardrail.py LiteLLM CustomGuardrail
Known limitations: no base64/UTF-16 decoding (a finding in decoded bytes has no offset in the original text); the wordlist filter can discard real secrets; the shared library is ~74 MB stripped, because trufflehog pulls in the AWS SDK, go-git, a WASM runtime and the Docker client; prebuilt wheels are Linux x86_64 only.
AGPL-3.0-or-later, because trufflehog is linked in. Note that a LiteLLM proxy is a network service: under AGPL §13 its users must be offered the corresponding source.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file litellm_trufflehog-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: litellm_trufflehog-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 23.6 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0721a1fb4510edae3af548ae12998fcdc6d44a73a008a560f5b4ec300754b0fe
|
|
| MD5 |
38b312fbf063c598d474e52a621120aa
|
|
| BLAKE2b-256 |
368ec536002d332625754aa61a45b655531eee3f3bce6a6f05e3facd0525f630
|
Provenance
The following attestation bundles were made for litellm_trufflehog-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on LLukas22/litellm-trufflehog
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
litellm_trufflehog-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
0721a1fb4510edae3af548ae12998fcdc6d44a73a008a560f5b4ec300754b0fe - Sigstore transparency entry: 2698023979
- Sigstore integration time:
-
Permalink:
LLukas22/litellm-trufflehog@3a902c211e2095079433852a7e11db436a3801db -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/LLukas22
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3a902c211e2095079433852a7e11db436a3801db -
Trigger Event:
release
-
Statement type: