Skip to content

fix(platforms): Keep OnDemand's tokens in the job, fix Coder's installs, and end what a session daemonizes - #421

Merged
ehfd merged 6 commits into
mainfrom
platform-integrations
Sep 28, 2026
Merged

ehfd merged 6 commits into
mainfrom
platform-integrations

Conversation

@ehfd

@ehfd ehfd commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Fixes to the platform integrations from #409, found while taking them upstream: OSC/ood_core#1001, OSC/ondemand#5849, OSC/ood-documentation#1431, coder/registry#1149, coder/coder#30026, and jupyterhub/jupyter-server-proxy#620. Each commit stands on its own and carries its evidence in its message; in short:

fix(ondemand): the session tokens stay in the job. The provisioning request put the master token and the session token on curl's command line, which every account on the compute node can list through ps, and handed both to a proxy the node's environment names. curl now reads them from its standard input and sends the request to the node itself.

  • Reproduced on main: the job, rendered by ood_core's basic template and run as a scheduler runs it, showed both tokens in the provisioning curl's arguments, and a logging proxy named in http_proxy received POST /api/tokens with both.
  • Verified through the same path: the only curl is curl -fsS -o /dev/null --config -, no process of the job carries a token on its command line, the proxy receives nothing, and the session token still authenticates the WebSocket as controller.

fix(coder): the latest release comes from GitHub's redirect, not its API. Unauthenticated API calls share 60 requests an hour per address, so workspaces behind one NAT used them up and the install had no version to download.

  • Reproduced on main with Coder 2.37.3 from a host whose quota was used up: curl: (22) ... 403, Selkies never installed, and the agent ended in start_error.
  • Verified from the same host with the quota still at 0: the redirect resolved 2.0.0, and the workspace installed it and turned healthy in 45 seconds.

fix(coder): Debian packages by the names releases now give them. Since bf0e273 the packages workflow names them selkies-<version>-debian<codename>-<arch>.deb, as docs/native.md derives them, while the module still asked for <codename> alone, which the next release does not publish. The 2.0.0 release now carries its Debian packages under both names.

  • Reproduced on main: debian:bookworm and debian:trixie derive bookworm-amd64.deb and trixie-amd64.deb, while packages.yaml and deb.sh produce debianbookworm-amd64.deb and debiantrixie-amd64.deb.
  • Verified: the same images derive the flavor's names, which 2.0.0 serves, Ubuntu 24.04 and 26.04 derive the names they did before, and a workspace on Debian 13 installed selkies-2.0.0-debiantrixie-amd64.deb.

fix(coder): PulseAudio where the workspace has no sound server. The package depends on the PulseAudio client alone, and selkies-session starts only a server the workspace has, so a workspace the module installed Selkies into streamed without audio.

  • Reproduced on main on codercom/example-desktop (Ubuntu 26.04, XFCE): "no sound server came up; the session has no audio", and Selkies started with audio off.
  • Verified in the same workspace: PulseAudio installs, Selkies starts with audio on, and the desktop streams at 60 frames a second through Coder's path app. pulseaudio resolves under apt (Ubuntu 24.04 and 26.04, Debian 12 and 13), dnf (Fedora 44, AlmaLinux 9), apk (Alpine 3.22), and pacman (Arch, where it conflicts with pipewire-pulse, which the check finds first).

docs: what a Jupyter host brings beside the wheel. pixelflux's wheel takes libgbm, libpixman, and libxkbcommon from the system, as a desktop carries them, and the Jupyter section named no host requirement.

  • Reproduced with selkies 2.0.0 from PyPI in a clean python:3.12 image with Xvfb, xterm, and PulseAudio: JupyterLab's Selkies card opened /selkies/, Firefox and WebKit received no video in 90 seconds, and the log read pixelflux library unavailable (libxkbcommon.so.0: ...).
  • With the three libraries added, the card streams at 62 frames a second in Chrome 154, Firefox 156, and WebKit 26.6, and a Jupyter server killed outright takes its session with it.

