Skip to content

Vanilla-OS/Differ

Repository files navigation

Differ

Differ is a REST-API for retrieving package changes between images on immutable Linux distributions. It was designed for allowing the user to visualize changes in package versions in Vanilla OS, but this tool can be used by any distribution without any changes to the code.

Build and Setup

Build and Run from Source

In order to setup Differ from source, you need Go 1.22 and a Sqlite manager (in this section we use the sqlite3 command line tool). First, you need to create a database to store all the images and releases Differ will manage, as well as auth information (see read-only subsection below). Using the sqlite3 tool, run the following commands:

$ sqlite3 /path/to/database.db
sqlite> create table auth("ID" INTEGER, name, pass TEXT, PRIMARY KEY("ID"));
sqlite> insert into auth values(1, 'admin_user', 'admin_password'); # Replace user and password with something secure

If everything was set up correctly, the auth table should look like this:

sqlite> select * from auth;
1|admin_user|admin_password

Now, all you need to do is use the provided Makefile. Once the binary is built, run it by passing the database path as argument.

$ make
$ ./differ path/to/database.db

When setting up Differ in production, you must run export GIN_MODE=release before running the binary.

Container Image

Alternatively, you can use the provided container image. In this case, all you need to do is pull it using either Docker or Podman, create a new container and pass the database path as argument. Differ will handle the database setup by using the contents of admin_user and admin_password environment variables.

$ podman pull ghcr.io/vanilla-os/differ:main
$ podman run --env 'admin_user=user' --env 'admin_password=password' differ path/to/database.db

Endpoints

Status

Simple check to see if the server is running correctly.

Endpoint: http://[base_url]/status

Parameters: None

Returns:

  • 200 OK on success.
{
    "status": "ok"
}

Images

Images are, as the name implies, image types shipped by the distribution. For example, a distro can ship both GNOME and a KDE versions as separate images, which would can be managed with the endpoints below:

Create image

Creates a new image in the dabatase. Every release (see subsection below) is attached to an image.

Endpoint: http://[base_url]/images/new

Parameters:

  • Name: Image name
  • URL: Where the image is hosted or its repository. For information purposes only.
{
    "name": "pico",
    "url": "https://github.com/Vanilla-OS/pico-image"
}

Returns:

  • 200 OK on success.

Get image by name

Retrieves information about an image given its name.

*Endpoint: http://[base_url]/images/[name]

Parameters: None

Returns:

  • 200 OK on success, alongside the image information.
{
    "image": {
        "name": "pico",
        "url": "https://github.com/Vanilla-OS/pico-image",
        "releases": [
            ...
        ]
    }
}
  • 400 Bad Request if image cannot be found.

Get all images

Retrieves information about all images.

Endpoint: http://[base_url]/images

Parameters: None

Returns:

  • 200 OK on success, alongside the images information.
{
    "images": [
        {
            "name": "pico",
            "url": "https://github.com/Vanilla-OS/pico-image",
            "releases": [
                ...
            ]
        },
        {
            "name": "core",
            "url": "https://github.com/Vanilla-OS/core-image",
            "releases": [
                ...
            ]
        }
    ]
}

Releases

A release is a new version of some image, where packages can be added, removed, upgraded or downgraded. A release also has a digest, which should be the image digest provided by the container manager.

Create release

Creates a new release for the given image.

Endpoint:http://[base_url]/images/[image]/new

Parameters:

  • Digest: Image digest
  • Packages: List of package names and versions in the current release. get_all_packages.py contains a script for extracting the list from a Debian-based distribution.
{
    "digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
    "packages": [
        {
            "name": "apt",
            "version": "2.7.3"
        },
        ...
    ]
}

Returns:

  • 200 OK on success.

Get latest release for image

Retrieves the most recent release from the image.

Endpoint: http://[base_url]/images/[image]/latest

Parameters: None

Returns:

  • 200 OK on success, alongside the release information.
{
    "release": {
        "digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
        "date": "2023-11-26T12:56:53.377838864Z",
        "packages": [
            ...
        ]
    }
}

Get specific release

Searches for a specific release by its digest.

Endpoint: http://[base_url]/images/[image]/[digest]

Parameters: None

Returns:

  • 200 OK on success, alongside the release information.
{
    "release": {
        "digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
        "date": "2023-11-26T12:56:53.377838864Z",
        "packages": [
            ...
        ]
    }
}
  • 400 Bad Request if release cannot be found.

Release diff

The most important endpoint in the API. Given two digests, generates a list of changed packages. This information is cached so future queries are nearly instant.

Endpoint: http://[base_url]/images/[image]/diff

Parameters:

  • Old digest: Digest of the older image, which is usually the image the user is currently on.
  • New digest: Digest of the newer image, which is usually the image the user wants to update to.
{
    "old_digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0c",
    "new_digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d"
}

Returns:

  • 200 OK on success, alongside the modified packages.
{
    "_new_digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
    "_old_digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0c",
    "added": [
        {
            "name": "zsh-common",
            "new_version": "5.9-5"
        }
    ],
    "downgraded": [
        {
            "name": "autoconf",
            "old_version": "2.71-3"
        }
    ],
    "removed": [
        {
            "name": "nano",
            "old_version": "6.2"
        }
    ],
    "upgraded": [
        {
            "name": "curl",
            "old_version": "1.0.3-aplha",
            "new_version": "8.2.1-2"
        }
    ]
}
  • 400 Bad Request if either digest cannot be found.