Validation Sets

Introduction

The Validation Sets API enables Validation Set assertions management.

All endpoints require a macaroon authenticated request with no special permissions.

See Macaroons for details on how to obtain a suitable macaroon for interacting with this API. You could also use the surl command line, available as a snap:

snap install surl

to exercise this (and other) store APIs. The following example authenticates to the store and saves the macaroon locally for use in subsequent requests:

surl -a prod -s production -e <email> [[-p <macaroon_permission>]]

surl -a prod -X <http-method> https://dashboard.snapcraft.io/<endpoint> [[-d '<json payload>']]

See surl help for the details about these and other surl options.

The Validation Sets API is exposed at the following base URLs:

Response Format

JSON will be returned in all responses from the API, including error responses, please refer to the following section for details about the format.

Prepare Validation Set assertion headers ready for signing.

Introduced in version 16

POST /api/v2/validation-sets/build-assertion

Prepare Validation Set headers for local signing.

Return Validation Set assertion headers ready for local signing by the publisher.

reqheader Authorization

macaroon authorization header for an active user

status 200

success

status 400

error

status 401

authentication required

Usage

As mentioned in the introduction, this endpoint can be exercised using the surl command as follows:

surl -s production -e your-email@example.com -X POST https://dashboard.snapcraft.io/api/v2/validation-sets/build-assertion -d '<input-data-as-json>'

where your-email@example.com has to be an existing account.

Example

Prepare assertion headers for a validation set named “acme-cert-2020-10”.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "name": "acme-cert-2020-10",
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "sequence": 3,
    "snaps": [
        {
            "name": "snap-name-1",
            "presence": "optional"
        },
        {
            "name": "snap-name-2"
        },
        {
            "name": "snap-name-3",
            "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
            "presence": "required",
            "revision": 2
        },
        {
            "name": "snap-name-4",
            "revision": 123
        },
        {
            "name": "snap-name-5",
            "presence": "invalid"
        }
    ]
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "authority-id": "AccountIDXXXOfTheRequestingUserX",
    "name": "acme-cert-2020-10",
    "revision": "20",
    "sequence": "3",
    "series": "16",
    "snaps": [
        {
            "id": "XXSnapIDForXSnapName1XXXXXXXXXXX",
            "name": "snap-name-1",
            "presence": "optional"
        },
        {
            "id": "XXSnapIDForXSnapName2XXXXXXXXXXX",
            "name": "snap-name-2"
        },
        {
            "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
            "name": "snap-name-3",
            "presence": "required",
            "revision": "2"
        },
        {
            "id": "XXSnapIDForXSnapName4XXXXXXXXXXX",
            "name": "snap-name-4",
            "revision": "123"
        },
        {
            "id": "XXSnapIDForXSnapName5XXXXXXXXXXX",
            "name": "snap-name-5",
            "presence": "invalid"
        }
    ],
    "timestamp": "2020-11-04T14:53:47Z",
    "type": "validation-set"
}

Errors

Request payload contains a number of request JSON schema violations.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "surprise-field": 123,
    "name": "too-long-name...",
    "sequence": 0,
    "snaps": [
        {
            "name": "snap-name-1",
            "presence": "nice-to-have"
        },
        {
            "name": "snap-name-2",
            "revision": -1
        }
    ]
}

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "message": "Additional properties are not allowed ('surprise-field' was unexpected) at /",
            "code": "invalid-request"
        },
        {
            "message": "'too-long-name...' is too long at /name",
            "code": "invalid-request"
        },
        {
            "message": "0 is less than the minimum of 1 at /sequence",
            "code": "invalid-request"
        },
        {
            "message": "'nice-to-have' is not one of ['required', 'optional', 'invalid'] at /snaps/0/presence",
            "code": "invalid-request"
        },
        {
            "message": "-1 is less than the minimum of 0 at /snaps/1/revision",
            "code": "invalid-request"
        },
        {
            "message": "'account-id' is a required property at /",
            "code": "invalid-request"
        }
    ]
}

