Skip to main content

fuzzbite

Fast fuzzy hashing and one-to-many fingerprint comparisons for Python, powered by ffuzzy and Rust.

Use ssdeep/CTPH fingerprints to find similar content. Hash a document once, then reuse a Matcher to search batches of stored fingerprints. No Python runtime dependencies.

Installation

Install from GitHub with uv:

uv add git+https://github.com/taranis-ai/fuzzbite.git

Source builds require Rust and a C linker. Supported platforms are Linux x86_64, Linux aarch64, and macOS arm64, with standard CPython 3.10–3.14.

Quick start

import fuzzbite

content = b"The river monitoring station reports water levels every morning. " * 20
fingerprint = fuzzbite.hash(content)

# Compare two fingerprints: scores range from 0 to 100.
score = fuzzbite.compare(fingerprint, fingerprint)  # 100

# Find the first candidate scoring at least 90.
matcher = fuzzbite.Matcher(fingerprint)
match = matcher.find_match([fingerprint], threshold=90)  # (0, 100)

Reuse the matcher across batches. Each result contains the candidate's index within that batch and its score, or None if nothing meets the threshold. Matching stops at the first qualifying candidate; it does not find the best match.

API

Function Returns
hash(data: bytes) An ssdeep fingerprint string
compare(left: str, right: str) An integer similarity score from 0 to 100
Matcher(fingerprint: str) A reusable comparison target
matcher.find_match(candidates, *, threshold=90) (index, score) or None
  • Encode text to bytes before hashing.
  • Pass candidates as a sequence of strings, such as a list or tuple. Generators are not accepted.
  • Thresholds range from 0 to 100, inclusive. An empty batch returns None.
  • Fingerprints must use blocksize:first:second, with valid ssdeep block sizes and digest lengths of at most 64 and 32 characters. Invalid fingerprints raise ValueError; filename suffixes and long-form second digests are not supported.
  • Candidate types are checked for the entire batch. Fingerprint syntax is checked only up to the first match, so malformed strings after a match are not parsed.

Hashing and scoring release the GIL. Candidate batches are copied before scanning, so use bounded batches for large collections. Content normalization, minimum content length, and storage are handled by the caller. A score of 100 does not prove that the original content is identical.

Compatibility

fuzzbite uses ffuzzy's ssdeep scoring. Scores can differ from ppdeep, so test your similarity threshold before switching implementations.

The compatibility suite compares 46 inputs and 2,116 ordered pairs against ppdeep 20260221. Hashes agree for those inputs; scores differ for 96 pairs. For example, one edited article scores 90 with ppdeep and 83 with fuzzbite. ffuzzy also validates fingerprints more strictly than ppdeep.

Benchmarks

On the included synthetic comparison corpus, full scans with Matcher took roughly 1.7–1.8× less time than a Python loop over fuzzbite.compare. Immediate matches can be faster with a single comparison because batching copies candidates.

See benchmark results and methodology for timings, ppdeep comparisons, and workload details. To run the benchmarks:

uv run --locked python -m benchmarks.run --output benchmark-results.json

Development

git clone https://github.com/taranis-ai/fuzzbite.git
cd fuzzbite
uv sync --locked --python 3.14

After changing Rust code, rebuild the extension:

uv run --locked maturin develop --release --locked

Run checks:

cargo fmt --check
PYO3_PYTHON="$(uv python find)" cargo clippy --locked --all-targets -- -D warnings
PYO3_PYTHON="$(uv python find)" cargo test --locked
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked python -m pytest -q

Build and verify distributions:

uv build
uv run --locked python scripts/validate_artifacts.py

On Linux, set MATURIN_PEP517_ARGS='--compatibility linux' for both commands. These local Linux wheels are platform-specific and cannot be uploaded to PyPI. CI checks all supported Python versions plus the latest stable Python, and builds on each supported platform. Workflows follow upstream action branches, latest uv, and stable Rust. Ubuntu x86_64 and macOS use -latest runners; Ubuntu ARM uses ubuntu-26.04-arm because GitHub does not provide a ubuntu-latest-arm label.

Publishing

The release workflow publishes a source archive and a macOS arm64 wheel when a GitHub release is published. Linux users build from source until portable Linux wheels are available.

Configure PyPI Trusted Publishing with these values:

Field Value
PyPI project name fuzzbite
Owner taranis-ai
Repository name fuzzbite
Workflow name release.yml
Environment name pypi

Create the pypi environment in the repository's GitHub settings and configure required reviewers to approve uploads. No PyPI API token is needed. The workflow must be committed before creating a release. Keep versions in pyproject.toml and Cargo.toml aligned, and use a matching tag such as v0.1.0.

License

GPL-2.0-or-later. See third-party notices for dependency licenses.

Metadata

Release files for fuzzbite 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fuzzbite 0.1.0
File Size Uploaded
fuzzbite-0.1.0.tar.gz 50.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fuzzbite 0.1.0
File Interpreter ABI Platform
fuzzbite-0.1.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details

Total release size: 312.2 kB

Release files / fuzzbite-0.1.0.tar.gz

Download URL fuzzbite-0.1.0.tar.gz
Size 50.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6a3541bacef95c0435c643da1c13b5595f767867eb90d26a6a6dbc823ef20f18
BLAKE2b-256 checksum
How to use checksums
dd62544c4cb0a4ea002617fb150e964e66e311ad528ccff019decc08c299d51b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / fuzzbite-0.1.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL fuzzbite-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
Size 261.7 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
f03424b6b2db145d64ea64cd0b9bca9df8578fd0a88aabdedf3a2a58fc8dc5c5
BLAKE2b-256 checksum
How to use checksums
260ad6bbea7f4547116984ea51818939786ac577166b090f9c0fe7802147f850
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.1

4 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page