Skip to content

Push and pull

internal/oci/push and internal/oci/pull store a package as an ordinary OCI artifact (artifact type application/vnd.bomify.package.v1+json; every type and annotation is listed in Registry format):

Bomify package to OCI artifact mapping

Source: docs/diagrams/oci-artifact.mmd

  • The config is the SBOM (application/vnd.cyclonedx+json or +xml, sniffed).
  • A component's layers, annotated land.bomify.purl, are tars of its files under transfer.LargeFileSize (64 MiB), and each larger file as raw parts of at most transfer.MaxLayerSize (4 GiB), so no layer outgrows a registry's limit. transfer.WriteTar zeroes mtimes and uid/gid, so a tar's digest depends only on names, modes, and content; otherwise re-pushing a component bomify had pulled would re-upload it. A part is the file's own bytes, so an unchanged large file is the same blob in every package, and uploads once.
  • Signatures, the report referrer, VEX referrers, and the provenance attestation (see Build provenance) are OCI referrers, so none changes the package digest.

Package layout in a registry

Source: docs/diagrams/registry-layout.mmd

Signature referrers in a registry

Source: docs/diagrams/registry-signatures.mmd

Layers transfer concurrently (--concurrency), each verified against its digest and size as it streams, with a progress bar per blob. push skips blobs the target already has. That's required, not an optimization: a local content/oci.Store rejects re-pushing a digest, which save hits when tags share a component. A part streams straight from its file, read once to hash and once to upload, so a large file is never copied; tars go through a temporary file.

pull restores a component from all its layers into a temporary directory and swaps it into place only once every layer verifies (see pull.restoreComponent). It checks the parts' annotations before downloading anything, creates each large file at full size, then unpacks the tars, refusing any entry naming a file that exists, and writes each part at its offset.

Save and load

bomify save/load (internal/oci/save) run push/pull unchanged against a local OCI image layout instead of a registry, then tar or untar it. Shared components are stored once, and signatures and report referrers travel inside the tarball, so load verifies and restores exactly as pull does.

Save and load flow

Source: docs/diagrams/save-load.mmd

Credentials

internal/auth is the one source of registry credentials, for login/logout, push/pull, and plugins through pkg/auth. login/logout write only <dataDir>/conf/auth.json, a Docker-format config backed by the native credential store as Docker's is. Reads try it first, then Docker's own config.json, so an existing docker login still works and bomify logout never removes it. Native helpers key secrets by host alone, so both tools share one secret for a host logged into with both.

Login verifies a credential before storing it, and falls back to plaintext in auth.json (with a warning) only when no credential helper exists, as docker login does. login --verify=false (Save) stores one unverified instead, the same way, for a host that isn't an OCI registry and so can't be checked: a Hugging Face hub bomify-plugin-huggingface pulls from and pushes to, or an HTTPS server bomify-plugin-generic downloads from. bomify exports the data directory in use as BOMIFY_DATA_DIR to the plugins it runs, so pkg/auth finds the same auth.json under --data-dir.