Requested sequence number is too high for an assertion with a given name.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "name": "acme-cert-2020-10",
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "sequence": 3050,
    "snaps": [
        {
            "name": "snap-name-1"
        },
        {
            "name": "snap-name-2"
        }
    ]
}

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "code": "invalid-field",
            "extra": {
                "field": "sequence",
                "value": 3050
            },
            "message": "The validation-set sequence 3050 is not valid: sequence should not be higher than 2."
        }
    ]
}

Requested validation set name “latest” is invalid.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "name": "latest",
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "sequence": 3,
    "snaps": [
        {
            "name": "snap-name-1"
        },
        {
            "name": "snap-name-2"
        }
    ]
}

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "code": "invalid-field",
            "extra": {
                "field": "name",
                "value": "latest"
            },
            "message": "The validation-set name 'latest' is not valid: 'latest' is reserved."
        }
    ]
}

List of snaps contains some snaps that do not exist, or the account does not have access to, or snaps that were never released, or were paired with incorrect snap IDs.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "name": "acme-final-vs",
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "sequence": 3,
    "snaps": [
        {
            "name": "snap-name-1"
        },
        {
            "name": "snap-name-x"
        },
        {
            "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
            "name": "snap-name-y"
        }
    ]
}

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "extra": {
                "value": [
                    {
                        "name": "snap-name-1"
                    },
                    {
                        "name": "snap-name-x"
                    },
                    {
                        "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
                        "name": "snap-name-y"
                    }
                ],
                "missing": [
                    {
                        "name": "snap-name-x"
                    },
                    {
                        "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
                        "name": "snap-name-y"
                    }
                ],
                "field": "snaps"
            },
            "message": "List of snap names that either do not exist, or the account does not have access to, or snaps that were never released, or were paired with incorrect snap IDs: ['snap-name-x', 'snap-name-y'].",
            "code": "invalid-field"
        }
    ]
}

List of snaps contains some snaps or snap/revision pairs for which no approved revisions exist.

Request:

POST /api/v2/validation-sets/build-assertion HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/json

{
    "name": "acme-final-vs",
    "account-id": "AccountIDXXXOfTheRequestingUserX",
    "sequence": 3,
    "snaps": [
        {
            "name": "snap-name-1"
        },
        {
            "name": "snap-name-x",
        },
        {
            "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
            "name": "snap-name-y",
            "revision": 42
        }
    ]
}

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "extra": {
                "value": [
                    {
                        "name": "snap-name-1"
                    },
                    {
                        "name": "snap-name-x",
                    },
                    {
                        "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
                        "name": "snap-name-y",
                        "revision": 42
                    }
                ],
                "missing": [
                    {
                        "name": "snap-name-x",
                    },
                    {
                        "id": "XXSnapIDForXSnapNameYXXXXXXXXXXX",
                        "name": "snap-name-y",
                        "revision": 42
                    }
                ],
                "field": "snaps"
            },
            "message": "No approved snap revision exist for snaps: ['snap-name-x', 'snap-name-y'].",
            "code": "invalid-field"
        }
    ]
}

Changelog

  • Version 16: New API endpoints for Validation Sets management.

Request JSON Schema

{
    "additionalProperties": false,
    "properties": {
        "account-id": {
            "description": "Issuer of the Validation Set",
            "maxLength": 100,
            "type": "string"
        },
        "name": {
            "description": "Validation Set name",
            "maxLength": 100,
            "type": "string"
        },
        "sequence": {
            "description": "Validation Set sequence",
            "minimum": 1,
            "type": "integer"
        },
        "snaps": {
            "items": {
                "additionalProperties": false,
                "description": "List of snaps in a Validation Set assertion",
                "properties": {
                    "components": {
                        "additionalProperties": false,
                        "description": "Snap components",
                        "patternProperties": {
                            "^[a-z0-9](?:-?[a-z0-9])*$": {
                                "oneOf": [
                                    {
                                        "description": "Snap component presence",
                                        "enum": [
                                            "required",
                                            "optional",
                                            "invalid"
                                        ],
                                        "type": "string"
                                    },
                                    {
                                        "additionalProperties": false,
                                        "properties": {
                                            "presence": {
                                                "description": "Snap component presence",
                                                "enum": [
                                                    "required",
                                                    "optional",
                                                    "invalid"
                                                ],
                                                "type": "string"
                                            },
                                            "revision": {
                                                "description": "Snap component revision",
                                                "minimum": 1,
                                                "type": "integer"
                                            }
                                        },
                                        "type": "object"
                                    }
                                ]
                            }
                        },
                        "type": "object"
                    },
                    "id": {
                        "description": "Snap ID",
                        "maxLength": 100,
                        "type": "string"
                    },
                    "name": {
                        "description": "Snap name",
                        "maxLength": 100,
                        "type": "string"
                    },
                    "presence": {
                        "description": "Snap presence",
                        "enum": [
                            "required",
                            "optional",
                            "invalid"
                        ],
                        "type": "string"
                    },
                    "revision": {
                        "description": "Snap revision",
                        "minimum": 0,
                        "type": "integer"
                    }
                },
                "required": [
                    "name"
                ],
                "type": "object"
            },
            "minItems": 1,
            "type": "array"
        }
    },
    "required": [
        "account-id",
        "name",
        "sequence",
        "snaps"
    ],
    "type": "object"
}

