Skip to content
Dev Tools Article

You don't need TSON to hash-pin your schemas

The JSON superset on HN's front page diagnoses schema evolution correctly, then prescribes a format nobody asked for.

Lenn Voss
Lenn Voss
Cloud & Infrastructure Writer · Aug 5, 2026 · 5 min read
You don't need TSON to hash-pin your schemas

A spec called TSON hit the Hacker News front page today with a pitch that sounds almost too clean: a JSON superset where schemas are immutable documents pinned by SHA-256 hash. A document names its schema by hash, the schema names its meta-schema by hash, and one hash verifies the whole chain. Change anything and you haven't updated a schema — you've created a new one, and both remain valid forever.

The core idea is genuinely good. It's also not new, and the discussion under the post — dozens of comments, most asking some version of "what problem does this solve?" — suggests the project hasn't made the case for the parts that are. It's worth pulling those apart, because the good idea deserves better than to sink with the format.

The rule TSON is right to hate

Every API style guide eventually converges on the same strange rule: never add a required field to an existing schema. Google's, Microsoft's, and Zalando's guidelines all codify it, and protobuf went further — proto3 dropped required entirely after years of proto2 docs treating it as a footgun. The rule exists for one reason: schemas mutate in place under a stable name. If order.proto can change while a thousand producers keep writing against yesterday's version, every new field must be optional or the world breaks. So real-world schemas rot into piles of optionals, and the guarantees they were supposed to encode live in wiki pages instead.

TSON's diagnosis is that the problem was never required fields — it's mutable schemas. Make the schema an immutable document identified by content hash and "evolution" stops being mutation. A new version is a new document with a new hash. Old and new contracts coexist at full strength, unknown fields become errors instead of silently tolerated slop, and a record can actually mean what it says. You route by hash and migrate by diff.

That's a correct and underrated observation. More format designers should internalize it.

We've been hash-pinning schemas for a decade

Here's the thing, though: the serialization world already runs on this idea. It just doesn't advertise it. Apache Avro's single-object encoding prefixes every message with an 8-byte CRC-64 fingerprint of the writer's schema, computed over a spec-defined Parsing Canonical Form so formatting differences can't change a schema's identity. Confluent's Schema Registry — the de facto standard in Kafka shops — registers schemas immutably and stamps every message with a schema ID, so consumers resolve the exact writer schema per record. The one real delta TSON offers over the registry model is self-verification: a content hash doesn't require trusting a registry. Avro's fingerprints already deliver that too.

Zoom out and hash-pinning is arguably the defining infrastructure pattern of the last decade. Subresource Integrity pins scripts, lockfiles pin packages, registries pin container images by digest, Unison content-addresses every function in the language. TSON applies a proven pattern to schema documents. Legitimate — but it's the well-lit part of the design space, not the frontier.

Where the wheels come off

Meanwhile, TSON hasn't yet shipped the boring prerequisite that makes any of this work: a canonical form. The sharpest question in the HN thread was also the simplest — does fiddling with a schema's whitespace change its hash? The published draft doesn't say. Avro needed Parsing Canonical Form before fingerprints meant anything; JSON signing needed RFC 8785 to nail down key ordering, number formatting, and whitespace. A hash-pinned schema system without canonicalization rules is a slogan, not a system.

And instead of that piece, TSON ships everything else: a new syntax (unquoted identifiers, optional commas, triple-quoted strings, a _ sentinel for "absent" as distinct from null), a new type algebra (five field states, inheritance with subtraction, refinement, templates, choice types), a meta-schema tower, and native dates, UUIDs, and arbitrary-precision numbers. Each is defensible alone. Together they're a whole new language riding on one insight — and history is unkind to that trade. Amazon Ion made the "JSON but typed" pitch in 2016 with Amazon's full weight behind it: timestamps, decimals, blobs, annotations, strict superset compatibility. A decade later it's still mostly an AWS-ecosystem citizen. Plain JSON's gravity is brutal, and "every valid JSON document is valid TSON" doesn't cut the switching cost, because the value only shows up once both sides of every integration speak the new dialect.

