On-premises Key Service API deployment guide
This guide provides comprehensive installation instructions for Axinom DRM Key Service On-Premises API.
About Key Service On-Premises APIβ
Axinom Key Service On-Premises API is a light-weight version of Axinom Key Service that contains the essential functionality for generating content keys and DRM signaling data used by encoders for protected video encoding. On-Premises API is intended for on-premises installations with minimal dependencies.
The user-facing API is designed to be identical or very close to the full Axinom cloud-hosted APIs for seamless transition.
Supported featuresβ
- SPEKE v1 (POST api/speke)
- API documentation (GET api/help)
Additional functionality will be added on-demand.
Before you beginβ
Make sure to meet the following pre-requisites:
- An x64 Linux host with Docker installed
- Access to Linux terminal
- Axinom DRM Tenant and On-Premises API configuration (available from Axinom Portal)
- On-Premises API DataEncryptionKey, provided as a separate secure deliverable.
- Credentials for Axinom Harbor Docker Registry (received from Axinom)
Examplesβ
Configuration package (ZIP) example:
- Name: KeyService_CompactApi_Production_Configuration_YourCompany_2026-01-01.zip
- Contents: appsettings.json
DataEncryptionKey example:
- Name: (SECRET!)_KeyService_CompactApi_Production_DataEncryptionKey_YourCompany_2026-01-01.txt
- Contents: "AAAAAAAAAAAAAAAAAAAAAA=="
Conventionsβ
This document has the following conventions:
-
Commands and their output are represented like
echo "Text" -
Commands are modeled for the Linux terminal. However, they have been chosen in a way that all of them work in Windows PowerShell.
-
All
dockercommands require administrative privileges. Therefore, make sure add your user to Docker group or applysudoon Linux. -
Identifiers and paths are represented like
this, and placeholders like{this}. -
Placeholders are explained the first time they are encountered with no further explanations in the rest of the document.
Pull Docker Imageβ
All On-Premises API Docker images are published to Axinomβs "Harbor" private Docker registry
harbor.axinom.com.
To log in to the registry (use the Harbor credentials you received from Axinom):
docker login harbor.axinom.com -u {username} -p {password}
...
Login Succeeded
To pull the Docker image (use the name of the selected image):
docker pull harbor.axinom.com/axinom-key-service-compact-api/app
...
Status: Downloaded newer image for harbor.axinom.com/axinom-key-service-compact-api/app:latest
Note: On-Premises API images are currently only available for x64 Linux.
Create Folders for Configsβ
On-Premises API expects a folder from which it can read the configuration files extracted from the configuration package provided by Axinom.
-
Create the folders:
mkdir ~/axinom-key-service-compact-api/config -
Extract and copy the contents of the Configuration Package into the Config folder.
- To verify the contents of the folder run
ls ~/{axinom-key-service-compact-api}/config
- To verify the contents of the folder run
Run the Containerβ
-
Decide which HTTP port the app will listen to on the host (further refered to as
{host_http_port}. The app inside the Docker image the will by default listen on 8080. -
Obtain the secret base64-encoded DataEncryptionKey provided to you by Axinom and provide it to the app during startup. Handle this key with care! With this key your secrets contained in the configuration package can be exposed. The app needs it to decrypt your configuration at runtime.
-
Run the docker container:
sudo docker run -d \
-p {host_http_port}:8080 \
-e AxinomDRM_App__DataEncryptionKey="AAAAAAAAAAAAAAAAAAAAAA==" \
-v ~/axinom-key-service-compact-api/config:/config \
registry.harbor.com/axinom-key-service-compact-api/app -
Verify that the app started up successfully and there are no errors in the Docker logs:
sudo docker logs {container_id}{container_id}- full or partial identifier of the previously started app container.Example:
docker logs 3bc85e55c
{"@timestamp":"2026-03-04T07:24:13.6028115Z","level":"INFO","details":"Starting Compact API v1.0.0 ...", "project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3549414Z","level":"INFO","details":"Now listening on: http://0.0.0.0:8080","project":"keyserver","api":"compact-api","production":"development","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3549414Z","level":"INFO","details":"Application started. Press Ctrl+C to shut down.","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3596054Z","level":"INFO","details":"Hosting environment: Production","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3596054Z","level":"INFO","details":"Content root path: \\app","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
Verify the app responds via HTTPβ
- In a web-browser on the client, navigate to the address where the app is hosted. For
example, if the host name is
compact-api.example.com, navigate tohttp://compact-api.example.com/api/ping. - Or locally on the host:
http://localhost:{host_http_port}/api/pingIf the container runs properly, the page should display:
OK
Verify the app responds to SPEKE requestβ
This tests an actual functional key service endpoint and verifies the app is configured correctly.
This comes with a few more pre-requisites:
- Set up HTTPS (see below sections). Or use HTTP only when testing locally, in order to not to expose authentication credentials and content key material.
curl -v http://localhost:8080/api/Speke \
--header 'Content-Type: application/xml' \
--header 'Authorization: Basic <base64(TENANT_ID_GUID:KEY_SERVICE_MANAGEMENT_KEY_GUID)>' \
--data '<?xml version="1.0"?><cpix:CPIX xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" id="test_ch1"><cpix:ContentKeyList><cpix:ContentKey kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/></cpix:ContentKeyList><cpix:DRMSystemList><cpix:DRMSystem systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2" kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/><cpix:DRMSystem systemId="9a04f079-9840-4286-ab92-e65be0885f95" kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/><cpix:DRMSystem systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/></cpix:DRMSystemList><cpix:ContentKeyPeriodList><cpix:ContentKeyPeriod id="keyPeriod_19d21813-874e-4804-8a56-1952722abeb5" index="213"/></cpix:ContentKeyPeriodList><cpix:ContentKeyUsageRuleList><cpix:ContentKeyUsageRule kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"><cpix:KeyPeriodFilter periodId="keyPeriod_19d21813-874e-4804-8a56-1952722abeb5"/></cpix:ContentKeyUsageRule></cpix:ContentKeyUsageRuleList></cpix:CPIX>'
Note:
- The Speke endpoint ignores the Content-Type header, but it's required for CURL to send the request properly (not as "form-urlencoded").
- See below for an example how to generate the authorization header from your credentials.
Secure your DataEncryptionKeyβ
As part of configuration deliverables, Axinom will provide you two sets of data:
-
A configuration package (ZIP) containing the app configuration, which contains encrypted sensitive data, such as key seeds and management keys.
-
A text DataEncryptionKey in base64 encoding. This will be used by the app at runtime to decrypt the sensitive data contained in the configuration package ZIP.
Do not store keep these two pieces of data together, because if both leak, your sensitive data will be exposed.
Provide the DataEncryptionKey to your app only during startup via secure means to limit its exposure. It is up to your system engineer to determine a sufficiently secure approach, fit for your hosting environment.
Set up HTTPSβ
Unless your client(s) (e.g. encoder) and the On-Premises API are in a private secure network, you need to set up a HTTPS proxy (e.g. NGINX or Apache, etc.) to secure the traffic between the app and the clients. Traffic must be secured due to the following reasons:
- The app uses HTTP Basic Authentication for authentication where fixed username (tenant ID) and password (Management Key) are used for authentication. These credentials are transmitted in the "Authorization" header and HTTPS is needed to secure them in transit.
- To secure content keys returned by the app.
NB! Supplying or configuring such a proxy is not part of Axinom On-Premises API deliverables. Consult your system engineer. Contact Axinom for advice, if needed.
To make HTTPS work you need to:
- Decide on the DNS name to call the On-Premises API (e.g. compact-api.example.com)
- Make sure the app is accessible in your environment using this name (e.g. via local DNS)
- Issue a TLS certificate for this name
- Configure the TLS credentials on your HTTPS proxy server.
- Make a test call using HTTPS from the client machine: https://compact-api.example.com:443/ping
Loggingβ
The application, by default, logs all information to the Docker console as structured JSON. It does not log to logfiles. Use a tool, such as FileBeat, to capture the logs from Docker output and forward them to where desired (e.g. to files or a logging service, such as Elastic).
There are three categories of logs, as determined by the JSON component attribute:
-
"Diagnostic" - includes startup info, startup errors, and general diagnostic information.
-
"ClientErrors" - stores details about user-errors, such as invalid requests or authentication errors. These are also returned to the client.
-
"ServerErrors" - stores details of internal server errors, when the app fails to process a request. These are not returned to clients due to security reasons. Contact Axinom if you notice any server errors.
Example startup logs:
{"@timestamp":"2026-03-04T07:24:13.6028115Z","level":"INFO","details":"Starting Compact API v1.0.0 ...", "project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3549414Z","level":"INFO","details":"Now listening on: http://0.0.0.0:8080","project":"keyserver","api":"compact-api","production":"development","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3549414Z","level":"INFO","details":"Application started. Press Ctrl+C to shut down.","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3596054Z","level":"INFO","details":"Hosting environment: Production","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
{"@timestamp":"2026-03-04T07:24:15.3596054Z","level":"INFO","details":"Content root path: \\app","project":"keyserver","api":"compact-api","environment":"production","component":"Diagnostics"}
All the logs have the following standard fields:
- "@timestamp" - time the log entry was generated
- "level" - the severity of the event (INFO, WARN, ERROR, FATAL)
- "details" - the log messages. In case of server errors, it also contains a detailed exception trace.
- "project" - the project name. For current app, it is "keyserver".
- "api" - the API name. For current app, it is "compact-api".
- "environment" - the environment name, e.g. "testing" or "production".
- "component" - the logger category: "Diagnostic", "ClientErrors" or "ServerErrors".
The "project", "api" and "environment" fields can be useful when capturing logs from multiple Axinom apps in order to later differentiate the source in a logging service.
Generating authorization headerβ
The authorization header is in the form:
Authorization: Basic base64(TENANT_ID_GUID:KEY_SERVICE_MANAGEMENT_KEY_GUID)
Provided the following credentials:
- Tenant ID: 50bde63e-1fe0-42e5-a0ac-e67a10bf5113
- Key Service Management Key: 188eb699-16d9-4c71-8afe-2c75973ab1b5
Header becomes:
Authorization: Basic base64(50bde63e-1fe0-42e5-a0ac-e67a10bf5113:188eb699-16d9-4c71-8afe-2c75973ab1b5)
Where:
Base64(50bde63e-1fe0-42e5-a0ac-e67a10bf5113:188eb699-16d9-4c71-8afe-2c75973ab1b5)
=> NTBiZGU2M2UtMWZlMC00MmU1LWEwYWMtZTY3YTEwYmY1MTEzOjE4OGViNjk5LTE2ZDktNGM3MS04YWZlLTJjNzU5NzNhYjFiNQ==
And the final result is:
Authorization: Basic NTBiZGU2M2UtMWZlMC00MmU1LWEwYWMtZTY3YTEwYmY1MTEzOjE4OGViNjk5LTE2ZDktNGM3MS04YWZlLTJjNzU5NzNhYjFiNQ==