Response JSON Schema

{
    "additionalProperties": false,
    "properties": {
        "account-id": {
            "description": "The \"account-id\" assertion header",
            "type": "string"
        },
        "authority-id": {
            "description": "The \"authority-id\" assertion header",
            "type": "string"
        },
        "name": {
            "description": "The \"name\" assertion header",
            "type": "string"
        },
        "revision": {
            "description": "The \"revision\" assertion header",
            "type": "string"
        },
        "sequence": {
            "description": "The \"sequence\" assertion header",
            "type": "string"
        },
        "series": {
            "description": "The \"series\" assertion header",
            "type": "string"
        },
        "snaps": {
            "items": {
                "additionalProperties": false,
                "description": "List of snaps in a Validation Set assertion",
                "properties": {
                    "components": {
                        "additionalProperties": false,
                        "description": "Snap components",
                        "patternProperties": {
                            "^[a-z0-9](?:-?[a-z0-9])*$": {
                                "oneOf": [
                                    {
                                        "description": "Snap component presence",
                                        "enum": [
                                            "required",
                                            "optional",
                                            "invalid"
                                        ],
                                        "type": "string"
                                    },
                                    {
                                        "additionalProperties": false,
                                        "properties": {
                                            "presence": {
                                                "description": "Snap component presence",
                                                "enum": [
                                                    "required",
                                                    "optional",
                                                    "invalid"
                                                ],
                                                "type": "string"
                                            },
                                            "revision": {
                                                "description": "Snap component revision",
                                                "type": "string"
                                            }
                                        },
                                        "type": "object"
                                    }
                                ]
                            }
                        },
                        "type": "object"
                    },
                    "id": {
                        "description": "Snap ID",
                        "maxLength": 100,
                        "type": "string"
                    },
                    "name": {
                        "description": "Snap name",
                        "maxLength": 100,
                        "type": "string"
                    },
                    "presence": {
                        "description": "Snap presence",
                        "enum": [
                            "required",
                            "optional",
                            "invalid"
                        ],
                        "type": "string"
                    },
                    "revision": {
                        "description": "Snap revision",
                        "type": "string"
                    }
                },
                "required": [
                    "name"
                ],
                "type": "object"
            },
            "minItems": 1,
            "type": "array"
        },
        "timestamp": {
            "description": "The \"timestamp\" assertion header",
            "type": "string"
        },
        "type": {
            "const": "validation-set",
            "description": "The \"type\" assertion header",
            "type": "string"
        }
    },
    "required": [
        "type",
        "authority-id",
        "series",
        "account-id",
        "name",
        "sequence",
        "revision",
        "timestamp",
        "snaps"
    ],
    "type": "object"
}

Manage account’s validation-set assertions.

Introduced in version 16

GET /api/v2/validation-sets/(?P<name>[\\w-]+)
POST /api/v2/validation-sets/(?P<name>[\\w-]+)

Manage account’s validation-set assertions.

Usage

As mentioned in the introduction, this endpoint can be exercised using the surl command as follows:

surl -s production -e your-email@example.com -X GET https://dashboard.snapcraft.io/api/v2/validation-sets

where your-email@example.com has to be an existing account.

GET /api/v2/validation-sets[/<name>]
reqheader Authorization

macaroon authorization header for a registered user

status 200

success

status 400

