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/
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file provide_uterm_cloudflare-0.5.3.tar.gz.
File metadata
- Download URL: provide_uterm_cloudflare-0.5.3.tar.gz
- Upload date:
- Size: 278.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63d2987ac773184834757253646725a42f5a0a9b6d4bff181e80329596e29b79
|
|
| MD5 |
4a85f15c2c9c1803cf226020f92dce42
|
|
| BLAKE2b-256 |
2a3f7ab80238aa887bff95538152fb56c182f231b4d2a89860869e3bc81466ae
|
Provenance
The following attestation bundles were made for provide_uterm_cloudflare-0.5.3.tar.gz:
Publisher:
release.yml on provide-io/provide-uterm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
provide_uterm_cloudflare-0.5.3.tar.gz -
Subject digest:
63d2987ac773184834757253646725a42f5a0a9b6d4bff181e80329596e29b79 - Sigstore transparency entry: 2655226306
- Sigstore integration time:
-
Permalink:
provide-io/provide-uterm@8f8d827e4c2940816b77cc8dafa4a3fe01a2d1a5 -
Branch / Tag:
refs/tags/v0.5.3 - Owner: https://github.com/provide-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8f8d827e4c2940816b77cc8dafa4a3fe01a2d1a5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file provide_uterm_cloudflare-0.5.3-py3-none-any.whl.
File metadata
- Download URL: provide_uterm_cloudflare-0.5.3-py3-none-any.whl
- Upload date:
- Size: 123.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc1584c19f986535b837e549f8716de11824c91615299753821b96a5d268afef
|
|
| MD5 |
1f63846355853f3aa6b235a171c040a7
|
|
| BLAKE2b-256 |
6eddec4c724c287efbff98186f2264becac43c9311ef92cc9c0739bed31d3ea9
|
Provenance
The following attestation bundles were made for provide_uterm_cloudflare-0.5.3-py3-none-any.whl:
Publisher:
release.yml on provide-io/provide-uterm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
provide_uterm_cloudflare-0.5.3-py3-none-any.whl -
Subject digest:
fc1584c19f986535b837e549f8716de11824c91615299753821b96a5d268afef - Sigstore transparency entry: 2655226322
- Sigstore integration time:
-
Permalink:
provide-io/provide-uterm@8f8d827e4c2940816b77cc8dafa4a3fe01a2d1a5 -
Branch / Tag:
refs/tags/v0.5.3 - Owner: https://github.com/provide-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8f8d827e4c2940816b77cc8dafa4a3fe01a2d1a5 -
Trigger Event:
release
-
Statement type: