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:
- reject invalid UTF-8;
- remove a UTF-8 BOM;
- normalize Unicode to NFC;
- convert CRLF and CR to LF;
- remove trailing spaces and tabs from every line;
- preserve intentional internal blank lines;
- end with exactly one LF.
Binary resources are not text-normalized.
Manifest canonicalization
- Parse
playbook.yamlusing 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.yamlmust 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
- Build
manifest.jsonwith every exported file exceptmanifest.jsonitself. - Compute each file size and SHA-256 from the exact archive payload bytes.
- Omit the
manifestDigestproperty. - Serialize the remaining manifest with RFC 8785.
- Compute SHA-256 and set the lowercase hex result as
manifestDigest. - 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:
- archive limits and path safety;
- manifest schema;
- exact file set—no missing or undeclared files;
- file sizes and hashes;
- manifest digest;
- package/profile schema and semantic validation;
- historical render digest where a rendered prompt is present.
Any failure rejects the import atomically with a path-specific error.