error

status 401

authentication required

GET requests to

  • /api/v2/validation-sets and

  • /api/v2/validation-sets/<name>,

return a list of headers for validation set assertions registered by the requesting user, that match requested criteria.

By default, a GET request to /api/v2/validation-sets returns headers for all validation sets owned by the requesting account, each at their latest sequence and latest revision. The same applies to /api/v2/validation-sets/<name>, except that the result will contain headers for one specific assertion.

In both cases

  • sequence=all query parameter can be added to request all sequences of the relevant assertions at their latest revisions.

  • sequence=latest query parameter is also supported (this is the default behavior).

The /api/v2/validation-sets/<name> endpoint additionally supports a sequence=N query parameter, where N is a specific sequence number.

GET Examples

Empty response: no validation sets found.

Request:

GET /api/v2/validation-sets/science-set?sequence=1 HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "assertions": []
}

Fetch all (2 in this example) validation set assertions owned by the requesting account, at their latest sequence and revision.

Request:

GET /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "assertions": [
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "certification-x1",
                "revision": "222",
                "sequence": "9",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName1XXXXXXXXXXX",
                        "name": "snap-name-1",
                        "presence": "optional"
                    },
                    {
                        "id": "XXSnapIDForXSnapName2XXXXXXXXXXX",
                        "name": "snap-name-2"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        },
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "acme-qa",
                "revision": "2",
                "sequence": "2",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
                        "name": "snap-name-3",
                        "presence": "required",
                        "revision": "2"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        }
    ]
}

Fetch all sequences of the “acme-qa” validation set at their latest revisions.

Request:

GET /api/v2/validation-sets/acme-qa?sequence=all HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "assertions": [
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "acme-qa",
                "revision": "2",
                "sequence": "2",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
                        "name": "snap-name-3",
                        "presence": "required",
                        "revision": "20"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        },
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "acme-qa",
                "revision": "1",
                "sequence": "1",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
                        "name": "snap-name-3",
                        "revision": "10"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        }
    ]
}

Fetch the latest sequence of the “acme-qa” validation set at its latest revision.

Request:

GET /api/v2/validation-sets/acme-qa?sequence=latest HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "assertions": [
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "acme-qa",
                "revision": "2",
                "sequence": "2",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
                        "name": "snap-name-3",
                        "presence": "required",
                        "revision": "20"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        }
    ]
}

GET Errors

Invalid numeric sequence query parameter value for the general variant (no name specified) of the endpoint.

Request:

GET /api/v2/validation-sets?sequence=22 HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "code": "invalid-sequence-filter",
            "extra": {
                "field": "sequence"
            },
            "message": "sequence must be one of ['all', 'latest']"
        }
    ]
}

Invalid sequence query parameter value for the specific validation set name variant of the endpoint.

Request:

GET /api/v2/validation-sets/acme-qa?sequence=forty-two HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error-list": [
    {
      "message": "sequence must be one of ['all', 'latest'] or a positive integer higher than 0",
      "code": "invalid-sequence-filter",
      "extra": {
        "field": "sequence"
      }
    }
  ]
}
POST /api/v2/validation-sets
reqheader Authorization

macaroon authorization header for a registered user

status 201

success

status 400

error

status 409

conflict

status 401

authentication required

POST requests to /api/v2/validation-sets are used to register new validation set assertions with the store.

The endpoint expects a valid validation-set type assertion, signed with a signing key registered with the store. The endpoint expects an x.ubuntu.assertion content-type request.

/api/v2/validation-sets/build-assertion endpoint can be used to obtain validation-set assertion headers as JSON, to be used as an input to snap sign, which will sign the assertion and output it in the x.ubuntu.assertion format, ready for sending to /api/v2/validation-sets in a POST request.

POST Examples

Register a self signed validation set assertion.

Request:

POST /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/x.ubuntu.assertion

type: validation-set
authority-id: AccountIDXXXOfTheRequestingUserX
revision: 2
series: 16
account-id: AccountIDXXXOfTheRequestingUserX
name: acme-bits
sequence: 2
snaps:
  -
    id: XXSnapIDForXSnapName1XXXXXXXXXXX
    name: snap-name-1
    presence: optional
  -
    id: XXSnapIDForXSnapName3XXXXXXXXXXX
    name: snap-name-3
    presence: required
    revision: 20
