Skip to main content

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 sessionSessionRuntime DO holds all session state (leases, snapshots, event sequence) in SQLite.
  • Fleet-wide session registrySESSION_REGISTRY Workers KV namespace; GET /api/sessions returns all active sessions across the fleet.
  • CF Access JWT auth — validates Cloudflare Access JWTs via JWKS; JWT_DEFAULT_ROLE env var assigns a role when the JWT carries no role claim.
  • Hijack REST APIPOST /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 for uterm listen telnet/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-connectPOST /api/connect creates 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.md as the target contract, not a guarantee that every edge case is identical today. Cells in that matrix use the labels defined in docs/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

provide_uterm_cloudflare-0.5.1.tar.gz (279.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

provide_uterm_cloudflare-0.5.1-py3-none-any.whl (123.0 kB view details)

Uploaded Python 3

File details

Details for the file provide_uterm_cloudflare-0.5.1.tar.gz.

File metadata

  • Download URL: provide_uterm_cloudflare-0.5.1.tar.gz
  • Upload date:
  • Size: 279.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.3

File hashes

Hashes for provide_uterm_cloudflare-0.5.1.tar.gz
Algorithm Hash digest
SHA256 1053ef9f88723c918fd3eeaedf979b8b27e96c883c0f3d6531c87f4d549fc224
MD5 342563cc68bcdaab3d27f431c74ebe66
BLAKE2b-256 95006eef3ebafac34b91505589f1d4025936187bbbb0eb55dab18ec557733e1d

See more details on using hashes here.

File details

Details for the file provide_uterm_cloudflare-0.5.1-py3-none-any.whl.

File metadata

File hashes

Hashes for provide_uterm_cloudflare-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c3558a946a906a08786d3ccb8b244bfc6b87de2027dbe04b17ecc270f301690e
MD5 a056607e036fb4d98278da20d4679b58
BLAKE2b-256 42dab6cdad7f1b3b5d871d6a33458215614616060fef414b4753f322e9348dff

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.6

2 files

0.5.5

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

This release

0.5.1 This release

2 files

0.5.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page