License rotation
Apply a renewed license bundle without downtime.
When your license is approaching expiry, your Scrydon account team will send a renewed bundle ({ "jwt": "…", "publicKey": "-----BEGIN PUBLIC KEY-----…" }). This runbook covers applying it.
No pod restart required. The license lives in the
platform_configrow of theauthdatabase, not in a Kubernetes Secret and not in an init container. Rotation happens in the platform UI and takes effect immediately —api-platform's Better-Auth license plugin reads the row on every request.
Before you start
- You have the renewed bundle (a JSON file shaped
{ "jwt": "…", "publicKey": "-----BEGIN PUBLIC KEY-----…" }). - An administrator account on the running platform.
Apply the renewed bundle
- Sign in to the platform as an admin and open Settings → License.
- Click Update license.
- Drop the renewed bundle JSON onto the upload area, or paste its contents into the textarea.
- Confirm the displayed claims (tier, CPU / RAM / VRAM, expiry). The platform verifies the JWT signature against the public key in the bundle before persisting it.
- Click Save.
The new license takes effect immediately on the next request — no pod restart, no Helm change. The previous bundle is overwritten in platform_config.
Tip: want to inspect the bundle before applying it? Use the in-browser License Checker — it decodes the JWT in your browser and shows the same claim summary as the UI step.
Verifying the new license is in effect
After saving, the Settings → License page should refresh with the new claims. From the CLI you can also call the authenticated license endpoint on api-platform:
# Inside the cluster — uses your existing platform session cookie:
curl -fsS https://app.yourdomain.com/api/auth/license/status \
-H "Cookie: $SCRYDON_SESSION" | jq '{tier, exp, cpu, ram, vram}'The exp field should match the renewed bundle's expiry.
What happens if a renewal is late
The license enforcement model is hot-loaded from the DB, so the failure mode is different from the old init-container model:
- The license has a built-in grace period (default 30 days past
exp) during which Scrydon continues to serve normally and shows a renewal banner in the UI. - After the grace period, license-enforced features (per your contract) start returning a 402-style response with a "Renew your license" message. Read access typically stays available; write paths gate.
- No pod is ever blocked from starting by a missing or expired license — license enforcement happens in-process at request time, not at boot.
Practical implication: a late renewal degrades the platform progressively after the grace period, but does not lock operators out of kubectl or out of rotating the bundle once it arrives.
Pre-seeding the license at install time
For brand-new installations the same bundle goes into the /setup wizard's first step. You can also pre-seed it via Helm values to skip the License step entirely:
# values.customer.yaml
auth:
secrets:
LICENSE: |
eyJhbGciOiJSUzI1NiIs... # bundle.jwt
LICENSE_PUBLIC_KEY: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFA... # bundle.publicKey
-----END PUBLIC KEY-----These values are read once on first install to seed the platform_config row; after that, the DB is the source of truth and the values entries can be removed at the next helm upgrade without affecting the platform.
Decoding a license
If you need to confirm what's in a bundle before applying it, use the in-browser License Checker. It runs locally — no network calls — and shows the tier, CPU / RAM / VRAM, customer identifier, and exp timestamp.
To decode the JWT manually:
JWT=$(jq -r .jwt license.bundle.json)
SEG=$(echo "$JWT" | cut -d. -f2 | tr '-_' '+/')
PAD=$(( (4 - ${#SEG} % 4) % 4 ))
PAYLOAD=$(printf '%s' "$SEG$(printf '=%.0s' $(seq 1 $PAD 2>/dev/null))" | openssl base64 -d -A)
echo "$PAYLOAD" | jq .Confirm:
exp— the expiry timestamp.sub— your customer identifier.scrydon.entitlements— your contracted resource limits.
Related
- Licensing — the full licensing model.
- Audit logging —
LICENSE_*events.