ADR-0003: Deterministic hash signatures for rendered projections
Status: accepted | Date: 2026-01-17
References: RFC-0000
Context
govctl renders markdown files from authoritative JSON/TOML sources (RFCs, ADRs, Work Items). These rendered markdown files are projections — read-only views intended for human consumption.
Problem: Without a mechanism to detect tampering, someone could edit docs/rfc/RFC-0000.md directly. This edit would:
- Be overwritten on next
govctl render - Create confusion about which version is authoritative
- Violate the Single Source of Truth (SSOT) principle
We need a mechanism to:
- Mark rendered files as generated (not authoritative)
- Detect when a rendered file has been edited directly
- Ensure verification is deterministic (same source always produces same hash)
Decision
Implement deterministic hash signatures embedded in rendered markdown.
Signature Format:
<!-- GENERATED: do not edit. Source: RFC-0000 -->
<!-- SIGNATURE: sha256:<64-hex-chars> -->
Hash Computation:
- Collect all source JSON content (RFC metadata + clauses, or ADR/Work Item TOML)
- Canonicalize JSON/TOML: sort object keys recursively, normalize whitespace
- For RFCs: sort clauses by
clause_idbefore hashing - Compute SHA-256 of the canonical representation
- Include a signature version prefix for future-proofing
Determinism Requirements:
- Object keys sorted alphabetically at all nesting levels
- Arrays preserve order (only objects get key-sorted)
- Consistent string escaping and number formatting
- No dependency on file modification times or filesystem order
Verification:
govctl checkextracts signature from rendered markdown- Recomputes hash from current source files
- Reports mismatch as a diagnostic error
Consequences
Positive:
- Enforces “edit the source, not the projection” discipline
- Tampered markdown is detected automatically by
govctl check - Deterministic hashes enable reproducible builds
- Clear provenance: every rendered file traces back to its source
Negative:
- Existing rendered markdown will fail verification until re-rendered
- Minor source reformatting (whitespace) changes the hash
- Adds complexity to the render pipeline
Migration:
- Run
govctl renderto regenerate all markdown with signatures - Commit the updated files