-
Notifications
You must be signed in to change notification settings - Fork 252
Update spec to include tag history endpoint #606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ef950c1
c97e594
5eaeef9
1d37c35
2516898
7e2fb38
50f747e
0c97df1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -23,7 +23,7 @@ GET /v2/_oci/ext/discover | |||||
| Repository-level extensions may be discovered with a standard GET as follows. | ||||||
|
|
||||||
| ```HTTP | ||||||
| GET /v2/{name}/_oci/ext/discover | ||||||
| GET /v2/<name>/_oci/ext/discover | ||||||
| ``` | ||||||
|
|
||||||
| The base extension returns an array of supported extensions with details of the endpoints as shown below. | ||||||
|
|
@@ -66,6 +66,133 @@ Content-Type: application/json | |||||
|
|
||||||
| Enumeration of the endpoints provided on this registry (as not all "OPTIONAL" endpoints may be present in all registries) | ||||||
|
|
||||||
| ### Component: `tag-history` | ||||||
|
|
||||||
| This component is for endpoints relating to tag history operations on a repository. | ||||||
|
|
||||||
| This endpoint returns the history of a tag, listing each manifest the tag has pointed to over time and each time the tag was deleted, in descending order by history entry timestamp (newest first). | ||||||
|
|
||||||
| Tag history MAY be retrieved with a standard `GET` as follows. | ||||||
|
|
||||||
| ```HTTP | ||||||
| GET /v2/<name>/_oci/tag-history/<tag> | ||||||
| ``` | ||||||
|
|
||||||
| `<name>` is the namespace of the repository, and `<tag>` is the name of the tag whose history is being queried. | ||||||
|
|
||||||
| 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. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think someone could interpret that this should they return |
||||||
| If the registry does not implement this extension, the registry MUST return a `404 Not Found` response code. | ||||||
| If the tag has no history, the registry SHOULD return a `200 OK` response code with an empty array. | ||||||
| Tag history SHOULD remain queryable after the tag is deleted. | ||||||
| Deleted manifests SHOULD NOT be removed from tag history. | ||||||
| Tag history MAY be deleted after the repository is deleted. | ||||||
|
|
||||||
| Upon success, the response body MUST be a JSON array of descriptor objects in the following format: | ||||||
|
|
||||||
| ```HTTP | ||||||
| 200 OK | ||||||
| Content-Length: <length> | ||||||
| Content-Type: application/json | ||||||
|
|
||||||
| [ | ||||||
| { | ||||||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||||||
| "size": 1234, | ||||||
| "digest": "sha256:a1a1a1...", | ||||||
| "annotations": { | ||||||
| "org.opencontainers.distribution.tag.created": "2026-05-04T03:02:01Z" | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||||||
| "size": 1234, | ||||||
| "digest": "sha256:a1a1a1...", | ||||||
| "annotations": { | ||||||
| "org.opencontainers.distribution.tag.deleted": "2026-05-03T03:02:01Z" | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||||||
| "size": 1234, | ||||||
| "digest": "sha256:b2b2b2...", | ||||||
| "annotations": { | ||||||
| "org.opencontainers.distribution.tag.created": "2026-04-03T02:01:00Z" | ||||||
| } | ||||||
| } | ||||||
| ] | ||||||
| ``` | ||||||
|
|
||||||
| Results MUST be sorted in descending order by the history entry timestamp, which is either the `org.opencontainers.distribution.tag.created` annotation value or the `org.opencontainers.distribution.tag.deleted` annotation value (i.e. the most recent entry appears first). | ||||||
|
|
||||||
| Each descriptor object in the response MUST be a create or delete entry. | ||||||
| A descriptor for a tag creation event MUST describe the manifest the tag was assigned to. | ||||||
| A descriptor for a tag deletion event MUST describe the manifest the tag pointed to immediately before deletion. | ||||||
|
|
||||||
| Manifest descriptors MUST include the following properties: | ||||||
|
jcarter3 marked this conversation as resolved.
|
||||||
|
|
||||||
| - **`mediaType`** *string*, REQUIRED | ||||||
|
|
||||||
| The media type of the manifest this tag 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 exactly one of the following event annotations: | ||||||
|
|
||||||
| - **`org.opencontainers.distribution.tag.created`** *string* | ||||||
|
|
||||||
| REQUIRED for tag creation events. 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. | ||||||
|
|
||||||
| - **`org.opencontainers.distribution.tag.deleted`** *string* | ||||||
|
|
||||||
| REQUIRED for tag deletion events. The RFC 3339 timestamp at which the tag was deleted from this manifest. Used as the sort key and as the cursor for time-based pagination. | ||||||
|
|
||||||
| ##### Query Parameters | ||||||
|
|
||||||
| The following query parameters MAY be provided: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps there could also be a query parameter for "digest" such that a client could verify that a tag did point at the manifest when given an image reference like It would return 404 if the digest isn't in the history but otherwise returning the expected JSON array of descriptor objects that includes the digest. I'm not sure if that'd be just the one descriptor (or two if deleted?) or perhaps up to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a comment for this above: This might cover it? Or we could send digest as a separate query param... maybe that's better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I like pinned digest in the tag, no need for a query parameter when we already have the format. Oh, but I don't see the pinned digest format defined in I think it may need guidance on what they should return when passed a pinned digest? Probably the only guarantee is that it must be in the list of |
||||||
|
|
||||||
| - **`n`** *integer*, OPTIONAL | ||||||
|
|
||||||
| 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, useful for determining if history is available (200) or not (404) for the tag query. | ||||||
| When `n` is not specified, the registry MAY apply a default limit. | ||||||
|
|
||||||
| - **`before`** *string (RFC 3339 timestamp)*, OPTIONAL | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe from or ot (older than)
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps a registry could consider every update of a tag as a delete followed by a create and so the timestamp would be the same. |
||||||
|
|
||||||
| When provided, only entries whose history entry timestamp is strictly less than (i.e. older than) `before` will be returned. | ||||||
| This is used for time-based pagination: pass the `org.opencontainers.distribution.tag.created` or `org.opencontainers.distribution.tag.deleted` value of the last entry returned in the previous response to retrieve the next page. | ||||||
|
|
||||||
| - **`digest`** *string*, OPTIONAL | ||||||
|
|
||||||
| When provided, the registry will validate if the provided digest was part of the given tag's history. | ||||||
| If it was not, a `400 Bad Request` response is given. | ||||||
| If it was, the tag listing response will include a descriptor with the provided digest along with the relevant create/delete timestamps. | ||||||
|
|
||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.. |
||||||
| ##### Pagination Example | ||||||
|
|
||||||
| To fetch the first page of up to 10 results: | ||||||
|
|
||||||
| ```HTTP | ||||||
| GET /v2/<name>/_oci/tag-history/<tag>?n=10 | ||||||
| ``` | ||||||
|
|
||||||
| To fetch the next page, pass the history entry timestamp of the last result from the prior response as the `before` parameter: | ||||||
|
|
||||||
| ```HTTP | ||||||
| GET /v2/<name>/_oci/tag-history/<tag>?n=10&before=2026-04-03T02%3A01%3A00Z | ||||||
| ``` | ||||||
|
|
||||||
| When the returned array contains fewer entries than `n` (or is empty), the client has reached the end of the history. | ||||||
|
|
||||||
| ## Code representations | ||||||
|
|
||||||
| Golang structures for these JSON structures is available at [`github.com/opencontainers/distribution-spec/specs-go/v1/extensions`](https://github.com/opencontainers/distribution-spec/tree/main/specs-go/v1/extensions/) | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.