Skip to content

Repository files navigation

state-persistence

A complete, tested utility for canonical hashing and digesting of JSON values.

A complete, tested building block for the Retsumdk ecosystem. Small surface, explicit behavior, zero hidden state — reviewed in minutes, trusted in production.

Features

  • Deterministic, stable normalization of JSON-serializable input
  • SHA-256 digesting over a canonical form
  • Structured, validated result shape with a passing test suite

Getting started

pip install -r requirements.txt
pytest -q

Why canonical hashing

Any time a piece of state is stored, cached, compared, or synced, someone eventually asks: "is this value the same as that value?" Naive equality fails across process boundaries — {'a': 2, 'b': 1} and {'b': 1, 'a': 2} are equal as Python dicts but serialize differently. state-persistence collapses every JSON-serializable value into one deterministic canonical string, then hashes it, so equality becomes a cheap digest comparison instead of a structural deep-compare.

How it works

input value
    │
    ▼
normalize()  ── sorted keys, compact separators, default=str for exotic types
    │
    ▼
canonical string
    │
    ▼
digest()  ── hashlib (sha256 by default) → 64-char hex digest

Usage

from state_persistence import normalize, digest, run

normalize({"b": 1, "a": 2})
# '{"a":2,"b":1}'  — key order in the input dict does not matter

digest({"k": "v"})
# 64-char hex digest, stable across runs and processes

run({"hello": "world"})
# {
#   "input_type": "dict",
#   "canonical": '{"hello":"world"}',
#   "length": 17,
#   "digest": "93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588"
# }

Running the module directly prints a structured result for a sample payload:

python state_persistence.py

API reference

Function Signature Returns
normalize (value: Any) -> str Deterministic canonical string: sorted-key compact JSON for dicts/lists, str(value) for scalars
digest (value: Any, algorithm: str = "sha256") -> str Hex digest of the canonical form; any hashlib algorithm name works
run (input_data: Any = None) -> dict Structured result: input_type, canonical, length, digest

Behavior notes

  • Order invariance — normalize({'b': 1, 'a': 2}) == normalize({'a': 2, 'b': 1}). Keys are sorted at every nesting level.
  • Compact separators — no spaces: '{"a":2}', never '{"a": 2}'.
  • Non-JSON-native values — objects the JSON encoder can't handle (e.g. datetime.date) are stringified via default=str, so date(2026, 9, 23) canonicalizes as "2026-09-23".
  • Scalars bypass JSON — normalize('x') is 'x', hashed as UTF-8 bytes directly.
  • Unknown algorithms raise — digest(value, "nope") raises AttributeError from hashlib lookup; there is no silent fallback.
  • Empty input defaults — run() with no argument canonicalizes {} rather than raising.

Real-world use case

An agent checkpoint store keeps one snapshot per workflow step. Two snapshots of the same logical state must not produce two rows. Instead of deep-comparing nested structures on every read, store digest(state) alongside each snapshot and index on it — equal digests collapse duplicates, and any drift in a single nested key produces a different digest immediately.

Project layout

state-persistence/
├── state_persistence.py        # normalize / digest / run implementation
├── test_state_persistence.py   # pytest suite (determinism, stability, result shape)
├── pyproject.toml              # packaging metadata
├── requirements.txt            # pytest
└── .github/workflows/ci.yml    # CI on push and pull_request

Testing

pip install -r requirements.txt
pytest -q
Test Behavior verified
test_normalize_deterministic Key order in the input dict cannot change the canonical string
test_digest_stable Same value → same digest, for scalars and nested structures
test_run_shapes_result run() returns input_type, positive length, and a 64-char hex digest

License

MIT © Retsumdk

About

Canonical JSON normalization and SHA-256 digesting utility with deterministic, sorted-key output

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages