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 plaintext .env file is written by shelve run — encrypted cache files (for example under ~/.shelve/cache/) may still be created for offline use.

terminal
shelve run -- <command> [args]

The double-dash is conventional — anything after -- is passed verbatim to your command. For npm-style scripts, shelve run dev resolves the script from package.json and runs it directly (without a package-manager wrapper that prints ELIFECYCLE on Ctrl-C).

If a script exits suspiciously fast, run shelve run --debug -- pnpm dev to see the resolved command, or use shelve run -- pnpm dev to bypass script resolution.

How it works

  1. The CLI reads your token (keychain, XDG file, or SHELVE_TOKEN), resolves team / project / environment, and fetches variables (or reads the encrypted cache).
  2. Variables are merged into a fresh environment map layered on process.env.
  3. Your command is spawned with stdio: 'inherit'.
  4. The child exit code is propagated to shelve run.

On fetch failure, the CLI falls back to a fresh encrypted cache when available (unless --no-cache is set). Failures emit structured errors instead of silent exits.

Examples

terminal
shelve run --env preview -- pnpm dev
shelve run dev
shelve run -- node scripts/migrate.ts --dry-run
shelve --json run -- pnpm test   # JSON applies to CLI messages only; child keeps normal stdio

Options

env
string
Environment to use (development, preview, production, …). Falls back to defaultEnv in shelve.json or SHELVE_DEFAULT_ENV.
template
string
Path to a .env.template file with shelve:// references and literal values (see below).
offline
boolean
Use only the encrypted offline cache. Fails if no cache exists for this project/environment.
no-cache
boolean
Disable cache reads and writes entirely.
cache-ttl
duration
Cache freshness override. Accepts 500ms, 30s, 15m, 2h, 7d, or milliseconds.
watch
boolean
Poll Shelve every ~5s for variable changes. Forwards SIGHUP to the child by default.
restart-on-change
boolean
With --watch, kill and respawn the child instead of sending SIGHUP (required when the child reads process.env only once).

Secret references (.env.template)

Commit a template and resolve secrets at runtime:

.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.
  • shelve://<env>/KEY targets another environment.

Run with:

terminal
shelve run --template .env.template -- pnpm dev

Unresolved references are warned; the command still runs with literal placeholders.

Encrypted offline cache

After every successful fetch, Shelve writes an AES-256-GCM payload to ~/.shelve/cache/, keyed via HKDF from your API token.

  • Token rotation or revocation invalidates the cache.
  • Tampering is detected (GCM auth tag).
  • Files are mode 0600; directory 0700.
terminal
shelve run --offline -- pnpm build

Watch mode

terminal
# Daemons that reload on SIGHUP
shelve run --watch -- node server.js

# Node apps that need a full respawn for new env
shelve run --watch --restart-on-change -- pnpm dev

AI-agent safety

Prefer shelve run in agent shells — secrets exist only in the child process memory.

shelve pull is blocked in agent environments unless --yes is passed explicitly. See Agents & automation.

Exit behaviour

Signal / eventBehaviour
Child exits normallySame exit code
Child crashesExit code propagated
Ctrl-C (SIGINT)Forwarded to child tree; graceful shutdown
SIGTERM / SIGHUPForwarded; 5s grace then SIGKILL
Parent process goneCLI tears down child tree (exit 129)
Watch reloadChild respawned or sent SIGHUP

Signal exits (130, 143, 129) are treated as normal shutdown in many paths.