# Environment variables

Environment variables let you point Roscoe at a non-default home directory, pin every service to a specific port, and select the AI backend without editing `roscoe.yaml`. Reach for them when you want to run dev and prod side-by-side, sandbox a test install, or wire Roscoe into CI without touching disk-resident config.

Every override here is read at process start. `roscoe.yaml` (see [/docs/configuration/roscoe-yaml](/docs/configuration/roscoe-yaml)) covers the same ports with persistence; env vars take precedence.

## Paths

### `ROSCOE_HOME`

Override the global home directory. Default: `~/.roscoe`. The DB (`roscoe.db`), workflows directory (`workflows/`), and global config (`roscoe.yaml`) all live underneath it.

```bash
ROSCOE_HOME=~/.roscoe-dev bun run dev
```

The dev server reads this from `.env.dev` to keep dev data isolated from a production `roscoe serve` install.

### `ROSCOE_REPO_HOME`

Last-resort fallback for the project repo root. CLI commands always derive the repo root from the current working directory's git root (this env var does not override cwd); MCP tool calls derive it from the per-call `cwd` argument. `ROSCOE_REPO_HOME` is consulted only when neither source produces a project: for example, running `roscoe serve` outside any git repo to pin it at a specific project. With this set, Roscoe treats `<ROSCOE_REPO_HOME>/.roscoe/` as the project source.

```bash
ROSCOE_REPO_HOME=/Users/me/work/myproject roscoe serve
```

### `ROSCOE_WORKFLOWS_DIR`

**Legacy.** Overrides only the **global** workflows directory (not the project one). Predates dual-source resolution; prefer `ROSCOE_HOME` unless you have a specific reason to split the workflows dir from the rest of the home.

## Ports

Each port has the same precedence: **CLI flag > env var > `roscoe.yaml` > built-in default**.

| Var               | Service                              | Default |
| ----------------- | ------------------------------------ | ------- |
| `ROSCOE_API_PORT` | `apps/server` REST API (dev)         | `8080`  |
| `ROSCOE_WEB_PORT` | `apps/web` Vite dev server           | `3000`  |
| `ROSCOE_PORT`     | `roscoe serve` combined-port default | `7777`  |

```bash
ROSCOE_PORT=4000 roscoe serve
```

Auto-fallback only applies to the built-in default: with no `--port` flag, `ROSCOE_PORT`, or `roscoe.yaml`'s `ports.serve` set, `roscoe serve` starts at 7777 and, if that's busy, tries up to 10 ports upward, printing whichever URL it lands on. Once you set the port explicitly (as above), a busy port is a hard failure: `roscoe serve` exits with `Port 4000 is in use` instead of picking a different one for you.

## AI backend

### `ANTHROPIC_API_KEY`

When set, AI nodes call the Anthropic SDK directly. When unset, Roscoe falls back to spawning the `claude` CLI as a subprocess. Both are documented in detail in [/docs/configuration/llm-backends](/docs/configuration/llm-backends).

```bash
ANTHROPIC_API_KEY=sk-ant-... roscoe run my-pipeline
```

In MCP mode `ai_agent` / `ai_judge` nodes don't call either backend: Claude itself supplies the response. `consensus` nodes hand their fan-out back to subagent-capable hosts (Claude Code / Cowork) the same way. It needs one of the two backends only when it runs server-side: in standalone mode, or when driven from Claude desktop chat. See [/docs/running/subscription](/docs/running/subscription).

## Analytics

Product telemetry — minimized, pseudonymous usage data plus optional crash
reports — ships **on by default**; see the
[`analytics`](/docs/configuration/roscoe-yaml#analytics) config section for the
two switches and what each does and doesn't collect. These variables override
that config.

### `DO_NOT_TRACK` / `ROSCOE_ANALYTICS`

Either hard-disables telemetry regardless of the `analytics.enabled` setting.
Set `DO_NOT_TRACK=1` (the cross-tool [convention](https://consoledonottrack.com/))
or `ROSCOE_ANALYTICS=off`.

```bash
DO_NOT_TRACK=1 roscoe serve
```

### `ROSCOE_POSTHOG_KEY` / `ROSCOE_POSTHOG_HOST`

`ROSCOE_POSTHOG_KEY` is the PostHog project ingest key telemetry is sent to.
**Telemetry is fully off unless this is set** — no key, no events, and the
client is never even constructed. `ROSCOE_POSTHOG_HOST` overrides the ingestion
host (default `https://us.i.posthog.com`; set `https://eu.i.posthog.com` for the
EU region).

## Internal

### `ROSCOE_SWEEP_TEST_RUNS`

Set to `1` to enable the `test_workflow` orphan-row sweep on MCP startup. Both MCP entry points (`roscoe mcp` and the dev `bun apps/cli/src/index.ts mcp`) set this automatically; you should not need to set it by hand. It exists as an opt-in so unit tests that exercise the MCP boot path don't accidentally clobber a developer's running install.

### `ROSCOE_RUN_DIR_TTL_HOURS`

How long a finished run's temporary folder (`<ROSCOE_HOME>/runs/<runId>/`, exposed to script nodes as `$ROSCOE_RUN_DIR`) is kept before it's cleaned up automatically on the next startup. Default `168` (7 days). Lower it to reclaim disk sooner or shorten how long this temporary data lingers. It's temporary working space. The durable record is always the run's node outputs in the database.

### `ROSCOE_CAPTURE_BROWSER`

Path to a Chromium executable for the recording renderer to use instead of the one it downloads into `<ROSCOE_HOME>/browsers/`. Skips every check the download makes; whoever sets it owns the pixels. See [/docs/running/record-a-run](/docs/running/record-a-run).

### `ROSCOE_RUN_DIR_ORPHAN_GRACE_MS`

Grace period before cleanup removes a run folder that has **no** matching run row (left over, e.g. from a deleted run). Default `3600000` (1 hour). The grace is keyed on the folder's modification time so a folder a concurrent process just created is never removed.

## See also

- [/docs/configuration/roscoe-yaml](/docs/configuration/roscoe-yaml) — port overrides with disk persistence.
- [/docs/configuration/workflow-sources](/docs/configuration/workflow-sources) — how `ROSCOE_REPO_HOME` and `ROSCOE_HOME` shape workflow resolution.
- [/docs/configuration/llm-backends](/docs/configuration/llm-backends) — what `ANTHROPIC_API_KEY` selects.
