← All Posts

Open Component Model in Production: Building Software Bills of Delivery for Cloud-Native Supply Chains

Matthias Bruns · · 10 min read
open-component-model software-supply-chain sbom cloud-native

The software supply chain has become a critical attack vector, with incidents like SolarWinds and Log4Shell exposing how vulnerable our interconnected systems really are. Traditional Software Bills of Materials (SBOMs) tell us what components exist, but they don’t capture the full picture of how software actually gets delivered and deployed. Enter the Open Component Model (OCM) – an open standard that goes beyond simple dependency tracking to create comprehensive Software Bills of Delivery (SBOD) for cloud-native environments.

Unlike SBOMs that focus on what’s in your code, OCM describes the complete delivery pipeline – from source repositories to runtime configurations. This matters because modern applications aren’t just code; they’re complex assemblies of container images, Helm charts, configuration files, and deployment manifests spread across multiple repositories and registries.

Note: The OCM project recently launched a next-generation rewrite at open-component-model/open-component-model. The new CLI and Go library are actively developed and marked Work In Progress — expect the API to stabilize over the coming months. All examples in this post use the new CLI.

What Makes OCM Different from Traditional Supply Chain Tools

The Open Component Model specification defines a technology-agnostic format for describing software delivery artifacts. Where SBOMs answer “what libraries am I using?”, OCM answers “what exactly needs to be delivered for this software to run?”

This distinction is crucial in cloud-native environments where your application might consist of:

  • Multiple microservices from different repositories
  • Container images with specific tags and digests
  • Kubernetes manifests with environment-specific configurations
  • Helm charts with values files
  • External dependencies like databases or message queues

Traditional tools struggle to connect these dots across repository boundaries. OCM creates a unified model that captures not just the artifacts, but their relationships and deployment context.

Core OCM Concepts for Production Implementation

Component Constructor Files

The heart of OCM is the component constructor – a YAML file that describes one or more deliverable software components. It serves as the input to the OCM CLI when creating component versions. Think of it as a shipping manifest listing everything needed to successfully deploy and run your software.

# component-constructor.yaml
components:
  - name: github.com/my-org/my-web-app
    version: 1.2.3
    provider:
      name: my-org
    resources:
      - name: app-image
        type: ociImage
        version: 1.2.3
        access:
          type: OCIImage/v1
          imageReference: registry.example.com/my-web-app:1.2.3
      - name: helm-chart
        type: helmChart
        version: 1.2.3
        access:
          type: Helm/v1
          helmRepository: registry.example.com
          helmChart: my-web-app
          version: 1.2.3
    sources:
      - name: app-source
        type: git
        access:
          type: File/v1alpha1
          uri: https://github.com/my-org/my-web-app

Component names follow a DNS-like convention (e.g. github.com/my-org/my-app). The sources section maintains traceability back to source code, while resources describes the deliverable artifacts. You can reference remote artifacts via access or embed local content directly via input.

Resource Types and Access Methods

OCM supports multiple resource types:

  • ociImage for container images
  • helmChart for Helm packages
  • blob for arbitrary data

Access types define how to retrieve external resources:

  • OCIImage/v1 — OCI-compliant registries
  • Helm/v1 — Helm repositories
  • LocalBlob/v1 — content stored in the same OCM repository
  • File/v1alpha1 — files referenced by URI

For local content, input types let you embed artifacts directly:

  • File/v1 — embed a single file
  • Dir/v1 — embed a directory as a tar archive
  • Helm/v1 — embed a local Helm chart
  • UTF8/v1 — embed inline text or structured data

This flexibility lets you model complex delivery scenarios where artifacts live in different systems.

Setting Up OCM in Multi-Repository Environments

Installation and Basic Configuration

Install the OCM CLI:

# Install via the official installer script
curl -sfL https://ocm.software/install-cli.sh | bash

# Or use the container image
docker run -t ghcr.io/open-component-model/cli:latest --help

# Verify installation
ocm version

For multi-repository setups, you’ll typically have one component per microservice plus an aggregate platform component. A practical repo structure:

my-platform/
├── services/
│   ├── user-service/
│   ├── payment-service/
│   └── notification-service/
├── charts/
│   └── platform-chart/
└── components/
    └── component-constructor.yaml

Creating Component Versions for Microservices

Each microservice generates its own component version during CI/CD. The constructor file supports environment variable substitution via Go template syntax:

