Cloudflare Pages builds run in Node. Workers and Pages functions run in V8 isolates (no Node fs / crypto). Same pattern as Vercel: decrypt at build time, let the framework inline values into the bundle or push them to CF’s secret store. Runtime isolates see pre-baked constants - no crypto at request time.
Pages (framework build)
For Next.js on Pages via @cloudflare/next-on-pages, SvelteKit via the SvelteKit adapter, or any framework with a Pages adapter:
-
Add Capy CLI as a dep so Cloudflare installs it during build:
-
Wrap the framework build command in your Pages project’s Build command setting (Cloudflare dashboard → Pages → Settings → Builds & deployments):
Examples:
- Next.js:
capy run -- bunx @cloudflare/next-on-pages
- SvelteKit:
capy run -- bun run build
- Astro:
capy run -- bun run build
-
Mint the two build vars. Run
capy deploy, pick Cloudflare Pages, then choose Set up CI deploy token + docs (or skip both prompts with capy deploy --platform cloudflare-pages --mode token). Capy hands you SECRETS_BLOB and PROJECT_KEY; set both as Pages environment variables (Settings → Environment Variables), per environment (Production / Preview).
-
Point your framework’s env config at the generated map. For Next on Pages this is
next.config.js’s env field (same as the Vercel flow). For SvelteKit use $env/static/private. For Astro use import.meta.env. capy run emits .capy/next-env.js for Next; other frameworks read process.env during the build and inline via their own mechanism.
-
Deploy. Cloudflare runs your build command (wrapped by
capy run), decrypts during build, inlines values, ships the bundle. Workers and Pages functions read the inlined constants at request time.
capy deploy can also skip Cloudflare’s build entirely. Pick Cloudflare Pages → Deploy now via connector and the cf-pages connector runs your build command on your machine with the selected build-time vars (VITE_*, NEXT_PUBLIC_*, PUBLIC_*, REACT_APP_* are pre-selected) in its environment, then uploads the output directory with wrangler pages deploy. There’s no capy run and no SECRETS_BLOB / PROJECT_KEY in that mode - Capy injects the values into the build process itself - but wrangler has to be on your PATH, and the Pages project has to exist already (wrangler pages project create <name>).
Workers (wrangler-only projects)
Pure Workers don’t have a framework build step, so values can’t be inlined the same way. They go into Cloudflare’s Worker secret store instead, and capy deploy puts them there for you:
The cf-worker connector asks for the Worker name and the directory holding wrangler.toml, which Capy branch ships to this target, and which variables to push (non-public-prefixed ones are pre-selected). It then decrypts on your machine, pipes those values to wrangler secret bulk --name <worker>, and runs wrangler deploy --name <worker>. Pick Via CI/CD instead of Deploy directly and it pushes the secrets but leaves the code deploy to your pipeline. wrangler has to be on your PATH and authenticated - a wrangler login session or CLOUDFLARE_API_TOKEN. capy deploy --target cf-worker --yes runs the whole thing unattended, taking the Worker name and directory from wrangler.toml.
Either way, your Worker reads values the standard way via the env parameter on the fetch handler:
By hand
wrangler secret bulk reads a JSON object of name/value pairs on stdin, so you can do the same upload inside a capy run step. List the names explicitly - piping all of process.env would push everything else in your shell or CI runner into Cloudflare’s secret store:
Wrap both commands in a deploy script for reproducibility:
Why the two paths differ
- Pages: the framework adapter generates a server bundle during build. Your framework’s
env config inlines decrypted values as string literals. Runtime reads literals - no runtime crypto, nothing in CF’s secret store.
- Workers: no framework build to hook into. Decryption still happens on your machine - by the
cf-worker connector, or by capy run if you script it yourself - but the results land in CF’s Worker secret store. The Worker reads them via env.X.
Both are zero-trust on the Capy service side: PROJECT_KEY never traverses the wire to Capy. Only the outer-wrapped portion of SECRETS_BLOB goes to the service, which returns a derived service_key that capy run combines with PROJECT_KEY locally to open the encrypted env. Cloudflare is inside the trust circle in both paths - on Pages it holds SECRETS_BLOB and PROJECT_KEY in the build environment and the deployed bundle carries the values as literals; on Workers it holds them as plaintext Worker secrets.
Revocation
Every mint has a deploy id. capy deploy list prints this project’s tokens; capy deploy revoke <deployId> kills one.
- Pages: a revoked deploy token fails the next Pages build -
capy run can no longer fetch the service key. Already-deployed bundles keep their inlined values until you redeploy.
- Workers: the values are already plaintext in Cloudflare’s store, so revoking a deploy token doesn’t reach them and the Worker keeps serving. To purge, delete each secret (
wrangler secret delete <NAME>) or overwrite them with a fresh push. If your Worker deploy runs in CI off SECRETS_BLOB, revoking does stop the next CI run from decrypting.
Revocation only gates new decrypts. If the values themselves may be exposed, change them at their source - capy rotate covers the credentials you set up with capy connect - then re-run capy deploy so the new values ship. See Deploying → Revocation. Last modified on August 11, 2026