Install Tenon Licensing Server
The Tenon licensing server is deployed via a Helm chart. This guide walks through the full two-phase provisioning flow: the chart installs and generates the in-enclave keypair, then you have Tangram Flex KMS-sign a delegation over that public key and apply it as a Kubernetes Secret.
For a full list of configurable chart values see the Tenon Licensing Server Chart Values.
Prerequisites
Infrastructure
The chart is compatible with any Kubernetes cluster meeting the following requirements:
- Kubernetes v1.23 or above
- A container runtime capable of running
linux/amd64images - Persistent volume support (for the Valkey seat-state store and the
activitydactivity store)
For a minimal single-node deployment (dev/test):
- 2 vCPU, 4 GB RAM, 20 GB storage
For a production deployment with replicaCount: 2 for licensed:
- 4 vCPU, 8 GB RAM, 50 GB storage
Tools
- Helm v3 —
helm version - kubectl —
kubectl version - Access to the Tangram Flex GitLab container registry (
registry-gitlab.tangramflex.tech)
Credentials and materials
Before you begin, you will need:
- Registry credentials — provided by Tangram Flex for pulling the
licensedanddashboardimages - Delegation JSON + root public key — provided by Tangram Flex after Phase 1 (see Phase 2 below)
Installation
The deployment is intentionally two-phase to keep the in-enclave delegate private key inside your cluster at all times:
- Phase 1 —
helm install: deploys all components and runs thekeygenhook, which generates the delegate keypair inside your cluster.licensedpods wait for the trust Secret. - Phase 2 — apply trust: you extract the delegate public key, send it to Tangram Flex, receive
a KMS-signed
delegation.jsonandroot-pub.pem, and apply them as a Secret.licensedstarts automatically.
Phase 1 — Helm install
1. Prepare your session
export TENON_NS="tenon-licensing"
export TENON_RELEASE="flex-license"
export TENON_VALUES_FILE="tenon_licensing_values.yaml"2. Add the Helm chart repository
helm repo add flex-licensing \
"https://gitlab.tangramflex.tech/api/v4/projects/<PROJECT_ID>/packages/helm/stable"
helm repo updateReplace <PROJECT_ID> with the GitLab project ID provided by Tangram Flex. You can also obtain it
from the project's GitLab page.
3. Create the namespace
kubectl create ns "$TENON_NS"4. Create the registry pull Secret
The licensed and dashboard images are hosted in the Tangram Flex GitLab container registry. Create
a Kubernetes image pull Secret with the credentials provided by Tangram Flex:
export REGISTRY_SECRET_NAME="flex-licensing-registry"
export REGISTRY_SERVER="registry-gitlab.tangramflex.tech"
export REGISTRY_USERNAME="<usernameProvidedByTangramFlex>"
export REGISTRY_PASSWORD="<passwordProvidedByTangramFlex>"
kubectl create secret docker-registry "$REGISTRY_SECRET_NAME" \
--docker-server="$REGISTRY_SERVER" \
--docker-username="$REGISTRY_USERNAME" \
--docker-password="$REGISTRY_PASSWORD" \
--namespace="$TENON_NS"Reference the Secret in your values override file:
global:
imagePullSecrets:
- flex-licensing-registry5. Create a values override file
Create tenon_licensing_values.yaml. At minimum, reference your pull Secret and set the image tags
provided by Tangram Flex:
global:
imagePullSecrets:
- flex-licensing-registry # matches the Secret created above
licensed:
image:
tag: "0.3.0" # pinned to the version provided by Tangram Flex
dashboard:
image:
tag: "0.3.0" # pinned to the version provided by Tangram Flex6. Install the chart
helm upgrade "$TENON_RELEASE" -n "$TENON_NS" flex-licensing/licensing-self-host \
--install \
-f "$TENON_VALUES_FILE"--wait for initial installsBy default (trust.waitForTrust: true), the trust Secret volume is required, so licensed pods
stay in ContainerCreating until Phase 2. Using --wait on a first install causes Helm to time out
and mark the release as failed. Omit --wait for Phase 1. You can use it safely on upgrades after
trust is already in place.
After install, confirm the keygen hook completed and the licensed pods are waiting:
kubectl -n "$TENON_NS" get pods -l app.kubernetes.io/instance="$TENON_RELEASE"
# licensed pods show ContainerCreating — expected until Phase 2The keygen hook has already run and generated the in-enclave delegate keypair. Extract the public key:
kubectl -n "$TENON_NS" get configmap licensed-delegate-pub \
-o jsonpath='{.data.delegate-pub\.pem}' > delegate-pub.pemSend delegate-pub.pem to your Tangram Flex contact, along with:
- Your organization name
- The number of floating seats needed (
max_seats) - The number of node-locked seats (if any)
- The machine fingerprints for node-locked seats (see below)
- The desired validity period (in days)
For node-locked seats, each machine must provide its fingerprint. Run this on each machine that needs a node-locked seat:
tenon license fingerprint
# → 9118df5be61bf9f3f4fe49ffa94037d4Send the hex strings to Tangram Flex and they will be baked into the signed delegation.
Phase 2 — Apply trust material
Once Tangram Flex returns delegation.json and root-pub.pem, apply them as a Kubernetes Secret:
kubectl -n "$TENON_NS" create secret generic licensed-trust \
--from-file=delegation.json \
--from-file=root-pub.pemThe licensed pods detect the Secret and start automatically (no restart required). Verify:
kubectl -n "$TENON_NS" rollout status deploy/"${TENON_RELEASE}-licensing-self-host-licensed"
kubectl -n "$TENON_NS" get pods -l app.kubernetes.io/instance="$TENON_RELEASE"Post-install
Retrieve generated credentials
The chart auto-generates an admin token (for reading activity data and accessing holder names) and, if dashboard auth is enabled, a dashboard admin password. Retrieve them after install:
# Admin token (for CLI and activityd access)
kubectl -n "$TENON_NS" get secret "${TENON_RELEASE}-licensing-self-host-admin-token" \
-o jsonpath='{.data.token}' | base64 -d; echo
# Dashboard admin password (if dashboard.auth.enabled=true)
kubectl -n "$TENON_NS" get secret "${TENON_RELEASE}-licensing-self-host-dashboard-auth" \
-o jsonpath='{.data.admin-password}' | base64 -d; echoAccess the operator dashboard
The dashboard is a ClusterIP service by default. Port-forward to reach it locally:
kubectl -n "$TENON_NS" port-forward \
svc/"${TENON_RELEASE}-licensing-self-host-dashboard" 8081:8080Then open http://localhost:8081 in your browser.
To expose the dashboard via a standard Kubernetes Ingress, add the following to your values override file:
dashboard:
ingress:
enabled: true
className: "nginx" # your ingress controller's class
annotations:
cert-manager.io/cluster-issuer: my-issuer # optional: cert-manager annotation
hosts:
- host: dashboard.licensing.example.mil
paths:
- path: /
pathType: Prefix
tls:
- secretName: dashboard-tls
hosts:
- dashboard.licensing.example.mil
# Required when networkPolicy.enabled=true (the default):
# allow the ingress controller namespace to reach the dashboard pod.
networkPolicy:
dashboardFromNamespaces:
- ingress-nginx # set this to your ingress controller's namespaceThe chart deploys a default-deny NetworkPolicy. When you expose the dashboard via ingress, the
ingress controller pods are in a different namespace and are blocked by default, producing a 502
from the ingress controller. Set networkPolicy.dashboardFromNamespaces to the namespace where your
ingress controller runs (e.g. ingress-nginx, nginx, traefik).
The dashboard has no built-in TLS or authentication for incoming connections — viewing is always read-only without sign-in. Mutating actions (Release all seats, Restart) require the admin password. For production, terminate TLS and add network-level access controls at the ingress or service-mesh layer before exposing the dashboard outside your cluster.
Renewal
Before the delegation's valid_until date lapses, request a renewed delegation.json from Tangram
Flex (same in-enclave public key, new expiration date, possibly updated seat count). Swap the Secret
and roll the deployment — no image rebuild or API connectivity required:
kubectl -n "$TENON_NS" delete secret licensed-trust
kubectl -n "$TENON_NS" create secret generic licensed-trust \
--from-file=delegation.json \
--from-file=root-pub.pem
kubectl -n "$TENON_NS" rollout restart \
deploy/"${TENON_RELEASE}-licensing-self-host-licensed"Upgrade
To upgrade the chart to a new version:
helm repo update
helm upgrade "$TENON_RELEASE" -n "$TENON_NS" flex-licensing/licensing-self-host \
--install \
--wait --timeout 5m \
-f "$TENON_VALUES_FILE"The keygen hook is idempotent — it will not rotate the existing in-enclave delegate keypair on upgrade.
Air-gapped (registry mirror)
If your enclave cannot reach registry-gitlab.tangramflex.tech, mirror the images to your internal
registry and set the override in your values file:
global:
imageRegistry: "your.internal.registry.mil"This replaces the default registry prefix for all images in the chart (including the bundled Valkey image).
External Valkey
The chart includes a bundled single-node Valkey instance suitable for most deployments. To use an external or managed Redis-compatible instance instead:
valkey:
enabled: false
externalAddr: "valkey.example.mil:6379"
existingPasswordSecret: "valkey-auth" # optional: Secret with key 'password'
existingPasswordSecretKey: password