timestamp: 2020-10-29T16:36:56Z
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ASignatureXAHRYhBLdCTW+zo7oGALa88HYj3UQq+srnBQJfoTiDAAoJEHYj3UQq+srnGS0P/1Hx
53VBM0SignatureX29ub5nN3lCZTSx1uCsAu8/hyR25iYOKcvHu8Rhvdc3T73skb+Zjk9HDtPnRa
no0UCM6lq8pSignatureXJ37vm34RF8E44iXxex8LM26A/kRrXO1q6hELacUB1wL6Obnni2+stQQ
Lm93C/PP9Hd54YQ1SignatureXCuvuzp5x/YaDgKA6UtHFe3QpNcFYpxflMCLmq6vbWi8hTybJKu
0wf+8cUeYaDm1E0+KP+3ySignatureXSignatureXxxm6qYiOEkIvN3jfLyarcFT8OEdjsr/vj32
rdpqPJXGTQcRJrKNGjEJmF9INnSignatureXGMfjFFw96uQlBJiy6RCa5AxVcY3D2xtJ8YJBH1eo
fwojpbg6XJgaexouAis3hQbo2AsCaZNSignatureXpcqdFQSWXI+ni84IkhD8o+YtvRHwRA+LmVC
Rg7Q6Lau9v3gWCplp1lIgogqsi3ZLqLciYzrSignatureXNXqMgOsQ/sAgjoeIuHFvBEnPPB3iWl
O4uRHNmMRI0jPeDhN94I+rL/EoV/kRmM7jIznP/41SignatureXdqVo0VqLnHcX0pw0x5jqp2Toc
s81q2i53AMgPS7FFhCV80ItTvqYFgN4nEtGttK960I7hTESignatureXCDFaNiKlMUPiE4w3

Response:

HTTP/1.1 201 Created
Content-Type: application/json

{
    "assertions": [
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "name": "acme-qa",
                "revision": "2",
                "sequence": "2",
                "series": "16",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "snaps": [
                    {
                        "id": "XXSnapIDForXSnapName1XXXXXXXXXXX",
                        "name": "snap-name-1",
                        "presence": "optional"
                    },
                    {
                        "id": "XXSnapIDForXSnapName3XXXXXXXXXXX",
                        "name": "snap-name-3",
                        "presence": "required",
                        "revision": "20"
                    }
                ],
                "timestamp": "2020-10-29T16:36:56Z",
                "type": "validation-set"
            }
        }
    ]
}

POST Errors

Attempt at registering an already existing revision of a validation set results in an HTTP 409 response.

Request:

POST /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/x.ubuntu.assertion

type: validation-set
authority-id: AccountIDXXXOfTheRequestingUserX
revision: 1
series: 16
account-id: AccountIDXXXOfTheRequestingUserX
name: acme-bits
sequence: 2
snaps:
  -
    id: XXSnapIDForXSnapName1XXXXXXXXXXX
    name: snap-name-1
    presence: optional
  -
    id: XXSnapIDForXSnapName3XXXXXXXXXXX
    name: snap-name-3
    presence: required
    revision: 13
timestamp: 2020-10-29T16:36:56Z
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ASignatureXAHRYhBLdCTW+zo7oGALa88HYj3UQq+srnBQJfoTiDAAoJEHYj3UQq+srnGS0P/1Hx
53VBM0SignatureX29ub5nN3lCZTSx1uCsAu8/hyR25iYOKcvHu8Rhvdc3T73skb+Zjk9HDtPnRa
no0UCM6lq8pSignatureXJ37vm34RF8E44iXxex8LM26A/kRrXO1q6hELacUB1wL6Obnni2+stQQ
Lm93C/PP9Hd54YQ1SignatureXCuvuzp5x/YaDgKA6UtHFe3QpNcFYpxflMCLmq6vbWi8hTybJKu
0wf+8cUeYaDm1E0+KP+3ySignatureXSignatureXxxm6qYiOEkIvN3jfLyarcFT8OEdjsr/vj32
rdpqPJXGTQcRJrKNGjEJmF9INnSignatureXGMfjFFw96uQlBJiy6RCa5AxVcY3D2xtJ8YJBH1eo
fwojpbg6XJgaexouAis3hQbo2AsCaZNSignatureXpcqdFQSWXI+ni84IkhD8o+YtvRHwRA+LmVC
Rg7Q6Lau9v3gWCplp1lIgogqsi3ZLqLciYzrSignatureXNXqMgOsQ/sAgjoeIuHFvBEnPPB3iWl
O4uRHNmMRI0jPeDhN94I+rL/EoV/kRmM7jIznP/41SignatureXdqVo0VqLnHcX0pw0x5jqp2Toc
s81q2i53AMgPS7FFhCV80ItTvqYFgN4nEtGttK960I7hTESignatureXCDFaNiKlMUPiE4w3

