cr/

directory
v0.30.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2026 License: Apache-2.0

README

d8 cr - Container Registry Tool

d8 cr is the container-registry subtree of the Deckhouse CLI. It is a self-contained re-implementation of crane-style workflows on top of go-containerregistry, extended with first-class commands for inspecting the filesystem of an image without running it.

It is intended for engineers, SREs and CI pipelines that need to interact with OCI/Docker registries from the command line: pulling and pushing images, listing tags, inspecting manifests and configs, and exploring image contents file-by-file.

Authentication piggybacks on the standard Docker config (~/.docker/config.json), so docker login / d8 login work transparently.


Table of contents


Features

Image transfer (pull / push)
  • Pull one or many images into a single artifact:
    • tarball (default) - docker-compatible multi-image tar that docker load and podman load read natively
    • oci - OCI image-layout directory that preserves the full multi-arch index and supports resumable pulls (already-downloaded layers are reused on rerun)
    • legacy - last-resort single-image format for very old docker load consumers
  • Push a local tarball or OCI layout to any OCI/Docker registry. Supports pushing multi-manifest OCI layouts as a single index (--index).
  • Layer cache (--cache-path) that is reused across pulls and self-heals corrupt entries on next access.
Inspection
  • manifest - print the raw manifest bytes exactly as the registry returned them. Pipe to jq, feed to a signature verifier, archive for audit.
  • config - print the raw image config JSON (entrypoint, env, labels, history, ...).
  • digest - resolve the sha256:... digest of an image, either from a registry or from a local tarball (--tarball). With --full-ref produces the pinnable reference registry/repo@sha256:....
  • ls REPO - list all tags in a repository.
  • catalog REGISTRY - list all repositories in a registry.
  • export - stream the merged filesystem of an image as a tar (crane- compatible: verbatim linknames, whiteouts filtered).
Filesystem inspection (d8 cr fs)

A purpose-built subtree that reads the merged filesystem an image would expose at runtime (with deleted-by-upper-layer files hidden), without unpacking anything to disk:

  • fs ls IMAGE [PATH] - list files at PATH (or the whole image), with optional long form (mode, size).
  • fs cat IMAGE PATH - print a single regular file to stdout (no need for docker run --rm IMAGE cat ...).
  • fs tree IMAGE [PATH] - render the filesystem as a tree, optionally capped by depth and annotated with human-readable sizes.
  • fs extract IMAGE -o DIR - extract the full merged filesystem to a directory, with path-traversal and absolute-symlink protections that the verbatim export does not perform.
Multi-arch handling

The global --platform os/arch[/variant][:osversion] flag resolves a multi-arch index to a single image for image-level commands (pull tarball, manifest, config, digest, all fs *, export). With --format oci, pull keeps the entire index by default - omit --platform and you get every platform on disk; pass it and you narrow down to one.

Security and connectivity
  • TLS by default, with --insecure to opt into plain HTTP and skip TLS verification. Localhost and RFC1918 hosts already auto-allow HTTP without --insecure.
  • Non-distributable (foreign) layers are skipped on push by default; enable them with --allow-nondistributable-artifacts for registries that proxy Windows base images and similar.
  • Verbose mode (-v) routes go-containerregistry debug output to stderr for troubleshooting auth, redirects, retries, etc.
Quality-of-life
  • Shell completion for image references, tags, paths inside images, enum flags, etc., for bash/zsh/fish/powershell. Completion-time network calls are bounded by a short timeout and degrade silently on failure.
  • Crashes-safe sinks: export removes a half-written file on error so downstream tar tf cannot mistakenly consume a truncated archive.
  • Resumable OCI pulls: re-running pull --format oci skips intact blobs and cleans up in-flight temp files left over from a Ctrl+C.

Command map

d8 cr
├── pull IMAGE... PATH         Pull image(s) to a tarball or OCI layout
├── push PATH IMAGE            Push a tarball or OCI layout to a registry
├── export IMAGE [TARBALL]     Stream the merged filesystem as a tar
├── ls REPO                    List tags in a repository
├── catalog REGISTRY           List repositories in a registry
├── manifest IMAGE             Print the raw manifest
├── config IMAGE               Print the raw config JSON
├── digest [IMAGE]             Print the digest (registry or --tarball)
└── fs                         Inspect / extract files inside an image
    ├── ls IMAGE [PATH]        List files
    ├── cat IMAGE PATH         Print one regular file
    ├── tree IMAGE [PATH]      Render the filesystem as a tree
    └── extract IMAGE -o DIR   Extract the merged filesystem to disk

Global flags

These persistent flags apply to every d8 cr subcommand:

Flag Purpose
-v, --verbose Enable debug logs on stderr
--insecure Allow plain HTTP and skip TLS verification
--allow-nondistributable-artifacts Include non-distributable (foreign) layers when pushing
--platform os/arch[/variant][:osversion] Resolve multi-arch indices to a specific platform (image-level commands only)

Use cases

1. Air-gapped delivery

Pull a fully reproducible set of images on a connected host as an OCI layout, ship the directory across the air gap, and push it back into a local registry:

d8 cr pull --format oci \
  registry.deckhouse.io/deckhouse/ce/install:stable \
  registry.deckhouse.io/deckhouse/ce/release:stable \
  ./deckhouse-bundle

# carry ./deckhouse-bundle across the air gap, then:

d8 cr push --index ./deckhouse-bundle registry.internal/deckhouse/bundle:v1

OCI layout preserves every architecture in the manifest list, blob digests are stable, and rerunning the same pull is resumable.

2. Docker-compatible distribution

Build a single tarball that docker load / podman load can ingest on a target machine without touching any registry:

d8 cr pull --platform linux/amd64 nginx:1.27 my-tools:1.0 ./images.tar
scp images.tar prod-host:/tmp/
ssh prod-host 'docker load -i /tmp/images.tar'
3. Reproducing a known image by digest

Pin a tag to its digest, then verify a deployment is using exactly that image:

DIGEST=$(d8 cr digest --full-ref registry.internal/app:v1.4.2)
echo "$DIGEST"
# registry.internal/app@sha256:dead...beef

kubectl get pod my-app -o jsonpath='{.spec.containers[0].image}'
# must match $DIGEST exactly
4. Inspecting a release without docker run

CI pipelines, security audits and bug-bounty workflows often need to look inside an image but cannot (or must not) execute it. d8 cr fs reads the merged filesystem directly from the registry:

# Is /etc/passwd what we expect?
d8 cr fs cat registry.internal/app:v1.4.2 etc/passwd

# Where is the binary?
d8 cr fs tree registry.internal/app:v1.4.2 usr/local/bin --size

# Full audit: extract to disk with path-traversal protection,
# then run an offline scanner.
d8 cr fs extract registry.internal/app:v1.4.2 -o ./rootfs
grep -r 'BEGIN PRIVATE KEY' ./rootfs || echo 'clean'
5. Discovering what is in a registry

Walk an unfamiliar registry without scripting against its HTTP API:

d8 cr catalog registry.internal | head
d8 cr ls registry.internal/team-x/payments
d8 cr ls --omit-digest-tags registry.internal/team-x/payments

The --omit-digest-tags filter strips the sha256-* tags that cosign and similar tools leave behind so you see only "real" human-readable tags.

6. Signature and audit pipelines

Feed a raw, byte-stable manifest into a signature verifier or store it for audit:

d8 cr manifest registry.internal/app:v1.4.2 > app-v1.4.2.manifest.json
sha256sum app-v1.4.2.manifest.json
cosign verify --key cosign.pub --signature app-v1.4.2.sig app-v1.4.2.manifest.json
7. Pushing build output to a registry

A common CI step at the tail of a build: load a tarball produced by the builder, then publish it with a digest reference written back to a file that downstream steps can read:

