This guide walks you through building a valid
OCI Image Layout
on your local filesystem using regshape. No registry or network connection is
required — all operations work entirely on disk.
An OCI Image Layout is a directory that stores OCI content (manifests, configs,
and layer blobs) content-addressed under blobs/sha256/, with a top-level
oci-layout marker file and an index.json (OCI Image Index) as the entry
point. It can represent any OCI artifact — container images, signatures, SBOMs,
or custom artifact types.
my-image/
├── oci-layout # {"imageLayoutVersion": "1.0.0"}
├── index.json # OCI Image Index
├── .regshape-stage.json # regshape staging state (not part of the OCI spec)
└── blobs/
└── sha256/
├── <hex> # layer tar blob
├── <hex> # config JSON blob
└── <hex> # manifest JSON blob
The typical workflow for building a layout is a four-step pipeline:
layout init → layout add layer (×N) → layout generate config → layout generate manifest
State is automatically persisted between steps in a staging file
(.regshape-stage.json) created by layout init.
regshape layout init --path ./my-imageThis creates the directory structure, writes the oci-layout marker,
creates an empty index.json, and seeds the staging file.
Initialised OCI Image Layout at ./my-image
Note: If the directory already contains a valid OCI Image Layout,
initwill refuse to overwrite it and exit with an error.
Add one or more layer blobs. Each call stages the layer and records its content-addressed descriptor.
regshape layout add layer \
--path ./my-image \
--file ./layer.tar.gzCompression handling: if the file is not already a gzip- or
zstd-compressed tar archive (detected by magic bytes), regshape automatically
compresses it with gzip. You can override this with --compress-format zstd.
# Force zstd compression for an uncompressed tar
regshape layout add layer \
--path ./my-image \
--file ./layer.tar \
--compress-format zstdLayer annotations: you can attach arbitrary key=value annotations to a
layer at add time, or add/update them later with layout annotate layer.
regshape layout add layer \
--path ./my-image \
--file ./layer.tar.gz \
--annotation org.opencontainers.image.created=2026-03-08 \
--annotation com.example.layer.role=baseRepeat layout add layer for each additional layer. Layers are staged in the
order they are added.
regshape layout generate config \
--path ./my-image \
--architecture amd64 \
--os linux \
--media-type application/vnd.oci.image.config.v1+jsonThis reads the staged layer digests to populate rootfs.diff_ids, writes the
config JSON blob, and records it in the staging file.
If you omit any flag, regshape will prompt you interactively:
Architecture [amd64]: arm64
OS [linux]:
Media type [application/vnd.oci.image.config.v1+json]:
Generated config sha256:abc123... (312 bytes)
regshape layout generate manifest \
--path ./my-image \
--ref-name latest \
--media-type application/vnd.oci.image.manifest.v1+jsonThis builds the OCI image manifest referencing the staged layers and config,
writes it as a blob, and registers it in index.json with the optional
human-readable reference name (org.opencontainers.image.ref.name).
Generated manifest [latest] sha256:def456... (578 bytes)
At any point you can inspect what has been staged:
regshape layout status --path ./my-imageLayers staged: 2
[0] sha256:aaa... (4194304 bytes) application/vnd.oci.image.layer.v1.tar+gzip
[1] sha256:bbb... (2097152 bytes) application/vnd.oci.image.layer.v1.tar+gzip
Config: set -> sha256:ccc...
Manifest: set -> sha256:def...
Add --json to any command for machine-readable output.
regshape layout show --path ./my-imagePrints index.json as pretty-printed JSON.
regshape layout validate --path ./my-imageChecks that:
- The
oci-layoutmarker is present and well-formed. index.jsonis a valid OCI Image Index.- Every blob digest listed in the index and its manifests is present on disk and matches its declared digest.
Layout at ./my-image is valid.
Merge (or replace) annotations on a staged layer without touching the blob:
regshape layout annotate layer \
--path ./my-image \
--index 0 \
--annotation com.example.role=baseLayer index is 0-based, as shown by
layout status.
Re-generate the config blob with new architecture, OS, or annotations. The old config blob is automatically deleted.
regshape layout update config \
--path ./my-image \
--architecture arm64Warning: If you have already generated a manifest, updating the config produces a new config digest that the existing manifest no longer references. You must re-run
layout generate manifest(orlayout annotate manifest) after updating the config.
Add or replace top-level manifest annotations. The old manifest blob is deleted
and index.json is updated atomically.
regshape layout annotate manifest \
--path ./my-image \
--annotation org.opencontainers.image.version=1.2.0# 1. Initialise
regshape layout init --path ./my-image
# 2. Add layers
regshape layout add layer \
--path ./my-image \
--file ./base.tar.gz \
--media-type application/vnd.oci.image.layer.v1.tar+gzip
regshape layout add layer \
--path ./my-image \
--file ./app.tar.gz \
--media-type application/vnd.oci.image.layer.v1.tar+gzip
# 3. Generate config
regshape layout generate config \
--path ./my-image \
--architecture amd64 \
--os linux \
--media-type application/vnd.oci.image.config.v1+json
# 4. Generate manifest
regshape layout generate manifest \
--path ./my-image \
--ref-name latest \
--media-type application/vnd.oci.image.manifest.v1+json
# 5. Validate
regshape layout validate --path ./my-imageAll CLI commands are thin wrappers over the regshape.libs.layout module,
which you can call directly in Python scripts or other tools.
from pathlib import Path
from regshape.libs.layout import (
init_layout,
stage_layer,
generate_config,
generate_manifest,
validate_layout,
)
from regshape.libs.models.mediatype import (
OCI_IMAGE_CONFIG,
OCI_IMAGE_LAYER_TAR_GZIP,
OCI_IMAGE_MANIFEST,
)
layout = Path("./my-image")
init_layout(layout)
with open("layer.tar.gz", "rb") as f:
stage_layer(layout, f.read(), OCI_IMAGE_LAYER_TAR_GZIP)
generate_config(layout, architecture="amd64", os_name="linux")
generate_manifest(layout, ref_name="latest")
validate_layout(layout)from regshape.libs.layout import (
update_config,
update_layer_annotations,
update_manifest_annotations,
read_stage,
)
# Inspect staging state
print(read_stage(layout))
# Update annotations on the first layer
update_layer_annotations(layout, 0, {"com.example.role": "base"})
# Re-generate config for a different architecture
update_config(layout, architecture="arm64")
# Add a version annotation to the manifest
update_manifest_annotations(layout, {"org.opencontainers.image.version": "1.0.0"})