Response:

HTTP/1.1 409 Conflict
Content-Type: application/json

{
    "error-list": [
        {
            "code": "conflict",
            "message": "Assertion request not valid: \"could not add assertion (revision 1 is already the current revision)\"."
        }
    ]
}

List of snaps contains some snaps that do not exist, or the account does not have access to, or snaps that were never released, or were paired with incorrect snap IDs.

Request:

POST /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/x.ubuntu.assertion

type: validation-set
authority-id: AccountIDXXXOfTheRequestingUserX
revision: 1
series: 16
account-id: AccountIDXXXOfTheRequestingUserX
name: acme-bits
sequence: 2
snaps:
  -
    id: XXSnapIDForXSnapName1XXXXXXXXXXX
    name: snap-name-1
  -
    id: XXSnapIDForXSnapNameXXXXXXXXXXXX
    name: snap-name-x
timestamp: 2020-10-28T11:11:11Z
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ASignatureXAHRYhBLdCTW+zo7oGALa88HYj3UQq+srnBQJfoTiDAAoJEHYj3UQq+srnGS0P/1Hx
53VBM0SignatureX29ub5nN3lCZTSx1uCsAu8/hyR25iYOKcvHu8Rhvdc3T73skb+Zjk9HDtPnRa
no0UCM6lq8pSignatureXJ37vm34RF8E44iXxex8LM26A/kRrXO1q6hELacUB1wL6Obnni2+stQQ
Lm93C/PP9Hd54YQ1SignatureXCuvuzp5x/YaDgKA6UtHFe3QpNcFYpxflMCLmq6vbWi8hTybJKu
0wf+8cUeYaDm1E0+KP+3ySignatureXSignatureXxxm6qYiOEkIvN3jfLyarcFT8OEdjsr/vj32
rdpqPJXGTQcRJrKNGjEJmF9INnSignatureXGMfjFFw96uQlBJiy6RCa5AxVcY3D2xtJ8YJBH1eo
fwojpbg6XJgaexouAis3hQbo2AsCaZNSignatureXpcqdFQSWXI+ni84IkhD8o+YtvRHwRA+LmVC
Rg7Q6Lau9v3gWCplp1lIgogqsi3ZLqLciYzrSignatureXNXqMgOsQ/sAgjoeIuHFvBEnPPB3iWl
O4uRHNmMRI0jPeDhN94I+rL/EoV/kRmM7jIznP/41SignatureXdqVo0VqLnHcX0pw0x5jqp2Toc
s81q2i53AMgPS7FFhCV80ItTvqYFgN4nEtGttK960I7hTESignatureXCDFaNiKlMUPiE4w3

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "extra": {
                "value": [
                    {
                        "id": "XXSnapIDForXSnapName1XXXXXXXXXXX",
                        "name": "snap-name-1"
                    },
                    {
                        "id": "XXSnapIDForXSnapNameXXXXXXXXXXXX",
                        "name": "snap-name-x"
                    }
                ],
                "missing": [
                    {
                        "id": "XXSnapIDForXSnapNameXXXXXXXXXXXX",
                        "name": "snap-name-x"
                    }
                ],
                "field": "snaps"
            },
            "message": "List of snap names that either do not exist, or the account does not have access to, or snaps that were never released, or were paired with incorrect snap IDs: ['snap-name-x'].",
            "code": "invalid-field"
        }
    ]
}

Assertion’s “account-id” header does not match its “authority-id” header.

Request:

POST /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/x.ubuntu.assertion

