Vercel’s build step runs in Node, regardless of whether your deployed functions run on Node or Edge. That means you can capy run -- next build during build, and Next.js inlines the decrypted values into the output bundle as string literals. Request-time functions read the literals - no runtime decryption, no service hop, nothing to load.
Setup (once per project)
-
Add Capy’s CLI as a dep so Vercel installs it during build:
-
Wrap your build (and dev) in
package.json:
-
Point
next.config.js at the generated env map. During the build, capy run writes .capy/next-env.js - a CommonJS module mapping every variable name it decrypted from SECRETS_BLOB to its process.env reference. Capy only writes that file in deployed mode (both SECRETS_BLOB and PROJECT_KEY set), and it gitignores .capy/, so guard the require or a local next dev fails on the missing file:
That’s it - no hand-maintained list. next-env.js regenerates on every build from whatever the blob holds, so a newly deployed variable reaches your code without touching next.config.js.
-
Run
capy deploy and pick Vercel at the platform prompt. Vercel has a connector, so Capy asks what you want to do next - pick Set up CI deploy token + docs (capy run in your CI) for the build-time flow on this page. The other option, Deploy now via connector, takes a different route: it decrypts the current branch locally and pushes the vars you select into Vercel’s own environment-variable store with the vercel CLI, then opens a keep.lock PR that Vercel’s git integration builds on merge - no SECRETS_BLOB, no capy run in the build. capy deploy --platform vercel --mode token skips both prompts.
Capy mints two env vars - SECRETS_BLOB and PROJECT_KEY - and opens a temporary page on 127.0.0.1 in your browser showing both values plus Vercel-specific instructions. If it can’t open that page it prints the two values in the terminal instead. Paste them into Settings → Environment Variables in the Vercel dashboard (or vercel env add SECRETS_BLOB production / vercel env add PROJECT_KEY production). Repeat for Preview and Development if you want Capy to decrypt there too. Press Ctrl+C to close the page when you’re done; it also shuts itself down after five minutes.
-
Deploy. Vercel clones your branch, installs
@capysc/cli, runs capy run -- next build, and the resulting bundle has your runtime values baked in. Both Node and Edge routes read them normally.
Vercel builds each git branch independently, and each Vercel environment uses whichever SECRETS_BLOB / PROJECT_KEY pair you pasted into it. SECRETS_BLOB is an encrypted snapshot of the .env in your working directory at the moment you ran capy deploy, so to give Preview a different set of values than Production, switch to that branch with capy checkout <branch> first, run capy deploy again, and paste the new pair into the Preview environment. Adding a secret later means re-running capy deploy and updating the pair - syncing with capy alone does not change what a deployed build sees.
What happens during the build
Why this beats runtime decryption:
- Runtime functions do zero crypto - they read already-inlined constants.
- Edge / Node / any framework - the mechanism is just
process.env.
- One service call per build, not per request.
- Revocation = redeploy. Intentional; no caches to chase.
PROJECT_KEY never leaves the build step. Only the outer-wrapped portion of SECRETS_BLOB travels to Capy’s service; the service verifies the deploy token isn’t revoked and returns a one-way derived service_key. That’s combined locally with PROJECT_KEY to derive the key that actually opens the encrypted env. See Cryptography → Deploying for the exact construction.
Local development
Same wrapper, different command:
With no SECRETS_BLOB / PROJECT_KEY set, capy run takes the local path: it decrypts the .env in the working directory, which needs that project’s keep.lock alongside it. On a cloud or BYOC profile it resolves the project key through Capy’s service on every run, so you have to be signed in and the service has to be reachable; on a local-only profile it resolves the key offline and prompts for your passphrase when the local key is locked.
Local runs don’t write .capy/next-env.js - next dev reads the decrypted values straight from process.env, which is why the require in next.config.js is guarded. See Getting started → Next.js for the end-to-end local loop. Last modified on August 11, 2026