---
title: "Run"
description: "Inject secrets into your application at runtime without writing a .env file."
canonical_url: "https://shelve.cloud/docs/cli/run"
---
# 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.

```bash [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).

<callout type="tip">

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.

</callout>

## 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

```bash [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

<field-group>
<field name="env" type="string">

Environment to use (`development`, `preview`, `production`, …). Falls back to `defaultEnv` in `shelve.json` or `SHELVE_DEFAULT_ENV`.

</field>

<field name="template" type="string">

Path to a `.env.template` file with `shelve://` references and literal values (see below).

</field>

<field name="offline" type="boolean">

Use **only** the encrypted offline cache. Fails if no cache exists for this project/environment.

</field>

<field name="no-cache" type="boolean">

Disable cache reads and writes entirely.

</field>

<field name="cache-ttl" type="duration" default="24h">

Cache freshness override. Accepts `500ms`, `30s`, `15m`, `2h`, `7d`, or milliseconds.

</field>

<field name="watch" type="boolean">

Poll Shelve every ~5s for variable changes. Forwards `SIGHUP` to the child by default.

</field>

<field name="restart-on-change" type="boolean">

With `--watch`, kill and respawn the child instead of sending `SIGHUP` (required when the child reads `process.env` only once).

</field>
</field-group>

## Secret references (`.env.template`)

Commit a template and resolve secrets at runtime:

```bash [.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:

```bash [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`.

```bash [terminal]
shelve run --offline -- pnpm build
```

## Watch mode

```bash [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](https://shelve.cloud/docs/cli/agents-automation).

## Exit behaviour

<table>
<thead>
  <tr>
    <th>
      Signal / event
    </th>
    
    <th>
      Behaviour
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Child exits normally
    </td>
    
    <td>
      Same exit code
    </td>
  </tr>
  
  <tr>
    <td>
      Child crashes
    </td>
    
    <td>
      Exit code propagated
    </td>
  </tr>
  
  <tr>
    <td>
      Ctrl-C (<code>
        SIGINT
      </code>
      
      )
    </td>
    
    <td>
      Forwarded to child tree; graceful shutdown
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        SIGTERM
      </code>
      
       / <code>
        SIGHUP
      </code>
    </td>
    
    <td>
      Forwarded; 5s grace then <code>
        SIGKILL
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Parent process gone
    </td>
    
    <td>
      CLI tears down child tree (exit <code>
        129
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      Watch reload
    </td>
    
    <td>
      Child respawned or sent <code>
        SIGHUP
      </code>
    </td>
  </tr>
</tbody>
</table>

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


## Sitemap

See the full [sitemap](https://shelve.cloud/sitemap.md) for all pages.
