Skip to content

Commit b8c3fae

Browse files
committed
docs: replace asset annotations with OCI ownership referrer
<!-- markdownlint-disable MD041 --> Updates §6.3 of the OCI storage backend spec to trace assets to their owning Component Version via a separate *ownership referrer* manifest (linked through the OCI `subject` field), instead of mutating the artifact with annotations. This matches ADR 0015 and keeps the artifact's digest and any existing OCI signatures intact. - Defines the referrer manifest (artifact type `application/vnd.ocm.software.ownership.v1+json`, empty config/layer, same repository as the artifact) and discovery via the OCI Referrers API with tag-schema fallback. - Rewrites integrity (§6.3.2) and verification (§6.3.3) around the referrer; drops the obsolete `software.ocm.base.digest` annotation and the `ociArtifactDigest/v1` annotation-stripping normalization. - Requires referrers to travel with the artifact across registries and OCI Image Layouts. <!-- Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. --> Fixes: open-component-model/ocm-project#1031 Signed-off-by: Piotr Janik <piotr.janik@sap.com>
1 parent 1f786fc commit b8c3fae

1 file changed

Lines changed: 56 additions & 105 deletions

File tree

  • doc/04-extensions/03-storage-backends

doc/04-extensions/03-storage-backends/oci.md

Lines changed: 56 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ The specification also introduces a Component Index artifact used for referrer-b
2929
* [6.3.1 Normative Keys](#631-normative-keys)
3030
* [6.3.2 Integrity Requirements](#632-integrity-requirements)
3131
* [6.3.3 Verification of annotated artifacts](#633-verification-of-annotated-artifacts)
32-
* [6.3.4 `ociArtifactDigest/v1` Normalization](#634-ociartifactdigestv1-normalization)
3332
* [7. Descriptor Selection Logic](#7-descriptor-selection-logic)
3433
* [8. Component Index (Referrer Anchor)](#8-component-index-referrer-anchor)
3534
* [8.1 Requirements](#81-requirements)
@@ -330,15 +329,58 @@ An index representing a component version:
330329

331330
### 6.3 Asset Annotations
332331

333-
When an OCI artifact (e.g., an OCI manifest or OCI index) is imported into a Component Version as a `resource` or `source`, OCM implementations **MAY** automatically add annotations to the artifact’s top-level manifest or index.
332+
When an OCI artifact (e.g., an OCI manifest or OCI index) is imported into a Component Version as a `resource` or `source`, OCM implementations **MAY** publish a separate OCI manifest, the _ownership referrer_, that carries asset annotations and is linked to the artifact via the OCI [`subject`](https://github.com/opencontainers/image-spec/blob/v1.1.0/manifest.md#image-manifest-property-descriptions) field.
334333
These annotations allow consumers to derive which Component Version now started shipping the artifact,
335334
and which artifact within that Component Version is referencing this image, thus providing a form of "asset location".
336335

336+
The referrer is discovered through the OCI [Referrers API](https://github.com/opencontainers/distribution-spec/blob/v1.1.1/spec.md#listing-referrers), which returns every manifest in a repository whose `subject` points at a given artifact. OCM marks its referrers with the artifact type `application/vnd.ocm.software.ownership.v1+json`: a small OCI manifest with no payload, used only to carry the asset annotations and link to the artifact.
337+
337338
These annotations are optional and **MUST NOT** affect any descriptor selection, or resolution semantics elsewhere in this specification.
338339

340+
The original artifact **MUST NOT** be modified. The artifact digest recorded in the Component Descriptor **MUST** remain identical to the digest produced before annotations were attached.
341+
342+
The ownership referrer is a standard OCI image manifest with the following shape:
343+
344+
```json
345+
{
346+
"schemaVersion": 2,
347+
"mediaType": "application/vnd.oci.image.manifest.v1+json",
348+
"artifactType": "application/vnd.ocm.software.ownership.v1+json",
349+
"config": {
350+
"mediaType": "application/vnd.oci.empty.v1+json",
351+
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
352+
"size": 2
353+
},
354+
"layers": [
355+
{
356+
"mediaType": "application/vnd.oci.empty.v1+json",
357+
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
358+
"size": 2
359+
}
360+
],
361+
"subject": {
362+
"mediaType": "<media type of the artifact manifest or index>",
363+
"digest": "<digest of the artifact manifest or index>",
364+
"size": <size of the artifact manifest or index>
365+
},
366+
"annotations": {
367+
"software.ocm.component.name": "<component name>",
368+
"software.ocm.component.version": "<component version>",
369+
"software.ocm.artifact": "<JCS-canonicalized artifact identity>"
370+
}
371+
}
372+
```
373+
374+
The referrer manifest:
375+
376+
* **MUST** use `artifactType` `application/vnd.ocm.software.ownership.v1+json`.
377+
* **MUST** set `subject` to the descriptor of the artifact's top-level manifest or index, using the unmodified artifact digest.
378+
* **MUST** be pushed into the same repository as the artifact.
379+
* **MUST** use the standard OCI empty `config` and a single empty `layers` entry per [OCI artifact guidance](https://github.com/opencontainers/image-spec/blob/v1.1.0/manifest.md#guidelines-for-artifact-usage).
380+
339381
#### 6.3.1 Normative Keys
340382

341-
Writers **MAY** add the following annotations:
383+
Writers **MAY** add the following annotations to the ownership referrer:
342384

343385
| Annotation Key | Description |
344386
|----------------------------------|------------------------------------|
@@ -372,120 +414,29 @@ Rules:
372414
Annotation values for `software.ocm.artifact` **MUST** be JSON-encoded strings because OCI annotation maps permit only string values.
373415
The JSON-encoded string **SHOULD** be canonicalized as per [JCS (RFC 8785)](https://www.rfc-editor.org/rfc/rfc8785) to ensure stable formatting.
374416

375-
Annotations **MUST** be written to the **top-level descriptor** of the OCI artifact:
376-
377-
* For manifest-based artifacts → `manifest.annotations`
378-
* For index-based artifacts → `index.annotations`
379-
380-
Annotations **MUST NOT** be added to nested manifests or indexes within an artifact.
381-
Only the top-level descriptor **MAY** carry these annotations.
417+
Annotations **MUST** be written to the **top-level descriptor** of the ownership referrer (`manifest.annotations`).
418+
Annotations **MUST NOT** be added to the original artifact, nor to nested manifests within the referrer.
382419

383420
#### 6.3.2 Integrity Requirements
384421

385422
To avoid mutating immutable artifacts:
386423

387-
1. If the artifact is newly created during import, for example during component creation,
388-
writers **MAY** inject annotations freely.
389-
In annotations are added on demand, writers **SHOULD** signal that the original digest was modified during import.
390-
391-
2. If the artifact already exists in a different location and modifying annotations would change its digest,
392-
writers **SHOULD NOT** add or modify annotations.
393-
If they do, the origin digest **SHOULD** be preserved with the annotation `software.ocm.base.digest="<original-digest>"`.
394-
In case of existing component version signatures, writers **SHOULD** reattest the modified artifact and its owning component version.
395-
In case an artifact is annotated multiple times, each annotation event **MUST** preserve the original digest in `software.ocm.base.digest`.
396-
397-
To ensure integrity, verifiers **MAY** decide to ignore annotations when verifying signatures or digests (see [6.3.3 Verification of annotated artifacts](#633-verification-of-annotated-artifacts)).
398-
The process to verify signatures **MUST** remain consistent regardless of annotation presence.
424+
1. The original artifact **MUST NOT** be modified. Its digest **MUST** remain identical to the digest produced before the ownership referrer was attached.
425+
2. The ownership referrer **MUST** be a separate OCI manifest pushed into the same repository as the artifact, linked to it via the OCI `subject` field.
426+
3. Existing OCI-level signatures over the artifact **MUST** remain valid because the artifact bytes are unchanged.
427+
4. A single artifact **MAY** carry multiple ownership referrers when several Component Versions ship the same artifact.
399428

400429
#### 6.3.3 Verification of annotated artifacts
401430

402-
When artifacts carry OCM-specific informational annotations, verifiers **MAY** ignore these annotations during verification.
403-
Doing so enables stable, annotation-independent digests and signatures.
404-
405-
To perform verification in an annotation-tolerant way, verifiers **MAY** apply the following procedure:
406-
407-
1. Retrieve the artifact manifest or index and verify its digest and signatures as usual.
408-
2. Check whether the artifact includes OCM-specific annotations such as
409-
`software.ocm.component.name`, `software.ocm.component.version`, or `software.ocm.artifact`.
410-
3. If such annotations exist, create an in-memory copy of the artifact descriptor with these annotations removed.
411-
4. Recompute the descriptor digest. This digest **MUST** match the value recorded in `software.ocm.base.digest`.
412-
5. This recomputed digest **MAY** then be used for signature verification in place of the original artifact digest.
413-
414-
This mechanism enables signature verification to remain stable regardless of the presence or absence of OCM-specific informational annotations.
415-
It also preserves the original component version digest when verifiers choose to ignore those annotations.
416-
417-
This behavior is optional. Verifiers **MAY** instead choose to validate the artifact exactly as stored, including all annotations.
418-
If so, both the digest and signatures **MUST** correspond to the annotated artifact, and any modification or addition of annotations **MUST** trigger re-signing.
419-
420-
#### 6.3.4 `ociArtifactDigest/v1` Normalization
421-
422-
Based on the annotations defined above, an extension to the normalisation algorithm [`ociArtifactDigest/v1`](../04-algorithms/artifact-normalization-types.md) is defined.
423-
424-
Component Version:
425-
426-
```yaml
427-
component:
428-
name: ocm.software/ocmcli
429-
provider: ocm.software
430-
version: 0.27.0
431-
resources:
432-
- access:
433-
localReference: sha256:64b586a57294adc5749324d0574df23499555de5168b4b1f1bd7ce9b06e2d49f
434-
mediaType: application/octet-stream
435-
type: localBlob
436-
digest:
437-
hashAlgorithm: SHA-256
438-
normalisationAlgorithm: genericBlobDigest/v1
439-
value: 64b586a57294adc5749324d0574df23499555de5168b4b1f1bd7ce9b06e2d49f
440-
extraIdentity:
441-
architecture: amd64
442-
os: windows
443-
labels:
444-
- name: downloadName
445-
value: ocm
446-
name: ocmcli
447-
relation: local
448-
type: executable
449-
version: 0.27.0
450-
```
431+
To verify that an ownership referrer is authentic, clients **MUST**:
451432

452-
OCI manifest annotations based on `sha256:64b586a57294adc5749324d0574df23499555de5168b4b1f1bd7ce9b06e2d49f` (informational, not normalized):
433+
1. Query the [OCI Referrers API](https://github.com/opencontainers/distribution-spec/blob/v1.1.1/spec.md#listing-referrers) for the artifact digest, filtered by `artifactType=application/vnd.ocm.software.ownership.v1+json`. Registries that do not support the Referrers API **MUST** be queried via the [referrers tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.1/spec.md#unavailable-referrers-api) (`<algorithm>-<hex>` tag).
453434

454-
```json
455-
{
456-
"annotations": {
457-
"software.ocm.base.digest": "sha256:abcd1234efgh5678ijkl9012mnopqrstuvwx3456yzab7890cdef1234abcd5678",
458-
"software.ocm.component.name": "ocm.software/ocmcli",
459-
"software.ocm.component.version": "0.27.0",
460-
"software.ocm.artifact": "[{\"identity\":{\"name\":\"ocmcli\",\"version\":\"0.27.0\",\"arch\":\"amd64\",\"os\":\"windows\"},\"kind\":\"resource\"}]"
461-
}
462-
}
463-
```
435+
2. Confirm the referrer's `subject.digest` equals the artifact digest recorded in the Component Descriptor.
464436

465-
The value of `software.ocm.resource` contains the **full identity** (name, version + extra identity).
466-
If normalized and annotations are removed, the digest **MUST** equal the value in `software.ocm.base.digest`.
467-
468-
A component descriptor may now declare a signature over the artifact without including the annotations in the signed content.
469-
This enables stable signatures regardless of annotation presence.
470-
471-
Thus, a client that supports annotation-tolerant verification **MAY** verify the artifact against the digest:
472-
473-
```yaml
474-
access:
475-
localReference: sha256:64b586a57294adc5749324d0574df23499555de5168b4b1f1bd7ce9b06e2d49f
476-
mediaType: application/octet-stream
477-
type: localBlob
478-
digest:
479-
hashAlgorithm: SHA-256
480-
normalisationAlgorithm: ociArtifactDigest/v1
481-
# original: 64b586a57294adc5749324d0574df23499555de5168b4b1f1bd7ce9b06e2d49f
482-
value: abcd1234efgh5678ijkl9012mnopqrstuvwx3456yzab7890cdef1234abcd5678
483-
```
437+
3. Confirm `software.ocm.component.name` and `software.ocm.component.version` match the expected Component Version.
484438

485-
Because the digest now ignores the annotations, the value corresponds to the base artifact without OCM-specific annotations.
486-
As the annotations were added after the component signature, the signature contains the value `abcd1234efgh5678ijkl9012mnopqrstuvwx3456yzab7890cdef1234abcd5678`.
487-
But because the client ignores the annotations during verification, the signature **MAY** still be verified, as
488-
the digest based on `software.ocm.base.digest` matches the signed content and is integral.
439+
When transferring Component Versions between OCI registries or into an OCI Image Layout, implementations **MUST** carry ownership referrers alongside the artifact and maintain the tag fallback where applicable.
489440

490441
## 7. Descriptor Selection Logic
491442

0 commit comments

Comments
 (0)