stablejson
Deterministic JSON serialization and SHA-256 hashing for Python.
stablejson turns supported JSON-compatible input into a deterministic compact JSON string and computes SHA-256 over its UTF-8 bytes.
Identical JSON-compatible values should produce identical digests across
processes and machines. That makes content addressing, cache keys, and
integrity checks reliable when ordinary json.dumps would still vary by key
order or formatting.
Installation
Requires Python 3.12+.
pip install stablejson
Releases are published from GitHub to PyPI using Trusted Publishing. No PyPI API token is stored in this repository.
For local development from a checkout:
pip install .
Quick start
Default value mode
from stablejson import canonicalize
result = canonicalize({"b": 1, "a": 2})
print(result)
input_kind defaults to "value", so Python objects are canonicalized directly.
Representative success response:
{
"ok": True,
"canonical": '{"a":2,"b":1}',
"sha256": "d3626ac30a87e6f7a6428233b3c68299976865fa5508e4267c5415c76af7a772",
"observed_input_kind": "value",
}
Document mode
from stablejson import canonicalize
result = canonicalize('{"b": 1, "a": 2}', input_kind="document")
print(result["canonical"], result["sha256"])
With input_kind="document", content must be a Unicode string containing one
JSON value. The parsed value is then canonicalized with the same rules as value
mode.
Both modes produce the same canonical text and digest for equivalent JSON data.
Failure response
Unsupported values fail closed. For example, floats are rejected:
canonicalize(1.0)
Representative failure response:
{
"ok": False,
"canonical": None,
"sha256": None,
"failure": {
"code": "UNSUPPORTED_VALUE",
"message": "unsupported value",
},
}
Public API
canonicalize(content, *, input_kind="value") -> dict
| Argument | Meaning |
|---|---|
content |
JSON-compatible Python value, or a JSON text string when input_kind="document" |
input_kind |
"value" (default) or "document" |
Invalid input_kind values fail closed with INVALID_REQUEST.
Also exported: INVALID_REQUEST, MALFORMED_JSON, UNSUPPORTED_VALUE,
FAILURE_MESSAGES, and __version__.
stablejson uses a versioned internal engine for compatibility with its original implementation. Ordinary callers do not pass engine identifiers.
Accepted value types (input_kind="value")
Noneboolintstrlistof accepted valuesdictwith string keys and accepted values
Rejected values
- floats, including finite floats such as
1.0 NaN/Infinityin Python values or JSON documentsbytes/bytearray- non-string mapping keys
- tuples, sets, custom objects, and other non-JSON-compatible types
- malformed JSON documents (
MALFORMED_JSON) - nesting deeper than 128 (
UNSUPPORTED_VALUE) - invalid
input_kind(INVALID_REQUEST)
Deterministic behavior
Serialization rules:
- object keys are sorted lexicographically
- separators are compact:
,and:with no spaces - Unicode is preserved (
ensure_ascii=False) - non-finite numbers are never emitted (
allow_nan=False)
SHA-256 behavior:
- digest is SHA-256 over the UTF-8 bytes of the canonical string
- digest is returned as a lowercase hex string in
sha256 - there is no algorithm prefix and no multibase encoding
Depth limit:
- structures may nest up to depth 128
- depth 129 and deeper are rejected with
UNSUPPORTED_VALUE
canonicalize does not mutate the caller-supplied content.
Security and limitations
- Pure computation only: no network, filesystem, subprocess, or other I/O
- Fail-closed typed errors instead of partial canonical output
- Callers remain responsible for bounding untrusted input size; this library enforces nesting depth, not byte length or wall-clock budgets
- This is not RFC 8785 JSON Canonicalization Scheme (JCS), and it does not claim JCS or RFC 8785 compatibility
- Floats are intentionally unsupported to keep this package's determinism contract narrow and exact. That is a product boundary, not a claim that floating-point JSON can never be serialized deterministically.
Development
From a repository checkout:
python -m pip install -e ".[dev]"
pytest -q
ruff check src tests
python -m pip install build
python -m build
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Release files for stablejson 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stablejson-0.1.1.tar.gz | 11.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stablejson-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.3 kB
Release files / stablejson-0.1.1.tar.gz
| Download URL | stablejson-0.1.1.tar.gz |
|---|---|
| Size | 11.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b7480aa842653e5bbf237826517de26d79f7bd25ec4c8812b4f631fed559564f
|
|
BLAKE2b-256 checksum How to use checksums |
6e9582c1eb8a1651cbb04b764140e3bbfe5a5e8569c86e03eded8a03f9c67101
|
| 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 Aug 2, 2026.
Transparency logRelease files / stablejson-0.1.1-py3-none-any.whl
| Download URL | stablejson-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
964a896218ec2afee7414dd485e46762932160c825d0077820d0fa090147b70d
|
|
BLAKE2b-256 checksum How to use checksums |
df705434f2f7ef5622808afe452c49ce3bd37248ba3743621e4cc670a4444104
|
| 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 Aug 2, 2026.
Transparency log