Nublado is versioned with semver. Dependencies are updated to the latest available version during each release. Those changes are not noted here explicitly.
Find changes for the upcoming release in the project's changelog.d directory.
- Rename the
Rubinmenu toJobsin the standard extensions installed in the JupyterLab base image. Beneath that, renameQuerytoTAP Query. This menu now separates jobs by dataset, although jobs may be listed under multiple datasets if the extension cannot determine what dataset that job was querying. - Change the default runtime mount location to
/etc/nubladofrom/opt/lsst/software/jupyterlab. This change should be invisible to users since all code that relies on its location should be using theNUBLADO_RUNTIME_MOUNTS_DIRenvironment variable to find it. - Stop creating an
.access_tokensymlink in the user's home directory to the injected Gafaelfawr access token. The lsst.rsp library, which should be used by most payloads, knows how to find the access token, and other languages should use theNUBLADO_TOKENenvironment variable. - Semantic version filters in the dropdown menu are no longer supported for weekly and daily images. Switching between calendar versions and semantic versions for
cutoffVersionwas potentially confusing. - Use
NUBLADO_as the prefix for environment variables to configure thenublado purgercommand-line tool instead ofRSP_SCRATCHPURGER_. This brings it in line with the rest of Nublado.
- Add a new command,
nublado images list, to list the available Nublado image tags given an image source configuration. - Add a new command,
nublado images prune, to delete some Nublado images from a repository based on a pruning policy. - Add a new command,
nublado images delete, to delete specific images by tag. - Provide the lab's Gafaelfawr token in the
NUBLADO_TOKENenvironment variable as well as theACCESS_TOKENenvironment variable. The latter will eventually be deprecated. - Add a new
cutoffDateattribute for dropdown menu filtering, used to restrict display of daily and weekly images to only those after a given date. Filtering of experimental images can now be done with bothcutoffVersionandcutoffDate, with the former applying to the semantic version of experimental builds based on releases and release candidates and the latter applying to builds based on weeklies and dailies. - Add a new
cutoffBuildattribute for dropdown menu filtering, used to restrict display of images with build numbers to only those with larger or equal build numbers. Images without build numbers are ignored.
- Restore lab startup code to increase log output limits. This was accidentally dropped as part of the init container changes.
- Retry Google Artifact Registry requests on internal service errors as well as service unavailable errors. These errors also seem to be transitory problems with the Google API.
- Relax the dependency on Safir in rubin.nublado.client to allow Safir 15.0.0.
- Add configuration setting for the timeout for prepulling images. The default of ten minutes may be too low for large images and slow networks.
- Mount the lab startup volume read-only in the lab container. It should only be written to by the init container and should not be available to the user as a writable directory.
- Always force the lab startup volume to use tmpfs instead of following the configuration for
/tmp. - Update JupyterHub to 5.4.4 (Zero to JupyterHub 4.3.3).
- Remove unused packages from the JupyterLab virtualenv in the jupyterlab-base image: hatchling, batch-nodejs-version, firefly-client, ipython-genutils, and nbval.
- Fix incorrect double-decoding of the body when reporting Kubernetes errors.
- Handle unexpected messages on a Kubernetes watch by logging them and restarting the watch, rather than raising an exception and failing the operation.
- Downgrade Google Artifact Registry retries to warnings so that they're not reported by Sentry.
- Change the element ordering and sort order for RSP versions to list and sort cycle and cycle build number before the RSP build number. The latest cycle build should be preferred regardless of the RSP build. Currently, no tags contain both values, so this should not change existing menu displays.
- Fix the display name of resolved aliases to use the base tag name without any cycle or architecture information.
- Fix the sort order of unknown and alias tags with an architecture to sort architecture-independent tags before architecture-specific tags, matching the behavior of non-alias tags.
- Always filter out architecture-specific images rather than including them if the non-architecture-specific image with the same tag is not present. This removes images for foreign architectures from the prepull targets and the drop-down menu.
- Retry requests to Google Artifact Registry to retrieve the list of images up to three times with a ten second pause between attempts before reporting a fatal error. This will hopefully reduce log noise from the intermittant Google API failures.
- Move the startup files created by the startup init container from
/lab_startupto/etc/nublado/startup. - Simplify the lab initialization code to merge environment variables with the environment set by the Nublado controller and remove some error handling that should only trigger if the Nublado init containers do not run correctly. This means labs built with the new jupyterlab-base will fail with pre-11.0.0 versions of Nublado by failing to start, not starting in degraded mode.
- In the Nublado startup init container, only put environment variable overrides in
/etc/nublado/startup/env.json, not the full environment of the init container, to avoid leaking init-specific environment variables into the lab container.
- Add
controller.watch_reconnect_timeoutconfig setting. Sometimes, Kubernetes watch connections can frequently drop with errors, or worse, they can silently stop receiveing events. This new setting specifies the amount of time to keep the connection open before explicitly reconnecting.
- Fix installation of files into the user's home directory by the startup init container.
- Remove filtering of the environment variables propagated to the user's lab, which broke Phalanx support for setting arbitrary environment variables.
- Add
clear_local_site_packagesparameter to notebook execution, which tells the lab to remove all locally-installed Python packages before executing the notebook.
- Switch from TeX to typst for PDF export of notebooks inside the jupyterlab-base container for Jupyter labs.
- Remove configuration of the image and command for the fsadmin pod. This pod will now always use the general Nublado container and a keep-alive loop for the startup command.
- Rename the
config.lab.tmpSourcesetting toconfig.lab.emptyDirSource. This setting now controls the KubernetesemptyDirconfiguration for both/tmpand the new/lab_startupdirectory. - Remove configuration for the fileserver image. This will now use the general Nublado container.
- Move the JupyterLab initialization logic from lsst.rsp.startup into the Nublado container and run it as an init container. It communicates with the startup code via a new
/lab_startupdirectory, allowing the lab startup code in the main container to be much simpler. - Add new configuration option
config.lab.standardInithomethat, if set to true, adds a standard home directory init container to user pods. This is the same as the previousnublado-inithomecontainer and is a privileged init container that creates the user home directory if it doesn't exist. - Add new configuration option
config.lab.homeVolumeName, used to determine which volume is mounted for the standard inithome init container. - If
RSP_SITE_TYPEis set toscience, add an additional init container to user labs that ensures the tutorial landing page is copied into the user's home directory before the lab is started. - Add the user WebDAV file server implementation to the Nublado repository and container.
- Add new
controller.config.lab.namespaceAnnotationssetting to add annotations to the per-user namespaces. The value may be a template in which{{uid}}will be replaced by the user's UID and{{gid}}will be replaced by the user's GID.
- Merge the
nublado-inithomecontainer with other support containers and the Nublado controller into a singlenubladocontainer. Configurations usingnublado-inithomeas an init container should change the container tonubladoand specifynublado inithomeas the container command. - Use the new Gafaelfawr client in the Nublado client mock to manage and verify tokens. Test suites that use the Jupyter mock from the Nublado client now must also mock Gafaelfawr and use the mock Gafaelfawr to generate a token and register corresponding user information before calling the Jupyter mock.
- Drop support for Python 3.12 in the Nublado client.
- Add support for specifying the command to run in lab init containers and the file system admin pod.
- Use Repertoire in the Nublado controller to find the Gafaelfawr API instead of assuming the URL structure of the local Phalanx environment.
- Add the
nubladocommand-line tool, which includes home directory provisioning, file system purging, and other supporting functionality, to the default file administration container.
- Stop imposing restrictions on usernames and group names in the Nublado controller apart from those already imposed by Gafaelfawr.
- Update JupyterHub to 5.4.3.
- Use updated Safir so that app metrics won't break the app in rare situations if the underlying Kafka infrastructure is down.
- Allow user group names where the only alphabetic character is the first one.
- Add support for pretty-printed ADQL queries to jupyterlab-base.
The API of the Nublado client provided as the rubin.nublado.client PyPI module has changed significantly. Users of that API will need updates for this version.
- Export all Nublado client symbols from
rubin.nublado.clientand make the internal module structure private. Any imports fromrubin.nublado.client.exceptionsorrubin.nublado.client.modelsmust be changed to instead import from the top-level module. - Use Repertoire for service discovery in the Nublado client.
NubladoClientno longer supports configuration with a base URL; instead, to use a custom service discovery configuration, create a RepertoireDiscoveryClientand pass it as an optional parameter toNubladoClient. - Remove the
GafaelfawrUserclass from the Nublado client. Callers should instead pass separateusernameandtokenarguments into the constructor. - Delete
JupyterLabSession.run_notebook_via_rsp_extensionand replace it withNubladoClient.run_notebook, which uses the same extension but does not require creating a session first. The notebook content to execute must be passed in; providing a path relative to the user's Nublado home directory is not supported. - Change the field names of the error portion of a notebook execution result to be more general and not exactly echo the nbclient exception attributes.
- Delete
JupyterLabSession.run_notebookandJupyterLabSession.run_notebook_cell. UseJupyterLabSession.run_pythonto run each cell instead. - Stop exposing username from
NubladoClientas a public attribute. - Rename
NubladoClient.open_lab_sessiontoNubladoClient.lab_sessionto make it more obvious that it returns a context manager, not an open session. - All the exceptions raised by the Nublado client have changed names. All exceptions inherit from
NubladoError, which has acontextattribute that can be used to add execution context information in the form of theCodeContextmodel. - Reject unknown attributes and adopt an alias of
classforimage_classin theNubladoImageclass provided by the client, allowing it to be used as a child model of services with YAML-based configuration. - Use service discovery in the Nublado client mock to determine what URLs to mock. There is no longer a way to independently configure the base URL; instead, ensure service discovery is mocked via Repertoire's mock support before mocking Jupyter.
- Simplify setup of the Nublado client mock. Fixtures should now use
register_mock_jupyteras an async context manager and yield its result (aMockJupyterobject), and no longer need to directly handle WebSocket patching. - Rewrite the API of the JupyterHub and JupyterLab mock provided as part of the client. Users of the mock should review the new API. Methods have changed names, and all information previously available or changeable via attributes is now done with methods.
- Enforce camel-case settings in the purger configuration.
- Add new
NubladoClient.wait_for_spawnmethod that can be used if the caller doesn't care about the spawn progress messages. - Provide
MockJupyter.create_mock_tokento create a mock Gafaelfawr token usable for authentication to the Jupyter mock provided by the Nublado client. - Add support for specifying a specific kernel in
NubladoClient.run_notebook. - Provide pretty-printed tooltips for user SQL query history in the base JupyterLab environment.
- Fix display of architecture-specific lab image tags when architectures are split across pages of a paginated tag list.
- Update JupyterHub to 5.4.2.
- Enable
refresh_pre_stopin the Gafaelfawr authenticator plugin to JupyterHub in the hope that this will fix some rarely-seen problems with long-running labs. - Update JupyterHub to 5.4.1 (Zero to JupyterHub 4.3.1).
- Update to the latest Repertoire client for compatibility with the new service discovery model. This release requires Repertoire 0.6.0 or later.
- Test the Nublado client and JuypterHub plugins with Python 3.14 as well as 3.12 and 3.13.
- Use Repertoire service discovery in the JupyterHub spawner plugin to find the Nublado controller API.
- Enable Sentry and Slack error reporting in the file purger.
- Support pagination when listing the tags for lab images from a Docker repository in the Nublado controller. This is required when using GitHub Container Registry as an image source if there are a significant number of image tags.
- Send the correct
Acceptheader when requesting container checksums from a Docker registry that may contain multi-architecture images. - Ignore prepuller updates for nodes that no longer exist instead of raising uncaught exceptions and pausing the prepuller.
- Mount Repertoire discovery information at
/etc/nublado/discovery/v1.jsonin user notebook pods. - Use Repertoire service discovery to find the logout URL for the JupyterHub Gafaelfawr plugin.
- Export of notebooks to PDF in the jupyterlab-base image now uses TeX instead of Chromium, which shrinks the size of the image by over 1GiB.
- Instrument client exceptions with Sentry metadata.
- Publish multi-platform images that support both linux/amd64 and linux/arm64.
- Always use
userfor the username in logging contexts, and include username in more log messages. - Filter out platform-specific tags from the spawner menu when corresponding generic tag exists.
- The jupyterlab-base image is now based on the Debian trixie release.
- Use uv workspaces and uv lock files to manage packages and dependencies.
- Optionally report Nublado controller errors to Sentry.
-
Resource requests for user lab pods can (and must) be specified explicitly. Before this change, only resource limits were specified and requests were calculated to be 1/4 of the limits. The
controller.config.lab.sizesentries now have a different format.Old:
sizes: - size: "small" cpu: 1.0 # This was a limit memory: "4Gi" # This was a limit
New:
sizes: - size: "small" resources: limits: cpu: 1.0 memory: "4Gi" requests: cpu: 0.25 memory: "0.75Gi"
- Push the purger Docker image to the correct Google Artifact Registry rather than overwriting the inithome container.
- Adopt updated Safir.
- Added Nublado controller route to control administrative filesystem pod. This allows a user with an admin token to spawn a pod that mounts user file systems with administrative privileges. That pod can then be entered by the administrator with
kubectl execfor remedial or forensic purposes. The pod can also be deleted through this route, and the status of the pod can be queried.
- Install ipython-genutils in jupyterlab-base since ipympl has an implicit dependency on it.
- Rebuild jupyterlab-base with newer dependencies.
- Improve handling of exceptions in the notebook execution extension.
- Include
.configin the list of directories moved aside when the user asks to reset their user environment.
- Add a build number as an optional portion of the image tag format.
- Fix the link to the WebDAV documentation in the HTML page returned after a user's file server was started.
- Add a cron job to purge file systems of old files. This was formerly maintained in the lsst-sqre/rsp-scratchpurger repository.
- Accept group names from Gafaelfawr that start with digits as long as they contain at least one letter. Group names of that type can arise from per-user groups for users with a username starting with a digit.
- Rebuild jupyterlab-base with lsst-rsp 0.9.4, which fixes a bug in overquota handling when a welcome page is configured and presents a better overquota landing page.
- Add new
controller.config.lab.defaultSizeconfiguration option that determines the default lab size in the spawner menu. If not set, or set to a lab size the user does not have sufficient quota to spawn, the default continues to be the first size the user is allowed to spawn. - Add
controller.config.fileserver.reconcileIntervalconfiguration option to control the frequency with which file server state is reconciled with Kubernetes. - Add
controller.config.lab.reconcileIntervalconfiguration option to control the frequency with which lab state is reconciled with Kubernetes. - Add
controller.config.images.refreshIntervalconfiguration option to control how often the image source is checked for new images and the Kubernetes nodes are checked for the list of cached images.
- Fix a race condition during file server Kubernetes reconciliation where a file server in the process of being created, but waiting for the ingress to become valid, could be deleted by the background reconciliation job.
- Fix background task timing with very short intervals. This will probably never arise in a production configuration but was triggered by the test suite.
- Simplify the HTML page shown after the user spawns a file server and point to the RSP documentation.
- Standardize on
write:filesas the scope for accessing the file server. This was already required to spawn the file server, butexec:notebookwas still used to control access to the spawned file server.
- Update JupyterHub to 5.3.0 (Zero to JupyterHub 4.2.0).
- Document how to customize Nublado JupyterLab behavior.
- Update the base Jupyter Lab extensions to support specifying the kernel to use via the
/executionendpoint.
- Fix builds when
jupyter labextension listexits with a non-zero status.
- When building the list of available lab images, only include versions from tags on the lab image name, not other Docker images in the same repository.
- When tagging jupyterlab-base for a new release, also move the
latesttag to the new release.
- Add
lessto the base JupyterLab container image.
- Fix the install locations of configuration files in the base JupyterLab container image.
- Push inithome containers to Google as well as GitHub since GitHub has a low rate limit on anonymous API requests.
- Update jupyterlab-server in the base JupyterLab container image.
- Fix uploads of
rubin-nublado-clientto PyPI.
- Support setting the interval for activity reporting from JupyterLab in the Nubaldo controller configuration.
- Add a configuration setting for where to redirect the user after logout from JupyterHub. When user subdomains are in use, this needs to point to
/logoutat the base URL for the Science Platform, not/logoutat the current hostname, which may be the JupyterHub hostname and thus create an infinite redirect loop. - Use the configured delete timeout when deleting invalid labs instead of a hard-coded 30s timeout.
- Fix the
NetworkPolicycreated for user labs so that it actually restricts access to only JupyterHub, its proxy, and the lab itself as was intended.
- Add support for per-user subdomains to the Nublado client. If they are enabled, they will be automatically detected and the Nublado client will adjust its HTTP requests accordingly.
- Add support to the Nublado client testing mocks for simulating per-user subdomains.
- Add
config.allowOptionsto theGafaelfawrIngressfor file servers so that the server will work correctly with Gafaelfawr 13.0.0 and later.
- Set
Sec-Fetch-Modein several places in the Nublado client to suppress harmless but annoying warnings in the JupyterHub logs.
- Add support to the Nublado controller for restricting the list of images shown in the dropdown menu based on age, count within a category, or a version cutoff.
- Fix an unbound variable error in the Nublado client in one error handling situation.
- Raise the upper bound on the Safir in the Nublado client to
<11.
- Fix
TypeErrorexception when trying to establish websocket connections. Version 14 of websockets changed the signature of theconnectmethod.
- Wait for the default service account of the user's lab namespace to be created before creating the
Podobject. Creation of the service account can be slow when Kubernetes is busy, and creation of thePodobject will fail if the service account does not exist. - Remove the limit on the connection pool used to contact the Nublado controller from the JupyterHub spawner, since otherwise JupyterHub stops being able to do any work once the connections waiting for spawn progress exhaust the connect pool.
- Remove the limit on the Kubernetes client connection pool in the Nublado controller. This will allow the controller to scale to more than 100 (the default) simultaneous watches.
- Remove the limit on the connection pool used to look up users in Gafaelfawr and query a Docker image repository.
- Fix unsafe data structure manipulation when deleting completed labs in the Nublado controller background reconciliation thread.
- Log metrics events in the Nublado controller for lab spawn successes and failures and a count of the number of active labs.
- Do not query Kubernetes for pod status when responding to status requests, and instead assume the internal state (which is reconciled periodically) is correct. The constant Kubernetes API requests for
Podstatus seemed to be overwhelming the control plane when there were a lot of pods running. Continue to ask Kubernetes directly immediately before spawn. - Report Kubernetes operation timeouts properly when they cause a spawn failure rather than throwing an uncaught exception.
- Add tolerations to prepuller pods as well as lab and file server pods so that images can be prepulled to tainted nodes.
- Support the new
spawnflag in the user's Gafaelfawr metadata. If set to false, tell the user that spawns aren't allowed rather than presenting a menu and reject spawns in the Nublado controller with a 403 error.
- Return a spawner form showing only an error rather than an HTTP failure if the user's quota does not allow them to spawn any of the configured notebook sizes.
- Drop support for Gafaelfawr groups without GIDs. Gafaelfawr has required all groups have GIDs since Gafaelfawr 11.0.0.
- Add documentation explaining how to use node selection and recommending
nodeSelectoroveraffinity.
- Add a
servicelabel toGafaelfawrIngressresources created for user file servers for proper Gafaelfawr metrics reporting.
- Change the base image for JupyterHub to
quay.io/jupyterhub/k8s-hubfromjupyterhub/jupyterhub. This means the JupyterHub image now uses Python 3.12.
- Remove the
Tokenlink from the JupyterHub page template, since user tokens for JupyterHub are not supported on the Rubin Science Platform.
- Update to JupyterHub 5.2.1.
- When reporting HTTP errors from the Nublado client, truncate the response body at the start rather than at the end. This makes it more likely that the error message from JupyterHub will appear in the truncated response.
- Drop the XSRF token and cookies before performing a JupyterHub login in the Nublado client. The client previously hung on to the XSRF token indefinitely, which resulted in errors if the token was expired in JupyterHub, such as by user session expiration.
- Fix broken formatting in error messages reported by the Nublado client.
- Improve error reporting of exceptions in the Nublado client and sanitize the reported body to remove some security tokens.
- Refactor exception handling in
NubladoClientto incorporate optional code context information (used by mobu) and additional exception metadata.
- Provide
JupyterLabSessionas an exported class fromrubin.nublado.client. This class represents an open WebSocket session with a Jupyter lab. - Add support for artificial Gafaeflawr tokens to the
MockJuyptermock of the JupyterHub and JupyterLab API. This allows the mock to extract a username from mock tokens sent by the code under test to the mocked APIs, rather than requiring the test client send anX-Auth-Request-Userheader. - Use the most recent Nublado release tag as the default base image for sciplat-lab container builds.
- Add a Docker image build for a
jupyterlab-baseimage, which provides a basic image that can be spawned as a lab container by Nublado and can be used as the basis for more complex images. - Add the Docker image build for
sciplat-lab, an image built on top ofjupyterlab-basethat provides a JupyterLab kernel that includes the Rubin Science Pipelines Python stack.
- Revert canonical PyPI module name back to
rubin-nublado-clientfor consistency with other projects. As before, this change should not affectpip install; either form of the name should work.
- Rename PyPI module for the Nublado client to
rubin.nublado.client. Either name should work forpip install.
- Push the new
rubin-nublado-clientmodule to PyPI on release.
- Add the Python module
rubin-nublado-client, which provides a client library for interacting with the Nublado-modified JupyterHub and JupyterLab services.
- The
/tmpdirectory in a lab pod now defaults to a tmpfs file system capped at 25% of the pod memory. Add a new configuration option to select between this default and the previous default of uncapped node-local storage.
- All timeout configuration options now support the syntax parsed by Safir's
parse_timedeltaand therefore support human-friendly durations such as6hor5m.
- Fix crash of the controller during startup if a Kubernetes node reports a cached image with no names.
- Fix bootstrapping of a development environment in an existing virtualenv. Previously, uv was not installed before nox attempted to use it.
- Work around a bug in sphinxcontrib-redoc that prevented building the documentation twice without errors.
- Add configuration settings for the lab launch command and configuration directory.
- Add limits and requests to prepulled pods.
- Update the underlying JupyterHub implementation to JupyterHub 5.0.0.
- Switch to uv for package management.
- Update JupyterHub to 4.1.5, which fixes more issues with XSRF handling.
- Move the
.eupsdirectory when performing user environment resets.
- Update JupyterHub to 4.1.4, which fixes more XSRF cookie issues.
- Move the admin route for deleting a user's file server from
/nublado/fileserver/v1/<username>to/nublado/fileserver/v1/users/<username>to better align with other routes and REST semantics.
- Add an admin-authenticated
/nublado/fileserver/v1/users/<username>GET route to get the status of a user's running file server (currently only 404 if not running or 200 with trivial content if running). - Add a
/nublado/fileserver/v1/user-statusroute a user to get the status of their own file server, which similarly returns 404 or 200 with trivial content.
- Update JupyterHub to 4.1.3, which includes several fixes for XSRF handling.
- Update to JupyterHub 4.1.0. This release has tighter XSRF handling than previous versions. Clients that talk directly to JupyterHub rather than using a browser, such as mobu or Noteburst, will need to be updated to support the stricter XSRF requirements.
- Nublado now uses uv to maintain frozen dependencies.
- Update to Safir 5.2.0, which rewrites the middleware to avoid the Starlette
BaseHTTPMiddlewareclass. This should hopefully produce better error reporting in some cases where exceptions were being mangled and lost by theBaseHTTPMiddlewarelogic.
- All user file servers are now protected by a
NetworkPolicythat prevents connections except via Gafaelfawr, ensuring that authentication is properly enforced. - Stop returning the requested lab environment from the lab status API. Nothing uses this information and it may contain secrets that should not be this readily accessible.
- Return the actual running Docker image reference from the lab status API rather than the form parameters the user sent when requesting a lab.
- Return prepuller configuration in the API response with snake-case fields rather than camel-case fields, as was originally intended.
- Restrict access to controller routes via the
admin:jupyterlabscope to only those routes that JupyterHub needs to use. Access to other administrative routes is now controlled withexec:admin. - Fix the response type for the
/spawner/v1/labs/{username}/eventsroute in the OpenAPI schema. - Quietly retry the file server pod watch after 410 errors, since under some circumstances they appear to happen every five minutes. Remove the delay when restarting after a 410 error without a resource version, since this appears to be a normal Kubernetes API response and the delay could miss events.
- Change the default Argo CD application for user file servers to
nublado-fileservers. Continue to usefileserversas the default namespace, since thenublado-fileserversnamespace would conflict with the reserved namespace pattern for user labs. - Classify the JSON API routes with tags that reflect who has access to that API.
- Add documentation on how to set up Google Artifact Registry as an image source for Nublado, and on why this is the recommended configuration when Nublado is running on Google Kubernetes Engine.
This is the first release of the new merged Nublado release. It contains the Nubaldo controller, a JupyterHub spawner implementation that uses the controller to create user labs, a JupyterHub authenticator implementation to use Gafaelfawr, and the Docker configuration to build a custom JupyterHub image containing that spawner and authenticator.
In previous versions of Nublado, the equivalents of these components were maintained in separate repositories with independent version numbers. As of this release, all of these components are maintained and released together using semver versioning. Further changes will be documented in this unified change log.