jupyterlab-rtclauncher
A small JupyterLab 4 extension for the NASA Research Platform. It adds Launcher tiles — under a Collaboration section — that start, join, and stop the project's shared real-time-collaboration (RTC) server.
Without it, members reach the shared server through JupyterHub's admin UI
(/hub/admin), which is why they were granted the broad-looking admin-ui
scope. This extension replaces that route so the scope can be dropped.
How it works
The tiles are state-driven: the Launcher shows only the actions that are valid right now, and repaints itself as the shared server's state changes.
server down │ [Start abtesting Collaboration] ──▶ new tab: /hub/spawn/abtesting-collab
─────────────┼──────────────────────────────────────────────────────────────────────────────
spawning │ [Starting abtesting Collaboration…] ──▶ new tab: hub progress page
─────────────┼──────────────────────────────────────────────────────────────────────────────
server up │ [Join abtesting Collaboration] ──▶ new tab: /user/abtesting-collab/lab
│ [Stop abtesting Collaboration] ──▶ confirm ──▶ DELETE server
─────────────┼──────────────────────────────────────────────────────────────────────────────
stopping │ [Stopping abtesting Collaboration…] ──▶ (re-checks status)
- On activation the frontend calls
GET /jupyterlab-rtclauncher/config, which reads the spawn-time environment and answers with the project and shared account name. - It then polls
GET /jupyterlab-rtclauncher/statusto track whether the shared server is running — every 15s when settled, every 3s while a spawn or stop is in flight, and immediately whenever the tab regains focus. - Start opens
<hubPrefix>/spawn/<project>-collab, Join opens/user/<project>-collab/lab, both in a new browser tab. Stop posts to/jupyterlab-rtclauncher/stopafter a confirmation dialog, which proxiesDELETE /hub/api/users/<project>-collab/server.
All three commands also live permanently in the command palette, where they dim
via isEnabled instead of appearing and disappearing.
Why separate tiles, and why they are swapped rather than relabelled
A single toggling tile hid Join entirely — a second member had no way in except to stop the session — and it put a destructive action on the same pixel as a benign one.
Three permanently-visible tiles were the alternative, but a Launcher tile has no
disabled state to fall back on: Card in @jupyterlab/launcher never consults
commands.isEnabled, so a "greyed out" tile is still fully clickable. Showing
only the valid subset is the only honest option there.
That subset is applied by adding and disposing launcher items, not by
changing a command's label. app.commands.notifyCommandChanged() does nothing
for the Launcher: it is a VDomRenderer bound to LauncherModel.stateChanged
and ignores the command registry's commandChanged signal, so a state-dependent
label renders once and then goes stale until a browser reload. launcher.add()
and disposing its return value are what emit stateChanged.
Why a new browser tab
The shared server is a different JupyterHub user server on its own URL prefix, so
entering it is a full-page navigation. Done in the current tab it evicts the
user's own running notebook from their only window. window.open must be called
synchronously from the click handler or pop-up blockers kill it; a blocked
pop-up is reported with the address so the user can open it manually.
Why stopping matters
The shared server bills the project pool for as long as it runs. JupyterHub's
idle culler reaps it after 1h idle, but an open browser tab keeps refreshing
last_activity and can defeat that — and since the admin-ui scope was dropped,
members have no other way to shut it down. The Stop action closes that gap.
Note the server's 10Gi EBS home persists whether it runs or not; stopping releases the compute, which is the expensive part.
Why the stop goes through the server extension
The hub's _xsrf cookie is scoped to Path=/hub/, so JavaScript served from
/user/<name>/lab cannot read it — and JupyterHub 5 requires that XSRF token for
cookie-authenticated, non-GET API calls. The stop therefore proxies through this
extension's server handler, which authenticates with the notebook server's own
JUPYTERHUB_API_TOKEN.
That token only carries admin:servers!user=<project>-collab when the user
genuinely holds it: JupyterHub resolves Spawner.server_token_scopes against the
user's own scopes and drops the rest. A user outside the project's Keycloak group
gets a token without it and the hub answers 403. The extension grants nothing.
Requires rp-deployment to request that scope via Spawner.server_token_scopes
(terraform/config/20-nojovyan.py). Without it, can_control is false and the
tile degrades to start-only.
Which projects get a tile
Any project with collaboration enabled — there is no hardcoded project list and no allowlist to configure here.
The enablement signal is the RP_COLLAB_ACCOUNT environment variable:
- A platform admin flips Real-Time Collaboration → Enable collaboration for a
project in the RPAdmin console, which provisions its login-less
<key>-collabprincipal. - RPAdmin's
GET /api/me/spawn-identity— already called on every spawn — now returnscollab_account: "<key>-collab"for that project, ornull. - rp-deployment
terraform/config/20-nojovyan.pyforwards a non-null value to the notebook asRP_COLLAB_ACCOUNT. - This extension shows the working tiles only when that variable is present.
So enabling a new project is a toggle in RPAdmin — no change to this extension,
the spawner, or the image. OSSProject supplies the project key for the tile's
label; the account name comes from RP_COLLAB_ACCOUNT so the naming convention
lives only in RPAdmin.
Projects without collaboration still get a tile (0.4.0) — a dimmed, informational one that says so when clicked, naming the Project Administrator as who can enable it. Before that, such a project simply had no tile, so nobody in it could discover the feature existed. The dimmed tile grants nothing and calls nothing; its only action is opening a dialog. It is still absent inside the shared collaboration server itself, where there is nothing left to launch.
Operational note. Since rp-deployment feature 100, RPAdmin's toggle is the only gate: the hub reads the enabled projects from
/api/internal/collab-projectsat startup and builds its JupyterHub roles from that, so this extension's tile and the hub's authorization can no longer disagree. What a newly enabled project still needs is a hub restart, since JupyterHub roles are startup config; until then the working tiles appear and the spawn is refused. Before feature 100 this was a second, hand-maintainedRP_COLLAB_PROJECT_KEYSallowlist in terraform, which is now only the boot-time fallback for an unreachable RPAdmin.
The tile grants nothing
It is a link. Starting the shared server requires admin:servers!user=<acct>
and entering it requires access:servers!user=<acct>; both are granted to the
project's Keycloak /smdc-oss/<project> group by rp-deployment
terraform/config/41-collab-accounts.py, and auto-revoke when Keycloak drops
the membership. A user without them who follows the link is refused by the hub.
The working tiles are replaced by the dimmed, informational one when
RP_COLLAB_ACCOUNT is unset (collaboration is off for the project), and the
Launcher shows nothing at all when the current server is the collaboration
server (JUPYTERHUB_USER ends in -collab). The server extension reports which
of those two it is as unavailable_reason; they were a single state until 0.4.0,
which is why the off-for-this-project case used to be invisible.
Install
pip install jupyterlab-rtclauncher
Both the frontend and the server extension are enabled automatically. To verify:
jupyter labextension list # jupyterlab-rtclauncher ... enabled OK
jupyter server extension list
Development
pip install -e ".[test]"
jupyter labextension develop . --overwrite
jlpm build
Run the tests:
python -m pytest tests/ # server extension
jlpm test # frontend
Rebuild after a source change with jlpm build, or run jlpm watch in one
terminal and jupyter lab in another.
Uninstall
pip uninstall jupyterlab-rtclauncher
Related
- Feature 083 — RTC collaboration accounts (
rpadmin/specs/083-rtc-collab-accounts/) jupyterlab-resource-tracker— the usage & cost extension shipped in the same image
Metadata
Release files for jupyterlab-rtclauncher 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jupyterlab_rtclauncher-0.4.0.tar.gz | 160.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jupyterlab_rtclauncher-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 204.6 kB
Release files / jupyterlab_rtclauncher-0.4.0.tar.gz
| Download URL | jupyterlab_rtclauncher-0.4.0.tar.gz |
|---|---|
| Size | 160.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ecf37052d07e58dce27b6381a8160b0dff97fec41afdb9b96feeea3542c4a581
|
|
BLAKE2b-256 checksum How to use checksums |
3489130a7dc2da59ecc6028fdd060e9c267ea3323accb2465d5565955ec88cac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / jupyterlab_rtclauncher-0.4.0-py3-none-any.whl
| Download URL | jupyterlab_rtclauncher-0.4.0-py3-none-any.whl |
|---|---|
| Size | 44.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b1ea3bf500e449840e32c3123683739df0a89405dc68339c743e671e44e472bd
|
|
BLAKE2b-256 checksum How to use checksums |
20df06272184949e180a3b778d106d7be11968c2e64bca41b2f5a50d61e57d99
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for jupyterlab-rtclauncher 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.