Skip to main content

Synopsis

Everything after -- is passed through to the child process verbatim. capy run has no --help of its own, so capy run --help forwards --help to the child rather than printing Capy’s help. With no command at all it prints Usage: capy run -- <command> [args...] and exits 1.

Description

capy run decrypts your project’s secrets in memory and spawns the given command with them set as environment variables. Your app reads env vars the normal way for its runtime - process.env, os.environ, ENV["KEY"], std::env::var, whatever. This is Capy’s single runtime mechanism - works for any language, any framework, local dev through production, no per-language SDK to install. Plaintext values live only in the child process’s memory and are never written to disk.

Two modes

capy run auto-detects which mode to run in based on what’s in process.env:

Local mode (default)

When SECRETS_BLOB and PROJECT_KEY are not set, capy run:
  1. Reads .env from the current working directory.
  2. If nothing in .env is encrypted, spawns the child right there - no key, no keep.lock, no network.
  3. Otherwise reads keep.lock in that same directory to learn which org and project it belongs to.
  4. Resolves your project key. On a cloud or BYOC profile that means authenticating silently against your existing session (refreshing the access token if it has expired) and resolving the key through the Capy service. On a local-only profile it happens entirely offline from your local key, with a passphrase prompt if the session is locked.
  5. Decrypts each capy:… snippet in .env and spawns the child with the decrypted values set in its environment.
This is the mode for local development. Run capy once in the directory to sync; capy run works from then on.
On a cloud or BYOC profile, local mode needs the Capy service reachable and your session valid on every run. There is no offline key cache and no CAPY_KEY override. That is deliberate: it means every decryption is authorized and audit-logged at the moment it happens, so removing someone’s access takes effect on their very next capy run rather than whenever a cached key happens to expire.The practical consequence for unattended machines: if the session is no longer valid, capy run exits with capy run: not authenticated. Run capy to sign in. and your process does not start. On a server you restart automatically, plan for that — or use deployed mode, which authenticates with a deploy token instead of a human session.A local-only profile is the exception: it resolves the project key offline from your passphrase-protected local key, so no service is contacted and capy run keeps working after capy lock (it just prompts for the passphrase).

Deployed mode

When both SECRETS_BLOB and PROJECT_KEY are set in process.env, capy run:
  1. Parses SECRETS_BLOB locally - extracts the deploy ID, the service-held outer blob, and the encrypted env map.
  2. Posts the outer blob to POST /deploy/{deployId}/decrypt on the Capy service. The service verifies the deploy token isn’t revoked and returns a derived service key.
  3. Combines PROJECT_KEY with the service key to derive the decrypt key, then AES-GCM-decrypts the env map.
  4. Spawns the child with those values set in its environment.
This is the mode for CI builds and production deploys. The two env vars come from capy deploy. No local .env file, no local key state on disk, no interactive auth.
If only one of SECRETS_BLOB / PROJECT_KEY is set, capy run exits with an error rather than silently falling back to local mode. Makes platform misconfiguration loud instead of silent.

Behavior

In both modes, capy run:
  • Forwards SIGINT, SIGTERM, and SIGHUP to the child.
  • Exits with the child’s exit code.
Precedence differs between the two modes:
  • Deployed mode: variables already set in the environment win over every value decrypted from SECRETS_BLOB. Platform-level overrides stay sovereign.
  • Local mode: plaintext keys in .env follow the same rule - the parent environment wins. Encrypted capy:… values do not: they’re re-applied after the merge, so a project secret always overrides a same-named variable inherited from your shell.

Examples

In containers

capy run can serve as the Docker entrypoint:
Make sure SECRETS_BLOB and PROJECT_KEY (from capy deploy) are present in the container’s environment at runtime.

Next.js on Vercel

In deployed mode, capy run also writes .capy/next-env.js before spawning the child. That file maps each decrypted variable name to its process.env reference. In next.config.js, guard the require since the file exists only in deployed builds (not in local dev):
Next.js inlines each value as a string literal at build time. No per-variable list to maintain in next.config.js. See Deploying → Vercel for the full walkthrough.

See also

Last modified on August 11, 2026