d8 cr push ./build/out.tar \
  registry.internal/app:${CI_COMMIT_TAG} \
  --image-refs ./build/image.ref

cat ./build/image.ref
# registry.internal/app@sha256:...
8. Local registry / kind workflows

Push to plain-HTTP local registries without fighting TLS:

# kind ships a local registry on 5000/HTTP
d8 cr push --insecure ./out.oci localhost:5000/dev/app:latest

Localhost is auto-allowed for HTTP, so --insecure is only needed for non-loopback plain-HTTP registries.

9. Diffing two images

Compare the file listing or a specific file across two tags:

diff \
  <(d8 cr fs ls -l registry.internal/app:v1.4.1 | sort) \
  <(d8 cr fs ls -l registry.internal/app:v1.4.2 | sort)

diff \
  <(d8 cr fs cat registry.internal/app:v1.4.1 etc/app/config.yaml) \
  <(d8 cr fs cat registry.internal/app:v1.4.2 etc/app/config.yaml)

Examples

A copy-pasteable cheat-sheet of the most common invocations.

Pull
# Default tarball (single image, docker load compatible)
d8 cr pull --platform linux/amd64 alpine:3.19 ./alpine.tar

# Multi-image tarball
d8 cr pull --platform linux/amd64 alpine:3.19 busybox:1.36 ./images.tar

# OCI layout, all platforms preserved
d8 cr pull --format oci alpine:3.19 ./alpine-oci

# OCI layout with a shared layer cache (resumable across runs)
d8 cr pull --format oci --cache-path ~/.cache/d8-cr alpine:3.19 ./alpine-oci

# Legacy docker tarball (single image, lossy on digests)
d8 cr pull --format legacy --platform linux/amd64 alpine:3.19 ./alpine-legacy.tar
Push
# Push a docker tarball
d8 cr push ./image.tar registry.internal/myapp:v1

# Push an OCI layout (single manifest)
d8 cr push ./image-oci registry.internal/myapp:v1

# Push an OCI layout as a multi-manifest index
d8 cr push --index ./bundle-oci registry.internal/bundle:v1

# Push and record the digest reference
d8 cr push ./image.tar registry.internal/myapp:v1 --image-refs ./image.ref

# Push to a plain-HTTP local registry
d8 cr push --insecure ./image.tar localhost:5000/myapp:v1
Inspect
# Manifest (raw bytes, pipe-friendly)
d8 cr manifest alpine:3.19 | jq .

# Multi-arch: pick a platform
d8 cr manifest --platform linux/arm64 alpine:3.19 | jq .

# Config (entrypoint, env, history, ...)
d8 cr config alpine:3.19 | jq '.config.Env'

# Digest of a remote image
d8 cr digest alpine:3.19
# sha256:abcdef...

# Digest with full pinnable reference
d8 cr digest --full-ref alpine:3.19
# index.docker.io/library/alpine@sha256:abcdef...

# Digest of a local tarball, selecting a specific tag
d8 cr digest --tarball ./images.tar alpine:3.19
List
# Tags
d8 cr ls registry.internal/team/app
d8 cr ls --full-ref registry.internal/team/app
d8 cr ls --omit-digest-tags registry.internal/team/app

# Repositories
d8 cr catalog registry.internal
d8 cr catalog --full-ref registry.internal
Export (crane-compatible filesystem tar)
# Stream to stdout, pipe to tar
d8 cr export alpine:3.19 - | tar tf - | head

# Write to file
d8 cr export alpine:3.19 alpine-fs.tar
Filesystem inspection (d8 cr fs)
# List the root of the merged filesystem
d8 cr fs ls alpine:3.19

# Long format (mode + size)
d8 cr fs ls -l alpine:3.19 etc

# Tree, with sizes, capped at depth 2
d8 cr fs tree alpine:3.19 --size -L 2

# Tree, directories first
d8 cr fs tree alpine:3.19 etc --dirsfirst

