From ef950c1f65a1f310ceb279faed5a29b08ada85b6 Mon Sep 17 00:00:00 2001 From: Jeff Carter Date: Mon, 4 May 2026 11:21:16 -0400 Subject: [PATCH 1/5] Update spec to include tag history endpoint Signed-off-by: Jeff Carter --- extensions/_oci.md | 144 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/extensions/_oci.md b/extensions/_oci.md index 1ae44e2b..2e63c0a3 100644 --- a/extensions/_oci.md +++ b/extensions/_oci.md @@ -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//_oci/ext/discover ``` The base extension returns an array of supported extensions with details of the endpoints as shown below. @@ -66,6 +66,148 @@ 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//_oci/tag-history/ +``` + +`` is the namespace of the repository, and `` 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. +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. +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: +Content-Type: application/json + +[ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 1234, + "digest": "sha256:a1a1a1...", + "annotations": { + "org.opencontainers.tag.created": "2026-05-04T03:02:01Z" + } + }, + { + "mediaType": "application/vnd.oci.empty.v1+json", + "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "size": 2, + "data": "e30=", + "annotations": { + "org.opencontainers.tag.deleted": "2026-05-03T03:02:01Z" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 1234, + "digest": "sha256:b2b2b2...", + "annotations": { + "org.opencontainers.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.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. + +Manifest descriptors MUST include the following properties: + +- **`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 `:`. + +- **`annotations`** *map of strings*, REQUIRED + + Annotations associated with the historical tag entry. MUST include: + + - **`org.opencontainers.tag.created`** *string*, REQUIRED + + 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: + +- **`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. + +##### Query Parameters + +The following query parameters MAY be provided: + +- **`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. + When `n` is not specified, the registry MAY apply a default limit. + +- **`before`** *string (RFC 3339 timestamp)*, OPTIONAL + + 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.tag.created` or `org.opencontainers.tag.deleted` value of the last entry returned in the previous response to retrieve the next page. + +##### Pagination Example + +To fetch the first page of up to 10 results: + +```HTTP +GET /v2//_oci/tag-history/?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//_oci/tag-history/?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/) From 5eaeef9b263c2d60e6b9b8ccdfb5ebef3fe06cd6 Mon Sep 17 00:00:00 2001 From: Jeff Carter Date: Thu, 2 Jul 2026 12:21:03 -0400 Subject: [PATCH 2/5] Update extensions spec to use relaxed language around route definition Signed-off-by: Jeff Carter --- extensions/_oci.md | 51 ++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/extensions/_oci.md b/extensions/_oci.md index 2e63c0a3..9db51075 100644 --- a/extensions/_oci.md +++ b/extensions/_oci.md @@ -82,11 +82,11 @@ GET /v2//_oci/tag-history/ 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. +If the tag has no history or the registry does not implement this extension, 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. +Deleted manifests SHOULD NOT be removed from tag history. Tag history MAY be deleted after the repository is deleted. +If a pinned digest is provided with the tag, the registry MUST return a `404 Not Found` if the digest is not part of the tag's history. Upon success, the response body MUST be a JSON array of descriptor objects in the following format: @@ -105,10 +105,9 @@ Content-Type: application/json } }, { - "mediaType": "application/vnd.oci.empty.v1+json", - "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", - "size": 2, - "data": "e30=", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 1234, + "digest": "sha256:a1a1a1...", "annotations": { "org.opencontainers.tag.deleted": "2026-05-03T03:02:01Z" } @@ -126,7 +125,9 @@ Content-Type: application/json 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. +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: @@ -144,37 +145,15 @@ Manifest descriptors MUST include the following properties: - **`annotations`** *map of strings*, REQUIRED - Annotations associated with the historical tag entry. MUST include: - - - **`org.opencontainers.tag.created`** *string*, REQUIRED - - 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: - -- **`mediaType`** *string*, REQUIRED - - MUST be `application/vnd.oci.empty.v1+json`. + Annotations associated with the historical tag entry. MUST include exactly one of the following event annotations: -- **`digest`** *string*, REQUIRED - - MUST be `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`. - -- **`size`** *integer*, REQUIRED - - MUST be `2`. - -- **`data`** *string*, REQUIRED - - MUST be `e30=`. - -- **`annotations`** *map of strings*, REQUIRED + - **`org.opencontainers.tag.created`** *string* - Annotations associated with the historical tag entry. MUST include: + 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.tag.deleted`** *string*, REQUIRED + - **`org.opencontainers.tag.deleted`** *string* - The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination. + 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 @@ -184,7 +163,7 @@ The following query parameters MAY be provided: 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. + 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 From 7e2fb3892070ace8a7ea3f55a9e0f773a58467eb Mon Sep 17 00:00:00 2001 From: Jeff Carter Date: Mon, 4 May 2026 11:21:16 -0400 Subject: [PATCH 3/5] Update spec to include tag history endpoint Signed-off-by: Jeff Carter --- extensions/_oci.md | 51 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/extensions/_oci.md b/extensions/_oci.md index 9db51075..2e63c0a3 100644 --- a/extensions/_oci.md +++ b/extensions/_oci.md @@ -82,11 +82,11 @@ GET /v2//_oci/tag-history/ 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 or 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 MUST return a `404 Not Found` response code. Tag history SHOULD remain queryable after the tag is deleted. -Deleted manifests SHOULD NOT be removed from tag history. +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. Tag history MAY be deleted after the repository is deleted. -If a pinned digest is provided with the tag, the registry MUST return a `404 Not Found` if the digest is not part of the tag's history. Upon success, the response body MUST be a JSON array of descriptor objects in the following format: @@ -105,9 +105,10 @@ Content-Type: application/json } }, { - "mediaType": "application/vnd.oci.image.manifest.v1+json", - "size": 1234, - "digest": "sha256:a1a1a1...", + "mediaType": "application/vnd.oci.empty.v1+json", + "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "size": 2, + "data": "e30=", "annotations": { "org.opencontainers.tag.deleted": "2026-05-03T03:02:01Z" } @@ -125,9 +126,7 @@ Content-Type: application/json 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 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. +Each descriptor object in the response MUST be either a manifest descriptor or a delete marker. Manifest descriptors MUST include the following properties: @@ -145,15 +144,37 @@ Manifest descriptors MUST include the following properties: - **`annotations`** *map of strings*, REQUIRED - Annotations associated with the historical tag entry. MUST include exactly one of the following event annotations: + Annotations associated with the historical tag entry. MUST include: + + - **`org.opencontainers.tag.created`** *string*, REQUIRED + + 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: + +- **`mediaType`** *string*, REQUIRED + + MUST be `application/vnd.oci.empty.v1+json`. - - **`org.opencontainers.tag.created`** *string* +- **`digest`** *string*, REQUIRED + + MUST be `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`. + +- **`size`** *integer*, REQUIRED + + MUST be `2`. + +- **`data`** *string*, REQUIRED + + MUST be `e30=`. + +- **`annotations`** *map of strings*, REQUIRED - 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. + Annotations associated with the historical tag entry. MUST include: - - **`org.opencontainers.tag.deleted`** *string* + - **`org.opencontainers.tag.deleted`** *string*, REQUIRED - 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. + The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination. ##### Query Parameters @@ -163,7 +184,7 @@ The following query parameters MAY be provided: 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 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 From 50f747ea1790d89ac9cb6a2e37b6180af8cc512a Mon Sep 17 00:00:00 2001 From: Jeff Carter Date: Thu, 2 Jul 2026 12:21:03 -0400 Subject: [PATCH 4/5] Update extensions spec to use relaxed language around route definition Signed-off-by: Jeff Carter --- extensions/_oci.md | 51 ++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/extensions/_oci.md b/extensions/_oci.md index 2e63c0a3..9db51075 100644 --- a/extensions/_oci.md +++ b/extensions/_oci.md @@ -82,11 +82,11 @@ GET /v2//_oci/tag-history/ 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. +If the tag has no history or the registry does not implement this extension, 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. +Deleted manifests SHOULD NOT be removed from tag history. Tag history MAY be deleted after the repository is deleted. +If a pinned digest is provided with the tag, the registry MUST return a `404 Not Found` if the digest is not part of the tag's history. Upon success, the response body MUST be a JSON array of descriptor objects in the following format: @@ -105,10 +105,9 @@ Content-Type: application/json } }, { - "mediaType": "application/vnd.oci.empty.v1+json", - "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", - "size": 2, - "data": "e30=", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 1234, + "digest": "sha256:a1a1a1...", "annotations": { "org.opencontainers.tag.deleted": "2026-05-03T03:02:01Z" } @@ -126,7 +125,9 @@ Content-Type: application/json 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. +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: @@ -144,37 +145,15 @@ Manifest descriptors MUST include the following properties: - **`annotations`** *map of strings*, REQUIRED - Annotations associated with the historical tag entry. MUST include: - - - **`org.opencontainers.tag.created`** *string*, REQUIRED - - 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: - -- **`mediaType`** *string*, REQUIRED - - MUST be `application/vnd.oci.empty.v1+json`. + Annotations associated with the historical tag entry. MUST include exactly one of the following event annotations: -- **`digest`** *string*, REQUIRED - - MUST be `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`. - -- **`size`** *integer*, REQUIRED - - MUST be `2`. - -- **`data`** *string*, REQUIRED - - MUST be `e30=`. - -- **`annotations`** *map of strings*, REQUIRED + - **`org.opencontainers.tag.created`** *string* - Annotations associated with the historical tag entry. MUST include: + 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.tag.deleted`** *string*, REQUIRED + - **`org.opencontainers.tag.deleted`** *string* - The RFC 3339 timestamp at which the tag was deleted. Used as the sort key and as the cursor for time-based pagination. + 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 @@ -184,7 +163,7 @@ The following query parameters MAY be provided: 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. + 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 From 0c97df14f8ea2e20e0008e714ee9e5111904ae6e Mon Sep 17 00:00:00 2001 From: Jeff Carter Date: Thu, 2 Jul 2026 15:46:48 -0400 Subject: [PATCH 5/5] Update language on tag history Signed-off-by: Jeff Carter --- extensions/_oci.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/extensions/_oci.md b/extensions/_oci.md index 9db51075..939d9a58 100644 --- a/extensions/_oci.md +++ b/extensions/_oci.md @@ -82,11 +82,11 @@ GET /v2//_oci/tag-history/ 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 or the registry does not implement this extension, the registry MUST return a `404 Not Found` response code. +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. -If a pinned digest is provided with the tag, the registry MUST return a `404 Not Found` if the digest is not part of the tag's history. Upon success, the response body MUST be a JSON array of descriptor objects in the following format: @@ -101,7 +101,7 @@ Content-Type: application/json "size": 1234, "digest": "sha256:a1a1a1...", "annotations": { - "org.opencontainers.tag.created": "2026-05-04T03:02:01Z" + "org.opencontainers.distribution.tag.created": "2026-05-04T03:02:01Z" } }, { @@ -109,7 +109,7 @@ Content-Type: application/json "size": 1234, "digest": "sha256:a1a1a1...", "annotations": { - "org.opencontainers.tag.deleted": "2026-05-03T03:02:01Z" + "org.opencontainers.distribution.tag.deleted": "2026-05-03T03:02:01Z" } }, { @@ -117,13 +117,13 @@ Content-Type: application/json "size": 1234, "digest": "sha256:b2b2b2...", "annotations": { - "org.opencontainers.tag.created": "2026-04-03T02:01:00Z" + "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.tag.created` annotation value or the `org.opencontainers.tag.deleted` annotation value (i.e. the most recent entry appears first). +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. @@ -147,11 +147,11 @@ Manifest descriptors MUST include the following properties: Annotations associated with the historical tag entry. MUST include exactly one of the following event annotations: - - **`org.opencontainers.tag.created`** *string* + - **`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.tag.deleted`** *string* + - **`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. @@ -169,7 +169,13 @@ The following query parameters MAY be provided: - **`before`** *string (RFC 3339 timestamp)*, OPTIONAL 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.tag.created` or `org.opencontainers.tag.deleted` value of the last entry returned in the previous response to retrieve the next page. + 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. ##### Pagination Example