Content addressing
Every contract listed in a manifest carries a content address: sha256: plus the SHA-256 (Secure Hash Algorithm, 256-bit) digest of the contract's canonical form (SR-006). Reformatting a contract doesn't change its address. Editing it does.
Address format
Address string #
sha256:b11ac43d1cf8120534e96ec2d90c24a4c2d9b20364fe01e27632ee7ad63b7dd4
Algorithm
- Parse the contract file as JSON. The input to the next step is the parsed JSON value, not the file bytes.
- Serialize the parsed value in its canonical form per RFC 8785 (Request for Comments 8785, the JSON Canonicalization Scheme, JCS).
- Encode the canonical form as UTF-8 bytes.
- Compute the SHA-256 digest of those bytes.
- Write the digest as lowercase hex and prefix it with
sha256:.
Canonical form
RFC 8785 canonical form, applied to the parsed JSON value:
| Aspect | Canonical form |
|---|---|
| Whitespace | None between tokens |
| Object member order | Sorted by the UTF-16 code units of member names |
| Numbers | ECMAScript shortest round-trip form |
| Strings | Only the escapes RFC 8785 requires |
| Trailing newline | None |
| Encoding for hashing | UTF-8 |
What changes the address
| Change to the file | Address changes |
|---|---|
| Whitespace or indentation | No |
| Object member order | No (members are sorted during canonicalization) |
| Any edit to content: a value, a member name, an added or removed member, array order | Yes |
Worked examples
Minimal contract #
File (any indentation){ "contractId": "button-primary", "component": "Button Primary" }
Canonical form (60 bytes)
{"component":"Button Primary","contractId":"button-primary"}
The address is sha256: followed by the lowercase hex SHA-256 digest of those 60 bytes. component sorts before contractId, so member order in the canonical form differs from the file.
Example contract file #
The repository's test suite recomputes this address from the canonical form of the example contract file and checks it against the manifest.
Node.js recipe #
The repository's test suite computes addresses with Node's crypto module and the canonicalize npm package (version 2.x) for RFC 8785. doc is the parsed JSON value.
import { createHash } from 'node:crypto';
import canonicalize from 'canonicalize';
const address = (doc) =>
'sha256:' + createHash('sha256').update(canonicalize(doc), 'utf8').digest('hex');
Mismatch handling
Recomputed address differs from the recorded address #
A consumer that recomputes an artifact's content address and finds it not equal to the recorded address treats that artifact as invalid and does not report it as conforming.
What the schema checks
The manifest schema checks only that address matches ^sha256:[0-9a-f]{64}$. It does not read the file at path and does not recompute the digest. Recomputation is a consumer obligation. This repository ships no checker tool for it.