Skip to main content

Software Provenance

A reviewer who receives only the artifact and the build system's own metadata has no independent way to re-derive which inputs that release declared.

ALX Protocol represents a build output and each recorded input as Blocks. Given those Blocks, a reviewer with no access to the build system can recompute both identities and revalidate the derivation graph using protocol rules alone. This page is where identity depth lives for the use-case set: how contentHash and blockHash differ, and why a release's declared inputs become part of the artifact's identity. ALX verifies deterministic identity, declared lineage, and graph structure; applications remain responsible for content semantics, permissions, trust decisions, ownership, governance, and storage.

The Problem

A release binary, container image, or published package is the product of source revisions, resolved dependencies, and toolchain configuration. The record of those inputs typically lives inside the system that performed the build: job logs, a build database, registry metadata. Each form is application-specific, so a downstream consumer or auditor must trust the build system's own account of the output.

The record is also fragile across producers. Two systems that write the same dependency set with different JSON key ordering produce different bytes from identical values. A consumer who receives only the artifact and a metadata file has no protocol-defined way to re-derive the producer's identifier, or to confirm that the inputs the file names are the ones that identifier covers.

How ALX Applies

Each recorded input becomes its own Block. The content field carries application-defined JSON describing that input, such as a repository revision, a resolved dependency manifest, or a toolchain configuration. The build output becomes a further Block whose parentHashes declare the blockHash of every direct input.

{
"type": "build-artifact",
"artifact": "payment-api",
"version": "2.4.1",
"builtAt": "2026-07-28T14:30:00Z"
}

That payload is application-defined content, not a complete Block; a complete Block also carries parentHashes, contentHash, and blockHash. The build-artifact label is the application's own; ALX defines no Block categories.

ALX canonicalizes content with the recursive-json-sort-v1 algorithm before hashing, sorting object keys by UTF-16 code units, so two producers that emit the same JSON values with different key ordering still derive the same contentHash. Producers that describe the same facts with different field names or value types record different content, so their identities differ.

contentHash identifies the canonicalized content alone. blockHash identifies that content together with the normalized parent set. Changing a declared input therefore changes the artifact's blockHash while its contentHash stays the same. Parent order never affects blockHash after normalization: parents are trimmed, lowercased, validated, deduplicated, and sorted.

Once the Blocks are in hand, an independent party runs three protocol operations with no access to the build system. validateBlock() recomputes both identities and compares them against the recorded values. validateGraph() checks parent resolution, duplicate relationships, self-references, and cycles. traceAttribution() then returns the reachable ancestors, the root Blocks, the minimum and maximum graph depth, and the count of distinct paths.

Key Concepts

  • contentHash is the Keccak-256 identity of the canonicalized content. Payloads that canonicalize identically share it, so a dependency manifest recorded the same way can be compared across Blocks without lineage context.
  • blockHash is the Keccak-256 identity of the canonicalized content together with the normalized parent set. It is the value a downstream Block cites when it declares an input.
  • parentHashes are the blockHash values of the direct inputs after normalization. Declare only direct inputs; earlier ancestors stay reachable through the graph.
  • Canonicalization converts content into a deterministic byte sequence before hashing, which lets independent implementations derive identical identities from equivalent input.
  • Graph Validation checks loaded Blocks for unresolved parents, duplicate relationships, self-references, and cycles. It does not establish that the loaded set is the whole lineage.
  • Conformance Vectors pair fixed inputs with the bytes, hashes, graph results, and verification outcomes a passing implementation reproduces.

What ALX Does Not Do

ALX does not observe the build. The parent set is a claim made by the application that created the Block, so a passing validation confirms only that the declared relationships are structurally valid and that the identities match. It does not confirm that the declaration is complete or truthful. If only part of a graph is loaded, treat the result as incomplete lineage rather than evidence that the missing Blocks do not exist.

The protocol defines no dependency ordering beyond an unordered parent set, and no storage format, availability guarantee, transport, or API protocol. Build step sequencing and delivering the Blocks to a reviewer therefore stay application concerns. Verification does not establish factual accuracy, ownership, authorization, policy compliance, legal validity, safety, or application intent. Review the Trust Boundary and Known Limitations before presenting a validation result as audit evidence.