Verifier setup for 2026.07.0 through 2026.08.2

Positron Server 2026.07.0 through 2026.08.2 do not validate a license file. These versions accept only a signed license token, so the Hub must mint one for each session. This page covers that setup.

Important

This setup is not recommended. It applies only to Positron Server 2026.07.0 through 2026.08.2. Positron Server 2026.09.0 and newer validate a license file directly, which removes the signing key, the Hub service, and the extra Python package. For those versions, follow the Get started guide instead.

What you need from Posit

Send email to academic-licenses@posit.co to request both files:

  • Signing key (signing-key.pem): the RSA private key that mints per-session license tokens
  • License file (license.lic): proof of entitlement
Note

Positron spells the CPU architecture three ways. The download filename uses x64 or arm64, the CDN path uses x86_64 or arm64, and the activation directory uses x86_64 or aarch64. Throughout this guide, <arch> means the activation directory name.

Step 1: Install Positron Server in the single-user image

Terminal
curl -L "https://cdn.posit.co/positron/releases/server/x86_64/positron-server-linux-x64-2026.08.2-4.tar.gz" \
  -o /tmp/positron-server.tar.gz

mkdir -p /opt/positron-server
tar -xzf /tmp/positron-server.tar.gz -C /opt/positron-server --strip-components=1

For the arm64 build, replace the CDN path segment with arm64 and the filename segment with arm64.

Place the license file inside Positron Server, and restrict it so only root, which covers the Hub and the verifier, can read it:

Terminal
install -m 600 license.lic /opt/positron-server/resources/activation/linux/<arch>/license.lic
Note

Mode 600 is correct for these versions only. The verifier reads this file as root, and sessions never touch it. Positron Server 2026.09.0 and newer need mode 644, because each session reads the file itself.

Step 2: Install jupyter-positron-server in the single-user image

Terminal
# for example, in The Littlest JupyterHub
/opt/tljh/user/bin/pip install 'jupyter-positron-server>=0.0.5'

This proxy extension runs as the user. It requests a license from the Hub at session start and passes it to positron-server. Version 0.0.5 or newer is required for compatibility with these Positron Server releases.

Step 3: Install jupyter-positron-verifier on the Hub

Install the minting service in the Hub Python environment, not the single-user image:

Terminal
# for example, in The Littlest JupyterHub
/opt/tljh/hub/bin/pip install jupyter-positron-verifier

Store the signing key where the Hub service account can read it and users cannot:

Terminal
mkdir -p /etc/positron
install -m 600 -o <hub-user> signing-key.pem /etc/positron/signing-key.pem

The signing key pairs with the public key embedded in positron-server. The license file is already in place from Step 1. The verifier reads it through license-manager, so the Hub needs no separate copy.

Step 4: Register the verifier as a JupyterHub service

Add this to jupyterhub_config.py:

jupyterhub_config.py
c.JupyterHub.services = [
    {
        "name": "positron-license",
        "url": "http://127.0.0.1:10101",
        "command": ["positron-verifier"],
        "environment": {
            "POSITRON_MINTING_KEY_FILE": "/etc/positron/signing-key.pem",
            "POSITRON_LICENSE_MANAGER_PATH": "/opt/positron-server/resources/activation/linux/<arch>/license-manager",
            "PORT": "10101",
        },
    }
]

c.JupyterHub.load_roles = [
    {
        "name": "positron-license-service",
        "services": ["positron-license"],
        "scopes": ["read:users"],
    }
]

POSITRON_MINTING_KEY_FILE points at the signing key from Step 3. POSITRON_LICENSE_MANAGER_PATH points at the license-manager binary inside the Positron Server install, which the verifier runs to confirm entitlement against license.lic. That binary sits in the same directory as the license.

Step 5: Point single-user servers at the minting endpoint

Tell jupyter-positron-server where to fetch licenses, and put positron-server on the session PATH, by adding this to jupyterhub_config.py:

jupyterhub_config.py
import os

c.Spawner.environment = {
    "PATH": "/opt/positron-server/bin:" + os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin"),
    "POSITRON_LICENSE_MINTING_ENDPOINT": "http://127.0.0.1:10101/services/positron-license/mint",
}

Step 6: Restart JupyterHub

Terminal
systemctl restart jupyterhub

The verifier starts automatically as a managed JupyterHub service.

How it works

When a student opens Positron:

  1. jupyter-positron-server calls the Hub minting endpoint, authenticated with its JUPYTERHUB_API_TOKEN, and sends the connection token for the session.
  2. jupyter-positron-verifier verifies the user token, confirms entitlement through license-manager, which reads license.lic, and returns a signed license JSON bound to that connection token.
  3. jupyter-positron-server starts positron-server with the license in POSITRON_LICENSE_KEY.
  4. positron-server verifies the RSA signature with its embedded public key and starts.
Hub (privileged)
  signing-key.pem            /etc/positron/, root-only, never reaches users
  jupyter-positron-verifier  confirms entitlement, mints a signed license
                             at each session start

Single-user server
  jupyter-positron-server    requests a license, passes it to positron-server
  positron-server            verifies the signed license and starts
  license.lic                mode 600, read only by the Hub and the verifier

Next steps