claude-home/workstation/claude-code-multi-account.md
Cal Corum b7ed0f8435
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 4s
docs: sync KB — claude-code-multi-account.md
2026-03-25 16:00:43 -05:00

7.3 KiB

title description type domain tags
Claude Code Multi-Account Setup with CLAUDE_CONFIG_DIR Guide to running multiple Claude Code OAuth accounts simultaneously using CLAUDE_CONFIG_DIR, direnv, and symlinked config directories. guide workstation
claude-code
oauth
direnv
config
multi-account

Claude Code Multi-Account Setup

Run two different Claude OAuth accounts simultaneously — one for specific projects, another for everything else — using the CLAUDE_CONFIG_DIR environment variable and direnv.

How CLAUDE_CONFIG_DIR Works

CLAUDE_CONFIG_DIR is an undocumented but fully implemented environment variable in the Claude Code binary. It controls where Claude Code looks for its entire configuration directory.

Behavior When Set

Aspect Default With CLAUDE_CONFIG_DIR
Config directory ~/.claude $CLAUDE_CONFIG_DIR
Global state file ~/.claude.json $CLAUDE_CONFIG_DIR/.config.json
OAuth credentials ~/.claude/.credentials.json $CLAUDE_CONFIG_DIR/.credentials.json
Keychain entry name Claude Code Claude Code-<hash> (8-char hash of config path)

Internal Implementation

From the Claude Code binary source:

Config dir resolution:

process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude")

Global state file (.config.json fallback):

// When CLAUDE_CONFIG_DIR is set, checks for .config.json first
if (existsSync(path.join(configDir, ".config.json")))
  return path.join(configDir, ".config.json");

Keychain collision avoidance:

// Adds hash suffix when CLAUDE_CONFIG_DIR is set
let suffix = !process.env.CLAUDE_CONFIG_DIR ? ""
  : `-${hash(configDir).substring(0, 8)}`;
return `Claude Code${suffix}`;

Propagated to subagents: CLAUDE_CONFIG_DIR is in the explicit list of env vars passed to child processes (subagents, background agents), ensuring they use the same config directory.

Setup Procedure

Prerequisites

  • Claude Code installed (native binary at ~/.local/bin/claude)
  • direnv (sudo dnf install direnv on Fedora/Nobara)

1. Create Alternate Config Directory

mkdir -p ~/.claude-ac

The -ac suffix stands for "alternate config" — name it whatever you like.

Share configuration between accounts so both get the same CLAUDE.md, settings, MCP servers, hooks, etc.:

# Core config files
ln -s ~/.claude/CLAUDE.md ~/.claude-ac/CLAUDE.md
ln -s ~/.claude/settings.json ~/.claude-ac/settings.json
ln -s ~/.claude/command-permissions.json ~/.claude-ac/command-permissions.json

# Directories (agents, commands, hooks, skills, patterns, memory, plugins)
for dir in agents commands hooks skills patterns memory plugins; do
  ln -s ~/.claude/$dir ~/.claude-ac/$dir
done

# MCP servers + global state
# When CLAUDE_CONFIG_DIR is set, Claude reads .config.json instead of ~/.claude.json
ln -s ~/.claude.json ~/.claude-ac/.config.json

These should remain independent per account:

Path Why independent
.credentials.json Different OAuth tokens per account
projects/ Session state tied to account
sessions/ Active session registry
history.jsonl Conversation history
todos/, tasks/, plans/ Conversation-scoped data
debug/, telemetry/, logs/ Per-instance diagnostics

These directories are created automatically by Claude Code on first use — no need to pre-create them.

3. Hook direnv Into Your Shell

Fish (~/.config/fish/config.fish, inside if status is-interactive):

direnv hook fish | source

Bash (~/.bashrc):

eval "$(direnv hook bash)"

4. Create .envrc for the Target Directory

For example, to use the alternate account in ~/work:

# ~/work/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.claude-ac"

Allow it:

direnv allow ~/work/.envrc

direnv automatically sets/unsets the env var when you cd into or out of ~/work (and all subdirectories).

5. Log In With the Second Account

From within the target directory (where direnv activates):

cd ~/work
claude auth login

This stores OAuth tokens in ~/.claude-ac/.credentials.json, completely separate from the primary account.

6. Verify

# In ~/work — should show alternate account
cd ~/work && claude auth status

# Outside ~/work — should show primary account
cd ~ && claude auth status

Both accounts can run simultaneously in separate terminal windows.

Current Configuration on This Workstation

Location Account Purpose
~/.claude Primary (cal.corum@gmail.com) All projects except ~/work
~/.claude-ac Alternate ~/work projects
~/work/.envrc direnv trigger for CLAUDE_CONFIG_DIR

How It All Fits Together

Terminal in ~/work/some-project/
  ↓ cd triggers direnv
  ↓ CLAUDE_CONFIG_DIR=~/.claude-ac
  ↓ claude starts
  ├── Config dir: ~/.claude-ac/
  ├── Auth: ~/.claude-ac/.credentials.json (alt account)
  ├── Settings: ~/.claude-ac/settings.json → symlink → ~/.claude/settings.json
  ├── MCP servers: ~/.claude-ac/.config.json → symlink → ~/.claude.json
  ├── Hooks: ~/.claude-ac/hooks/ → symlink → ~/.claude/hooks/
  └── Keychain: "Claude Code-a1b2c3d4" (hashed, no collision)

Terminal in ~/other-project/
  ↓ no .envrc, CLAUDE_CONFIG_DIR unset
  ↓ claude starts
  ├── Config dir: ~/.claude/ (default)
  ├── Auth: ~/.claude/.credentials.json (primary account)
  └── Keychain: "Claude Code" (default)

Troubleshooting

Auth shows the wrong account

Check that direnv loaded correctly:

echo $CLAUDE_CONFIG_DIR
# Should show ~/.claude-ac when in ~/work, empty otherwise

If empty, ensure:

  1. direnv hook is in your shell config
  2. You opened a new shell after adding the hook
  3. direnv allow ~/work/.envrc was run

MCP servers not loading

Verify the .config.json symlink:

ls -la ~/.claude-ac/.config.json
# Should point to ~/.claude.json

direnv doesn't activate in subdirectories

direnv walks up the directory tree, so ~/work/.envrc covers all of ~/work/**. If it doesn't activate:

direnv status  # Shows which .envrc is loaded and why

Need to re-login

cd ~/work  # Ensure direnv sets the env var
claude auth logout
claude auth login

Subagents use wrong account

CLAUDE_CONFIG_DIR is in Claude Code's propagated env var list, so subagents inherit it automatically. If a subagent somehow uses the wrong account, verify the parent process has CLAUDE_CONFIG_DIR set.

Caveats

  • Undocumented feature: CLAUDE_CONFIG_DIR does not appear in claude --help or official docs. However, it is deeply integrated into the binary (config resolution, keychain naming, subagent propagation), suggesting it's intentional infrastructure.
  • Version sensitivity: When Claude Code updates add new shared config files to ~/.claude/, the alternate config directory won't automatically get symlinks for them. Periodically check for new files that should be symlinked.
  • Session isolation: Even with symlinked memory, session history and project state are per-config-dir. Each account maintains its own conversation history.