Skip to content

Multi-Identity Management

Managing multiple AI accounts - Complete guide to setting up and switching between personal, educational, and work AI identities.


Table of Contents


Overview

What Is Multi-Identity?

Multi-Identity is the ability to use different AI accounts for different purposes without rebuilding Docker containers or logging out.

Example use cases: - Personal projects → Use personal Claude account - University assignments → Use educational Gemini account - Work projects → Use company-provided OpenAI account - Testing → Switch between accounts to test different API quotas

Supported Identities

AI Agent Identity Types Storage Format
Gemini CLI edu1, edu2, edu3, edu4 OAuth JSON files
Claude Code personal, edu1, edu2, work Session JSON files
OpenAI/ChatGPT chatgpt, edu1, edu2, work Session JSON files

The Problem

Without Multi-Identity

Old approach (single account only):

# ~/.config/claude/config.json - one file, one account
# To switch accounts:
# 1. Logout from Claude
# 2. Login with different account
# 3. Overwrite config.json
# 4. Restart container
# 5. Repeat for every switch

Problems: - ❌ Time-consuming to switch - ❌ Easy to forget which account is active - ❌ Risk of using wrong account for a project - ❌ Can't have multiple sessions simultaneously

With Multi-Identity

New approach (file-based identity switching):

# Multiple files, one per account
~/.claude/session-personal.json
~/.claude/session-edu1.json
~/.claude/session-work.json

# dev() prompts: "Which Claude account?"
# You select: edu1
# dev() mounts: ~/.claude/session-edu1.json
# Inside container: Claude uses edu1 account

Benefits: - ✅ Instant switching (just select in dev()) - ✅ Always know which account is active - ✅ No logout/login cycles - ✅ Each project can use different accounts


The Solution

File-Based Identity Storage

Key insight: Store each account's credentials in separate files on the host, then mount the desired file at runtime.

How it works:

  1. Setup Phase (one-time):
  2. Authenticate each AI account separately
  3. Save credentials to named files (edu1, personal, work, etc.)
  4. Organize in ~/.gemini/, ~/.claude/, ~/.openai/

  5. Runtime Phase (every dev() session):

  6. dev() prompts: "Which account?"
  7. You select: e.g., "edu1"
  8. dev() mounts that specific file into container
  9. Agent uses that identity

  10. Switching (anytime):

  11. Exit container
  12. Run dev() again
  13. Select different account
  14. New container uses new identity

Directory Structure

Complete Layout

~/.gemini/                      # Gemini OAuth credentials
├── oauth-edu1.json             # Educational account 1
├── oauth-edu2.json             # Educational account 2
├── oauth-edu3.json             # Educational account 3
└── oauth-edu4.json             # Educational account 4

~/.claude/                      # Claude session files
├── session-personal.json       # Personal account
├── session-edu1.json           # Educational account 1
├── session-edu2.json           # Educational account 2
└── session-work.json           # Work/company account

~/.openai/                      # OpenAI/ChatGPT session files
├── session-chatgpt.json        # Personal ChatGPT account
├── session-edu1.json           # Educational account 1
├── session-edu2.json           # Educational account 2
└── session-work.json           # Work/company account

Create Directory Structure

Run on server:

# Create directories
mkdir -p ~/.gemini ~/.claude ~/.openai

# Verify
ls -la ~/.gemini ~/.claude ~/.openai


Gemini Identity Setup

Prerequisites

  • Google account (personal or educational)
  • Access to Google Cloud Console (for OAuth)
  • Gemini CLI installed (pre-installed in Docker image)

Step 1: Obtain OAuth Credentials

Option A: Use Existing OAuth File

If you already have a Gemini OAuth file from another machine:

# Copy from local machine to server
scp ~/path/to/oauth.json kavi@100.80.53.55:~/.gemini/oauth-edu1.json

Option B: Create New OAuth Credentials

  1. Go to Google Cloud Console
  2. Create new project or select existing
  3. Enable Gemini API
  4. Create OAuth 2.0 Client ID (Desktop app)
  5. Download JSON file
  6. Rename and upload:
    # On local machine
    mv ~/Downloads/client_secret_*.json oauth-edu1.json
    scp oauth-edu1.json kavi@100.80.53.55:~/.gemini/
    

Step 2: Authenticate

On server:

# Create temp container to authenticate
docker run --rm -it \
  -v ~/.gemini:/root/.gemini \
  my-dev-env:latest \
  /bin/bash

# Inside container
gemini auth login --credentials /root/.gemini/oauth-edu1.json

# Follow browser prompts to authenticate
# This updates oauth-edu1.json with refresh token
exit

Step 3: Verify

# Test authentication worked
docker run --rm \
  -v ~/.gemini/oauth-edu1.json:/root/.gemini/oauth_creds.json:ro \
  -e "GOOGLE_API_KEY=your-api-key" \
  my-dev-env:latest \
  gemini --version

Repeat for Additional Accounts

For edu2, edu3, edu4:

# Repeat steps with different Google accounts
# Save as oauth-edu2.json, oauth-edu3.json, oauth-edu4.json


Claude Identity Setup

Prerequisites

  • Anthropic account (personal, educational, or work)
  • Claude Code installed (pre-installed in Docker image)
  • ANTHROPIC_API_KEY in Infisical

Step 1: Authenticate Account

On server:

# Create temp container with Infisical secrets
infisical export --env=dev --projectId=personal-vault > /tmp/env-$$.list

docker run --rm -it \
  --env-file /tmp/env-$$.list \
  -v ~/.claude:/root/.config/claude \
  my-dev-env:latest \
  /bin/bash

# Inside container
claude login

# Follow prompts (uses ANTHROPIC_API_KEY from env)
# This creates /root/.config/claude/config.json
exit

rm /tmp/env-$$.list

Step 2: Save Session with Identity Label

# Rename to indicate which account
mv ~/.claude/config.json ~/.claude/session-personal.json

Step 3: Repeat for Other Accounts

For educational account:

# Get educational ANTHROPIC_API_KEY
# (If different from personal - otherwise same file works)

# Re-authenticate
docker run --rm -it \
  -e "ANTHROPIC_API_KEY=sk-ant-edu-..." \
  -v ~/.claude:/root/.config/claude \
  my-dev-env:latest \
  claude login

# Save as edu1
mv ~/.claude/config.json ~/.claude/session-edu1.json

For work account:

# Same process with work ANTHROPIC_API_KEY
# Save as session-work.json

Step 4: Verify

# Test each session file works
docker run --rm \
  -e "ANTHROPIC_API_KEY=sk-ant-..." \
  -v ~/.claude/session-personal.json:/root/.config/claude/config.json:ro \
  my-dev-env:latest \
  claude --version

OpenAI Identity Setup

Prerequisites

  • OpenAI account (personal or educational)
  • API key or ChatGPT session

Step 1: Authenticate

On server:

# Create temp container with OpenAI credentials
infisical export --env=dev --projectId=personal-vault > /tmp/env-$$.list

docker run --rm -it \
  --env-file /tmp/env-$$.list \
  -v ~/.openai:/root/.openai \
  my-dev-env:latest \
  /bin/bash

# Inside container (if OpenAI CLI supports login)
# openai login
# OR manually create session file with API key

exit
rm /tmp/env-$$.list

Step 2: Save Session Files

# If login created session file
mv ~/.openai/session.json ~/.openai/session-chatgpt.json

# Or manually create
cat > ~/.openai/session-chatgpt.json << 'EOF'
{
  "api_key": "sk-proj-...",
  "organization": "org-..."
}
EOF

Step 3: Repeat for Additional Accounts

# For educational account
cat > ~/.openai/session-edu1.json << 'EOF'
{
  "api_key": "sk-proj-edu-...",
  "organization": "org-edu-..."
}
EOF

# For work account
cat > ~/.openai/session-work.json << 'EOF'
{
  "api_key": "sk-proj-work-...",
  "organization": "org-work-..."
}
EOF

Switching Identities

Using dev() Function

The dev() function handles identity switching automatically:

Example session:

# On server
dev my-project

# Prompts appear:

Gemini Account Prompt:

🤖 Select Gemini Account:
   [1] edu1 (default)
   [2] edu2
   [3] edu3
   [4] edu4
👉 Choose (1-4): 2
You select 2 → dev() mounts ~/.gemini/oauth-edu2.json

Claude Account Prompt:

🧠 Select Claude Account:
   [1] personal (default)
   [2] edu1
   [3] edu2
   [4] work
   [0] Skip (no Claude session)
👉 Choose (0-4): 1
You select 1 → dev() mounts ~/.claude/session-personal.json

OpenAI Account Prompt:

🔮 Select OpenAI/ChatGPT Account:
   [1] chatgpt (default)
   [2] edu1
   [3] edu2
   [4] work
   [0] Skip (no OpenAI session)
👉 Choose (0-4): 0
You select 0 → dev() skips OpenAI (no file mounted)

Result: Container launches with Gemini edu2, Claude personal, no OpenAI.

Behind the Scenes

What dev() does:

# Based on your selections, dev() builds Docker command:

docker run --rm -it \
  --env-file /tmp/env-$$.list \
  -v ~/.gemini/oauth-edu2.json:/root/.gemini/oauth_creds.json:ro \
  -v ~/.claude/session-personal.json:/root/.config/claude/config.json:ro \
  # (OpenAI skipped - no mount) \
  -v ~/Coding:/app \
  -w /app/my-project \
  my-dev-env:latest \
  /bin/bash

Inside container:

# Check which identities are active
ls -la /root/.gemini/oauth_creds.json      # → edu2
ls -la /root/.config/claude/config.json    # → personal
ls -la /root/.openai/session.json          # → doesn't exist (skipped)

# Use agents
gemini    # Uses edu2 account
claude    # Uses personal account

Switching Mid-Session

