Run
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.
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
- 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.
- The variables are spliced into a fresh environment map and merged onto your process's environment.
- Your command is spawned via
node:child_process.spawn, in its own process group, so Shelve can forwardSIGINT/SIGTERMcleanly. - The exit code of your command becomes the exit code of
shelve run— seamless in CI.
Examples
# 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
development, preview, production). Overrides defaultEnv from shelve.json.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.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:
NODE_ENV=production
DATABASE_URL=shelve://DATABASE_URL
PROD_DATABASE_URL=shelve://production/DATABASE_URL
- Lines without
shelve://pass through verbatim. shelve://KEYpullsKEYfrom the current environment, optionally renaming it on the left-hand side.shelve://<env>/KEYexplicitly 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
0600permissions; the directory with0700.
Use --offline=only on planes, in restricted CI environments, or anywhere outbound egress is blocked.
Watch mode
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 / event | Behaviour |
|---|---|
| Child exits normally | shelve run exits with the same code |
| Child crashes | Shelve propagates the exit code |
Ctrl-C (SIGINT) | Forwarded to the entire process group; child terminates cleanly |
SIGTERM | Forwarded to the process group, grace period 5s, then SIGKILL |
| Variable change (watch mode) | Child is respawned with the new env |