One token for your Cloudflare Python Worker's secrets
Integrations
Cloudflare made Python Workers generally available on September 21, 2026. A Python API can now run on Workers, use Cloudflare bindings, and keep the frameworks its team already knows. The first external API call still brings a familiar problem: where should its credential live?
For a Worker, the usual answer is a secret binding. Add a GitHub token with
wrangler secret put GITHUB_TOKEN, read self.env.GITHUB_TOKEN, and repeat
for each provider, Worker, and environment. That works. It also makes every
deployment's configuration another place to maintain the same credentials.
Seekrit's Python SDK now has an async Worker adapter. Store one service token in Cloudflare, resolve the application's environment at the point of use, and decrypt the response inside the Worker. The setup stays one binding as the application adds more credentials.
The runtime change that matters
The regular Python SDK was written for a process: it calls urllib, decrypts
with cryptography, and can load the result into os.environ. Its async HTTP
transport adapters run that synchronous resolve in a thread executor.
A Python Worker runs in Pyodide. Cloudflare exposes a native async fetch
through the workers module, and its threading module is not functional.
The integration belongs on that async path. It also needs packages compiled
for the WebAssembly runtime rather than a developer's laptop.
The new seekrit.cloudflare.AsyncClient uses workers.fetch, with a timeout
that covers the response body and an abort signal for cancellation. It
refuses redirects. The crypto implementation stays the same: Pywrangler
installs the Pyodide build of cryptography, and the client unwraps data keys,
decrypts each layer, merges the app over its groups, and expands secret
references.
That distinction is backed by tests: the adapter is exercised against the SDK's shared decryption fixture, including Unicode, empty values, layer precedence, and references. Local Worker checks cover the runtime and its actual package bundle. Those checks do not constitute a production deployment or a live Seekrit authentication test.
One binding, resolved in the handler
Create a service token for the application's Seekrit environment, with key grants for its app and composed groups. Store it through the interactive Cloudflare secret prompt:
uv run pywrangler secret put SEEKRIT_TOKEN
Then resolve inside the handler:
from workers import Response, WorkerEntrypoint
from seekrit import SeekritError
from seekrit.cloudflare import AsyncClient
class Default(WorkerEntrypoint):
async def fetch(self, request):
try:
secrets = await AsyncClient(
token=getattr(self.env, "SEEKRIT_TOKEN", None),
timeout=10.0,
).resolve()
except SeekritError:
return Response("Secret resolution unavailable", status=503)
# Use the mapping in application code; never return its values.
return Response.from_json({"configured": bool(secrets)})
The response is deliberately just a setup check. In an application, pass
secrets["GITHUB_TOKEN"] directly to the GitHub client, or use it in the
Authorization header of a request to a fixed, trusted GitHub URL. Do not
forward a credential to an incoming URL chosen by the caller.
The adapter is new in the SDK source; the previous 0.10.0 release does not
contain it. The runnable repository example
uses the local SDK until a release containing seekrit.cloudflare is
published. The Python Workers guide
has the complete Pywrangler manifests, local setup, deployment commands,
and a FastAPI route using Cloudflare's ASGI adapter.
Rotation without another application deploy
resolve() fetches on each call. Adding a key to Seekrit or rotating an
existing value changes what the next handler receives; it does not require
setting another Cloudflare binding or deploying application code again.
Resolve once when a handler needs several keys, and reuse that mapping for
the rest of the request.
The app can compose shared credentials from a group and override a tenant's environment slice:
secrets = await AsyncClient(
token=self.env.SEEKRIT_TOKEN,
overrides={"tenants": authenticated_tenant},
).resolve()
The override selects within the token's authorized composition. Choose it
from authenticated application context, and keep the resulting values local
to the request. A module-level tenant cache or os.environ.update(secrets)
would turn that separation into shared state.
There is a tradeoff: a handler that resolves depends on Seekrit being reachable. On a refusal, timeout, or decryption failure, the client fails closed. An application that introduces a cache also introduces a delay before rotation and revocation are observed, so give that cache an explicit lifetime and decide what an API refusal does to it.
When bindings are the right fit
Runtime resolution is one choice. If an existing Python Worker already reads
self.env.GITHUB_TOKEN and you want to keep that code, Seekrit's Cloudflare
sync can push the environment into
the Worker's secret bindings. Python and JavaScript read those bindings in
the same way; this path needs no Python SDK change.
Sync requires an explicit decryption grant and acknowledgement because Seekrit decrypts for the push. Runtime resolution keeps decryption in the customer's Worker. In either case, code running with the credential can read it: this integration centralizes storage and access, rather than isolating a key from untrusted code in the same Worker.
For applications and agents executing untrusted code, the Cloudflare agent integration covers keeping keys in an egress handler outside the workload. For a trusted Python API, start with the Python Workers guide: one token, an async resolve, and credentials used only where the request needs them.