type: validation-set
authority-id: AccountIDXXXOfTheRequestingUserX
revision: 1
series: 16
account-id: AccountIDXYZOfDifferentUserXYZXY
name: acme-bits
sequence: 2
snaps:
  -
    id: XXSnapIDForXSnapName1XXXXXXXXXXX
    name: snap-name-1
timestamp: 2020-10-28T11:11:11Z
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ASignatureXAHRYhBLdCTW+zo7oGALa88HYj3UQq+srnBQJfoTiDAAoJEHYj3UQq+srnGS0P/1Hx
53VBM0SignatureX29ub5nN3lCZTSx1uCsAu8/hyR25iYOKcvHu8Rhvdc3T73skb+Zjk9HDtPnRa
no0UCM6lq8pSignatureXJ37vm34RF8E44iXxex8LM26A/kRrXO1q6hELacUB1wL6Obnni2+stQQ
Lm93C/PP9Hd54YQ1SignatureXCuvuzp5x/YaDgKA6UtHFe3QpNcFYpxflMCLmq6vbWi8hTybJKu
0wf+8cUeYaDm1E0+KP+3ySignatureXSignatureXxxm6qYiOEkIvN3jfLyarcFT8OEdjsr/vj32
rdpqPJXGTQcRJrKNGjEJmF9INnSignatureXGMfjFFw96uQlBJiy6RCa5AxVcY3D2xtJ8YJBH1eo
fwojpbg6XJgaexouAis3hQbo2AsCaZNSignatureXpcqdFQSWXI+ni84IkhD8o+YtvRHwRA+LmVC
Rg7Q6Lau9v3gWCplp1lIgogqsi3ZLqLciYzrSignatureXNXqMgOsQ/sAgjoeIuHFvBEnPPB3iWl
O4uRHNmMRI0jPeDhN94I+rL/EoV/kRmM7jIznP/41SignatureXdqVo0VqLnHcX0pw0x5jqp2Toc
s81q2i53AMgPS7FFhCV80ItTvqYFgN4nEtGttK960I7hTESignatureXCDFaNiKlMUPiE4w3

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "code": "invalid-request",
            "message": "account-id and authority-id must match the requesting user."
        }
    ]
}

Assertion’s signing key is not registered with the store.

Request:

POST /api/v2/validation-sets HTTP/1.1
Host: dashboard.snapcraft.io
Authorization: Macaroon root=..., discharge=...
Content-Type: application/x.ubuntu.assertion

type: validation-set
authority-id: AccountIDXXXOfTheRequestingUserX
revision: 1
series: 16
account-id: AccountIDXXXOfTheRequestingUserX
name: acme-bits
sequence: 2
snaps:
  -
    id: XXSnapIDForXSnapName1XXXXXXXXXXX
    name: snap-name-1
timestamp: 2020-10-28T11:11:11Z
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ASignatureXAHRYhBLdCTW+zo7oGALa88HYj3UQq+srnBQJfoTiDAAoJEHYj3UQq+srnGS0P/1Hx
53VBM0SignatureX29ub5nN3lCZTSx1uCsAu8/hyR25iYOKcvHu8Rhvdc3T73skb+Zjk9HDtPnRa
no0UCM6lq8pSignatureXJ37vm34RF8E44iXxex8LM26A/kRrXO1q6hELacUB1wL6Obnni2+stQQ
Lm93C/PP9Hd54YQ1SignatureXCuvuzp5x/YaDgKA6UtHFe3QpNcFYpxflMCLmq6vbWi8hTybJKu
0wf+8cUeYaDm1E0+KP+3ySignatureXSignatureXxxm6qYiOEkIvN3jfLyarcFT8OEdjsr/vj32
rdpqPJXGTQcRJrKNGjEJmF9INnSignatureXGMfjFFw96uQlBJiy6RCa5AxVcY3D2xtJ8YJBH1eo
fwojpbg6XJgaexouAis3hQbo2AsCaZNSignatureXpcqdFQSWXI+ni84IkhD8o+YtvRHwRA+LmVC
Rg7Q6Lau9v3gWCplp1lIgogqsi3ZLqLciYzrSignatureXNXqMgOsQ/sAgjoeIuHFvBEnPPB3iWl
O4uRHNmMRI0jPeDhN94I+rL/EoV/kRmM7jIznP/41SignatureXdqVo0VqLnHcX0pw0x5jqp2Toc
s81q2i53AMgPS7FFhCV80ItTvqYFgN4nEtGttK960I7hTESignatureXCDFaNiKlMUPiE4w3