fix(session): the launcher adopts what the session orphans. XFCE's ssh-agent daemonizes and makes itself undumpable, so the sweep that finds escaped processes by the runtime directory in their environment could not see it, and each session left one running wherever nothing else reaps the session: Coder, Jupyter, or a scheduler that tracks no cgroup. The launcher is now the child subreaper of everything the session orphans. It ends those orphans with the session, SIGTERM before SIGKILL, and collects them as they exit, leaving its own children to their Popen, which keeps Selkies' exit status.

  • Reproduced on main: test_session_launcher, with a stand-in agent that daemonizes and makes itself undumpable, fails 6 checks: the agent survives in all five stand-in blocks, and the PipeWire-fallback block holds two uncollected zombies. A real XFCE session on Debian 13 leaves ssh-agent under PID 1 with its socket directory.
  • Verified: the suite passes 74 of 74, the same XFCE session's agent ends with it and removes its socket directory, the unit tier passes (142), and test_jupyter_proxy passes 24 of 24 on X11 and Wayland.

What else it can affect. The OnDemand change touches only the provisioning request, and the Coder changes run only where the module installs something. The launcher now collects exited children every half second and reads each process's stat beside its environment when it stops. pre-commit run --all-files passes. Not covered: the OnDemand app through a live portal's own dashboard (the same provisioning ran under a portal's Slurm through ood_core's new template, which uses this request), and aarch64.

Related. With the Coder agent's SSH_CONNECTION in the session's environment, the first connection to a Debian 13 workspace with a sound server aborts Selkies 2.0.0 (free(): invalid size). pcmflux 2.1.0's wheel bundles a second libxcb, which takes over the host copy's own calls once libpulse opens an X connection, as it does only when SSH_CONNECTION is set. selkies-project/pcmflux@a85622b leaves libxcb to the host, and a 2.1.0 wheel rebuilt that way streamed the same session with audio. Until a release carries it, the PulseAudio commit here exposes the abort on Debian 13, where such workspaces streamed without audio before.

…ay from proxies

The job provisioned the session token with curl -H and -d, which put
the master token and the session token on a command line any account on
the compute node can read through ps while the request runs; with the
master token, any of them could replace the token table. A node whose
environment names an http_proxy also handed both tokens to that proxy.
curl now reads the URL, headers, and body from its standard input and
sends the request to the node itself, bypassing any proxy.

Reproduced on main: the job, rendered by ood_core's basic template with
the dashboard's staged paths and run as a scheduler runs it, ran one
provisioning curl whose arguments held the master token and the session
token, and a logging proxy named in http_proxy received the POST to
/api/tokens with both.

Verified on this commit through the same path: the only provisioning
curl is `curl -fsS -o /dev/null --config -`, no process of the job
carries either token on its command line, the proxy receives nothing,
and the session token still authenticates the WebSocket as controller.

Only the provisioning request changes. Its JSON body has no spaces, so
curl's unquoted config form takes it as is.
…er than its rate-limited API

The module read the latest tag from api.github.com, whose
unauthenticated limit of 60 requests an hour is shared by everything
behind one address, so workspaces behind one NAT used it up and the
install then had no version to download. It now reads the tag from the
releases/latest redirect of the web page, which has no such limit.

Reproduced on main with Coder 2.37.3 from a host whose unauthenticated
quota was used up: the API answered 403 "API rate limit exceeded", the
workspace's script log began with "curl: (22) The requested URL
returned error: 403", Selkies was never installed, and the agent ended
in start_error.

Verified from the same host with the quota still at 0: the redirect
resolved 2.0.0, and the workspace installed it and turned healthy in 45
seconds.

Only an empty selkies_version reaches this; four lines replace two.
The packages workflow names the Debian packages after the flavor, as
the images do (selkies-<version>-debiantrixie-amd64.deb), and
docs/native.md derives that name from os-release, while the module
still asked for selkies-<version>-trixie-amd64.deb, which the next
release does not publish. The name is now ID and VERSION_CODENAME on
Debian, as it is ID and VERSION_ID on Ubuntu; 2.0.0 carries its Debian
packages under both names.

Reproduced on main: in debian:bookworm and debian:trixie the module
derives bookworm-amd64.deb and trixie-amd64.deb, while main's
packages.yaml and deb.sh produce debianbookworm-amd64.deb and
debiantrixie-amd64.deb.

Verified on this commit: the same images derive the flavor's names,
which 2.0.0 serves, ubuntu:24.04 and ubuntu:26.04 derive the names they
did before, and a Coder workspace on Debian 13 installed
selkies-2.0.0-debiantrixie-amd64.deb through the module.

One line.
The package depends on the PulseAudio client library alone, and
selkies-session starts only a sound server the workspace has, so a
workspace the module installed Selkies into streamed without audio.
The install step now adds the distribution's PulseAudio where neither
pulseaudio nor pipewire-pulse is installed; a workspace that already
has Selkies and Xvfb still installs nothing.

Reproduced on main with Coder 2.37.3 on codercom/example-desktop
(Ubuntu 26.04, XFCE): the session log read "no sound server came up;
the session has no audio" and Selkies started with audio off.

Verified on this commit in the same workspace: PulseAudio installs,
"pulseaudio serves the session's audio", Selkies starts with audio on,
and the desktop streams at 60 frames a second through Coder's path app.
The package resolves under every package manager the module installs
with: apt on Ubuntu 24.04 and 26.04 and Debian 12 and 13, dnf on Fedora
44 and AlmaLinux 9, apk on Alpine 3.22, and pacman on Arch, where it
conflicts with pipewire-pulse, which the check finds first.

One command in the install block, run only where it already installs.
pip install 'selkies[jupyter]' gives a Jupyter server the Selkies
launcher item and the page loads, but the pixelflux wheel takes
libgbm, libpixman, and libxkbcommon from the system, as a desktop
carries them. On a host without them the capture extension fails to
import and the page shows no picture, and the page describing the
Jupyter integration named no host requirement.

Reproduced with selkies 2.0.0 from PyPI in a clean python:3.12 image
with Xvfb, xterm, and PulseAudio: JupyterLab's Selkies card opened
/selkies/ in a new tab, Firefox 156 and WebKit 26.6 received no video
in 90 seconds, and the log read "pixelflux library unavailable
(libxkbcommon.so.0: cannot open shared object file ...)".

With libgbm1, libpixman-1-0, and libxkbcommon0 added, the same card
streams the desktop at 62 frames a second in Chrome 154, Firefox 156,
and WebKit 26.6, and a Jupyter server killed outright takes its session
and its runtime directory with it.

Documentation only; the list links pixelflux's own rather than
copying its distribution names beyond Debian and Ubuntu.
…its environment ends with it

selkies-session found what a desktop daemonizes out of its process
groups by the runtime directory in each process's environment, which an
agent that makes itself undumpable hides. XFCE's ssh-agent does, so
each session left one running under the host's init, with its socket
directory in /tmp, wherever nothing else reaps the session: Coder,
Jupyter, or a scheduler that tracks no cgroup. The launcher now makes
itself the child subreaper of everything the session orphans, finds
those orphans by their parent, sends them SIGTERM with the process
groups before SIGKILL, and collects them once they exit. The session's
own children stay with their Popen, which keeps Selkies' exit status.

Reproduced on main with the integration suite's stand-in desktop, given
an agent that daemonizes and makes itself undumpable as ssh-agent does:
it survived SIGTERM in all five blocks that run the stand-in (X11,
PulseAudio past a failing PipeWire, stock Xvfb, Wayland, detached), and
in the PipeWire block the launcher held two uncollected zombies, the
servers it had killed. In a real XFCE session on Debian 13 under Selkies
2.0.0's package, XFCE's ssh-agent ran under PID 1 and outlived the
session, its socket directory with it.

Verified on this commit: the suite passes 74 of 74 (68, and 6 failures,
before), with the agent gone and no zombie under the launcher, including
a process the stand-in orphans that exits at once, as a menu's double
fork does. The same XFCE session's ssh-agent runs under the launcher and
ends with the session, removing its socket directory. The unit tier
passes (142), and the Jupyter suite passes 24 of 24 on X11 and Wayland,
a killed server still taking the session with it. os.waitid behaves the
same on Python 3.9, 3.12, and 3.15.

What else changes: the launcher collects exited children every half
second, the sweep reads each process's stat beside its environment, and
stragglers get SIGTERM before SIGKILL.
Copilot AI lite review requested due to automatic review settings September 28, 2026 02:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ehfd
ehfd merged commit 58c9432 into main Sep 28, 2026
95 of 97 checks passed
@ehfd
ehfd deleted the platform-integrations branch September 28, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants