Skip to content

Update spec to include tag history endpoint#606

Open
jcarter3 wants to merge 1 commit intoopencontainers:mainfrom
jcarter3:tag_history
Open

Update spec to include tag history endpoint#606
jcarter3 wants to merge 1 commit intoopencontainers:mainfrom
jcarter3:tag_history

Conversation

@jcarter3
Copy link
Copy Markdown
Contributor

@jcarter3 jcarter3 commented Apr 23, 2026

Updates distribution spec to include new endpoint for retrieving the history of a given tag. The response is a json list of descriptors,including the created timestamp and sorted in descending order (newest first).
The list can be paged.

Comment thread spec.md Outdated
@jcarter3 jcarter3 force-pushed the tag_history branch 2 times, most recently from ba1f62d to dd534be Compare May 4, 2026 19:24
@jcarter3 jcarter3 changed the title **WIP** Update spec to include tag history endpoint Update spec to include tag history endpoint May 4, 2026
@jcarter3 jcarter3 marked this pull request as ready for review May 4, 2026 19:25
Copy link
Copy Markdown
Member

@mikebrow mikebrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some thoughts

Comment thread extensions/_oci.md

Specifies the maximum number of results to return.
The registry MAY return fewer results than requested if fewer historical entries exist.
When `n` is zero, this endpoint MUST return an empty array.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When `n` is zero, this endpoint MUST return an empty array.
When `n` is zero, this endpoint MUST return an empty array, useful for determining if history is available (200) or not (404) for the tag query.

Comment thread extensions/_oci.md
When `n` is zero, this endpoint MUST return an empty array.
When `n` is not specified, the registry MAY apply a default limit.

- **`before`** *string (RFC 3339 timestamp)*, OPTIONAL
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe from or ot (older than)

Suggested change
- **`before`** *string (RFC 3339 timestamp)*, OPTIONAL
- **`ot`** *string (RFC 3339 timestamp)*, OPTIONAL

Comment thread extensions/_oci.md Outdated

- **`before`** *string (RFC 3339 timestamp)*, OPTIONAL

When provided, only entries whose `org.opencontainers.tag.created` value is strictly less than (i.e. older than) `before` will be returned.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When provided, only entries whose `org.opencontainers.tag.created` value is strictly less than (i.e. older than) `before` will be returned.
When provided, only entries whose `org.opencontainers.tag.created` value is strictly less than (i.e. older than) the `ot` timestamp will be returned.

Comment thread extensions/_oci.md
When `n` is zero, this endpoint MUST return an empty array.
When `n` is not specified, the registry MAY apply a default limit.

- **`before`** *string (RFC 3339 timestamp)*, OPTIONAL
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a chance to have two results with the exact same time stamp..

Comment thread extensions/_oci.md

When provided, only entries whose `org.opencontainers.tag.created` value is strictly less than (i.e. older than) `before` will be returned.
This is used for time-based pagination: pass the `org.opencontainers.tag.created` value of the last entry returned in the previous response to retrieve the next page.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another idea would be to include a "newer-than" option, or a "between" timestamp1/timestamp2 allowing a client to query new tags or a time range..

Signed-off-by: Jeff Carter <jeff.carter@docker.com>
Copy link
Copy Markdown
Contributor

@sudo-bmitch sudo-bmitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's some early feedback. I still need to spend some time implementing this before considering approving, and we need a conformance test.

Comment thread extensions/_oci.md
Tag history MAY be retrieved with a standard `GET` as follows.

```HTTP
GET /v2/<name>/_oci/tag-history/<tag>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think <component>=tag, <module>=history, and use parameters for passing the tag, better follows the extensions syntax documented at https://github.com/opencontainers/distribution-spec/tree/main/extensions#repository-level-extensions

Suggested change
GET /v2/<name>/_oci/tag-history/<tag>
GET /v2/<name>/_oci/tag/history/?tag=<tag>

(Note that I personally wish we weren't so prescriptive and only specified the extension is _<extension> with everything after that being implementation defined, but I suspect I lost that battle when we added extensions to the spec.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How hard is it to push back and change this? That design makes every downstream extension significantly worse. If there aren't any defined extensions yet, it should be possible?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at the PR where we introduced them, I'd want to pull in other maintainers like @stevvooe to weigh in: #111 (comment)

Comment thread extensions/_oci.md

A successful request MUST return a `200 OK` response code.
If the repository does not exist, the registry MUST return a `404 Not Found` response code.
If the tag has no history, the registry MUST return a `404 Not Found` response code.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If the tag has no history, the registry MUST return a `404 Not Found` response code.
If the tag has no history or the registry does not implement this extension, the registry MUST return a `404 Not Found` response code.

Comment thread extensions/_oci.md
If the repository does not exist, the registry MUST return a `404 Not Found` response code.
If the tag has no history, the registry MUST return a `404 Not Found` response code.
Tag history SHOULD remain queryable after the tag is deleted.
Deleted manifests MUST NOT be removed from tag history.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MUST feels a bit strong here, and may break some retention and redaction requirements of some registries.

Suggested change
Deleted manifests MUST NOT be removed from tag history.
Deleted manifests SHOULD NOT be removed from tag history.

Comment thread extensions/_oci.md
If the tag has no history, the registry MUST return a `404 Not Found` response code.
Tag history SHOULD remain queryable after the tag is deleted.
Deleted manifests MUST NOT be removed from tag history.
History entries for deleted manifests MUST continue to include the descriptor fields recorded when the tag was assigned to the manifest.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this rephrasing the previous line? If so, I'd consolidate the two, with a "SHOULD".

Comment thread extensions/_oci.md
]
```

Results MUST be sorted in descending order by the history entry timestamp, which is either the `org.opencontainers.tag.created` annotation value or the `org.opencontainers.tag.deleted` annotation value (i.e. the most recent entry appears first).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In image spec, we use the prefix org.opencontainers.image. For distribution, perhaps we should standardize on org.opencontainers.distribution to avoid conflicts with other specs?

Comment thread extensions/_oci.md

Results MUST be sorted in descending order by the history entry timestamp, which is either the `org.opencontainers.tag.created` annotation value or the `org.opencontainers.tag.deleted` annotation value (i.e. the most recent entry appears first).

Each descriptor object in the response MUST be either a manifest descriptor or a delete marker.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Each descriptor object in the response MUST be either a manifest descriptor or a delete marker.
Each descriptor object in the response MUST be either a create or delete entry.

Comment thread extensions/_oci.md

Each descriptor object in the response MUST be either a manifest descriptor or a delete marker.

Manifest descriptors MUST include the following properties:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Manifest descriptors MUST include the following properties:
Tag create entries, which includes replacing an existing tag, MUST include the following properties:

Comment thread extensions/_oci.md

The RFC 3339 timestamp at which the tag was assigned to this manifest. Used as the sort key and as the cursor for time-based pagination.

Delete markers MUST be represented by the OCI empty descriptor and MUST include the following properties:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Delete markers MUST be represented by the OCI empty descriptor and MUST include the following properties:
Tag delete entries MUST include the following properties:

Comment thread extensions/_oci.md
Comment on lines +155 to +177
- **`mediaType`** *string*, REQUIRED

MUST be `application/vnd.oci.empty.v1+json`.

- **`digest`** *string*, REQUIRED

MUST be `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`.

- **`size`** *integer*, REQUIRED

MUST be `2`.

- **`data`** *string*, REQUIRED

MUST be `e30=`.

- **`annotations`** *map of strings*, REQUIRED

Annotations associated with the historical tag entry. MUST include:

- **`org.opencontainers.tag.deleted`** *string*, REQUIRED

The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rephrase to use the descriptor of the deleted entry. And now with so much overlap, I think we can probably merge the two create/delete sections and then document the two possible annotations.

Suggested change
- **`mediaType`** *string*, REQUIRED
MUST be `application/vnd.oci.empty.v1+json`.
- **`digest`** *string*, REQUIRED
MUST be `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`.
- **`size`** *integer*, REQUIRED
MUST be `2`.
- **`data`** *string*, REQUIRED
MUST be `e30=`.
- **`annotations`** *map of strings*, REQUIRED
Annotations associated with the historical tag entry. MUST include:
- **`org.opencontainers.tag.deleted`** *string*, REQUIRED
The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination.
- **`mediaType`** *string*, REQUIRED
The media type of the manifest this tag previously pointed to at that point in time.
- **`size`** *integer*, REQUIRED
The size in bytes of the manifest.
- **`digest`** *string*, REQUIRED
The digest of the manifest, in the form `<algorithm>:<encoded>`.
- **`annotations`** *map of strings*, REQUIRED
Annotations associated with the historical tag entry. MUST include:
- **`org.opencontainers.distribution.tag.deleted`** *string*, REQUIRED
The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants