Pull OCI Artifacts Natively

Complete the pack guide first

This guide continues from Add OCI Artifacts. It assumes you already built the component versions from that guide — an embedded OCI image layout, an externally referenced image, and an ORAS-fetched layout — each sitting in a local ./transport-archive CTF. Complete it before running the steps below.

With a component version built, transferring it with a local blob uploader internalizes its OCI artifacts and maps them to native OCI manifests in the target registry. They become pullable directly with standard OCI tooling like docker pull, oras pull, or crane — no OCM CLI required.

OCM v2 makes this possible through its OCI-compatible index representation. When a component version is stored in an OCI registry, native OCI artifacts (images, Helm charts, OCI image layouts) stored as local blobs are mapped to proper OCI manifests within the component version’s index. This means they can be accessed directly by digest using any OCI-compliant client.

What You’ll Learn

  • Transfer a component version with a local blob uploader so external OCI image references are internalized as local blobs
  • Access local blobs natively from an OCI registry by their localReference digest and media type
  • Pull the artifact with docker, oras, and crane without the OCM CLI

Estimated time: ~15 minutes

Prerequisites

Tutorial

Starting from the component version that embeds an OCI image layout as a local blob, transfer it to an OCI registry where it becomes natively accessible.

  1. Transfer to an OCI registry

    Transfer the component version to an OCI registry. First, create a transfer configuration that copies all resources by value:

    cat > .ocmconfig << 'EOF'
    type: generic.config.ocm.software/v1
    configurations:
      - type: localblob.uploader.transfer.config.ocm.software/v1alpha1
    EOF

    The CLI automatically merges .ocmconfig from the current directory with your other OCM configuration (such as $HOME/.ocmconfig), so credentials and resolvers stay in effect. Passing a file with --config would replace that configuration instead.

    ocm transfer cv \
      ./transport-archive//github.com/acme.org/native-oci-demo:1.0.0 \
      <your-registry>

    Replace <your-registry> with your registry address (e.g. http://localhost:5001 for a local HTTP registry, or ghcr.io/my-org/ocm for a remote HTTPS registry).

    Note

    For local registries running without TLS, use the http:// scheme prefix (e.g. http://localhost:5001). HTTPS registries work without a scheme prefix.

    During transfer, OCM stores the OCI manifest and its layers as native OCI objects in the registry. The component version’s index references the manifest directly.

  2. Access the image natively

    After transfer, inspect the component version in the registry to find the image’s digest:

    ocm get cv <your-registry>//github.com/acme.org/native-oci-demo:1.0.0 -o yaml

    Look for the localReference field in the resource’s access specification — it contains the digest of the native OCI manifest:

    resources:
      - access:
          localReference: sha256:...
          mediaType: application/vnd.oci.image.manifest.v1+json
          type: LocalBlob/v1
        name: my-oci-artifact
        type: ociArtifact

    You can pull the artifact natively using the localReference digest combined with the component version’s repository path:

    # Using ORAS
    oras pull <your-registry>/component-descriptors/github.com/acme.org/native-oci-demo@sha256:...
    
    # Using crane
    crane pull <your-registry>/component-descriptors/github.com/acme.org/native-oci-demo@sha256:... image.tar
    Note

    The repository path follows the pattern <registry>/<subpath>/component-descriptors/<component-name>. The digest from localReference addresses the manifest directly within the component version’s OCI index.

Starting from the component version that references an external OCI image, transfer it with a local blob uploader configuration to internalize the image as a local blob, and then access it natively from the target registry.

  1. Transfer with a local blob uploader

    Transfer the component to your target registry, copying all resources by value. Create the .ocmconfig if you have not already:

    cat > .ocmconfig << 'EOF'
    type: generic.config.ocm.software/v1
    configurations:
      - type: localblob.uploader.transfer.config.ocm.software/v1alpha1
    EOF
    ocm transfer cv \
      ./transport-archive//github.com/acme.org/transfer-demo:1.0.0 \
      <your-registry>
    Note

    For local registries running without TLS, use the http:// scheme prefix (e.g. http://localhost:5001). HTTPS registries work without a scheme prefix.

    With the local blob uploader configuration, OCM:

    1. Downloads the image from ghcr.io/stefanprodan/podinfo:6.9.1
    2. Stores it as a local blob in the target component version
    3. Maps it to a native OCI manifest in the component version’s index
    4. Updates the access specification with a localReference (digest) pointing to the native OCI manifest
  2. Inspect the transferred component

    ocm get cv <your-registry>//github.com/acme.org/transfer-demo:1.0.0 -o yaml

    After transfer with the local blob uploader, the access specification changes from an external reference to a local blob:

    resources:
      - access:
          localReference: sha256:...
          mediaType: application/vnd.oci.image.index.v1+json
          referenceName: stefanprodan/podinfo:6.9.1
          type: LocalBlob/v1
        name: app-image
        relation: external
        type: ociImage
        version: 1.0.0

    Key observations:

    • access.type changed from ociArtifact to LocalBlob/v1 — the image is now embedded
    • localReference contains the digest of the stored image manifest/index
    • mediaType is application/vnd.oci.image.index.v1+json (or application/vnd.oci.image.manifest.v1+json for single-platform images)
    • referenceName preserves the original image reference for traceability
    • relation remains external — this indicates the resource was originally sourced externally, even though it is now stored locally
  3. Pull the image natively

    Use the localReference digest to pull the image with standard OCI tooling. The image is stored within the component version’s repository:

    # Using docker
    docker pull <your-registry>/component-descriptors/github.com/acme.org/transfer-demo@sha256:...
    
    # Using crane
    crane manifest <your-registry>/component-descriptors/github.com/acme.org/transfer-demo@sha256:...

    The image is stored as a first-class OCI manifest in the registry. No OCM tooling is required to access it — any OCI-compliant client works.

    You can also download through OCM:

    ocm download resource <your-registry>//github.com/acme.org/transfer-demo:1.0.0 \
      --identity name=app-image \
      --output app-image-download

Starting from the component version that embeds an ORAS-fetched OCI image layout, transfer it to a target registry and pull it natively.

  1. Transfer

    cat > .ocmconfig << 'EOF'
    type: generic.config.ocm.software/v1
    configurations:
      - type: localblob.uploader.transfer.config.ocm.software/v1alpha1
    EOF
    
    ocm transfer cv \
      ./transport-archive//github.com/acme.org/fetched-oci-demo:1.0.0 \
      <your-registry>
  2. Inspect the transferred component version

    After transfer, inspect the component version in the registry:

    ocm get cv <your-registry>//github.com/acme.org/fetched-oci-demo:1.0.0 -o yaml

    The resource access shows a local blob reference:

    resources:
      - access:
          localReference: sha256:262578cde928d5c9eba3bce079976444f624c13ed0afb741d90d5423877496cb
          mediaType: application/vnd.oci.image.index.v1+json
          type: LocalBlob/v1
        name: app-image
        relation: local
        type: ociArtifact
        version: 1.0.0

    Key observations:

    • localReference contains the digest of the stored image manifest/index — this is the value you need for native access
    • mediaType is application/vnd.oci.image.index.v1+json (multi-platform) or application/vnd.oci.image.manifest.v1+json (single-platform)
    • relation: local confirms the artifact is stored inside the component version, not referenced externally
  3. Pull natively

    Use the localReference digest from the previous step to pull the artifact with standard OCI tooling:

    # Pull using the localReference digest
    oras pull <your-registry>/component-descriptors/github.com/acme.org/fetched-oci-demo@sha256:abc123...

    The image is stored as a first-class OCI manifest in the registry. No OCM tooling is required to access it — any OCI-compliant client works.

How Native Access Works

Under the hood, OCM v2 stores component versions as OCI Image Indexes. When a local blob has an OCI-native media type (image manifest, image index, or OCI image layout), it is stored as a separate OCI manifest referenced from the component version’s index — not as an opaque layer.

  flowchart TB
    subgraph index ["Component Version (OCI Index)"]
        Desc["Descriptor Manifest<br/>software.ocm.descriptor=true"]
        NativeImg["Image Manifest<br/>(native OCI artifact)"]
    end

    Desc -- "layer" --> NonOCI["config.yaml<br/>(non-OCI blob)"]
    Desc -- "ocm download resource" --> OCM["OCM CLI consumer"]
    NativeImg -- "docker pull / oras pull / crane pull" --> Pull["Any OCI client"]

    style Pull fill:#dcfce7,color:#166534
    style OCM fill:#dbeafe,color:#1e40af

This means:

  • Non-OCI blobs (plain files, config data) are stored as layers in the descriptor manifest, accessed only through OCM tooling
  • Native OCI artifacts (images, Helm charts) are stored as separate manifests in the index, accessible both through OCM and directly through any OCI client

Check Your Understanding

What is the difference between localReference and globalAccess?
  • localReference is a content-addressable digest that identifies the blob within the component version’s storage. It is stable across transfers — the same digest works regardless of which registry hosts the component, because it is derived from the blob content itself. It works with any OCM repository implementation (CTF archives, OCI registries). For native OCI artifacts stored in an OCI registry, you can access them directly using this digest combined with the component version’s repository path.
  • globalAccess is an optional, location-specific access specification that points to the artifact in a particular registry. It is not set by default — the globalAccessPolicy must be explicitly configured to enable it. Note that this reference becomes invalid after mirroring — when the component is transferred to a different registry, the globalAccess still points to the original registry. Always use localReference for stable, location-independent access.
How do I enable globalAccess references?

By default, globalAccess is not populated. To opt in, use the two-step transfer workflow with a transfer specification:

  1. Generate the transfer spec:

    ocm transfer cv --dry-run -o yaml \
      ./transport-archive//github.com/acme.org/native-oci-demo:1.0.0 \
      <your-registry> > spec.yaml
  2. Edit spec.yaml and add globalAccessPolicy: auto to the spec of each node that writes a local blob to the OCI registry: OCIAddLocalResource nodes, TransferOCILocalBlobToOCILocalBlob or TransferCTFLocalBlobToOCILocalBlob nodes for resources that already are local blobs in the source (an OCI registry or a CTF archive), and TransferOCIArtifactToOCILocalBlob nodes for OCI images copied as local blobs. Only these node types, which target an OCI registry, accept the field:

    - type: OCIAddLocalResource/v1alpha1
      spec:
        globalAccessPolicy: auto
        # ... other fields ...
    - type: TransferCTFLocalBlobToOCILocalBlob/v1alpha1
      spec:
        globalAccessPolicy: auto
        # ... other fields ...
    - type: TransferOCIArtifactToOCILocalBlob/v1alpha1
      spec:
        globalAccessPolicy: auto
        # ... other fields ...
  3. Execute the modified spec:

    ocm transfer cv --transfer-spec spec.yaml

This is an experimental feature carried over from OCM v1 for backwards compatibility. Its future availability is being evaluated by the community.

With globalAccessPolicy: auto, the descriptor looks like this after transfer:

resources:
  - access:
      localReference: sha256:abc123...
      mediaType: application/vnd.oci.image.index.v1+json
      referenceName: stefanprodan/podinfo:6.9.1
      type: LocalBlob/v1
      globalAccess:
        imageReference: <your-registry>/component-descriptors/github.com/acme.org/native-oci-demo@sha256:abc123...
        type: ociArtifact
    name: app-image
    relation: external
    type: ociArtifact
    version: 1.0.0

The globalAccess.imageReference provides a direct pullable reference. Note that this reference may become stale if the component is transferred to another registry using tooling that does not update the globalAccess field.

Without globalAccess, you can still access native OCI artifacts directly using the localReference digest and the component version’s repository path in the registry.

Can I control how artifacts are stored in the target?

Yes. A local blob uploader configuration stores artifacts as local blobs within the component version. To upload them as standalone OCI artifacts in the target registry, add an OCI uploader entry before the local blob entry:

type: generic.config.ocm.software/v1
configurations:
  - type: oci.uploader.transfer.config.ocm.software/v1alpha1
  - type: localblob.uploader.transfer.config.ocm.software/v1alpha1

With the local blob uploader, the artifact stays within the component version’s index. With the OCI uploader, it is stored independently.

Both options make the artifact natively accessible in OCI registries. For details, see the Transfer Configuration Reference.

Cleanup

Remove the tutorial artifacts:

rm -rf /tmp/ocm-native-oci /tmp/ocm-transfer-native /tmp/ocm-fetch-oci

Next Steps