The project's current state doesn't help the case. It's a working draft that's explicitly subject to change until it freezes as version 1, with one reference implementation — a Java 25 library with single-digit GitHub stars and a CLI that can validate, compile, and hash documents. The whole thing is theory-first: an eighteen-article derivation of what a schema is preceded any running code. Commenters also poked at mechanics, from pinning hashes in a ?sha256= query parameter (which sits oddly with RFC 3986's semantics) to the absence of comments in a format that will inevitably get drafted for config duty. The reception ran from "just use protobuf" to accusations that the site was vibe-coded. Rough edges are forgivable in a draft. Betting production data contracts on one isn't.

How to get the good part today

If the pitch resonates, you can have the substance now without adopting anyone's draft notation.

On Kafka with Avro, you already have it — single-object encoding carries the schema fingerprint, and registry IDs are immutable by construction. For HTTP APIs, keep JSON Schema: canonicalize the schema with an RFC 8785 library (implementations exist for Java, JavaScript, Go, Python, and .NET), SHA-256 the canonical bytes, and publish the pin wherever your stack declares schemas — a describedby link header, an envelope field, a versioned URL. The pin is the point, not the placement. Then treat the hash as the contract: store it in a lockfile next to your package hashes and fail CI when a schema's hash changes without a deliberate version bump. That's maybe ten lines of scripting, and it buys you the guarantee TSON is selling — a schema that cannot drift under your feet — inside the toolchain you already run.

TSON itself is worth watching as design literature. Field states plus subtraction is a nicer evolution story than JSON Schema's allOf gymnastics, and if the canonicalization gap gets closed before v1 freezes, the schema layer could matter even if the syntax never does. But today it's a research project wearing a data format's clothes. Steal the idea. Skip the format.

Sources & further reading

  1. TSON (Typed Schema Object Notation) — tson.io
  2. TSON - A JSON superset with immutable, hash-pinned schemas — news.ycombinator.com
  3. tson-java reference implementation — github.com
  4. Apache Avro Specification — avro.apache.org
  5. RFC 8785 JSON Canonicalization Scheme (JCS) — rfc-editor.org
Lenn Voss
Written by
Lenn Voss · Cloud & Infrastructure Writer

Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.

Discussion 5

Join the discussion

Sign in or create an account to comment and vote.

Tess O'Brien @typescript_tess · 1 week ago

the hash-chaining idea is solid but yeah, this reads like solution in search of a problem. what's the actual migration story here

Will Carter @weekend_warrior_will · 1 week ago

the real gotcha here is operational: what's your versioning and migration story when you've got 47 different schema hashes in production and need to coordinate a breaking change across services? feels like tson punts on the messy part—hash-pinning is clean until you're actually running it.

Maya Ito @opensource_maya · 1 week ago

exactly right. i've lived through this with content-addressed specs, and the hard part isn't proving identity—it's governance. you end up needing a registry layer anyway (what's the canonical hash for "v2 of user schema that most services should migrate to?"), and then you're building versioning on top, which defeats the elegance premise. tson wants to be immutable, but systems need to be upgradeable, and those are different problems.

Paul Nguyen @pragmatic_paul · 1 week ago

yeah, governance is the killer. but i'd push back slightly—you don't *need* a registry if you just pin your schemas in postgres and version them with boring integers. you get immutability (old rows never change their schema reference), auditability, and the ability to say "services should use schema_id=42" without inventing content addressing. tson makes this feel like a breakthrough when it's really just... what databases have been doing.

Tom Becker @terminal_tom · 1 week ago

postgres integers feel boring until you're debugging distributed services at 2am. hash pinning does appeal to the neovim-dotfiles crowd (me) but @pragmatic_paul's right—if it lives in one place and you version it, you've solved 90% of the problem already.

Related Reading