# component-constructor.yaml
components:
  - name: ${COMPONENT_NAME}
    version: ${COMPONENT_VERSION}
    provider:
      name: ${PROVIDER_NAME}
    resources:
      - name: app-image
        type: ociImage
        version: ${COMPONENT_VERSION}
        access:
          type: OCIImage/v1
          imageReference: ${REGISTRY_URL}/${IMAGE_NAME}:${IMAGE_TAG}
    sources:
      - name: source
        type: git
        access:
          type: File/v1alpha1
          uri: https://github.com/${GITHUB_REPOSITORY}

Here’s how to wire this into a GitHub Actions workflow:

name: Build and Package Component
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Install OCM CLI
      run: curl -sfL https://ocm.software/install-cli.sh | bash

    - name: Build container image
      run: |
        docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} .
        docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }}

    - name: Add component version to OCM repository
      env:
        COMPONENT_NAME: github.com/${{ github.repository }}
        COMPONENT_VERSION: ${{ github.ref_name }}
        PROVIDER_NAME: ${{ github.repository_owner }}
        REGISTRY_URL: ghcr.io
        IMAGE_NAME: ${{ github.repository }}
        IMAGE_TAG: ${{ github.ref_name }}
        GITHUB_REPOSITORY: ${{ github.repository }}
      run: |
        ocm add component-version \
          --repository ghcr.io/${{ github.repository_owner }}/ocm \
          --constructor component-constructor.yaml

This ensures every service build produces a traceable component version capturing the exact artifacts and their sources.

Aggregating Components for Platform Delivery

For platform-level deployments, create aggregate components that reference individual service components:

# platform-component-constructor.yaml
components:
  - name: github.com/my-org/my-platform
    version: 2.1.0
    provider:
      name: my-org
    componentReferences:
      - name: user-service
        componentName: github.com/my-org/user-service
        version: 1.5.2
      - name: payment-service
        componentName: github.com/my-org/payment-service
        version: 2.3.1
      - name: notification-service
        componentName: github.com/my-org/notification-service
        version: 1.1.0
    resources:
      - name: platform-chart
        type: helmChart
        version: 2.1.0
        access:
          type: Helm/v1
          helmRepository: ghcr.io/my-org/charts
          helmChart: platform
          version: 2.1.0

This creates a complete bill of delivery for your entire platform, with precise version tracking for each component.

Implementing Comprehensive Supply Chain Security

Signature Verification

OCM supports cryptographic signing of component versions, enabling end-to-end verification of your supply chain. Keys are configured via .ocmconfig rather than passed as flags:

# ~/.config/ocm/config or .ocmconfig in your project
type: generic.config.ocm.software/v1
configurations:
- type: credentials.config.ocm.software
  consumers:
  - identity:
      type: RSA/v1alpha1
      algorithm: RSASSA-PSS
      signature: default
    credentials:
    - type: Credentials/v1
      properties:
        private_key_pem_file: /path/to/signing.key
        public_key_pem_file: /path/to/signing.pub

With credentials configured:

# Sign a component version
ocm sign component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  --signature my-org-signature

# Verify signature before deployment
ocm verify component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0

# Verify a specific named signature
ocm verify component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  --signature my-org-signature

Integrate signature verification into your deployment pipelines to ensure only signed components reach production.

Inspecting and Scanning Component Versions

OCM component versions provide the perfect input for comprehensive vulnerability scanning. Inspect a component version to extract its artifact references:

# Inspect a component version (YAML output)
ocm get component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  --output yaml

# Download a specific resource for scanning
ocm download resource \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  --identity name=app-image \
  --output ./app-image.tar

# Scan the downloaded image
trivy image --input ./app-image.tar

This approach ensures you’re scanning the exact artifacts that will be deployed, not just what’s in your source repositories.

Transferring Components Between Registries

OCM’s transfer capability is central to air-gapped or multi-environment deployments:

# Transfer from a local CTF archive to an OCI registry
ocm transfer component-version \
  ctf::./transport-archive//github.com/my-org/my-platform:2.1.0 \
  ghcr.io/production-registry/ocm \
  --copy-resources

# Recursive transfer (includes all referenced component versions)
ocm transfer component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  ghcr.io/production-registry/ocm \
  --recursive --copy-resources

Monitoring and Observability for OCM Components

Runtime Correlation

