# How to Configure OpenWork Environment Variables and Dev Profiles: A Complete Guide

> Master OpenWork environment variables and dev profiles. Learn to set OPENWORK_DEV_MODE and OPENWORK_DEV_PROFILE to manage Electron data, ports, and credentials across worktrees.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-01

---

**Set `OPENWORK_DEV_MODE=1` and use `OPENWORK_DEV_PROFILE` to isolate Electron user data, CDP ports, and credentials across worktrees.**

OpenWork uses a purpose-built environment variable system to manage local development workflows, particularly when running multiple Git worktrees or isolating sensitive credentials. This article explains every variable, how the dev profile system works under the hood, and the exact commands for common scenarios based on the `different-ai/openwork` source code.

## Core Environment Variables

OpenWork recognizes six primary environment variables that control development behavior. These are defined across `.env.dev`, [`package.json`](https://github.com/different-ai/openwork/blob/main/package.json) scripts, and the Electron main process.

| Variable | Purpose | Default | Defined In |
|----------|---------|---------|------------|
| `OPENWORK_DEV_MODE` | Enables built-in dev mode with profile logic | `1` | [`.env.dev`](https://github.com/different-ai/openwork/blob/dev/.env.dev) |
| `OPENWORK_DEV_PROFILE` | Selects the dev profile (`auto`, custom name, or unset) | *unset* (legacy fallback) | [[`package.json`](https://github.com/different-ai/openwork/blob/main/package.json)](https://github.com/different-ai/openwork/blob/dev/package.json#L7) |
| `OPENWORK_ELECTRON_REMOTE_DEBUG_PORT` | CDP port for Chrome DevTools Protocol | `0` (auto-select) | [[`package.json`](https://github.com/different-ai/openwork/blob/main/package.json)](https://github.com/different-ai/openwork/blob/dev/package.json#L7) |
| `OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN` | Uses isolated mock keychain instead of OS keychain | `1` (dev worktrees) | [[`package.json`](https://github.com/different-ai/openwork/blob/main/package.json)](https://github.com/different-ai/openwork/blob/dev/package.json#L7) |
| `OPENWORK_ELECTRON_DISABLE_PROTOCOL_REGISTRATION` | Prevents custom URL protocol registration | `1` (tests) | [`dev-profile.test.mjs`](https://github.com/different-ai/openwork/blob/dev/apps/desktop/electron/dev-profile.test.mjs) |
| `OPENWORK_ELECTRON_USERDATA` | Direct override for Electron user-data directory | *unset* | [`dev-profile.test.mjs`](https://github.com/different-ai/openwork/blob/dev/apps/desktop/electron/dev-profile.test.mjs) |

In `apps/desktop/electron/main.mjs`, the Electron main process reads `process.env.OPENWORK_DEV_PROFILE` and logs the resolved profile path at startup. The `OPENWORK_ELECTRON_USERDATA` variable takes precedence over profile-based paths when explicitly set.

## How the Dev Profile System Works

When you launch `pnpm dev`, OpenWork prints a diagnostic banner:

```text
[openwork] dev profile=/Users/you/.openwork/dev-<hash> cdp=http://127.0.0.1:9223

```

The **profile directory** stores isolated Chromium data: cookies, localStorage, IndexedDB, and credential caches. This isolation prevents collisions between developers or branches.

The **CDP endpoint** (`9223` in the example) exposes Chrome DevTools Protocol for remote debugging, automated browser testing, or external tooling like `browser_navigate` and `browser_snapshot`.

### Profile Naming Modes

- **`auto`**: Hashes the current Git worktree path to generate a stable, unique directory per checkout. Ideal for parallel worktree development.
- **Custom name (e.g., `my-feature`)**: Creates a deterministic, reusable profile. Useful for CI pipelines or persistent debugging sessions.
- **Unset**: Falls back to a legacy identifier derived from the project name. Not recommended for multi-worktree setups.

The mock keychain (`OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1`) prevents macOS keychain authorization prompts during development. Disable it only when testing real OAuth flows that require OS credential storage.

## Common Development Workflows

### Single Checkout Development

Run the standard dev server with shared profile state:

```bash
pnpm dev

```

No additional environment configuration required. Uses legacy profile fallback.

### Parallel Worktree Development

Run multiple worktrees simultaneously with fully isolated profiles and auto-assigned ports:

```bash
pnpm dev:worktree

```

This script in [`package.json`](https://github.com/different-ai/openwork/blob/main/package.json) automatically sets:

```bash
OPENWORK_DEV_PROFILE=auto
OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0
OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1

```

### Named Profile with Custom Settings

Create a deterministic profile for reproducible paths or CI:

```bash
OPENWORK_DEV_PROFILE=feature-x \
OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0 \
PORT=0 \
pnpm dev

```

Setting `PORT=0` allows Vite to select a free port, preventing collisions with other instances.

### Real System Keychain for OAuth Testing

When authenticating against live providers that require OS-level credential storage:

```bash
OPENWORK_DEV_PROFILE=auth-test \
OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=0 \
pnpm dev

```

### Capturing the CDP Endpoint Programmatically

Extract the Chrome DevTools URL for external tooling:

```bash

# Start dev server and capture CDP URL

export CDP_URL=$(pnpm run -s print-cdp 2>/dev/null || echo "http://127.0.0.1:9223")

# Use in Node.js script

node -e "
const CDP = require('chrome-remote-interface');
CDP({ target: '$CDP_URL' })
  .then(client => {
    const { Network, Page } = client;
    return Page.navigate({ url: 'https://example.com' });
  })
  .catch(console.error);
"

```

## Key Source Files

| File | Role |
|------|------|
| [`.env.dev`](https://github.com/different-ai/openwork/blob/dev/.env.dev) | Base environment values including `OPENWORK_DEV_MODE` and placeholder secrets |
| [[`package.json`](https://github.com/different-ai/openwork/blob/main/package.json)](https://github.com/different-ai/openwork/blob/dev/package.json#L7) | `dev:worktree` script definition with auto-profile environment setup |
| [`apps/desktop/electron/main.mjs`](https://github.com/different-ai/openwork/blob/dev/apps/desktop/electron/main.mjs) | Profile resolution and startup logging |
| [`apps/desktop/electron/dev-profile.test.mjs`](https://github.com/different-ai/openwork/blob/dev/apps/desktop/electron/dev-profile.test.mjs) | Test suite validating profile selection, mock keychain, and protocol registration flags |
| [[`scripts/dev-web-local.sh`](https://github.com/different-ai/openwork/blob/main/scripts/dev-web-local.sh)](https://github.com/different-ai/openwork/blob/dev/scripts/dev-web-local.sh) | Web-only dev server with environment propagation to Vite |

## Summary

- Set `OPENWORK_DEV_MODE=1` to enable the development profile system (typically pre-configured in `.env.dev`).
- Use `OPENWORK_DEV_PROFILE=auto` for automatic isolation across Git worktrees, or assign custom names for reproducible environments.
- The mock keychain (`OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1`) prevents OS prompts but must be disabled for real OAuth flows.
- Override `OPENWORK_ELECTRON_USERDATA` directly when you need complete control over the Electron data directory.
- Reference `apps/desktop/electron/main.mjs` for the runtime implementation and `dev-profile.test.mjs` for expected behavior.

## Frequently Asked Questions

### What happens if I don't set `OPENWORK_DEV_PROFILE`?

OpenWork falls back to a legacy identifier derived from the project name. This works for single-checkout development but causes state collisions when running multiple worktrees simultaneously. As implemented in `different-ai/openwork`, the fallback is intentionally simple to maintain backward compatibility.

### Why does `pnpm dev:worktree` set CDP port to 0?

Setting `OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0` instructs Electron to bind to any free port, preventing conflicts when multiple dev servers run concurrently. The actual port is logged in the startup banner and can be captured via the `print-cdp` helper or parsed from stdout.

### Can I use the real macOS keychain in development?

Yes. Set `OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=0` before starting the dev server. This is necessary for testing OAuth flows that require OS-level credential persistence, but expect authorization prompts and potential state sharing between profiles.

### How do I completely override the profile directory?

Set `OPENWORK_ELECTRON_USERDATA` to an absolute path. This bypasses all `OPENWORK_DEV_PROFILE` logic and directs Electron to use your specified directory for all user data. According to `dev-profile.test.mjs`, this is primarily used in test suites for temporary directory injection.