Documentation
¶
Overview ¶
Package conformance is an independent reader for the DevProof bundle format.
It exists to answer one question: does the format specification, read on its own, describe the artifacts DevProof actually produces? A test that checks a writer against its own reader proves only that the two agree. If both share a constant, a sort order, or an encoding helper, they share its bugs, and a round trip passes while the artifact is unreadable by anyone else.
So this package is deliberately built from docs/bundle-format.md and nothing else. It imports no other DevProof package — not the media-type constants, not the canonical encoders, not the digest helpers. Every value it compares against is written out here from the specification text, and every structure it parses it parses again from scratch. Where this package and the main implementation disagree, one of them is wrong, and finding out which is the entire point.
It reads raw tar blocks rather than using archive/tar. That package is lenient by design: it accepts GNU and base-256 encodings, silently joins the USTAR prefix field onto the name, and hides how a value was spelled. Every one of those kindnesses conceals exactly the deviation this package exists to find, and a reader built on it accepted archives no conforming writer produces. The one outside dependency is a Unicode normalizer, because NFC is a specification rule and its tables are not in the standard library.
It is intentionally simple and unoptimized. It buffers what a streaming reader would not, because being obviously correct matters more here than being fast, and a second implementation that is clever enough to be wrong in the same way as the first has no value.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CheckCanonicalJSON ¶ added in v0.4.0
CheckCanonicalJSON verifies that bytes are RFC 8785 canonical JSON.
The config and the manifest are the two documents whose digests are the artifact's identity, so "the same document" has to mean "the same bytes". Two writers that agree on every value and disagree on key order produce two different subject digests for one payload, and the disagreement is invisible to anything that compares decoded values.
This used to be a json.Compact round trip, which only catches insignificant whitespace. Reordered keys, a non-minimal string escape, and a number spelled with a leading plus or an exponent all survived it.
The scan is over the raw bytes rather than over a decoded value, because a decoder has already thrown away the spelling that is the thing in question.
Types ¶
type BlobFetcher ¶
BlobFetcher supplies a blob by digest.
The digest is the caller's to verify or not; this package verifies every blob it receives regardless, which is what lets the same code check a layout on disk and a response from a registry.
type FileRecord ¶
FileRecord is one canonical file.
type Level ¶ added in v0.4.0
type Level int
Level selects how much of the specification a verification applies.
The three levels answer three different questions, and conflating them is how an implementation ends up claiming more than it checked:
- LevelStructure asks whether the artifact is intact and internally consistent, and whether expanding it is safe. Every digest is recomputed, the inventory is compared against what the layer actually held, and no path may escape the destination.
- LevelCanonical asks whether these are the only bytes a conforming writer could have produced for this tree. Entry order, fixed header fields, the extended-header restriction, the frozen gzip header, RFC 8785 member order, and the portable path rules are all in this level and none of them affect whether the artifact can be read.
- LevelBytes asks whether a writer reproduces the published vectors exactly. That is a property of an implementation rather than of an artifact: it is checked by building the documented input tree and comparing the result with vectors/format/v1. VerifyVectors runs the comparison for the published files themselves.
A deviation found above the requested level is reported in Report.Deviations rather than discarded, so a level-1 pass still says what it tolerated.
type Report ¶
type Report struct {
// Level is the level the read was performed at.
Level Level
// Deviations are canonical-encoding differences found while reading.
//
// At [LevelCanonical] a read fails on the first one, so this is empty or
// the read returned an error. At [LevelStructure] it is the list of things
// the pass tolerated, which is the difference between "verified" and
// "verified, and here is what a stricter reader would have said".
Deviations []string
// ManifestDigest is the subject identity: sha256 over the manifest bytes.
ManifestDigest string
// TreeDigest is recomputed here from the layer, not read from the config.
TreeDigest string
// ConfigTreeDigest is what the config claimed.
ConfigTreeDigest string
FileCount int64
TotalSize int64
// Files is the inventory as recomputed from the layer.
Files []FileRecord
}
Report is what an independent read established.
func VerifyLayout ¶
VerifyLayout reads a bundle from an OCI image layout and checks it against the format specification.
reference selects the subject: a tag matched against org.opencontainers.image.ref.name, or a "sha256:..." digest. An empty reference is accepted only when the layout holds exactly one manifest, because guessing which of several a caller meant is how the wrong artifact gets verified.
func VerifyManifest ¶
func VerifyManifest(manifestBytes []byte, fetch BlobFetcher, level Level) (*Report, error)
VerifyManifest checks a manifest and everything it references.
func VerifyVectors ¶ added in v0.4.0
VerifyVectors checks a set of published byte vectors against each other and against this reader.
This is LevelBytes. The manifest, config, and layer in the set are read as one artifact at LevelCanonical, and the subject digest the set publishes is compared with the one recomputed from the manifest bytes. An implementation demonstrates byte conformance by producing files that pass this from the documented input tree: identical values are not enough, because two encoders that agree on every value and disagree on a spelling give one payload two identities.
fsys is the directory holding the vector files, which lets the same check run against this repository's vectors/format/v1 and against another implementation's output.
type Vector ¶ added in v0.4.0
type Vector string
Vector names one published byte vector.
const ( VectorManifest Vector = "manifest.json" VectorConfig Vector = "config.json" VectorLayer Vector = "layer.tar.gz" VectorLayerTar Vector = "layer.tar" VectorTreeRecords Vector = "tree-records.bin" VectorTreeDigest Vector = "tree-digest.txt" VectorSubjectDigest Vector = "subject-digest.txt" VectorGzipSample Vector = "gzip-sample.gz" )
The published vectors for format v1.
They live in vectors/format/v1 at the repository root rather than under a testdata directory, because an implementation in another language has to be able to read them. Regenerating them is correct only when introducing a new format version.