vercel/turborepo

Remote Cache OpenAPI specification doesn't mention `x-artifact-tag` for `/v8/artifacts/{hash}` get API

trivikr opened this issue · 0 comments

Verify canary release

  • I verified that the issue exists in the latest Turborepo canary release.

Link to code that reproduces this issue

N/A, this is an issue with Remote Cache OpenAPI

What package manager are you using / does the bug impact?

npm

What operating system are you using?

Mac

Which canary version will you have in your reproduction?

2.1.2-canary.1

Describe the Bug

Remote Cache OpenAPI specification doesn't mention x-artifact-tag for /v8/artifacts/{hash} get API.

The existing OpenAPI specification doesn't seem to be versioned in URL, so I'm copying the contents here for v3.0.3.

put

Contains x-artifact-tag in header

{
        "description": "Uploads a cache artifact identified by the `hash` specified on the path. The cache artifact can then be downloaded with the provided `hash`.",
        "operationId": "uploadArtifact",
        "security": [
          {
            "bearerToken": []
          }
        ],
        "summary": "Upload a cache artifact",
        "tags": ["artifacts"],
        "responses": {
          "202": {
            "description": "File successfully uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "urls": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array",
                      "description": "Array of URLs where the artifact was updated",
                      "example": [
                        "https://api.vercel.com/v2/now/artifact/12HKQaOmR5t5Uy6vdcQsNIiZgHGB"
                      ]
                    }
                  },
                  "required": ["urls"],
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "One of the provided values in the request query is invalid.\nOne of the provided values in the headers is invalid\nFile size is not valid"
          },
          "401": {
            "description": ""
          },
          "402": {
            "description": "The account was soft-blocked for an unhandled reason.\nThe account is missing a payment so payment method must be updated"
          },
          "403": {
            "description": "The customer has reached their spend cap limit and has been paused. An owner can disable the cap or raise the limit in settings.\nThe Remote Caching usage limit has been reached for this account for this billing cycle.\nRemote Caching has been disabled for this team or user. An owner can enable it in the billing settings.\nYou do not have permission to access this resource."
          }
        },
        "parameters": [
          {
            "in": "header",
            "description": "The artifact size in bytes",
            "required": true,
            "schema": {
              "description": "The artifact size in bytes",
              "type": "number"
            },
            "name": "Content-Length"
          },
          {
            "in": "header",
            "description": "The time taken to generate the uploaded artifact in milliseconds.",
            "required": false,
            "schema": {
              "type": "number",
              "description": "The time taken to generate the uploaded artifact in milliseconds.",
              "example": 400
            },
            "name": "x-artifact-duration"
          },
          {
            "in": "header",
            "description": "The continuous integration or delivery environment where this artifact was generated.",
            "required": false,
            "schema": {
              "type": "string",
              "description": "The continuous integration or delivery environment where this artifact was generated.",
              "example": "VERCEL",
              "maxLength": 50
            },
            "name": "x-artifact-client-ci"
          },
          {
            "in": "header",
            "description": "1 if the client is an interactive shell. Otherwise 0",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "1 if the client is an interactive shell. Otherwise 0",
              "example": 0,
              "minimum": 0,
              "maximum": 1
            },
            "name": "x-artifact-client-interactive"
          },
          {
            "in": "header",
            "description": "The base64 encoded tag for this artifact. The value is sent back to clients when the artifact is downloaded as the header `x-artifact-tag`",
            "required": false,
            "schema": {
              "type": "string",
              "description": "The base64 encoded tag for this artifact. The value is sent back to clients when the artifact is downloaded as the header `x-artifact-tag`",
              "example": "Tc0BmHvJYMIYJ62/zx87YqO0Flxk+5Ovip25NY825CQ=",
              "maxLength": 600
            },
            "name": "x-artifact-tag"
          },
          {
            "name": "hash",
            "description": "The artifact hash",
            "in": "path",
            "required": true,
            "schema": {
              "example": "12HKQaOmR5t5Uy6vdcQsNIiZgHGB",
              "description": "The artifact hash",
              "type": "string"
            }
          },
          {
            "description": "The Team identifier to perform the request on behalf of.",
            "in": "query",
            "name": "teamId",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The Team slug to perform the request on behalf of.",
            "in": "query",
            "name": "slug",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }

get

Does not contain x-artifact-tag in header

{
        "description": "Downloads a cache artifact indentified by its `hash` specified on the request path. The artifact is downloaded as an octet-stream. The client should verify the content-length header and response body.",
        "operationId": "downloadArtifact",
        "security": [
          {
            "bearerToken": []
          }
        ],
        "summary": "Download a cache artifact",
        "tags": ["artifacts"],
        "responses": {
          "200": {
            "description": "The artifact was found and is downloaded as a stream. Content-Length should be verified.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "An octet stream response that will be piped to the response stream."
                }
              }
            }
          },
          "400": {
            "description": "One of the provided values in the request query is invalid.\nOne of the provided values in the headers is invalid"
          },
          "401": {
            "description": ""
          },
          "402": {
            "description": "The account was soft-blocked for an unhandled reason.\nThe account is missing a payment so payment method must be updated"
          },
          "403": {
            "description": "The customer has reached their spend cap limit and has been paused. An owner can disable the cap or raise the limit in settings.\nThe Remote Caching usage limit has been reached for this account for this billing cycle.\nRemote Caching has been disabled for this team or user. An owner can enable it in the billing settings.\nYou do not have permission to access this resource."
          },
          "404": {
            "description": "The artifact was not found"
          }
        },
        "parameters": [
          {
            "in": "header",
            "description": "The continuous integration or delivery environment where this artifact is downloaded.",
            "schema": {
              "type": "string",
              "description": "The continuous integration or delivery environment where this artifact is downloaded.",
              "example": "VERCEL",
              "maxLength": 50
            },
            "name": "x-artifact-client-ci"
          },
          {
            "in": "header",
            "description": "1 if the client is an interactive shell. Otherwise 0",
            "schema": {
              "type": "integer",
              "description": "1 if the client is an interactive shell. Otherwise 0",
              "example": 0,
              "minimum": 0,
              "maximum": 1
            },
            "name": "x-artifact-client-interactive"
          },
          {
            "name": "hash",
            "description": "The artifact hash",
            "in": "path",
            "required": true,
            "schema": {
              "example": "12HKQaOmR5t5Uy6vdcQsNIiZgHGB",
              "description": "The artifact hash",
              "type": "string"
            }
          },
          {
            "description": "The Team identifier to perform the request on behalf of.",
            "in": "query",
            "name": "teamId",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The Team slug to perform the request on behalf of.",
            "in": "query",
            "name": "slug",
            "schema": {
              "type": "string"
            }
          }
        ]
      }

As per documentation in Artifact Integrity and Authenticity Verification, it appears that get call needs to have x-artifact-tag in header for turborepo client to process it.

Turborepo will verify the Remote Cache artifacts' integrity and authenticity when they're downloaded. Any artifacts that fail to verify will be ignored and treated as a cache miss by Turborepo.

The client source code checks for signature in x-artifact-tag

let body = if let Some(signer_verifier) = &self.signer_verifier {
let expected_tag = response
.headers()
.get("x-artifact-tag")
.ok_or(CacheError::ArtifactTagMissing(Backtrace::capture()))?;

Expected Behavior

The Remote Cache OpenAPI specification should mention x-artifact-tag for /v8/artifacts/{hash} get API

To Reproduce

Go through Remote Cache API specification, documentation and client implementation in description

Additional context

No response