# Read a single file
d8 cr fs cat alpine:3.19 etc/os-release

# Extract the whole filesystem (with safety checks)
d8 cr fs extract alpine:3.19 -o ./alpine-rootfs
Global flags in action
# Verbose: show go-containerregistry debug logs
d8 cr -v ls registry.internal/team/app

# Insecure: plain HTTP + skip TLS verify
d8 cr --insecure ls localhost:5000/myapp

# Platform: pick a specific arch from a multi-arch index
d8 cr --platform linux/arm64/v8 manifest registry.internal/app:v1

# Foreign layers: include them on push
d8 cr --allow-nondistributable-artifacts push ./win.tar registry.internal/win:base

Architecture

d8 cr is wired in internal/cr/cmd/cr.go and is composed of small, single-responsibility packages:

Package Responsibility
internal/cr/cmd Root cobra command, persistent flags, integration tests
internal/cr/cmd/basic Top-level subcommands: pull, push, export, ls, catalog, manifest, config, digest
internal/cr/cmd/fs fs subtree: ls, cat, tree, extract
internal/cr/cmd/completion Shell-completion helpers (bounded, fail-silent)
internal/cr/cmd/rootflagnames Single source of truth for persistent flag names
internal/cr/internal/registry Thin domain layer over go-containerregistry (fetch / push / list / options)
internal/cr/internal/image Pure operations over v1.Image / v1.ImageIndex (multi-arch resolve, ...)
internal/cr/internal/imageio Disk I/O: tarball + OCI image-layout load/save
internal/cr/internal/imagefs Merged-filesystem reader with whiteout handling and safe extraction
internal/cr/internal/output Text rendering for fs subcommands

Subcommands share a single *registry.Options populated by the root command's PersistentPreRunE. This means flag values set on the root are always observed by leaf commands before their RunE runs - the wiring is fully exercised by hermetic integration tests in internal/cr/cmd/integration_test.go against an in-memory registry.

Directories

Path Synopsis
cmd
Package cr wires the `d8 cr` subtree.
Package cr wires the `d8 cr` subtree.
completion
Package completion provides shell-completion helpers for the `d8 cr` subtree.
Package completion provides shell-completion helpers for the `d8 cr` subtree.
fs
Package fs implements the `d8 cr fs` subtree - filesystem inspection of container images.
Package fs implements the `d8 cr fs` subtree - filesystem inspection of container images.
rootflagnames
Package rootflagnames holds the persistent-flag names of the `d8 cr` root command in one place.
Package rootflagnames holds the persistent-flag names of the `d8 cr` root command in one place.
internal
image
Package image holds pure operations over v1.Image / v1.ImageIndex values that stand on their own (multi-arch resolution, future diff helpers, ...).
Package image holds pure operations over v1.Image / v1.ImageIndex values that stand on their own (multi-arch resolution, future diff helpers, ...).
imagefs
Package imagefs reads the filesystem of an OCI/Docker image as the merged view that a running container would see, and extracts it to disk with path-traversal and absolute-symlink-rewrite protection.
Package imagefs reads the filesystem of an OCI/Docker image as the merged view that a running container would see, and extracts it to disk with path-traversal and absolute-symlink-rewrite protection.
imageio
Package imageio bridges v1.Image / v1.ImageIndex and the local filesystem: loading and saving tarballs (docker format) and OCI image layouts.
Package imageio bridges v1.Image / v1.ImageIndex and the local filesystem: loading and saving tarballs (docker format) and OCI image layouts.
output
Package output renders imagefs data for `d8 cr fs` subcommands as human-readable text.
Package output renders imagefs data for `d8 cr fs` subcommands as human-readable text.
registry
Package registry is the thin domain layer of `d8 cr` on top of go-containerregistry/pkg/v1/*.
Package registry is the thin domain layer of `d8 cr` on top of go-containerregistry/pkg/v1/*.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL