Cloudflare Workers
Generate a deployment target on Node.js and validate Wrangler bindings directly inside a Cloudflare Worker.
Env 0.3.0 admits a narrow Cloudflare Workers runtime path: a generated server target for the deployment lifecycle may call check(env) or load(env) inside a Worker handler when every selected entry uses a first-party Env codec.
Authoring, generation, CLI checks, and planning still run on a supported Node.js release. The Worker imports the generated target and the @astilba/env/runtime dependency behind it; it does not import the root package, browser runtime, CLI, or Vite integration.
Declare one deployment target
Section titled “Declare one deployment target”Declare Worker configuration on Node.js:
import { defineEnvironment, env } from "@astilba/env";
export default defineEnvironment({ id: "com.example.worker", entries: { apiOrigin: env.public.deployment.origin(), signingKey: env.private.deployment.secret(), }, consumers: { worker: env.server(["apiOrigin", "signingKey"]), }, targets: { workerDeployment: env.process("worker", { apiOrigin: "API_ORIGIN", signingKey: "SIGNING_KEY", }), },});Generate and commit the application-owned output:
pnpm exec astilba-env generatepnpm exec astilba-env generate --checkThis creates .astilba/env/workerDeployment.server.ts. Generation does not read Worker bindings for a deployment target and does not contact Cloudflare.
Declare Wrangler bindings
Section titled “Declare Wrangler bindings”Keep non-secret values in vars, and declare required secret names with secrets.required:
{ "$schema": "node_modules/wrangler/config-schema.json", "name": "example-worker-staging", "main": "src/index.ts", "compatibility_date": "2026-07-29", "vars": { "API_ORIGIN": "https://staging-api.example.com" }, "secrets": { "required": ["SIGNING_KEY"] }, "kv_namespaces": [ { "binding": "CACHE", "id": "<your-staging-cache-kv-namespace-id>" } ]}Configure the secret value through Cloudflare, outside source control. secrets.required records the required name; it does not contain the secret.
The same built Worker artifact can use another binding set:
{ "$schema": "node_modules/wrangler/config-schema.json", "name": "example-worker-production", "main": "src/index.ts", "compatibility_date": "2026-07-29", "vars": { "API_ORIGIN": "https://api.example.com" }, "secrets": { "required": ["SIGNING_KEY"] }, "kv_namespaces": [ { "binding": "CACHE", "id": "<your-production-cache-kv-namespace-id>" } ]}Env validates values at runtime. Its provider-neutral inventory command can compare the declared string names with an application-supplied name list; it does not call Cloudflare, inspect binding kinds, or decide when a changed set should redeploy.
Choose the latest compatibility date supported by your installed Wrangler release. Env does not impose its own date floor. The 0.3.0 release matrix exercises the exact packed archive with stock Wrangler 4.115.0 and its bundled workerd at compatibility date 2026-07-29; it does not replace Wrangler’s transitive runtime.
Generate Cloudflare’s binding types
Section titled “Generate Cloudflare’s binding types”Generate the Worker Env interface from Wrangler configuration:
pnpm exec wrangler typespnpm exec wrangler types --checkwrangler types derives binding types from your configuration. Use --check in CI, so the generated interface cannot drift.
The generated target accepts that interface without requiring a string index signature:
import { check } from "../.astilba/env/workerDeployment.server";
export default { async fetch(request: Request, env: Env): Promise<Response> { const result = check(env);
if (!result.ok) { return Response.json( { diagnostics: result.diagnostics, ok: false }, { status: 500 } ); }
const cached = await env.CACHE.get(request.url);
return Response.json({ apiOrigin: result.value.apiOrigin, cached: cached !== null, }); },} satisfies ExportedHandler<Env>;check(env) reads only API_ORIGIN and SIGNING_KEY, then returns an exact owned result. The unrelated CACHE capability binding remains available to application code and does not enter the configuration result.
Use load(env) instead when invalid deployment configuration should throw. Do not log the original env object on failure.
Pass string bindings without coercion
Section titled “Pass string bindings without coercion”Every source binding selected by the generated target must be a string or undefined. A missing optional binding may be undefined; a present binding is decoded from its exact string value.
Cloudflare also allows JSON values in vars and objects for capability bindings. Env does not stringify or coerce those values. If a selected source name resolves to a JSON value, KV namespace, D1 database, service binding, or another capability object, check returns a redacted invalid-value diagnostic and load throws.
Unselected bindings are different: Env does not read or reject them. Keep CACHE and other capabilities outside the target mapping, then use them directly through the Wrangler-generated Env interface.
Keep the compatibility surface narrow
Section titled “Keep the compatibility surface narrow”Env’s generated runtime does not need the nodejs_compat compatibility flag for this path. Add that flag only when other application dependencies require Node.js APIs.
The Env 0.3.0 Workers claim includes:
- generated server targets for the
deploymentlifecycle; - first-party Env codecs; and
- direct
check(env)orload(env)calls inside the handler.
It does not include:
- request-lifecycle generated targets;
opaqueentries or caller-provided Standard Schema validators;- the root
@astilba/envauthoring export in workerd; @astilba/env/browseror@astilba/env/vitein workerd; or- the Env CLI in workerd.
Know what Env does not operate
Section titled “Know what Env does not operate”Env does not provide:
- Worker, route, or binding provisioning;
- secret storage or rotation;
- a live Cloudflare binding query or Wrangler-specific parser;
- a Cloudflare provider API client; or
- automatic deployment or redeployment planning.
Wrangler and Cloudflare own those operations. If duplicated string-binding names are the problem, convert a Wrangler-produced name list into Env’s provider-neutral observed format and follow Check name inventory drift. See the official Wrangler configuration reference for binding configuration and required-secret behavior.