# User-Level vs Project-Level Codex Config: Key Differences Explained

> Understand the key differences between user-level and project-level Codex config. Learn how they impact your development workflow and manage settings effectively.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: deep-dive
- Published: 2026-07-29

---

**User-level Codex configuration lives in `~/.codex` and provides machine-wide defaults, while project-level configuration resides in the current working directory and overrides those defaults for that specific repository only.**

The `openai/codex-plugin-cc` repository implements a hierarchical configuration system that distinguishes between global user preferences and repository-specific settings. Understanding how these two layers interact is essential for managing defaults across development environments while maintaining per-project flexibility. The system resolves configurations through `resolveCodexHome()` for global settings and `config/read` requests for local overrides.

## Understanding User-Level Codex Configuration

The **user-level** layer acts as the global baseline for every Codex session on your machine. According to the source code in `plugins/codex/scripts/lib/codex.mjs`, the CLI first determines where user settings live by calling `resolveCodexHome()` at lines 54-55, which defaults to `~/.codex` unless overridden by the `CODEX_HOME` environment variable.

This directory stores machine-wide defaults including:

- Default model provider and endpoint settings
- API key locations and authentication status
- Shared runtime configurations
- Global feature flags like `stopReviewGate`

When you run any Codex command, these values load first and serve as fallbacks whenever a project does not explicitly define its own settings.

## Understanding Project-Level Codex Configuration

The **project-level** layer lives inside your repository's working directory and fine-tunes behavior for that specific codebase. As implemented in `plugins/codex/scripts/lib/codex.mjs` at lines 871-876, the client sends a `client.request("config/read", { cwd })` to the app-server, which locates and returns the project-specific JSON configuration.

Project-level settings typically include:

- ** `stopReviewGate`**: Enforces mandatory code review gates for the repository
- **Custom provider aliases**: Per-project API endpoints or model selections
- **Sandbox mode**: Directory-specific read-write restrictions
- **Default models**: Overriding the global model for this codebase only

These values take precedence over user-level defaults for the duration of the command execution.

## How Configuration Precedence Works

The merging logic resides in `plugins/codex/scripts/lib/state.mjs`, specifically within the `getConfig(workspaceRoot)` function. When a plugin or hook requests configuration, the system first loads the user-level defaults from `CODEX_HOME`, then overlays any project-level values found via the `config/read` request.

A concrete example appears in `plugins/codex/scripts/stop-review-gate-hook.mjs` at line 146. The hook calls `getConfig(workspaceRoot)` and checks the `stopReviewGate` property:

- If the project config contains `{"stopReviewGate": true}`, the gate activates for that repository.
- If the project omits the flag, the hook falls back to the user-level default (typically `false`).

This **overlay pattern** ensures that project settings always win, while user defaults provide a safety net for unspecified values.

## Practical Configuration Examples

### Setting User-Level Global Defaults

Create persistent defaults that apply across all projects by writing to the Codex home directory:

```bash

# Ensure the directory exists (resolves to ~/.codex by default)

mkdir -p "$(codex home)"

# Write global configuration

cat > "$(codex home)/config.json" <<EOF
{
  "model_provider": "openai",
  "model": "gpt-4o-mini",
  "stopReviewGate": false
}
EOF

```

The `resolveCodexHome()` utility ensures these settings are available for every Codex invocation on your machine.

### Creating Project-Specific Overrides

Add a configuration file to your repository root to override global settings for team members working in that directory:

```json
{
  "stopReviewGate": true,
  "model": "gpt-4o",
  "sandbox": "read-write"
}

```

When Codex executes within this directory, the app-server (handled in `plugins/codex/scripts/lib/app-server.mjs`) processes the `config/read` request and returns these values, which then override your `~/.codex` defaults.

### Accessing Merged Configuration in Plugins

Plugin authors can retrieve the fully merged configuration using the state utility:

```javascript
import { getConfig } from "./lib/state.mjs";

function enforcePolicy(workspaceRoot) {
  // Returns merged user-level + project-level config
  const cfg = getConfig(workspaceRoot);
  
  if (cfg.stopReviewGate) {
    // Execute review gate logic specific to this repository
    console.log("Review gate enforced via project config");
  }
}

```

This pattern appears throughout the codebase, including in `plugins/codex/scripts/lib/args.mjs`, which seeds command-line options from the merged configuration object.

## Summary

- **User-level config** resides in `~/.codex` (or `CODEX_HOME`) and provides global defaults read via `resolveCodexHome()`.
- **Project-level config** lives in the repository root, loaded via the `config/read` request handled by the app-server.
- **Precedence rules**: Project settings override user defaults when merged through `getConfig(workspaceRoot)`.
- **Implementation**: The `stop-review-gate-hook.mjs` demonstrates real-world usage, checking the merged configuration to activate features only when enabled at the project level.

## Frequently Asked Questions

### Where is user-level Codex config stored?

By default, user-level configuration is stored in `~/.codex`, though you can relocate it by setting the `CODEX_HOME` environment variable. The `resolveCodexHome()` function in `plugins/codex/scripts/lib/codex.mjs` handles this resolution at lines 54-55.

### Can project-level settings override user-level defaults?

Yes. When `getConfig(workspaceRoot)` executes, it merges both layers and gives precedence to project-level values. If a setting exists in both the user home directory and the project configuration file, the project value takes effect for that repository only.

### How does the Codex CLI read project-level configuration?

The client sends a JSON-RPC request `client.request("config/read", { cwd })` to the app-server, as seen in `plugins/codex/scripts/lib/codex.mjs` at lines 871-876. The server locates the configuration file in the specified working directory and returns it for merging with global defaults.

### What happens if a configuration key is missing from the project settings?

The system falls back to the user-level default. For example, if a project does not define `stopReviewGate`, the hook in `stop-review-gate-hook.mjs` uses the value from `~/.codex/config.json` or the built-in default, ensuring continuous operation without requiring every project to explicitly define every setting.