Skip to main content

Synopsis

The -- separator is optional. Capy passes the child command and its arguments through unchanged, including the child’s own flags. capy run has no Capy-specific options and 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. Capy decrypts values in memory and passes them to the child process. It does not write decrypted values to disk; your application can still choose to do so.

Two modes

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

Local mode (default)

When neither complete deployed runtime pair is 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 either complete runtime pair is set in process.env, capy run enters deployed mode:
  • SECRETS_BLOB and PROJECT_KEY are emitted by the token-and-instructions flow.
  • _SECRETS_BLOB and _PROJECT_KEY are also supported; when both complete pairs are present, Capy uses the underscore-prefixed pair.
It then:
  1. Parses the selected 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 the selected 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 runtime pair comes from capy deploy. No local .env file, local key state, or interactive auth is required.
Each runtime pair must be complete. If only one value from either pair is set, capy run exits instead of falling back to local mode. When both pairs are complete, the underscore-prefixed pair is used.

Behavior

In both modes, capy run:
  • Forwards SIGINT, SIGTERM, and SIGHUP to the child.
  • Exits with the child’s exit code.
Precedence differs between local mode and the deployed runtime pairs:
  • Deployed mode with SECRETS_BLOB / PROJECT_KEY: variables already set in the environment win over decrypted values. Platform-level overrides stay sovereign.
  • Deployed mode with _SECRETS_BLOB / _PROJECT_KEY: decrypted values win over same-named values already in the environment. This lets a platform retain an older plaintext variable while the new runtime pair takes effect.
  • 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 one complete runtime pair from capy deploy is 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 Build-time inlining for the walkthrough.

See also

Last modified on October 2, 2026