Registries

Introduction

The Registries API enables Registry 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 Registries 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 Registry assertion headers ready for signing.

Introduced in version 16

POST /api/v2/registries/build-assertion

Prepare Registry headers for local signing.

Return Registry 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/registries/build-assertion -d '<input-data-as-json>'

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

Example

Prepare assertion headers for a registry named “network”.

Request:

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

{
    "account-id": "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN",
    "name": "network",
    "views": {
        "wifi-setup": {
            "rules": [
                {"request": "ssids", "storage": "wifi.ssids"},
                {"request": "ssid", "storage": "wifi.ssid", "access": "read-write"},
                {"request": "password", "storage": "wifi.psk", "access": "write"},
                {"request": "status", "storage": "wifi.status", "access": "read"},
                {"request": "private.{placeholder}", "storage": "wifi.{placeholder}"}
            ]
        }
    },
    "body": "{\n  \"storage\": {\n    \"schema\": {\n      \"wifi\": {\n        \"values\": \"any\"\n      }\n    }\n  }\n}",
    "timestamp": "2024-03-06T09:00:00Z"
}

Response:

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

{
    "type": "registry",
    "account-id": "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN",
    "authority-id": "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN",
    "name": "network",
    "revision": "0",
    "views": {
        "wifi-setup": {
            "rules": [
                {"request": "ssids", "storage": "wifi.ssids"},
                {"request": "ssid", "storage": "wifi.ssid", "access": "read-write"},
                {"request": "password", "storage": "wifi.psk", "access": "write"},
                {"request": "status", "storage": "wifi.status", "access": "read"},
                {"request": "private.{placeholder}", "storage": "wifi.{placeholder}"}
            ]
        }
    },
    "body": "{\n  \"storage\": {\n    \"schema\": {\n      \"wifi\": {\n        \"values\": \"any\"\n      }\n    }\n  }\n}",
    "timestamp": "2024-03-06T09:00:00Z"
}

Errors

Request payload contains a number of request JSON schema violations.

Request:

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

{
    "surprise-field": 123,
    "name": "name",
    "views": [
        "wifi-setup": {
            "rules": [
                {"request": "ssids", "storage": "wifi.ssids"},
                {"request": "ssid", "storage": "wifi.ssid", "access": "read-write"},
                {"request": "password", "storage": "wifi.psk", "access": "write"},
                {"request": "status", "storage": "wifi.status", "access": "read"},
                {"request": "private.{placeholder}", "storage": "wifi.{placeholder}"}
            ]
        }
    ],
    "body": ""
}

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": "'account-id' is a required property at /",
            "code": "invalid-request"
        }
    ]
}

Request JSON Schema

{
    "additionalProperties": false,
    "definitions": {
        "ContentRule": {
            "additionalProperties": false,
            "properties": {
                "access": {
                    "enum": [
                        "read",
                        "write",
                        "read-write"
                    ]
                },
                "content": {
                    "items": {
                        "oneOf": [
                            {
                                "$ref": "#/definitions/Rule"
                            },
                            {
                                "$ref": "#/definitions/ContentRule"
                            }
                        ]
                    },
                    "minItems": 1,
                    "type": "array"
                },
                "request": {
                    "minLength": 1,
                    "type": "string"
                },
                "storage": {
                    "minLength": 1,
                    "type": "string"
                }
            },
            "required": [
                "storage",
                "content"
            ],
            "type": "object"
        },
        "Rule": {
            "additionalProperties": false,
            "properties": {
                "access": {
                    "enum": [
                        "read",
                        "write",
                        "read-write"
                    ]
                },
                "request": {
                    "minLength": 1,
                    "type": "string"
                },
                "storage": {
                    "minLength": 1,
                    "type": "string"
                }
            },
            "required": [
                "storage"
            ],
            "type": "object"
        }
    },
    "properties": {
        "account-id": {
            "description": "Issuer of the Registry",
            "minLength": 1,
            "type": "string"
        },
        "body": {
            "type": "string"
        },
        "name": {
            "description": "Registry name",
            "minLength": 1,
            "type": "string"
        },
        "views": {
            "additionalProperties": false,
            "description": "Views in a Registry assertion",
            "maxProperties": 1,
            "minProperties": 1,
            "patternProperties": {
                "^([a-z]-?)+[a-z]+$": {
                    "additionalProperties": false,
                    "minProperties": 1,
                    "properties": {
                        "filters": {
                            "items": {
                                "minProperties": 1,
                                "patternProperties": {
                                    "^.+$": {
                                        "additionalProperties": false,
                                        "properties": {
                                            "optional": {
                                                "type": "boolean"
                                            }
                                        },
                                        "type": "object"
                                    }
                                },
                                "type": "object"
                            },
                            "minItems": 1,
                            "type": "array"
                        },
                        "rules": {
                            "items": {
                                "anyOf": [
                                    {
                                        "$ref": "#/definitions/Rule"
                                    },
                                    {
                                        "$ref": "#/definitions/ContentRule"
                                    }
                                ]
                            },
                            "minItems": 1,
                            "type": "array"
                        }
                    },
                    "required": [
                        "rules"
                    ],
                    "type": "object"
                }
            },
            "propertyNames": {
                "maxLength": 128
            },
            "type": "object"
        }
    },
    "required": [
        "account-id",
        "name",
        "views",
        "body"
    ],
    "type": "object"
}

Manage account’s registry assertions.

Introduced in version 16

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

Manage account’s registry 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/registries

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

GET /api/v2/registries[/<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/registries and

  • /api/v2/registries/<name>,

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

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

GET Examples

Empty response: no registries found.

Request:

GET /api/v2/registries 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) registry assertions owned by the requesting account.

Request:

GET /api/v2/registries 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",
                "views": {
                    "wifi-setup": {
                        "rules": [
                            {
                                "request": "ssids",
                                "storage": "wifi.ssids"
                            },
                            {
                                "access": "read-write",
                                "request": "ssid",
                                "storage": "wifi.ssid"
                            },
                            {
                                "access": "write",
                                "request": "password",
                                "storage": "wifi.psk"
                            },
                            {
                                "access": "read",
                                "request": "status",
                                "storage": "wifi.status"
                            },
                            {
                                "request": "private.{placeholder}",
                                "storage": "wifi.{placeholder}"
                            }
                        ]
                    }
                },
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "body-length": "92",
                "name": "network",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "timestamp": "2024-03-06T09:00:00Z",
                "type": "registry"
            },
            "body": "{\n  \"storage\": {\n    \"schema\": {\n      \"wifi\": {\n        \"values\": \"any\"\n      }\n    }\n  }\n}"
        },
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "views": {
                    "acme-views": {
                        "rules": [
                            {
                                "request": "acme-headers",
                                "storage": "acme.headers"
                            },
                        ]
                    }
                },
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "body-length": "0",
                "name": "network",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "timestamp": "2024-03-06T09:00:00Z",
                "type": "registry"
            },
            "body": ""
        }
    ]
}

Fetch the registry assertion named ‘wifi-setup’ owned by the requesting account.

Request:

GET /api/v2/registries/wifi-setup 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",
                "views": {
                    "wifi-setup": {
                        "rules": [
                            {
                                "request": "ssids",
                                "storage": "wifi.ssids"
                            },
                            {
                                "access": "read-write",
                                "request": "ssid",
                                "storage": "wifi.ssid"
                            },
                            {
                                "access": "write",
                                "request": "password",
                                "storage": "wifi.psk"
                            },
                            {
                                "access": "read",
                                "request": "status",
                                "storage": "wifi.status"
                            },
                            {
                                "request": "private.{placeholder}",
                                "storage": "wifi.{placeholder}"
                            }
                        ]
                    }
                },
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "body-length": "92",
                "name": "network",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "timestamp": "2024-03-06T09:00:00Z",
                "type": "registry"
            },
            "body": "{\n  \"storage\": {\n    \"schema\": {\n      \"wifi\": {\n        \"values\": \"any\"\n      }\n    }\n  }\n}"
        }
    ]
}
POST /api/v2/registries
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/registries are used to register new registry assertions with the store.

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

/api/v2/registries/build-assertion endpoint can be used to obtain registry 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/registries in a POST request.

POST Examples

Register a self signed registry assertion.

Request:

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

type: registry
authority-id: AccountIDXXXOfTheRequestingUserX
account-id: AccountIDXXXOfTheRequestingUserX
name: network
views:
wifi-setup:
    rules:
    -
        request: ssids
        storage: wifi.ssids
    -
        access: read-write
        request: ssid
        storage: wifi.ssid
    -
        access: write
        request: password
        storage: wifi.psk
    -
        access: read
        request: status
        storage: wifi.status
    -
        request: private.{placeholder}
        storage: wifi.{placeholder}
timestamp: 2024-03-06T09:00:00Z
body-length: 92
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{
    "storage": {
        "schema": {
            "wifi": {
                "values": "any"
            }
        }
    }
}

<the-key>

Response:

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

{
    "assertions": [
        {
            "headers": {
                "account-id": "AccountIDXXXOfTheRequestingUserX",
                "views": {
                    "wifi-setup": {
                        "rules": [
                            {
                                "request": "ssids",
                                "storage": "wifi.ssids"
                            },
                            {
                                "access": "read-write",
                                "request": "ssid",
                                "storage": "wifi.ssid"
                            },
                            {
                                "access": "write",
                                "request": "password",
                                "storage": "wifi.psk"
                            },
                            {
                                "access": "read",
                                "request": "status",
                                "storage": "wifi.status"
                            },
                            {
                                "request": "private.{placeholder}",
                                "storage": "wifi.{placeholder}"
                            }
                        ]
                    }
                },
                "authority-id": "AccountIDXXXOfTheRequestingUserX",
                "body-length": "92",
                "name": "network",
                "sign-key-sha3-384": "XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "timestamp": "2024-03-06T09:00:00Z",
                "type": "registry"
            },
            "body": "{\n  \"storage\": {\n    \"schema\": {\n      \"wifi\": {\n        \"values\": \"any\"\n      }\n    }\n  }\n}"
        }
    ]
}

POST Errors

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: registry
authority-id: AccountIDXXXOfTheRequestingUserX
account-id: AccountIDXYZOfDifferentUserXYZXY
name: network
views:
    wifi-setup:
        rules:
        -
            request: ssids
            storage: wifi.ssids
        -
            access: read-write
            request: ssid
            storage: wifi.ssid
        -
            access: write
            request: password
            storage: wifi.psk
        -
            access: read
            request: status
            storage: wifi.status
        -
            request: private.{placeholder}
            storage: wifi.{placeholder}
timestamp: 2024-03-06T09:00:00Z
body-length: 92
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{
    "storage": {
        "schema": {
            "wifi": {
                "values": "any"
            }
        }
    }
}

<the-key>

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: registry
authority-id: AccountIDXXXOfTheRequestingUserX
account-id: AccountIDXXXOfTheRequestingUserX
name: network
views:
    wifi-setup:
        rules:
        -
            request: ssids
            storage: wifi.ssids
        -
            access: read-write
            request: ssid
            storage: wifi.ssid
        -
            access: write
            request: password
            storage: wifi.psk
        -
            access: read
            request: status
            storage: wifi.status
        -
            request: private.{placeholder}
            storage: wifi.{placeholder}
timestamp: 2024-03-06T09:00:00Z
body-length: 92
sign-key-sha3-384: XSignXKeyXHashXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{
    "storage": {
        "schema": {
            "wifi": {
                "values": "any"
            }
        }
    }
}

<the-key>

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\")\"."
        }
    ]
}

Response JSON Schema

{
    "additionalProperties": false,
    "properties": {
        "assertions": {
            "description": "List of registry 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"
                            },
                            "sign-key-sha3-384": {
                                "description": "Signing key ID",
                                "type": "string"
                            },
                            "timestamp": {
                                "description": "The \"timestamp\" assertion header",
                                "type": "string"
                            },
                            "type": {
                                "const": "registry",
                                "description": "The \"type\" assertion header",
                                "type": "string"
                            },
                            "views": {
                                "additionalProperties": false,
                                "description": "Views in a Registry assertion",
                                "maxProperties": 1,
                                "minProperties": 1,
                                "patternProperties": {
                                    "^([a-z]-?)+[a-z]+$": {
                                        "additionalProperties": false,
                                        "minProperties": 1,
                                        "properties": {
                                            "filters": {
                                                "items": {
                                                    "minProperties": 1,
                                                    "patternProperties": {
                                                        "^.+$": {
                                                            "additionalProperties": false,
                                                            "properties": {
                                                                "optional": {
                                                                    "type": "boolean"
                                                                }
                                                            },
                                                            "type": "object"
                                                        }
                                                    },
                                                    "type": "object"
                                                },
                                                "minItems": 1,
                                                "type": "array"
                                            },
                                            "rules": {
                                                "items": {
                                                    "anyOf": [
                                                        {
                                                            "$ref": "#/definitions/Rule"
                                                        },
                                                        {
                                                            "$ref": "#/definitions/ContentRule"
                                                        }
                                                    ]
                                                },
                                                "minItems": 1,
                                                "type": "array"
                                            }
                                        },
                                        "required": [
                                            "rules"
                                        ],
                                        "type": "object"
                                    }
                                },
                                "propertyNames": {
                                    "maxLength": 128
                                },
                                "type": "object"
                            }
                        },
                        "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"
}