Create a model assertion

At the heart of custom Ubuntu Core image creation is the model assertion. An assertion is a signed recipe that describes the components that comprise a complete image. An assertion is provided as JSON in a text file which is signed by a GPG key associated with the publisher’s Ubuntu One account.

The model contains:

  • identification information, such as the developer-id and model name.
  • which essential snaps make up the device system.
  • other required or optional snaps that implement the device functionality.

See below for details on how to download and modify a model file to include your own selection of snaps.


1. Download a model file

The quickest way to create a new model assertion is to edit a model that already exists. Reference models for every supported Ubuntu Core device can be found in the snapcore/models GitHub repository.

For this project, we’re going to modify the 64-bit reference model for the Raspberry Pi: ubuntu-core-22-pi-arm64.json.

Download and save the file locally with the following wget command. We’ve called the file my-model.json:

wget -O my-model.json https://raw.githubusercontent.com/snapcore/models/master/ubuntu-core-22-pi-arm64.json

2. Edit the model file

We now need to edit my-model.json using a text editor:

nano my-model.json

The following fields in my-model.json need to be changed:

2.1 "authority-id" and "brand-id"

"authority-id": "canonical",
"brand-id": "canonical",

These properties define the authority responsible for the image. Change both instances of the string “canonical” to your developer id, retrieved with the snapcraft whoami command. (“xSfWKGdLoQBoQx88”, in our example output). This links the image to your Ubuntu One account and ensures that only you can push image updates to devices using your model.

2.2 "timestamp"

    "timestamp": "2020-03-31T12:00:00.0Z", 

This needs to be provided at the end of the process; we’ll come back to this.

2.3 "snaps"

    "snaps": [
        {
            "name": "pi",
            "type": "gadget",
            "default-channel": "22/stable",
            "id": "YbGa9O3dAXl88YLI6Y1bGG74pwBxZyKg"
        },
       ...

This section lists the snaps to be included in the image. pi (shown above), pi-kernel, core22 and snapd are the four snaps required for a functioning Ubuntu Core device.

Additional snaps are included in the same way, with each snap requiring the following fields:

  • name: simply the snap name.
  • type: the type of snap. This is app for standard application snaps.
  • default-channel: the channel to install the snap from.
  • id: a unique snap identifier associated with every published snap. This is snap-id in the output from snap info <snap-name>.

For this tutorial, we’re going to add the AdGuard Home snap, an open source network-wide blocker for advertising and tracking. It’s an ideal candidate for an Ubuntu Core image like this because it benefits from frequent autonomous updates and a confined environment.

To add the AdGuard Home snap to our image, we need to add the following JSON stanza to the snaps section in our my-model.json (note the comma you need to append to the preceding snap entry to show continuation):

        },
	    {
            "name": "adguard-home",
            "type": "app",
            "default-channel": "latest/stable",
            "id": "UXZIkJfJT2SPCGejjnSjOBqJ71yHk8bw"
        }

Snaps do not have dependencies, but they do require the presence of the base snap they were built on. AdGuard Home is built using a base of core22 (see the output from snap info adguard-home --verbose | grep "base:"), which is the default base for our image, so no further edits are necessary.

3. Complete model example

After finishing all your edits, the completed my-model.json text file should now contain the following:

{
    "type": "model",
    "series": "16",
    "model": "ubuntu-core-22-pi-arm64",
    "architecture": "arm64",
    "authority-id": "bJzr2XzZg6Qv6Z53HIeziXyxtn1XItIq",
    "brand-id": "bJzr2XzZg6Qv6Z53HIeziXyxtn1XItIq",
    "timestamp": "2022-04-04T10:40:41+00:00",
    "base": "core22",
    "grade": "signed",
    "snaps": [
        {
            "name": "pi",
            "type": "gadget",
            "default-channel": "22/stable",
            "id": "YbGa9O3dAXl88YLI6Y1bGG74pwBxZyKg"
        },
        {
            "name": "pi-kernel",
            "type": "kernel",
            "default-channel": "22/stable",
            "id": "jeIuP6tfFrvAdic8DMWqHmoaoukAPNbJ"
        },
        {
            "name": "core22",
            "type": "base",
            "default-channel": "latest/stable",
            "id": "amcUKQILKXHHTlmSa7NMdnXSx02dNeeT"
        },
        {
            "name": "snapd",
            "type": "snapd",
            "default-channel": "latest/stable",
            "id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"
        },
        {
            "name": "adguard-home",
            "type": "app",
            "default-channel": "latest/stable",
            "id": "UXZIkJfJT2SPCGejjnSjOBqJ71yHk8bw"
        }
    ]
}

After the file has been created, the next step is to sign it. See Sign a model for further details.

Can I clarify that a “model assertion” is, by definition, signed? For precision, it seems that the text file one creates initially is just the “model”, and once it is signed, it becomes a “model assertion.” What this suggests is that it’s redundant to describe something as a “signed model assertion” since the model assertion is signed by definition. Thoughts?

I might update this page in a couple ways to make it more current. First, use a core22 example. Also, find a snap other than adguard-home as that now has a base snap of core22; pick something else that has an older base snap to explicitly show how to add an older snap in a core22 model file.

1 Like

This is a good point, and I totally agree. We should explain the distinction and then use the terms consistently. I’ll update this and take a look over our other assertion pages.

I admit, I’m not sure what the consensus is.

I’m going to mention again that I think it’s confusing to refer to “signed model assertions”, or to direct the reader to “sign” the model assertion. By definition, any assertion is already “a digitally signed document” (right there in the first sentence), https://ubuntu.com/core/docs/reference/assertions. I think the proper verbiage is that one creates a “model”, then signs it to get the “model assertion.”

P.S. If you read the explanation on this page, it is confusing in that it directs the reader to download a model assertion. Again, that is not what is happening; it is a model that is downloaded, edited and subsequently signed.

1 Like

Came across this issue with adguard-home as it needs a base snap core22.
Please consider using another snap as an example or up the tutorial to use core22.

I’ll repeat myself just to bring this to the top – you should distinguish between text-based “models”, and signed “assertions.” I believe the word “assertion” should be reserved for digitally-signed objects. So the thing displayed on this page is not an assertion, it is just a model, to be subsequently signed.

Thanks for bringing this up. I’ve made a start on being consistent with the use of assertion only for the signed artefact, and I’ll change other references as I go through the docs.

@ycheng And thank you for flagging that adguard-home is now built on core22. I’ve updated the model example to reflect this change.