Response:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error-list": [
        {
            "code": "invalid-request",
            "message": "Assertion request not valid: \"could not validate assertion (no matching public key \"XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\" for signature by \"AccountIDXXXOfTheRequestingUserX\")\"."
        }
    ]
}

Changelog

  • Version 16: New API endpoints for Validation Sets management.

Response JSON Schema

{
    "additionalProperties": false,
    "properties": {
        "assertions": {
            "description": "List of validation-set assertions",
            "items": {
                "additionalProperties": false,
                "properties": {
                    "headers": {
                        "additionalProperties": false,
                        "description": "Assertion headers",
                        "properties": {
                            "account-id": {
                                "description": "The \"account-id\" assertion header",
                                "type": "string"
                            },
                            "authority-id": {
                                "description": "The \"authority-id\" assertion header",
                                "type": "string"
                            },
                            "name": {
                                "description": "The \"name\" assertion header",
                                "type": "string"
                            },
                            "revision": {
                                "description": "The \"revision\" assertion header",
                                "type": "string"
                            },
                            "sequence": {
                                "description": "The \"sequence\" assertion header",
                                "type": "string"
                            },
                            "series": {
                                "description": "The \"series\" assertion header",
                                "type": "string"
                            },
                            "sign-key-sha3-384": {
                                "description": "Signing key ID",
                                "type": "string"
                            },
                            "snaps": {
                                "items": {
                                    "additionalProperties": false,
                                    "description": "List of snaps in a Validation Set assertion",
                                    "properties": {
                                        "components": {
                                            "additionalProperties": false,
                                            "description": "Snap components",
                                            "patternProperties": {
                                                "^[a-z0-9](?:-?[a-z0-9])*$": {
                                                    "oneOf": [
                                                        {
                                                            "description": "Snap component presence",
                                                            "enum": [
                                                                "required",
                                                                "optional",
                                                                "invalid"
                                                            ],
                                                            "type": "string"
                                                        },
                                                        {
                                                            "additionalProperties": false,
                                                            "properties": {
                                                                "presence": {
                                                                    "description": "Snap component presence",
                                                                    "enum": [
                                                                        "required",
                                                                        "optional",
                                                                        "invalid"
                                                                    ],
                                                                    "type": "string"
                                                                },
                                                                "revision": {
                                                                    "description": "Snap component revision",
                                                                    "type": "string"
                                                                }
                                                            },
                                                            "type": "object"
                                                        }
                                                    ]
                                                }
                                            },
                                            "type": "object"
                                        },
                                        "id": {
                                            "description": "Snap ID",
                                            "maxLength": 100,
                                            "type": "string"
                                        },
                                        "name": {
                                            "description": "Snap name",
                                            "maxLength": 100,
                                            "type": "string"
                                        },
                                        "presence": {
                                            "description": "Snap presence",
                                            "enum": [
                                                "required",
                                                "optional",
                                                "invalid"
                                            ],
                                            "type": "string"
                                        },
                                        "revision": {
                                            "description": "Snap revision",
                                            "type": "string"
                                        }
                                    },
                                    "required": [
                                        "name"
                                    ],
                                    "type": "object"
                                },
                                "minItems": 1,
                                "type": "array"
                            },
                            "timestamp": {
                                "description": "The \"timestamp\" assertion header",
                                "type": "string"
                            },
                            "type": {
                                "const": "validation-set",
                                "description": "The \"type\" assertion header",
                                "type": "string"
                            }
                        },
                        "required": [
                            "type",
                            "authority-id",
                            "series",
                            "account-id",
                            "name",
                            "sequence",
                            "snaps",
                            "sign-key-sha3-384",
                            "timestamp"
                        ],
                        "type": "object"
                    }
                },
                "required": [
                    "headers"
                ],
                "type": "object"
            },
            "type": "array"
        }
    },
    "required": [
        "assertions"
    ],
    "type": "object"
}