RCA-2026-03-27-002: Claude Code Credential Vault Architecture
Executive Summary
Claude Code sessions failed with "Invalid API key" after implementing vault-based credential isolation. Root cause: symlink pointed to wrong file (credentials.json instead of .credentials.json). Claude Code stores OAuth tokens in .credentials.json (dot-prefixed hidden file), not the settings file. Resolution: create symlink for .credentials.json to vault, enabling proper credential isolation with gcvault mount/unmount cycle.
Timeline
| Time | Event |
|---|---|
2026-03-25 19:17 |
Initial symlink created: |
2026-03-27 ~09:00 |
Discovered Claude still worked with vault unmounted (unexpected) |
2026-03-27 09:02 |
Identified |
2026-03-27 09:03 |
Deleted |
2026-03-27 09:04 |
New Claude sessions fail: "Invalid API key" |
2026-03-27 09:04 |
Re-login via |
2026-03-27 09:05 |
Created correct symlink: |
2026-03-27 09:06 |
Verified mount/unmount cycle works correctly |
Problem Statement
Symptoms
-
Claude Code worked when credentials vault was unmounted (defeating purpose of vault isolation)
-
After deleting local credential file, Claude failed with "Invalid API key"
-
/logincommand created new local file instead of using symlinked vault file
Expected Behavior
-
Claude Code should fail when credentials vault is unmounted
-
Claude Code should work when credentials vault is mounted
-
OAuth tokens should be stored in vault, not locally
Actual Behavior
-
Claude Code used local
.credentials.jsonfallback file -
Symlink to
credentials.jsononly handled settings, not auth tokens
Root Cause
5 Whys Analysis
| Why # | Question and Answer |
|---|---|
1 |
Why did Claude work with vault unmounted? |
2 |
Why was the token in a different file? |
3 |
Why wasn’t this known during initial setup? |
4 |
Why wasn’t the hidden file discovered? |
5 |
Why does Claude use two credential files? |
Root Cause Statement
|
Claude Code separates credentials into two files:
Initial vault symlink targeted the wrong file. The auth token lives in the hidden dot-prefixed file. |
Claude Code Credential Architecture
| File | Purpose | Contents |
|---|---|---|
|
Settings & metadata |
|
|
OAuth authentication |
Access token for Claude Max/Pro subscription |
Key Discovery
The oauthAccount object in credentials.json contains:
accountUuid, displayName, emailAddress, organizationName, organizationRole...
But NOT the actual OAuth token. The token is stored separately in .credentials.json.
Resolution
Immediate Actions
# 1. Move auth token to vault
mv ~/.claude/.credentials.json ~/atelier/_vaults/mounted/credentials/claude/
# 2. Create symlink for auth file (the important one)
ln -s ~/atelier/_vaults/mounted/credentials/claude/.credentials.json ~/.claude/.credentials.json
# 3. Verify both symlinks exist
ls -la ~/.claude/ | grep cred
Expected Configuration
~/.claude/
βββ .credentials.json -> vault/.credentials.json # AUTH TOKEN
βββ credentials.json -> vault/credentials.json # SETTINGS
Verification
# Test vault isolation
gcvault unmount credentials
claude "test" 2>&1 | head -2 # Should fail: Invalid API key
gcvault mount credentials
claude "test" 2>&1 | head -2 # Should work
Prevention
Checklist for Credential Vault Symlinks
-
Always use
ls -la(notls -l) to see hidden files -
Check for dot-prefixed variants of config files
-
Test isolation by unmounting vault and verifying failure
-
Document both the settings file AND auth file locations
find Command Lesson (CLI Mastery)
During investigation, a find syntax error was identified:
# WRONG - orphaned argument
find /path -type d "claude"
# CORRECT - use -name predicate
find /path -type d -name "claude"
find /path -name "*.json" -type f
find /path -iname "claude" # case-insensitive
Lessons Learned
| Category | Lesson |
|---|---|
Hidden files |
Always check for dot-prefixed variants when working with credential/config files |
Vault architecture |
Test the failure case (unmount) before considering setup complete |
Claude Code internals |
OAuth tokens stored separately from account metadata for security isolation |
find syntax |
Predicates ( |
Related Documents
-
RCA: Kroki Orphan Containers - Similar "hidden behavior" discovery pattern