Skip to main content

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")

  • None
  • bool
  • int
  • str
  • list of accepted values
  • dict with string keys and accepted values

Rejected values

  • floats, including finite floats such as 1.0
  • NaN / Infinity in Python values or JSON documents
  • bytes / 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)

Source distribution for stablejson 0.1.1
File Size Uploaded
stablejson-0.1.1.tar.gz 11.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for stablejson 0.1.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.1 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