One of OCM’s most powerful features is the ability to correlate runtime behavior with specific component versions. Add OCM metadata to your Kubernetes deployments:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    ocm.software/component: github.com/my-org/user-service
    ocm.software/version: 1.5.2
    ocm.software/platform-component: github.com/my-org/my-platform
    ocm.software/platform-version: 2.1.0
spec:
  template:
    metadata:
      labels:
        ocm.software/component: github.com/my-org/user-service
        ocm.software/version: 1.5.2

This enables powerful queries in your monitoring systems:

# Alert on errors for specific component versions
rate(http_requests_total{code=~"5.."}[5m]) > 0.1
  and on(pod)
  kube_pod_labels{label_ocm_software_component="github.com/my-org/user-service"}

Supply Chain Drift Detection

Monitor for unauthorized changes to your supply chain by comparing runtime state with OCM component versions:

#!/bin/bash
# drift-detection.sh

# Get expected images from OCM component version
expected_images=$(ocm get component-version \
  ghcr.io/my-org/ocm//github.com/my-org/my-platform:2.1.0 \
  --output json | jq -r '.resources[].access.imageReference // empty')

# Get actual running images
actual_images=$(kubectl get pods \
  -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\n')

# Compare and alert on differences
diff <(echo "$expected_images" | sort) <(echo "$actual_images" | sort) || {
  echo "ALERT: Supply chain drift detected!"
  exit 1
}

Run this check regularly to catch unauthorized deployments or configuration drift.

Best Practices for Production OCM Implementation

Component Naming and Versioning Strategy

OCM component names follow a DNS-like convention — use your GitHub organization path (e.g. github.com/my-org/my-service) to ensure global uniqueness. For versioning, align with SemVer:

  • Patch versions (1.2.1 → 1.2.2): Bug fixes, security patches
  • Minor versions (1.2.0 → 1.3.0): New features, backward-compatible changes
  • Major versions (1.0.0 → 2.0.0): Breaking changes, architecture updates

Include build metadata as resource labels in your constructor:

resources:
  - name: app-image
    type: ociImage
    version: ${COMPONENT_VERSION}
    labels:
      - name: build.commit
        value: ${GITHUB_SHA}
      - name: build.timestamp
        value: ${BUILD_TIMESTAMP}
      - name: build.pipeline
        value: github-actions
    access:
      type: OCIImage/v1
      imageReference: ${REGISTRY_URL}/${IMAGE_NAME}:${IMAGE_TAG}

Repository Organization

Structure your repositories to support OCM workflows:

organization/
├── services/
│   ├── user-service/           # Individual service repos
│   ├── payment-service/
│   └── notification-service/
├── platform/
│   ├── charts/                 # Shared Helm charts
│   ├── configs/               # Environment configs
│   └── components/            # Platform component constructors
└── infrastructure/
    ├── terraform/             # Infrastructure as code
    └── policies/              # Security and compliance policies

Automation and Integration

Automate component version creation in your CI/CD pipelines. Never manually create component constructors that reference artifacts you haven’t built — generate them from your build process to ensure accuracy. Use environment variable substitution in constructor files rather than hardcoding versions.

For GitOps workflows, store your aggregate platform constructor in Git and trigger OCM transfers on version bumps. Combined with the OCM Kubernetes controller, you can drive deployments directly from component versions in an OCI registry.

Looking Forward: OCM in the Cloud-Native Ecosystem

The Open Component Model represents a significant step forward in supply chain security and observability. The new-generation implementation at open-component-model/open-component-model is an active rewrite that brings a cleaner library API, a redesigned CLI, and a Kubernetes controller for GitOps-style deployments. It’s backed by SAP and the broader CNCF community.

Key areas of active development include deeper integration with policy engines like Open Policy Agent, enhanced multi-cloud transfer scenarios, and language bindings beyond Go. The project acknowledges the WIP status openly — the CLI command surface and library API will stabilize as the project matures.

For organizations serious about supply chain security, OCM isn’t just another tool – it’s a comprehensive approach to understanding and controlling what gets delivered to production. Start with a pilot project: pick one service, write its component constructor, push it to an OCI registry, and verify you can reproduce the exact delivery from the descriptor alone. The investment in proper supply chain tracking pays dividends when you need to respond quickly to security incidents or compliance audits.

The future of software delivery is traceable, verifiable, and secure. OCM provides the foundation to build that future today.

Reader settings

Font size