Skip to content

The overlay

Everything that is specific to your hub lives in one directory, the overlay: host names, identity provider, secrets, package stack, limits, the content your coding agents get. The platform itself never contains any of it, so an upgrade replaces the platform and leaves the overlay alone, and the same overlay can be applied to a single host or to a cluster.

Where it is:

  • On a single host, /opt/hub/overlay/<name>, written by install.sh install from the options you gave, or synced from us if we maintain your configuration. Changes are applied with bash install.sh upgrade <the version you run>.
  • On the jump host of your own cluster, the same path, written by install.sh generate --cluster or synced from us. Changes are applied the same way, from the jump host.

Two files carry most of it: hub.env, typed settings with a comment at every key (the reference lists them all), and secrets.env, which exists only on your host and is never part of anything we exchange.

Files

File What it holds
hub.env Every typed setting: features, registry, package snapshots, sizes, limits. Reference.
secrets.env Passwords and keys, one per line (see Secrets). Gitignored, kept out of every artifact.
kustomization.yaml What the hub consists of: the IDE tiles in landing-page order, the deploy stack, which Redis and which registry, which auth patch.
namespace.yaml The Kubernetes namespace of this hub. One hub per namespace; several can share a cluster.
patches/shinyproxy.yaml, patches/shinyproxy-apps.yaml The host names of the hub and of the apps, and the ingress class.
patches/auth-*.yaml The identity provider (Authentication). kustomization.yaml names the one in use.
patches/quota.yaml What the hub may use of the cluster: CPU, memory, number of pods.
patches/registry.yaml The registry's Ingress: class and TLS.
patches/user-egress.yaml Addresses that user code must not reach: the node, other namespaces.
r-packages.txt, python-packages.txt The package stack, one package per line, installed into the IDE images and the base images apps are built on.
apt-packages.txt, setup.sh System packages and anything else the R images need; run at image build.
certs/*.crt CA certificates your network needs: trusted in every image and by the platform. On your host only.
templates/<name>/ Project templates for hub::new_app() and RStudio's New Project dialog, replacing or adding to the platform's.
claude-code/ Your additions for the coding agents: CLAUDE.md, rules/, skills/, plugins.txt.
monitoring/, backup/ Dashboards and alerts; backups to an S3 bucket. Optional, applied when the directory exists.
generated/ Written by the scripts at every apply. Never edited.

What a change costs:

  • A change to hub.env, the patches or the agent content is an apply. Sessions and apps keep running; a change that rolls the platform's pods takes effect for new sessions.
  • A change to the package stack, apt-packages.txt, setup.sh, certs/ or templates/ is an image build, minutes, and new sessions start on the new images. On a host, upgrade to the version you run does both.
  • A change to secrets.env alone rolls nothing: after the apply, delete the ShinyProxy pods (kubectl delete pod -n <namespace> -l app=shinyproxy). Sessions and apps survive, their state is in Redis.

Authentication

The hub authenticates through ShinyProxy, so any backend it supports works. The overlay ships a patch per kind; kustomization.yaml lists the one in use, and each patch says what to register at the provider.

auth-simple.yaml
Built-in users, for bringing a hub up and for the smoke test. The admin's password is simple-admin-password in secrets.env.
auth-oidc.yaml
OpenID Connect: Keycloak, Microsoft Entra ID, Okta, Auth0, Cognito. Register a client with the redirect URI https://<fqdn>/login/oauth2/code/shinyproxy and the sign-out URL https://<fqdn> (exactly, no trailing slash) for both host names, the hub's and the apps'. The patch names the claims: username-attribute is the user id (email for Keycloak, preferred_username or oid for Entra ID), roles-claim the groups (groups, with a group mapper in Keycloak; cognito:groups for Cognito). The client secret is oidc-client-secret in secrets.env. The installer's --auth oidc writes this patch.
auth-ldap.yaml
LDAP and Active Directory. The user id is the login name (uid or sAMAccountName); groups come from the group search and are matched by name.
auth-saml.yaml
SAML 2.0: ADFS, Entra ID, Okta, Shibboleth. Register the service provider for both host names with the ACS URL https://<fqdn>/saml/SSO. name-attribute is the user id, roles-attribute carries the groups.

Three things must hold whatever the backend:

  1. The user id is permanent. It names the user's home volume, owns their apps and identifies them to the deploy service. Changing the backend or the id attribute later orphans every existing home directory.
  2. Groups arrive as groups. admin-groups in the auth patch and access-groups in an app's hub.yaml refer to the names the provider sends.
  3. Apps read the user from the request, through hub::user() and hub::groups(), never from environment variables, which belong to the container and not to the person using it.

What the hub consists of

components: in kustomization.yaml lists the IDE tiles in the order of the landing page (vscode, jupyterlab, rstudio), the deploy stack (the apps page, the deploy service), one Redis (single for one pod, sentinel for three that survive a node, external for one you run) and one registry (internal, the platform's own behind the Ingress; external for Harbor or any v2 registry; ecr). Drop what you do not use: the image build builds only what the components need.

features= in hub.env says what the hub is for, r, python and deploy, and selects the rules, skills, plugins and instructions the coding agents get. Keep it in step with the components. ai=false makes a hub without any AI: IDE images without Claude Code and Posit Assistant, and none of the agent content.

Packages and images

The R stack is r-packages.txt, pinned to the snapshot date r_snapshot; the Python stack python-packages.txt with py_snapshot. Both are installed into the IDE images and into the base images that apps are built on, so an app that uses only the stack builds in seconds. deploy_mode decides whether an app may bring its own lock file on top of that.

A private mirror goes in as cran= (a Posit Package Manager URL keeps binaries), pypi_index= and sysreqs_api=. The CA certificates your network needs go into certs/; the image build trusts them everywhere, including in Node, which Claude Code runs on. System packages the R images need go into apt-packages.txt, anything else into setup.sh.

All of this is the customer layer: the platform images are the same for every customer and come from our registry, and a generic build puts your files on top, inside your network if need be. Adding a certificate rebuilds that layer only, in minutes.

Secrets

secrets.env, one key per line, no comments after a value:

Key Used for
redis-password The hub's Redis, where sessions live. Generated by the installer.
simple-admin-password The built-in admin with auth-simple.yaml.
oidc-client-secret The OpenID Connect client.
registry-password The hub's registry, user hub: every pull of a built image and every push of a build.
smtp-password The mail account that sends alerts and the "Report issue" mail.
object-store-access-key, object-store-secret-key The S3 bucket for backups and the stateless registry.

The installer generates what it can and asks for the rest. Keep a copy of the file in your password manager; it exists nowhere else, and a diagnose archive never contains it.