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.
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 secureIf everything was set up correctly, the auth table should look like this:
sqlite> select * from auth;
1|admin_user|admin_passwordNow, 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.dbWhen setting up Differ in production, you must run export GIN_MODE=release before running the binary.
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.dbSimple check to see if the server is running correctly.
Method: GET
Endpoint: http://[base_url]/status
Parameters: None
Returns:
200 OKon success.
{
"status": "ok"
}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:
Creates a new image in the dabatase. Every release (see subsection below) is attached to an image.
Method: POST
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 OKon success.
Retrieves information about an image given its name.
Method: GET
Endpoint: http://[base_url]/images/[name]
Parameters: None
Returns:
200 OKon success, alongside the image information.
{
"image": {
"name": "pico",
"url": "https://github.com/Vanilla-OS/pico-image",
"releases": [
...
]
}
}400 Bad Requestif image cannot be found.
Retrieves information about all images.
Method: GET
Endpoint: http://[base_url]/images
Parameters: None
Returns:
200 OKon 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": [
...
]
}
]
}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.
Creates a new release for the given image.
Method: POST
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.pycontains a script for extracting the list from a Debian-based distribution.
{
"digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
"packages": [
{
"name": "apt",
"version": "2.7.3"
},
...
]
}Returns:
200 OKon success.
Retrieves the most recent release from the image.
Method: GET
Endpoint: http://[base_url]/images/[image]/latest
Parameters: None
Returns:
200 OKon success, alongside the release information.
{
"release": {
"digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
"date": "2023-11-26T12:56:53.377838864Z",
"packages": [
...
]
}
}Searches for a specific release by its digest.
Method: GET
Endpoint: http://[base_url]/images/[image]/[digest]
Parameters: None
Returns:
200 OKon success, alongside the release information.
{
"release": {
"digest": "sha256:a99e4593b23fd07e3761639e9db38c0315e198d6e39dad6070e0e0e88be3de0d",
"date": "2023-11-26T12:56:53.377838864Z",
"packages": [
...
]
}
}400 Bad Requestif release cannot be found.
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.
Method: GET
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 OKon 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 Requestif either digest cannot be found.