Checksum
The bundle checksum is a SHA-256 hash that guarantees no artifact was modified after the package was created. The import engine recomputes and verifies it before processing any content.
What the Checksum Protects
The checksum detects:
- Accidental corruption during transfer or storage
- Malicious modification of artifact content after export
- Incomplete downloads where not all bytes arrived
- Substitution attacks where a trusted package name is used with modified content
What is Hashed
The bundle checksum is SHA-256 of all artifact file contents concatenated in install order. The manifest itself is not included in the hash (because the manifest contains the hash — a circular dependency).
// Install order from manifest: ["ent-44", "rule-305", "form-2005", "thread-2002", "proc-1001"]
// Checksum input = concatenation:
bytes(artifacts/entities/ent-44.json)
+ bytes(artifacts/rules/rule-305.json)
+ bytes(artifacts/forms/form-2005.json)
+ bytes(artifacts/workflows/thread-2002.json)
+ bytes(artifacts/workflows/proc-1001.json)
// Hash:
checksum = "sha256:" + hex(SHA256(concatenatedBytes))
Per-Artifact Hashes
In addition to the bundle checksum, each artifact entry in the manifest includes its own hash field — the SHA-256 of that single artifact file's bytes:
{
"artifacts": [
{
"id": "proc-1001",
"file": "artifacts/workflows/proc-1001.json",
"hash": "sha256:abc123def456789..." // SHA-256 of proc-1001.json bytes only
}
]
}
On import, the engine verifies both:
- Each artifact file individually against its per-artifact hash
- The full bundle by recomputing the overall checksum
If an individual artifact hash fails but the bundle checksum matches, this indicates a manifest error. If the bundle checksum fails, at least one file was modified.
Verification Algorithm
// Import engine verification (pseudocode)
async VerifyChecksumAsync(zip, manifest):
// Step 1: verify each artifact individually
for artifact in manifest.artifacts:
fileBytes = zip.ReadFile(artifact.file)
actualHash = "sha256:" + hex(SHA256(fileBytes))
if actualHash != artifact.hash:
throw ChecksumMismatch(artifact.id, artifact.hash, actualHash)
// Step 2: verify the full bundle
concatenated = []
for artifactId in manifest.installOrder:
artifact = manifest.FindArtifact(artifactId)
fileBytes = zip.ReadFile(artifact.file)
concatenated.append(fileBytes)
actualBundleChecksum = "sha256:" + hex(SHA256(concatenate(concatenated)))
if actualBundleChecksum != manifest.checksum:
throw BundleChecksumMismatch(manifest.checksum, actualBundleChecksum)
Checksum Format
"checksum": "sha256:3a9f1c4e8d2b7f6a1e5c9d3b0a4f8e2c7d1a6b9e3f5c8d2a0b7e4f1c9d3a6b0"
^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
prefix 64 hex characters = 256 bits = SHA-256 output
The sha256: prefix is mandatory and allows future support for alternative hash algorithms (e.g., sha512:) without breaking parsers.
Computing the Checksum (C# Example)
public static string ComputeBundleChecksum(
IReadOnlyList<SerializedArtifact> artifacts,
IReadOnlyList<string> installOrder)
{
using var sha256 = SHA256.Create();
using var combined = new MemoryStream();
// Concatenate artifact bytes in install order
foreach (var artifactId in installOrder)
{
var artifact = artifacts.First(a => a.Id == artifactId);
combined.Write(artifact.Bytes);
}
combined.Position = 0;
var hash = sha256.ComputeHash(combined);
return "sha256:" + Convert.ToHexString(hash).ToLowerInvariant();
}
public static string ComputeArtifactHash(byte[] artifactBytes)
{
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(artifactBytes);
return "sha256:" + Convert.ToHexString(hash).ToLowerInvariant();
}
Checksum Failure Response
HTTP 400 Bad Request
{
"error": "ChecksumMismatch",
"message": "The bundle checksum in the manifest does not match the computed checksum. The package may have been corrupted or tampered with.",
"expected": "sha256:3a9f1c4e...",
"actual": "sha256:7b2c8d5f..."
}