Skip to content

Hub enrollment#

Every Lens Agents installation enrolls with Lens Hub once, over outbound HTTPS. Until it has, the installation serves its setup screen, sign-in, enrollment status, and /health. Every other REST and MCP call, and every sandbox ingress host, gets 503 with fleet_enrollment_required, and no sandbox starts.

Once enrolled, the installation runs on its own. Agent work continues through Lens Hub outages, including across restarts. Only a revocation in Lens Hub stops it. Lens Hub never connects in to the installation.


Connect from the setup screen#

This is the default. The chart generates a setup code into the <release>-hub-setup Secret and keeps it across upgrades. Only people who can read the release's Secrets have it.

  1. Read the code:

    kubectl -n <namespace> get secret lens-agents-hub-setup \
      -o jsonpath='{.data.setupCode}' | base64 -d
    

    The install notes and the setup screen print the same command for your release.

  2. Open config.publicUrl in a browser. The Connect to Lens Hub screen asks for the Setup code.

  3. Choose Continue to Lens Hub, sign in there, and choose who owns the installation in Lens Hub.
  4. Lens Hub sends you back, the installation enrolls, and you sign in.

The setup code never leaves the installation, and the token Lens Hub issues for it never reaches the browser. A wrong code is answered after a second.

GitOps

Argo CD, helm template, and other tools that render without cluster access generate a new code on every render. Put your own code, at least 16 characters, in a Secret and set hub.setup.existingSecret. Set hub.setup.enabled=false to allow only the Helm values below.


Connect with Helm values#

Register the installation in Lens Hub yourself, as a fleet of kind platform: POST /api/fleets with a Lens Hub token that manages fleets, and {"namespace": "...", "name": "...", "kind": "platform", "publicUrl": "https://agents.example.com"}. publicUrl is config.publicUrl; Lens ID sign-in needs it. Lens Hub answers with the fleet ID and a Fleet Access Token that it shows only once.

Request enrollment with PUT /api/fleets/<fleet-id>/enrollment and {"mode": "create", "authMode": "lens-id"}, or "customer-sso" with your own identity provider. The installation completes enrollment itself when it starts.

Store the Fleet Access Token in a Secret, under accessToken, and set the values:

kubectl create secret generic lens-agents-hub \
  --from-literal=accessToken="<fleet-access-token>"

helm upgrade lens-agents oci://ghcr.io/lensapp/lens-agents \
  --reset-then-reuse-values \
  --set hub.fleetId=<fleet-id> \
  --set hub.accessTokenSecret.name=lens-agents-hub

Use a Fleet Access Token, never a personal Lens Hub token. These values win over an installation connected from the setup screen, and hide that path.

config.publicUrl must be the address you registered in Lens Hub. Enrollment checks that the two match.


Check enrollment#

curl -fsS https://agents.example.com/v1/installation | jq '{enrolled, connection}'
{
  "enrolled": true,
  "connection": "connected"
}

connection is connecting, connected, or unavailable. The endpoint needs no authentication — it is what the setup screen itself polls.

nexusctl installation status --server https://agents.example.com

An enrolled installation that cannot reach Lens Hub keeps working and shows a connection warning in the admin UI. The platform logs each failed heartbeat with its cause.


What enrollment sends#

Call When Carries
Setup exchange Once, from the setup screen only The one-time code Lens Hub gave the browser. Lens Hub answers with the fleet ID and the Fleet Access Token.
Enrollment Once The Fleet Access Token. Lens Hub answers with the fleet ID, owner, public URL, and sign-in mode, which the installation checks against its own.
Heartbeat Every 60 seconds The platform version.

Nothing about your agents, sandboxes, policies, credentials, or audit trail is part of any of these calls. No licence is issued or refreshed.

With the default Lens ID sign-in, Lens Hub has Lens Cloud provision the installation's own OAuth client when it enrolls. People then sign in through that client, unless oidc.clientId names another. With your own identity provider, set oidc.useExternalProvider=true; the installation still enrolls once. See SSO.

Separately, a default install emits product telemetry. Data sovereignty has the complete picture of what leaves your cluster.


Values#

Value Default Purpose
hub.url https://hub.lns.run The Lens Hub the installation enrolls with.
hub.publicUrl (empty) Where people open Lens Hub, when it differs from hub.url.
hub.fleetId (empty) The installation's fleet ID in Lens Hub. Empty offers the setup screen.
hub.accessTokenSecret.name (empty) Secret holding the Fleet Access Token.
hub.accessTokenSecret.key accessToken Key in that Secret.
hub.setup.enabled true Offer the setup screen while hub.fleetId is empty.
hub.setup.existingSecret (empty) Your own Secret holding the setup code. Empty generates <release>-hub-setup.
hub.setup.key setupCode Key of the setup code in that Secret.
oidc.useExternalProvider false true for sign-in through your own identity provider.

Upgrading from Lens Cloud activation#

Before 0.32.0 an installation activated once against Lens Cloud. On the upgrade to 0.32.0 it forgets that activation: its installation ID, Lens Cloud sign-in client, and licence are cleared. Users, organizations, projects, and data stay.

It then enrolls like a new installation, either way above, and everyone signs in once more. Agent work waits until it has enrolled, so plan the upgrade for a time you can complete enrollment.

Upgrade with --reset-then-reuse-values, as the commands on this site do. --reuse-values keeps the old chart's defaults and fails on the new hub values. Remove activation.apiUrl and activation.bootstrapClientId from your values; they are no longer read.

Air-gapped installs and deployments that must never reach Lens Hub are configured per environment. Talk to us.