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 byinstall.sh installfrom the options you gave, or synced from us if we maintain your configuration. Changes are applied withbash install.sh upgrade <the version you run>. - On the jump host of your own cluster, the same path, written by
install.sh generate --clusteror 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/ortemplates/is an image build, minutes, and new sessions start on the new images. On a host,upgradeto the version you run does both. - A change to
secrets.envalone 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-passwordinsecrets.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/shinyproxyand the sign-out URLhttps://<fqdn>(exactly, no trailing slash) for both host names, the hub's and the apps'. The patch names the claims:username-attributeis the user id (emailfor Keycloak,preferred_usernameoroidfor Entra ID),roles-claimthe groups (groups, with a group mapper in Keycloak;cognito:groupsfor Cognito). The client secret isoidc-client-secretinsecrets.env. The installer's--auth oidcwrites this patch. auth-ldap.yaml- LDAP and Active Directory. The user id is the login name (
uidorsAMAccountName); 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-attributeis the user id,roles-attributecarries the groups.
Three things must hold whatever the backend:
- 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.
- Groups arrive as groups.
admin-groupsin the auth patch andaccess-groupsin an app'shub.yamlrefer to the names the provider sends. - Apps read the user from the request, through
hub::user()andhub::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.