---
title: "Env and config"
description: "Required env vars, .env precedence, the global config file, and the ralphy config verb."
---

`ralphy` reads two API keys, a handful of optional environment overrides, the project's `.env`, and a per-workspace JSON config. The precedence is fixed and documented here so a missing key in CI never silently picks up a different value than you expected.

## Required env vars

Two keys gate every meaningful operation:

<ParamField path="OPENROUTER_API_KEY" type="string" required>
  OpenRouter API key for every LLM + media model call. Get one at [openrouter.ai/keys](https://openrouter.ai/keys). `ralphy setup` pings the key against `GET /api/v1/auth/key` to verify.
</ParamField>

<ParamField path="ELEVENLABS_API_KEY" type="string" required>
  ElevenLabs API key for voiceover, music, and SFX generation, plus the Scribe transcription default. Get one at [elevenlabs.io/app/settings/api-keys](https://elevenlabs.io/app/settings/api-keys). `ralphy setup` pings against `GET /v1/user`.
</ParamField>

Both are required for a green `ralphy doctor`. The CLI raises `E_ENV_KEY_MISSING` (exit 4) the first time it needs a missing key.

## Optional env overrides

| Var | What it controls | Default |
|---|---|---|
| `RALPHY_PROJECT_DIR` | Override project auto-detection — point at a specific checkout. | (auto-detected) |
| `RALPHY_HOME` | Root for `~/.ralphy/` — projects from `ralphy new`, global config. | `$HOME/.ralphy` |
| `RALPHY_REPO_ROOT` | Repo root for `ralphy skill new` and other scaffold verbs. | `process.cwd()` |
| `RALPHY_BIN` | The path the installer recorded as the binary path. | (set by install.sh) |
| `RALPHY_BIN_DIR` | The directory containing the binary. Used to detect Homebrew installs. | (set by installer) |
| `RALPHY_ASSETS_MANIFEST_URL` | Override the companion-repo manifest URL. | (compiled-in default) |
| `RALPHY_DOCTOR_NO_UPDATE_CHECK` | Set to `1` to skip the 24h update check in `doctor`. | unset |
| `NO_COLOR` | Honored by `chalk` — disables ANSI colors when set to any value. | unset |
| `HOME` | Resolves `~`. Used by Claude installer + skill detection. | (system) |

You'll rarely need any of these — defaults are right for both dev and binary installs.

## .env files

Every project carries a `<project>/.env`. When ralphy resolves a project root (see [CLI overview](/cli/overview)), it reads `.env` and merges keys into `process.env` — **but only for keys not already set**. Real shell-exported variables always win.

Order of precedence, lowest to highest:

1. Compiled-in default (e.g. `RALPHY_HOME = $HOME/.ralphy`).
2. `<project>/.env`.
3. Real environment variables (`export FOO=...` in your shell, `env FOO=...` prefix on the command).
4. CLI flag (`--cwd <path>` for the project root).

A typical `.env`:

```bash
OPENROUTER_API_KEY=sk-or-...
ELEVENLABS_API_KEY=xi-...
OPENROUTER_REFERER=https://yourdomain.example
OPENROUTER_TITLE=My UGC pipeline
```

`OPENROUTER_REFERER` and `OPENROUTER_TITLE` are optional headers OpenRouter uses for usage analytics. `ralphy setup` doesn't manage them — set them by hand if you want them tracked.

## .envrc + direnv

If you use [direnv](https://direnv.net), a `.envrc` at the project root will auto-load when you `cd` in. The CLI doesn't depend on direnv — `.env` is read directly — but the two coexist fine. `ralphy setup` writes to `.env`; if you keep `.envrc` in sync manually, your shell sees the keys without launching ralphy.

## Global config file

Two files matter:

- **`~/.config/ralphy/config.json`** — global. Holds `default_project_dir` (the link from `ralphy setup --link <path>`) and `imports[]` (profiles you've imported).
- **`<project>/.ralphy/config.json`** — per-workspace. Free-form key/value, managed by `ralphy config`.

The two are separate by design: global config is about *which* project ralphy points at; workspace config is about *how* that project behaves.

## ralphy config

```bash
ralphy config list                                         # everything
ralphy config get budgets.singleProject                    # dotted-path lookup
ralphy config set budgets.singleProject 5                  # auto-typed (number)
ralphy config set doctor.checkUpdates false                # auto-typed (boolean)
ralphy config set notes.lastBatch "spring-2026-001..010"   # string
```

`set` does best-effort type inference: `true`/`false` become booleans, parseable numbers become numbers, everything else stays a string. Dotted paths build the nested object on the fly.

Full surface: [`/reference/cli/config`](/reference/cli/config).

## Reading config from code

The CLI uses `cli/lib/config.ts` — `loadConfig()`, `saveConfig()`, `getNestedValue()`, `setNestedValue()`. The on-disk shape is unscoped JSON; the verb interprets dotted keys.

Common keys today:

| Key | Type | Used by |
|---|---|---|
| `doctor.checkUpdates` | bool | `ralphy doctor` — set to `false` to skip the 24h update check |
| `budgets.<scope>` | number | `E_BUDGET_EXCEEDED` gate (per-project / per-batch caps) |
| `imports[]` | array | global config only — populated by `ralphy setup --import-profile` |

There's no enforced schema. The CLI reads what it expects and ignores the rest.

## Putting it together

When `ralphy generate image` runs, it sees:

1. `--cwd` (flag) — if set, this is the project root.
2. Walk up from cwd looking for `package.json#name = "ugc-cli"`.
3. `RALPHY_PROJECT_DIR` (env) — if set and valid.
4. `~/.config/ralphy/config.json#default_project_dir` — if set and valid.

Then it reads `<root>/.env` (without overwriting `process.env`), checks `OPENROUTER_API_KEY` is set (raises `E_ENV_KEY_MISSING` if not), and proceeds.

## Related

- [Setup and doctor](/cli/setup-doctor) — how keys land in `.env`
- [CLI overview](/cli/overview) — `--cwd` and project auto-detection
- [Error catalog](/cli/error-catalog) — `E_ENV_KEY_MISSING`, `E_PROJECT_NOT_LINKED`
- [cli/lib/project-root.ts](https://github.com/alecs5am/ralphy/blob/main/cli/lib/project-root.ts), [cli/commands/config.ts](https://github.com/alecs5am/ralphy/blob/main/cli/commands/config.ts)
