provide-uterm-cloudflare
Cloudflare Workers companion package for provide-uterm. Runs the provide-uterm control plane on Cloudflare Workers using Durable Objects, with a fleet-wide session registry backed by Workers KV.
What it does
Each terminal session gets its own Durable Object (SessionRuntime). The DO arbitrates WebSocket traffic between the runtime worker connector and browser clients, stores hijack leases and snapshots in SQLite, and publishes events to all connected browsers. A fleet-wide session list is maintained in Workers KV.
Installation
pip install provide-uterm-cloudflare
Or install from the monorepo with uv:
uv pip install -e packages/provide-uterm-cloudflare
Deploy
The worker entrypoint is src/worker_entry.py (referenced by main in
wrangler.toml). It lives at the package src/ root on purpose: wrangler bundles
the directory of the main file, so anchoring it there preserves the full
provide/uterm/cloudflare/ tree in the bundle and the worker's qualified imports
resolve as-is.
The Pyodide runtime also needs a flat vendor tree of the pure-Python deps the
worker imports (structlog, provide.telemetry, and the provide.uterm.*
modules). pywrangler sync produces a layout the worker can't import, so build it
with the helper script, then deploy with wrangler directly (not pywrangler deploy, which would re-sync and overwrite the tree):
bash .ci/vendor_cf_worker.sh # build python_modules/
cd packages/provide-uterm-cloudflare
CLOUDFLARE_API_TOKEN=… npx wrangler deploy # publish
# Required secrets (AUTH_MODE is jwt-only; the worker 500s without these):
npx wrangler secret put WORKER_BEARER_TOKEN # >=32 high-entropy chars
npx wrangler secret put WEBHOOK_SECRET_KEY # base64 AES-256 key
Key features
- Durable Object per session —
SessionRuntimeDO holds all session state (leases, snapshots, event sequence) in SQLite. - Fleet-wide session registry —
SESSION_REGISTRYWorkers KV namespace;GET /api/sessionsreturns all active sessions across the fleet. - CF Access JWT auth — validates Cloudflare Access JWTs via JWKS;
JWT_DEFAULT_ROLEenv var assigns a role when the JWT carries no role claim. - Hijack REST API —
POST /hijack/{id}/acquire,POST /hijack/{id}/send,POST /hijack/{id}/release,GET /hijack/{id}/snapshot. - WebSocket proxy — three WS endpoints per session:
/ws/worker/{worker_id}/term— runtime worker protocol (JSON frames)/ws/browser/{worker_id}/term— browser/operator protocol (JSON frames)/ws/raw/{worker_id}/term— raw stream mode foruterm listentelnet/SSH gateways
- Hibernation-safe — uses CF WebSocket Hibernation API; state survives DO sleep/wake cycles.
- WS session resumption — browser reconnects reclaim their role and hijack ownership via one-time tokens stored in DO SQLite; see
docs/cf-do-architecture.md. - Quick-connect —
POST /api/connectcreates sessions in KV; SPA serves the connect form at/app/connect. Supports shell, websocket, and ushell connector types.
Auth modes
Set AUTH_MODE in wrangler.toml or .dev.vars. jwt is the only
supported value — the worker is always internet-facing, so any other mode
(dev/none are removed) raises a ValueError at config load.
| Mode | Behavior |
|---|---|
jwt |
Validates CF Access JWT; role from claim or JWT_DEFAULT_ROLE. CF Access service-token JWTs (with a common_name claim and no human email claim) are accepted, but are only granted the admin role when JWT_SERVICE_TOKEN_ADMIN=1 is set (defaults off); otherwise they get their roles from the normal claim/scope/default-role path. |
WORKER_BEARER_TOKEN is also required and must clear a 32-character /
non-placeholder entropy floor.
Current gaps
Every intentional difference from the FastAPI backend — auth modes, capability
refusals, the /api/health payload, hibernation, the tunnel transport — is
tabulated with its reason and its pinning test in
docs/cloudflare-divergence-matrix.md.
Changing edge-runtime behavior means updating that table and its edge tests in
the same commit. The gaps below are the ones that are not yet settled either way:
- The quick-connect form creates sessions in KV but the CF worker cannot run shell/SSH/telnet connectors itself — a worker process must bridge in via WS.
- The hijack REST surface is intended to match the FastAPI contract, but there
are still backend-parity gaps; treat
docs/protocol-matrix.mdas the target contract, not a guarantee that every edge case is identical today. Cells in that matrix use the labels defined indocs/parity-labels.md.
Commands
uv run pywrangler dev # local dev server (sync deps + wrangler dev)
uv run pywrangler deploy # deploy to Cloudflare
uterm-cf build # build only
uterm-cf deploy --env production
Docker alternative
# Build and run from repo root
docker build -f docker/Dockerfile.cf -t provide-uterm-cf .
docker run --rm -p 27788:27788 provide-uterm-cf
# JWT auth test
docker run --rm -p 27788:27788 \
-e AUTH_MODE=jwt \
-e JWT_JWKS_URL=https://<team>.cloudflareaccess.com/cdn-cgi/access/certs \
-e JWT_ISSUER=https://<team>.cloudflareaccess.com \
-e JWT_AUDIENCE=<aud-tag> \
provide-uterm-cf
Tests
Unit tests (no network required):
uv run pytest tests/ -v
E2E tests against a local wrangler dev instance or the live worker:
E2E=1 uv run pytest -m e2e -v
REAL_CF=1 REAL_CF_URL=https://provide-uterm-cloudflare.neurotic.workers.dev uv run pytest -m e2e -v
Related
- Main package:
provide-uterm - Terraform for KV provisioning:
terraform/
Metadata
Release files for provide-uterm-cloudflare 0.5.6
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 | Actions |
|---|---|---|---|---|
| provide_uterm_cloudflare-0.5.6.tar.gz | 278.1 kB | With provenance | Details for provide_uterm_cloudflare-0.5.6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| provide_uterm_cloudflare-0.5.6-py3-none-any.whl | Python 3 | none | any | Details for provide_uterm_cloudflare-0.5.6-py3-none-any.whl |
Total release size: 401.2 kB
Release files / provide_uterm_cloudflare-0.5.6.tar.gz
| Download URL | provide_uterm_cloudflare-0.5.6.tar.gz |
|---|---|
| Size | 278.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d3d7924e0b71d0075a0cff2178e928cf94f94a0fef1f6f9a8d3b95c960fa59b6
|
|
BLAKE2b-256 checksum How to use checksums |
71071c367945a350164c5c9b0cd15124bcdf7d45fc0b913798d09189c83db8d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / provide_uterm_cloudflare-0.5.6-py3-none-any.whl
| Download URL | provide_uterm_cloudflare-0.5.6-py3-none-any.whl |
|---|---|
| Size | 123.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dacd88a3267494300b06c7a9fe1b5f904b77c8c59717759fbadcb4ce5c99ee97
|
|
BLAKE2b-256 checksum How to use checksums |
51c43c9d51544b8fce4380d64eb3ee39ba958757d91b579badb05454da01899b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logSecurity for provide-uterm-cloudflare 0.5.6
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for provide_uterm_cloudflare-0.5.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logA 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.