Skip to content

dizzy.engine.dagstore.canonical

dizzy.engine.dagstore.canonical

Canonical JSON for hashing — RFC 8785 (JCS) on a deliberately narrowed subset.

This is the single serialization the content hash is computed over. Transport codecs may re-encode events however they like; this form is the identity-bearing one, so it must be reproducible byte-for-byte in any language. The house rules that narrow plain JSON down to something a hash can tolerate:

  • No floats. Amounts, durations, scores travel as ints or decimal strings. (JCS does define float serialization, but requiring every future Rust/TS worker to reproduce ECMAScript float formatting is a portability trap.)
  • Ints stay inside ±(2^53 − 1). Beyond that, ECMAScript-style serializers switch to exponent notation while Python would print full decimal — so the range where the two agree is the only range allowed.
  • Absent ≠ null. Both are representable and hash differently; emitters must omit absent fields. This module can't enforce the convention, but it round-trips both faithfully so violations are at least visible.
  • Object keys sort by UTF-16 code units (per JCS), not Python's code-point order. The two differ only for keys containing astral-plane characters.

NotCanonicalizable

Bases: ValueError

The value falls outside the hashed subset (float, big int, non-str key…).

Source code in dizzy/src/dizzy/engine/dagstore/canonical.py
33
34
class NotCanonicalizable(ValueError):
    """The value falls outside the hashed subset (float, big int, non-str key…)."""

canonical_json(value)

Serialize value to canonical JSON bytes (UTF-8).

Deterministic: equal values (regardless of dict insertion order) always produce identical bytes. Raises NotCanonicalizable for anything outside the hashed subset.

Source code in dizzy/src/dizzy/engine/dagstore/canonical.py
89
90
91
92
93
94
95
96
97
98
def canonical_json(value: Any) -> bytes:
    """Serialize *value* to canonical JSON bytes (UTF-8).

    Deterministic: equal values (regardless of dict insertion order) always
    produce identical bytes. Raises NotCanonicalizable for anything outside
    the hashed subset.
    """
    out: list[str] = []
    _serialize(value, out)
    return "".join(out).encode("utf-8")