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:
Staging: https://dashboard.staging.snapcraft.io/api/v2/registries
Production: https://dashboard.snapcraft.io/api/v2/registries
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"
}