Metadata-Version: 2.4
Name: knurl
Version: 0.5.0
Summary: Rock-solid primitives for content-addressable hashing, chain fingerprinting, and config diffing
Project-URL: Homepage, https://github.com/spiritengine/knurl
Project-URL: Repository, https://github.com/spiritengine/knurl
Project-URL: Issues, https://github.com/spiritengine/knurl/issues
Author-email: Patrick Smyth <patricksmyth01@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: content-addressable,cryptography,diffing,fingerprinting,hashing,json-patch,merkle
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: hypothesis>=6.0; extra == 'dev'
Requires-Dist: jsonpatch>=1.32; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Provides-Extra: diff
Requires-Dist: jsonpatch>=1.32; extra == 'diff'
Description-Content-Type: text/markdown

# knurl

Rock-solid primitives for content-addressable hashing, chain fingerprinting, and config diffing.

Extracted from the SpiritEngine project.

## Modules

- **canon** - Canonical JSON serialization (RFC 8785 + mandatory Unicode NFC
  normalization). Deterministic, byte-identical across implementations and Python
  versions. Floats are rejected by default (pass `accept_floats=True` for
  non-hash use); integers keep their exact digits; NaN/Infinity, unassigned code
  points, and duplicate-after-NFC keys raise `CanonError`.
- **hash** - Content-addressable hashing
- **chain** - Merkle-like chain fingerprinting
- **diverge** - Divergence detection for fingerprint chains
- **diff** - JSON Patch (RFC 6902) diffs
- **yield_** - Yield data serialization

## Installation

```bash
pip install knurl

# With diff support (requires jsonpatch)
pip install knurl[diff]
```

## Usage

```python
from knurl import canon, hash, chain, diverge

# Canonical serialization
canonical_bytes = canon.serialize({"b": 1, "a": 2})  # b'{"a":2,"b":1}'

# Content-addressable hashing
content_hash = hash.compute("hello world")        # 'sha256:...' (strings)
blob_hash = hash.compute_bytes(b"\x00\x01\x02")   # 'sha256:...' (raw bytes)
file_hash = hash.compute_file("video.mp4")        # 'sha256:...' (streamed, multi-GB safe)
tree_hash = hash.compute_tree("mirror/")          # 'sha256:...' (reproducible directory digest)

# Same walk, but keep the per-file content hashes (for content-addressed dedup)
manifest = hash.compute_tree_manifest("mirror/")  # TreeManifest(digest, entries, paths)
manifest.digest                                   # == compute_tree("mirror/")
manifest.entries["index.html"]                    # {'type': 'file', 'hash': 'sha256:...'} (NFC key)
manifest.paths["index.html"]                      # real on-disk relative path, to re-read the bytes

# Chain fingerprinting
fingerprints = chain.fingerprint([config1, config2, config3])

# Divergence detection
result = diverge.find(old_fingerprints, new_fingerprints)
```

## Requirements

- Python 3.10+
- No dependencies (stdlib only)
- Optional: `jsonpatch` for diff module
