Package-Server

The appman-package-server is a command-line utility that can be used by the developer to simulate an app-store or update-server like backend and conveniently test package installations over HTTP connections.

Its main purpose is for interactive use on a developer's desktop machine, but it can also be used on a server to distribute packages to multiple devices or other developers' machines during the development phase.

Note: Do not use this tool in a production environment!

Configuration

This server accepts unsigned as well as developer-signed packages. The appman-packager can create all these variations. On the download side it distributes unsigned packages by default, but can be configured to store-sign packages on the fly.

Please also see the Package Installation documentation for more in-depth information about package installations.

There is not much to configure when running the server. The only thing that needs to be specified is a data directory that the package-server will use to manage packages. All configuration options are available both via command-line arguments and via a YAML based configuration file. If an amps-config.yaml file exists in the data directory, it will be parsed implicitly. These configuration files can be created and edited manually, but there's also the convenient --create-config option to generate or modify the file using command-line arguments.

Command-line options always take precedence over the configuration file though.

Argument
[YAML field]
Description
--dd or --data-directory <dir>The data directory to use. This defaults to the current directory, but only if it contains an amps-config.yaml file. Otherwise you need to specify the directory explicitly.
--la or --listen-address <address>
[listenAddress]
The network address to listen on. Can be one of <ip address>, any or localhost. An optional port number can be appended to any of the options via :<port number>. The default is localhost:8020.
--pi or --project-id <id>
[projectId]
Set the project id to a non-empty ASCII string. The default is PROJECT. This setting is optional, but helps to make sure packages are not mixed between incompatible projects.
--sc <certificate file>
[storeSignCertificate]
The certificate file for signing store downloads. The default is no certificate, which means that downloads are simply not store signed.
--sp <password>
[storeSignPassword]
The password for the store signing certificate. The default is no password.
--dc <certificate file>
[developerVerificationCaCertificates]
The CA certificate files to verify developer signatures on upload. The command-line option can be used multiple times, while the YAML field accepts a list of strings, if multiple CA certificates are needed. The default is to not verify developer signatures at all.
--cc or --create-configSave the current configuration to the config file amps-config.yaml in the data directory. This makes it easy to generate a configuration file for future use from known good command-line arguments. Settings from an existing config file are preserved, comments are not.

The appman-package-server naturally supports the standard Unix –help command-line option.

Example Configuration File

These configuration files follow the same pattern as all YAML files used by the application-manager. The first document representing a file-format header and the second document being the actual configuration data:

%YAML 1.1
---
formatType: amps-configuration
formatVersion: 1
---
listenAddress: 'any:7070'
projectId: 'HELLO'

Command-line Output

This is an example output, starting the package-server on a console with ANSI color support:

$ appman-package-server --dd /tmp/ps-data
Qt ApplicationManager Package Server
> Data directory: /tmp/ps-data
> Project: PROJECT
> Verify developer signature on upload: no
> Add store signature on download: no
> Scanning .packages
 + adding   hello-world.red [all]
> HTTP server listening on: 127.0.0.1:8020

Glossary

TermMeaning
idA unique identifier for a package. This is usually a reverse domain name, but can be anything as long as it is unique. See the manifest definition for more information.
architectureTo facilitate a mixed development environment (e.g. Windows and macOS desktops, Linux/ARM targets), the same packages can be built for multiple architectures and the package-server is able to distribute the matching one to the requesting client. The architecture is a string that identifies the binary architecture of a package. It is a token that shouldn't be parsed or interpreted by user code. On the client side, the current architecture can be retrieved via PackageManager::architecture. On the server side, the package-server parses the contents of any uploaded package to determine the architecture, based on any binary or shared library found within. If no such file is found, the architecture defaults to all (platform-independent).
hardware-idThe installer part of the application manager needs a unique device ID, if you want to deliver application packages that are bound to a specific device unit from an app-store. The use case here is to prevent customers from buying apps once and then sharing them with others for free. The support for this feature in the package-server isn't about security, but to make it easy for developers who need to test this mechanism. See the hardware id documentation in the Installation chapter for more information.

Usage

The available packages can be maintained both via file-system operations and via an HTTP REST API. The package-server creates a .packages directory inside the data directory. This directory contains the actual packages and is managed by the package-server. You can remove packages from this directory, but you have to restart the server if you do.

File-System Operations

The package-server creates two special directories inside the data directory: upload and remove:

  • Any valid package file placed into the upload directory will be parsed and - after verification - moved and renamed to the internal .packages directory.
  • Any file that is created in the remove directory, has its filename matched against all package ids or the combination of package id and architecture. All matching packages are then removed from the internal .packages directory. Usually you would use the touch command to create these filenames.

Examples

# Upload a package:
$ cp app.ampkg <datadir>/upload/

# Remove the 'hello.world' packages for all architectures:
$ touch <datadir>/remove/hello.world

# Remove the 'hello.world' package for Windows 64bit only:
$ touch <datadir>/remove/hello.world_windows_x86_64

HTTP REST Interface

The package-server provides an HTTP REST API to both upload and download packages. As this is strictly a development tool only, there is no support for authentication or authorization!

The following table lists the available API endpoints:


/hello (GET or POST)

This should always be the first call to the server, as this verifies that the project-id matches between client and server. Technically there is no need to call this endpoint to setup a session, but it is recommended to do so, to guarantee project compatibility.

Arguments
project-idrequired<project-id>
Result

The server will respond with HTTP code 200 and a JSON object containing the following fields:

statusEither ok or incompatible-project-id.

/package/list (GET or POST)

Returns a list of available packages. This list can additionally be filtered via the optional category, filter and architecture parameters.

Arguments
categoryoptionalOnly return packages that are in the given category.
filteroptionalOnly return packages whose names contain the given string.
architectureoptionalOnly return packages that are compatible with the given architecture.
Result

The server will respond with HTTP code 200 (ok) and a JSON array of objects, each containing the following fields:

idThe id of the package.
architectureThe architecture of the package.
namesAll the names of the package as an object (see the manifest definition for more details).
descriptionsAll the descriptions of the package as an object (see the manifest definition for more details).
versionThe version string of the package.
categoriesA list of category names of the package.
iconUrlA relative URL on this server to the icon file of the package.

/package/icon (GET or POST)

Returns the icon of a package as a standard PNG image file download.

Arguments
idrequiredSpecify the id of the package to get the icon for.
architectureoptionalSpecify the architecture of the requested package. If omitted, the server will use the platform independent (all) package, if available.
Result

There are multiple possible scenarios:

404 (not found)No package can be found for id or the package does not contain an icon.
200 (ok)A matching package containing an icon was found and the download started. The icon is sent with the mime-type set as image/png.

/package/download (GET or POST)

Request a download of a package identified by id. The package is store-signed, if the server is configured to do. If the request parameters include a hardware-id, it will be incorporated into the store signature.

Arguments
idrequiredSpecify the id of the package to download.
architectureoptionalSpecify the architecture of the package to download. If omitted, the server will use the platform independent (all) package, if available.
hardware-idoptionalThe hardware id of the device that is downloading the package. If specified, the package's store signature will include this token.
Result

There are multiple possible scenarios:

404 (not found)No package can be found for id.
500 (internal error)The package-server was instructed to store-sign the download package, but the signing failed. The detailed error message is printed on the server's console.
200 (ok)A matching package was found, it was store-signed (if configured) and the download started. The package is sent with the mime-type set as application/octet-stream.

/package/upload (PUT)

Upload a package to the server. The server will parse and verify the package. If the package is valid, it will be added to server's list of available packages. If something failed, the server will respond with an error message.

Result

The server will respond with HTTP code 200 and a JSON object containing the following fields:

statusok, if the package was uploaded successfully, fail otherwise.
resultOne of added (a new package was uploaded), updated (an existing package was updated) or no changes (the exact same package had already been uploaded before).
idThe id of the uploaded package.
architectureThe architecture of the uploaded package.
messageIf the upload failed, this field contains a human readable error message.

/package/remove (POST)

Sends a package removal request to the server. The server will remove all packages that match the given id and architecture. If no architecture is specified, all architectures for the given package are removed.

Arguments
idrequiredSpecify the id of the package to remove.
architectureoptionalIf specified, only remove the package for the given architecture.
Result

The server will respond with HTTP code 200 and a JSON object containing the following fields:

statusok if at least one package was removed, fail otherwise.
removedThe number of removed packages (as integer).

/category/list (GET or POST)

Returns all category names that are defined by all the currently available packages on the server.

Result

The server will respond with HTTP code 200 and a JSON array of category names. If there are no packages available, or none of them have categories, the array will be empty.


Examples

# Send a hello request:
$ curl -s 'http://localhost:8020/hello?project-id=MYPROJID' | json_pp

# Upload a package:
$ curl -s -X PUT --data-binary "@hello-world.ampkg" 'http://localhost:8020/package/upload' | json_pp

# Remove a package:
$ curl -s -X POST 'http://localhost:8020/package/remove?id=hello.world&architecture=all' | json_pp

# Download a package for Linux/x86-64, bound to a specific hardware id:
$ curl -s -X POST 'http://localhost:8020/package/download?id=hello.world&architecture=linux_x86_64&hardware-id=08154711'

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.