To switch identities:

  1. Exit container:

    exit
    

  2. Re-run dev():

    dev my-project
    

  3. Select different accounts:

    Gemini: edu1 (was edu2)
    Claude: work (was personal)
    OpenAI: chatgpt (was skipped)
    

  4. New container uses new identities:

    gemini    # Now uses edu1
    claude    # Now uses work
    


Security Considerations

File Permissions

Critical: Identity files contain sensitive credentials.

Set correct permissions:

# Only owner can read/write
chmod 600 ~/.gemini/*.json
chmod 600 ~/.claude/*.json
chmod 600 ~/.openai/*.json

# Verify
ls -la ~/.gemini/
# Should show: -rw------- (600)

Read-Only Mounts

Why :ro flag?

dev() mounts identity files as read-only:

-v ~/.claude/session-personal.json:/root/.config/claude/config.json:ro
#                                                                     ^^
#                                                                   read-only

Benefits: - ✅ Container can't modify host credentials - ✅ Prevents accidental corruption - ✅ Prevents malicious code from stealing/changing credentials - ✅ Audit trail (host files never change)

Backup Identity Files

These files are critical - losing them means re-authenticating all accounts.

Backup strategy:

# Create backup directory
mkdir -p ~/identity-backups

# Backup all identity files (encrypted)
tar czf - ~/.gemini ~/.claude ~/.openai | \
  gpg --symmetric --cipher-algo AES256 \
  > ~/identity-backups/ai-identities-$(date +%Y%m%d).tar.gz.gpg

# Copy to secondary server
scp ~/identity-backups/ai-identities-*.gpg ubuntu@100.81.231.36:~/backups/

Restore if needed:

# Decrypt and extract
gpg --decrypt ~/identity-backups/ai-identities-20250115.tar.gz.gpg | \
  tar xzf - -C ~/

Never Commit to Git

Add to .gitignore:

# In any project's .gitignore
.gemini/
.claude/
.openai/
oauth*.json
session*.json

Why: These files contain secrets - committing them exposes credentials.

Separate Accounts by Purpose

Best practice: Use different accounts for different purposes:

Account Type Use For Why
Personal Personal projects, experiments Quota isolation
Educational School assignments, research Compliance (school policies)
Work Company projects Legal (company owns output)

Don't mix: Using work account for personal projects may violate employment agreements.


Troubleshooting

"OAuth file not found"

Symptom:

Error: /root/.gemini/oauth_creds.json not found

Causes: 1. File doesn't exist on host 2. dev() didn't mount the file 3. Wrong account selected

Solution:

# Check file exists
ls -la ~/.gemini/oauth-edu1.json

# If missing, set up that account (see Gemini Identity Setup above)

# If exists, verify dev() mounts it
# (Check dev() function selection logic)

"Session expired"

Symptom:

Error: Authentication failed, session expired

Cause: Session file is outdated (tokens expired)

Solution:

# Re-authenticate that account
# For Claude personal:
infisical export --env=dev --projectId=personal-vault > /tmp/env-$$.list

docker run --rm -it \
  --env-file /tmp/env-$$.list \
  -v ~/.claude:/root/.config/claude \
  my-dev-env:latest \
  claude login

# Save new session
mv ~/.claude/config.json ~/.claude/session-personal.json

rm /tmp/env-$$.list

"Wrong account being used"

Symptom: Selected edu1 but agent uses personal account

Cause: File mount is incorrect

Debug:

# Inside container, check which file is mounted
ls -la /root/.gemini/oauth_creds.json
ls -la /root/.config/claude/config.json

# If wrong file, exit and check dev() selection logic
exit

"Permission denied" reading identity file

Symptom:

Error: Permission denied: /root/.gemini/oauth_creds.json

Cause: File permissions too restrictive or mount failed

Solution:

# Check host file permissions
ls -la ~/.gemini/oauth-edu1.json
# Should be: -rw------- (600)

# If too restrictive (e.g., 400), fix:
chmod 600 ~/.gemini/oauth-edu1.json

Multiple files for same account

Symptom: Confused about which file is current

Solution: Use consistent naming and date backups:

# Current files (no date)
~/.claude/session-personal.json

# Backups (with date)
~/.claude/session-personal-20250115.json.bak


Summary

Multi-Identity Management: - ✅ Store each AI account in separate files on host - ✅ Use file-based switching via dev() prompts - ✅ Mount files read-only into container for security - ✅ Switch identities by exiting and re-running dev() - ✅ Backup files with GPG encryption - ✅ Never commit identity files to Git

Directory Structure:

~/.gemini/oauth-{edu1,edu2,edu3,edu4}.json
~/.claude/session-{personal,edu1,edu2,work}.json
~/.openai/session-{chatgpt,edu1,edu2,work}.json

File Permissions: chmod 600 (owner read/write only)

Best Practices: 1. Separate accounts by purpose (personal/edu/work) 2. Use read-only mounts (:ro) 3. Backup encrypted to secondary server 4. Re-authenticate when sessions expire 5. Never commit to Git

What's Next: - Learn about AI Agents Integration - how agents use identities - Learn about .ai-context.md Standard - project configuration - Learn about dev() Function - orchestration details