License
Your license is a signed token, verified locally. There is no license server, no activation call, and nothing to reach on the internet.
Your installation ID
A production license is bound to your installation's logical ID — never to a machine, a MAC address or a hostname, because pods and hosts are replaceable and your license should not care. The ID is generated on first start and stored in your database. An evaluation license is usually not bound, and can be issued before you install.
You see the ID when you create the first administrator — bootstrap-superadmin prints Installation ID: <uuid> — and in the backend log at every start:
docker compose logs backend | grep -i installation # Compose
kubectl -n <namespace> logs deploy/<release>-backend | grep -i installation # Helm
Send it to Lutra Labs with your order reference, and tell us the release version you run. You receive the license token back.
Install it
The file is the simplest way:
| Package | How |
|---|---|
| Compose | Put the token in secrets/lutraid_license, then docker compose restart backend |
| Helm | Create a Secret holding the token and set license.existingSecret, then kubectl rollout restart deploy/<release>-backend |
Or install it through the API, with no restart. Sign in as an administrator first; the session is kept in cookies.txt:
BASE=https://id.example.gov.si # your PUBLIC_BASE_URL
printf 'Admin email: '; read -r EMAIL
printf 'Password: '; stty -echo; read -r PASSWORD; stty echo; echo
printf '{"email":"%s","password":"%s"}' "$EMAIL" "$PASSWORD" \
| curl -fsS -c cookies.txt -H "Origin: $BASE" -H 'content-type: application/json' \
--data @- "$BASE/api/auth/sign-in/email" >/dev/null
printf '{"token":"%s"}' "$(tr -d '[:space:]' < license.lic)" \
| curl -sS -b cookies.txt -H "Origin: $BASE" -H 'content-type: application/json' \
--data @- "$BASE/v1/admin/license"
A token that cannot be installed is refused with 422 and the exact reason, and nothing is stored:
| Reason | Meaning |
|---|---|
malformed | Not a LutraID license token — usually a truncated copy or paste |
bad-signature | The token was altered after it was signed |
unknown-kid | Signed with a key this release does not know. See Version compatibility |
wrong-audience | Not a self-hosted license |
not-yet-valid | Its validity starts in the future. Check the host clock |
binding-mismatch | Bound to a different installation ID |
An expired token is not refused: it installs, and the installation follows the expiry ladder below.
Check the state any time — this works before any license is installed too:
curl -sS -b cookies.txt -H "Origin: $BASE" "$BASE/v1/admin/license"
Renewal is the same operation. Nothing is deleted; licenses are append-only and the newest valid one wins.
Secrets are read once, at process start. Installing a license by file means restarting the backend; the API does not.
What expiry does
Warn, then degrade. Never stop. An identity platform that stops issuing credentials at midnight because a purchase order took three weeks is not something we are willing to ship.
| State | Behaviour |
|---|---|
| Valid | Everything normal |
| Warning — approaching expiry | Banner and a response header. Nothing degrades |
| Grace — past expiry, within the grace period | Non-dismissible banner. Nothing degrades |
| Expired | Configuration changes refused. No new issuers, verifiers, organisations, API keys or profiles. Enterprise feature configuration stops |
| Missing | Runs normally for a 30-day onboarding window from first start, then behaves as expired. A missing or bad license file never stops a deployment from booting |
These never stop, in any state
Credential issuance. Credential verification. Status-list publication. .well-known metadata. Key operations. Revocation. Existing-user login. Existing SSO login — an expired license will never lock an administrator out of their own identity provider. Data export.
You can always get your data out, whatever the license says.
Version compatibility
A license only verifies on releases that ship its signing key. If you are pinned to an older release and receive a license signed with a newer key, it is refused with unknown-kid.
Tell us your deployed version when you request a license or a renewal and this never comes up.