Skip to content
Docs · Documentation

Kubyl Server

Credentials and sign-ins

The encrypted credential store, shared or per-device sign-ins on the Team plan, and exec plugins such as aws, gke and kubelogin.

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.

Shared or per-device sign-ins (Team plan)

By default every paired device uses the clusters as the same Kubernetes user: one connection per cluster, one set of tokens, one set of permissions. One device signs in, all devices are signed in. That suits a household or a small team with one service account.

A company usually wants the opposite: each person signs in with their own account and sees only what that account's RBAC allows. Switch the server to per-device sign-ins in settings.json. In the Helm chart, put the same JSON under settings: in your values.

settings.json
{
  "server": {
    "credentials": "per_device",
    "shared_contexts": ["ops-staging"]
  }
}

It needs the Team plan and a restart. On any other plan the server keeps running with shared sign-ins and says so in Server.Info (credential_mode, credential_mode_configured), the console and the web UI.

What changes in per_device mode

  • Each paired device has its own connections, tokens, watches, port-forwards, shell sessions and push notifications. A device signs in to a cluster itself (Clusters, Sign in); the server keeps that device's refresh token apart from every other device's. One device's sign-in, sessions and data are never visible to another. Revoking a device ends all of it and deletes its stored tokens.
  • A device that has not signed in never acts as someone else. Tokens written into the kubeconfig (an OIDC refresh-token, an oc login token) are ignored: the cluster shows “sign-in required” until the device signs in.
  • Which clusters a device can use stays your decision. The cluster list, the read-only and production rules and the display names come from your settings.json and kubeconfigs, the same for everyone. Devices cannot add clusters or kubeconfigs.
  • Clusters that are one identity for everybody are turned off. A cluster whose kubeconfig uses a static token, a client certificate, basic auth or an exec plugin (which runs as the server's user) has no personal sign-in. The app lists it greyed out with the reason, and every call that names it fails with FAILED_PRECONDITION. To keep such a cluster usable on purpose, list its context name under shared_contexts: it then works for every device with that one identity, as in shared mode. Clusters that sign in with OIDC (an auth-provider or kubelogin) or OpenShift OAuth are per device.
  • A user with a phone and a tablet signs in on each of them: the identity is the paired device.
  • Sign-ins that open a browser use fixed loopback ports on the server; two devices doing that at the same moment may collide. The device-code sign-in has no such limit.

Going back to shared (or leaving the Team plan) ends the devices' own connections; their tokens stay stored and work again when per_device returns.

Exec plugins and cluster access

The server runs the commands of your kubeconfigs' exec credential plugins (aws eks get-token, gke-gcloud-auth-plugin, kubelogin…) on the server, as the server's user. The container image contains none of them (it ships Helm, Node.js and the AI agents; see Updates and image contents): build your own image on top of it, or mount the binaries and put them on PATH.

Dockerfile
FROM harbor.tyrola.dev/kubyl/server:latest
COPY --chmod=755 aws-iam-authenticator /usr/local/bin/

Plugins that need a browser or a login you do once (az login, gcloud auth login) must have their state in a directory the server's user can read: mount it and set the plugin's home variable. OIDC logins with the device-code flow or the browser flow can also be done from the phone (Clusters, Sign in); the server keeps the refresh token.

Something missing or wrong? Open an issue on GitHub.