| v3.0.0
CLI

Run

Inject secrets into your application at runtime without writing a .env file.

shelve run spawns your command with every variable from your Shelve project already present in its environment. No .env file is written, no secret ever touches disk.

terminal
shelve run -- <command> [args]

The double-dash is conventional — anything after -- is passed verbatim to your command. For simple npm-style scripts, shelve run dev works too: it uses ni to detect your package manager and run the script.

How it works

  1. Shelve reads your token (from the OS keychain or the XDG config file), resolves your team / project / environment, and fetches the variables for that env.
  2. The variables are spliced into a fresh environment map and merged onto your process's environment.
  3. Your command is spawned via node:child_process.spawn, in its own process group, so Shelve can forward SIGINT / SIGTERM cleanly.
  4. The exit code of your command becomes the exit code of shelve run — seamless in CI.

Examples

terminal
# Run your dev server with production-like secrets
shelve run --env=preview -- pnpm dev

# Short form when the command is a package.json script
shelve run build

# Explicit command + flags
shelve run -- node scripts/migrate.ts --dry-run

Options

env
string
Environment to use (for example development, preview, production). Overrides defaultEnv from shelve.json.
watch
boolean
Restart the child process whenever variables change upstream. Combine with a dev server that already reloads on source edits.
offline
'auto' | 'never' | 'only'
How to use the encrypted offline cache. auto (default) refreshes variables from Shelve and falls back to the cache if the network is down. never disables the cache entirely. only refuses to make any network call.
cache-ttl
duration
How long a cached payload is considered fresh. Accepts 500ms, 30s, 15m, 2h, 7d, or a raw number in milliseconds.

Secret references (.env.template)

If a .env.template file is present in the current directory, Shelve uses it as a layout for the injected environment. Each line is either a literal value or a shelve:// reference that resolves against the current environment:

.env.template
NODE_ENV=production
DATABASE_URL=shelve://DATABASE_URL
PROD_DATABASE_URL=shelve://production/DATABASE_URL
  • Lines without shelve:// pass through verbatim.
  • shelve://KEY pulls KEY from the current environment, optionally renaming it on the left-hand side.
  • shelve://<env>/KEY explicitly targets another environment — useful for mirroring production values into a preview command.

References that cannot be resolved are reported in a grouped diagnostic and the command aborts before spawning the child, so you never run a process with partially-hydrated secrets.

Encrypted offline cache

After every successful fetch, Shelve writes the payload to ~/.shelve/cache/<hash>.cache, sealed with a key derived from your current token via HKDF. The cache is scoped per team + project + environment; switching environments uses a different key.

  • Loss of the token invalidates the cache automatically (the derived key no longer matches).
  • Tampering with the cache file is detected via AES-GCM authentication tags — a modified file fails to decrypt.
  • Files are created with 0600 permissions; the directory with 0700.

Use --offline=only on planes, in restricted CI environments, or anywhere outbound egress is blocked.

Watch mode

terminal
shelve run --watch -- node server.js

With --watch, Shelve opens a server-sent events stream and listens for variable.updated / variable.deleted events on your project. When a change lands the child process is sent SIGTERM, waits for it to exit (or is killed after a grace period), and is respawned with the new environment. This is the closest thing to a managed "hot reload for secrets".

AI-agent safety

When shelve run is invoked from a shell whose environment looks like an AI coding agent (Cursor, Claude Code, Aider, Continue, Codex, …), it prints a reminder that secrets will only exist in the spawned process's memory and will never be visible to the agent itself. Unlike shelve pull, run never prompts — running with an agent attached is safe by design.

Exit behaviour

Signal / eventBehaviour
Child exits normallyshelve run exits with the same code
Child crashesShelve propagates the exit code
Ctrl-C (SIGINT)Forwarded to the entire process group; child terminates cleanly
SIGTERMForwarded to the process group, grace period 5s, then SIGKILL
Variable change (watch mode)Child is respawned with the new env