Skip to content
Docs · Server license and credentials

Kubyl Server

Server license and credentials

Activate Kubyl Server, the license states and grace period, moving a license, and the encrypted credential store.

Activation

A subscription is one server. You attach it to your install with an activation code from your customer portal at kubyl.dev.

  • Online (the default): in the console's License tab press A and type or paste the code, or use the web UI's License page, or the app's license screen. The server sends the licensing service the code and a signed request with its install id and its public keys (nothing else: no clusters, no devices, no usage) and gets a lease bound to this install. It then renews itself: the lease is valid until the end of your paid period (a year at most), after which the server asks again. Once a day while the lease is valid it picks up changes such as more devices or another plan.
  • At startup, for containers and Kubernetes: set KUBYL_SERVER_ACTIVATION_CODE (or --activation-code; prefer the variable, arguments show up in ps). A server without a lease then activates itself, retrying while the licensing service is unreachable. A server that has a lease ignores it, and a refused code (unknown, in use by another install) is logged once. A code that isn't well formed stops the server at startup.
  • Air-gapped ("license": {"online": false} in settings.json): the server never calls out. Enter the install id shown on the License tab in the portal and you get the lease by email for each period. Install it with F in the console (paste the text or give the file's path), on the web UI's License page, or from Kubyl Mobile (open the file with “Open in Kubyl”, or paste it on the license screen). Install each new lease before the old one runs out.

License states

Without a lease the server can pair devices and show its license screens, and nothing else. The credentials, the paired devices and the cluster list are encrypted with keys that only a lease for this install delivers, so an unlicensed server doesn't connect to any cluster.

StateWhenWhat works
UnlicensedNo license yet.Pairing, devices, the license screens.
LicensedA valid lease.Everything, up to your plan's device limit: Personal 3 paired devices, Team unlimited.
GraceThe lease ran out less than 3 days ago and the renewal is pending or failing.Everything. A banner says why.
LockedGrace is over, the subscription ended, the install was released, or the server's clock went back (clock_rollback: fix the date and time).Pairing and the license screens only. Open streams end.

A failed payment stays “pending” while the payment provider retries; the 3 days of grace are for that. A cancelled subscription locks the server when the paid period ends. A locked server keeps its devices and unlocks as soon as a valid file arrives. It asks hourly, or press N on the License tab.

Moving or changing the license

  • Move to a new server: press R on the License tab (or “Release this install” on the web UI's License page; the app's license screen follows). The server tells the licensing service, locks at once, forgets its keys (what it stored can't be read any more) and makes a new identity. The next activation code is shown once, on that screen.
  • After losing the config directory, release the binding in the customer portal instead.
  • Switch to a different license on the same install: remove the installed one first (X on the License tab, or the web UI), then activate or apply the new one.

Credential store

Cluster sign-ins (OIDC and OpenShift refresh tokens, Argo CD sessions) are kept in credentials.enc in the config directory, the paired devices in devices.enc and the cluster list in clusters.enc. They use ChaCha20-Poly1305 with 32-byte keys derived from the lease's install secret and the local key. The server's identity for the licensing service is in identity.enc (the local key alone).

The local key comes from KUBYL_SERVER_KEY (base64) or, without it, from credentials.key in the config directory. That file is made on first start with mode 0600, and the server refuses it if others can read it. Tokens, refresh tokens, pairing and activation codes and the admin token are never written to the log.

Something missing or wrong? Open an issue on GitHub.