JCS - JSON Canonicalization for Python 3
This is a Python 3 package for a JCS (RFC 8785) compliant JSON canonicalization.
The main author of this code is Anders Rundgren. The original source code is at cyberphone/json-canonicalization including comprehensive test data.
Installation
$ pip install jcs
Using JCS
import jcs
data = jcs.canonicalize({"tag": 4})
Numbers
JCS serializes every JSON number as an IEEE 754 double precision value
(RFC 8785, section 3.1). Python float values
are doubles already and are serialized as is. Python int values have arbitrary precision, so
canonicalize accepts an integer only if a double represents it exactly and its canonical form
reads back as the same integer. It raises a ValueError for any other integer, instead of silently
rounding it:
>>> jcs.canonicalize({"id": 2**53 + 1})
Traceback (most recent call last):
...
ValueError: Integer 9007199254740993 is not exactly representable in canonical JSON ...
Silent rounding would let different values produce identical canonical bytes, so a signature or hash
over one value would also match the other. An accepted integer equals its double, so no other
integer or float shares its canonical bytes, and json.loads reads them back as the same integer.
Keep in mind:
- Canonical JSON writes the shortest digits of the nearest double.
2**60is an exact double, but it serializes as1152921504606847000, a different integer, socanonicalize(2**60)raises.canonicalize(1152921504606847000)raises too, because a double cannot represent it. - From
1e21on, numbers are written with an exponent (10**21becomes1e+21) andjson.loadsreads them asfloat, so every integer that a double represents exactly is accepted. - For signed or hashed data, keep integers within the I-JSON safe range of
±(2**53 - 1)(RFC 7493, section 2.2) and use strings for large identifiers, counters, or amounts. - Canonical JSON from JavaScript and other double based implementations can contain integers like
1152921504606847000, whichjson.loadsreads as integers that a double cannot represent. To canonicalize such text the way those implementations do, and the way jcs 0.2 did, parse integers as floats:jcs.canonicalize(json.loads(text, parse_int=float)).
The integer check is an addition to the original reference code. The serialization of all accepted values is unchanged.
Development
uv sync # Install dev dependencies
uv run prek install # Install git hooks (quality gates on commit, tests on push)
uv run prek run --all-files # Run all quality gates manually
uv run pytest --cov=jcs # Run tests with coverage floor
Releasing
- Set the version in
pyproject.tomlandjcs/__init__.pyand date the CHANGELOG entry - Commit, then push a matching tag:
git tag v0.3.0 && git push origin v0.3.0
The release workflow runs the quality gates and tests, checks that the tag matches the package version, builds, and publishes to PyPI via trusted publishing with attestations.
Metadata
Release files for jcs 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| jcs-0.3.0.tar.gz | 14.3 kB | With provenance | Details for jcs-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| jcs-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for jcs-0.3.0-py3-none-any.whl |
Total release size: 30.1 kB
Release files / jcs-0.3.0.tar.gz
| Download URL | jcs-0.3.0.tar.gz |
|---|---|
| Size | 14.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92fecdd084a3e2cf896d11fdd5e2c52b6b86996061a3455fb3a4885cf651c6d6
|
|
BLAKE2b-256 checksum How to use checksums |
33c96dc379416691ab9b576b5fd88f14ddb4900376566350a6a1c2e55238d983
|
| 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 Nov 6, 2026.
Transparency logRelease files / jcs-0.3.0-py3-none-any.whl
| Download URL | jcs-0.3.0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c8e0113e717873f22ea7090cf877eed45a8715997eedc60c18ff789a3e89106d
|
|
BLAKE2b-256 checksum How to use checksums |
525be767349db2c71dff47d3692c30d780443ba5dfb844b626ef6fd54debc817
|
| 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 Nov 6, 2026.
Transparency logSecurity for jcs 0.3.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jcs-0.3.0.tar.gz
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 Nov 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.