Files
DevRunbook-Public/docs/29-package-integrity-canonicalization.md
DevRunbook release export cfd2804e27
Managed validation / full (push) Successful in 3m18s
Publish DevRunbook source
2026-09-03 04:09:17 +02:00

3.9 KiB

29 — Package integrity and canonicalization

Purpose

Digest behavior must be identical across Windows, Linux and macOS and must not depend on YAML formatting, archive order or local filesystem metadata.

Text normalization

For every declared text file:

  1. reject invalid UTF-8;
  2. remove a UTF-8 BOM;
  3. normalize Unicode to NFC;
  4. convert CRLF and CR to LF;
  5. remove trailing spaces and tabs from every line;
  6. preserve intentional internal blank lines;
  7. end with exactly one LF.

Binary resources are not text-normalized.

Manifest canonicalization

  • Parse playbook.yaml using safe YAML parsing.
  • Reject duplicate mapping keys, custom tags, non-finite numbers and YAML values that cannot be represented as JSON.
  • Apply schema-defined semantic defaults in one versioned normalization function.
  • Convert the result to JSON-compatible values.
  • Serialize using RFC 8785 JSON Canonicalization Scheme.

YAML comments and key order do not affect the digest.

Package file inventory

package.files is authoritative.

  • Every listed file must exist as a regular file below the package root.
  • Every package file other than playbook.yaml must be listed.
  • Directories, symlinks, hardlinks, device files and executables are rejected.
  • Paths are slash-separated, relative, normalized and unique.
  • The main template and all partials must have the appropriate declared role.
  • Evaluation and example IDs must match the package metadata and quality references.

Package digest payload

Construct this logical object:

{
  "algorithm": "devrunbook-package-v1",
  "manifest": "<canonical JSON object, not a string>",
  "files": [
    {
      "path": "CHANGELOG.md",
      "role": "changelog",
      "sizeBytes": 123,
      "sha256": "..."
    }
  ]
}

Include only files with digest: true, sorted by UTF-8 path bytes. File SHA-256 is computed over normalized text bytes or original binary bytes. Serialize the payload with RFC 8785 and SHA-256 the resulting UTF-8 bytes.

Changing any digested file changes the package digest and therefore requires a new published version.

Render digest

The render digest is SHA-256 over the exact final prompt bytes after platform composition:

  • UTF-8;
  • NFC;
  • LF endings;
  • stable headings and list formatting;
  • exactly one final LF;
  • no generation timestamp inside the prompt unless declared as an input.

Repository Profile digest

Parse and validate the profile, remove metadata.contentDigest, apply normalized ordering/defaults, serialize using RFC 8785 and SHA-256 the canonical bytes. The stored contentDigest must match on import.

Run Pack manifest digest

  1. Build manifest.json with every exported file except manifest.json itself.
  2. Compute each file size and SHA-256 from the exact archive payload bytes.
  3. Omit the manifestDigest property.
  4. Serialize the remaining manifest with RFC 8785.
  5. Compute SHA-256 and set the lowercase hex result as manifestDigest.
  6. Write the final manifest as pretty JSON with LF endings. Pretty formatting does not define the digest; canonical JSON with the field omitted does.

Archive construction

  • Paths sorted lexicographically by UTF-8 bytes.
  • Fixed permission bits for regular files.
  • Fixed archive timestamps, preferably the ZIP epoch supported by the library.
  • No extra fields containing local user, host or filesystem metadata.
  • Compression level may differ without affecting file or manifest digests.
  • Archive-level SHA-256 may be stored as artifact metadata but is not part of manifest.json.

Verification

Import verifies in this order:

  1. archive limits and path safety;
  2. manifest schema;
  3. exact file set—no missing or undeclared files;
  4. file sizes and hashes;
  5. manifest digest;
  6. package/profile schema and semantic validation;
  7. historical render digest where a rendered prompt is present.

Any failure rejects the import atomically with a path-specific error.