Cloudflare Python Workers
Give a Python Worker one secret binding, SEEKRIT_TOKEN, and resolve the rest
inside its handler with seekrit.cloudflare.AsyncClient. Seekrit returns
ciphertext and wrapped data keys; the SDK decrypts inside your Worker. Adding
or rotating an application credential in Seekrit does not require updating
each Cloudflare binding.
Cloudflare announced Python Workers general availability on September 21,
2026. Python runs on Pyodide,
and Pywrangler
installs packages for that runtime. The Seekrit adapter uses native
workers.fetch, while decryption uses the existing SDK's cryptography
implementation and its Pyodide build.
The async adapter is new in the SDK source; the previous 0.10.0 release does
not include it. Use a release containing seekrit.cloudflare, or run the
repository example,
which points at the local SDK through tool.uv.sources.
1. Create a service token
Create an app environment in Seekrit, add the credentials your Worker needs, and create a service token for that environment. The token needs permission to resolve secrets and key grants for the app and any composed groups. Use separate tokens for development, staging, and production.
SEEKRIT_TOKEN is the bootstrap credential. Store it as a Cloudflare secret,
never in source or Wrangler's plaintext vars.
2. Set up the Python Worker
Install current uv
(0.12.3 or newer) and Node.js 22 or newer. In a new directory, create
pyproject.toml:
[project]
name = "seekrit-python-worker"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = ["seekrit"]
[dependency-groups]
dev = ["workers-py>=1.17.5", "workers-runtime-sdk>=1.9.2"]
Install Wrangler in the project so its version is explicit:
npm install --save-dev 'wrangler@^4.139.0'
Create wrangler.jsonc:
{
"name": "seekrit-python-worker",
"main": "src/entry.py",
"compatibility_date": "2026-09-30",
"compatibility_flags": ["python_workers"],
"secrets": { "required": ["SEEKRIT_TOKEN"] },
"observability": { "enabled": true }
}
The python_workers flag is still required after GA. Pywrangler resolves
compatible packages
and vendors them for deployment. Let it select the runtime's cryptography
wheel; a native macOS or Linux wheel cannot run in the Worker.
3. Resolve inside the handler
Create src/entry.py:
from workers import Response, WorkerEntrypoint
from seekrit import SeekritError
from seekrit.cloudflare import AsyncClient
class Default(WorkerEntrypoint):
async def fetch(self, request):
if request.method != "GET":
return Response("Method not allowed", status=405, headers={"allow": "GET"})
try:
secrets = await AsyncClient(
token=getattr(self.env, "SEEKRIT_TOKEN", None)
).resolve()
except SeekritError:
return Response("Secret resolution unavailable", status=503)
# Pass secrets["YOUR_API_KEY"] to your provider client here.
# This setup check returns no secret names or values.
return Response.from_json({"configured": bool(secrets)})
This handler verifies the integration without publishing a credential.
Replace the success response with your application logic. Resolve once per
handler and pass values directly to the clients that need them. get() is also
async and performs a new resolve:
client = AsyncClient(
token=self.env.SEEKRIT_TOKEN,
timeout=10.0,
overrides={"shared": "staging"},
)
api_key = await client.get("API_KEY")
Pass api_url="https://your-seekrit-api.example.com" for a self-hosted
installation. interpolate=False returns stored reference text literally;
otherwise the SDK expands secret references after merging the app and group
layers.
The timeout covers the request and response body, and the client aborts the
underlying fetch on timeout or cancellation. Redirects are refused. API
refusals raise SeekritApiError with status and code; invalid grants or
tampered ciphertext raise SeekritCryptoError. Any failure stops resolution
without returning a partial set.
4. Run locally and deploy
Create a gitignored .dev.vars containing the development token:
SEEKRIT_TOKEN=<your development service token>
Then run:
uv run pywrangler dev
Open the local URL printed by Pywrangler. With a valid token and a nonempty
environment, the example returns {"configured": true}. Keep .dev.vars,
.venv-workers, and python_modules out of Git.
With Cloudflare authentication configured, deploy and add the production token through the interactive secret prompt:
uv run pywrangler deploy
uv run pywrangler secret put SEEKRIT_TOKEN
The example returns 503 until the token is installed. Test the deployed URL and confirm the setup response. Add or rotate a credential in Seekrit and make another request: this client has no plaintext cache, so the next resolve reads the current environment without an application redeploy.
For a named Wrangler environment, deploy and set the secret with the same
environment flag. Repeat secrets.required in the named environment's config,
because that setting is not inherited:
// Add to wrangler.jsonc
{
"env": {
"staging": {
"name": "seekrit-python-worker-staging",
"secrets": { "required": ["SEEKRIT_TOKEN"] }
}
}
}
uv run pywrangler deploy --env staging
uv run pywrangler secret put SEEKRIT_TOKEN --env staging
FastAPI
Add FastAPI to your project's dependencies:
uv add fastapi
Cloudflare's ASGI adapter puts Worker bindings in the request scope. Resolve from that request's environment:
from fastapi import FastAPI, HTTPException, Request
from workers import asgi
from seekrit import SeekritError
from seekrit.cloudflare import AsyncClient
app = FastAPI()
@app.get("/ready")
async def ready(request: Request):
env = request.scope["env"]
try:
secrets = await AsyncClient(
token=getattr(env, "SEEKRIT_TOKEN", None)
).resolve()
except SeekritError:
raise HTTPException(status_code=503, detail="Secret resolution unavailable")
return {"configured": bool(secrets)}
Default = asgi.entrypoint(app)
Keep resolution in an async route. seekrit.Client, seekrit.load(), and
into_env() are the process-oriented APIs. The existing
AsyncSeekritTransport HTTP adapters still resolve through a thread executor;
Cloudflare's threading module is not functional in Python Workers.
Use seekrit.cloudflare.AsyncClient here.
Values, rotation, and the sync alternative
Resolved credentials are plaintext in the Worker's memory, readable by code
running in that isolate. Keep them out of logs, responses, shared state, and
persistent storage. Avoid loading os.environ: it is shared process state,
which is especially risky when requests select different tenant overrides.
Derive any tenant selection from authenticated application context.
Revoking the service token prevents future resolves; it cannot erase values already copied by the application. Rotate exposed credentials at their issuers. If you add your own cache, its lifetime also determines when a secret rotation or access refusal becomes visible to the application.
If you want credentials provided as ordinary Python Worker bindings instead,
sync to Cloudflare and read
self.env.API_KEY. That path works independently of the async SDK adapter.
It uses Seekrit's explicit server-side decryption grant to push plaintext
into Cloudflare; runtime resolution decrypts in your Worker and needs only its
service token.