Context
Auth profiles are currently stored per-agent at ~/.remoteclaw/agents/{agentId}/agent/auth-profiles.json. With the per-agent auth config field (#421, done ✅), profile selection is config-driven — the store itself should be a single global file, architecturally a peer of agents/, not a child.
Current architecture (wrong)
~/.remoteclaw/
└── agents/
├── main/agent/auth-profiles.json ← "main" store
├── agent_A/agent/auth-profiles.json ← agent-specific (merged with main)
└── agent_B/agent/auth-profiles.json ← agent-specific (merged with main)
Compensating complexity:
ensureAuthProfileStore(agentDir) merges main + agent-specific profiles
mergeAuthProfileStores() resolves conflicts (agent wins)
- Inheritance: subagents with no store auto-clone from main
syncSiblingAgents in onboarding copies credentials across agent dirs
Target architecture
~/.remoteclaw/
├── config.json5
├── auth-profiles.json ← single global store
└── agents/
├── main/agent/ ← sessions, workspace (no auth)
└── agent_A/agent/
Per-agent credential selection is handled by the auth config field:
agents:
list:
- id: agent_A
auth: "anthropic:team-key" # selects from global store
- id: agent_B
auth: "anthropic:personal-key" # different profile, same store
Scope
1. Change storage path
src/auth/paths.ts — resolveAuthStorePath():
- Remove
agentDir parameter
- Resolve to
~/.remoteclaw/auth-profiles.json (via resolveStateDir())
- Legacy path resolution similarly updated
2. Simplify auth store loading
src/auth/store.ts:
loadAuthProfileStore() — remove agentDir parameter, load from global path only
ensureAuthProfileStore() — remove merge logic, direct load
- Remove
mergeAuthProfileStores()
- Remove inheritance/clone logic ("inherited auth-profiles from main agent")
3. Remove agentDir threading through auth chain
Remove agentDir parameter from auth-related function signatures and all call sites:
resolveAuthStorePath(agentDir) → resolveAuthStorePath()
ensureAuthProfileStore(agentDir) → ensureAuthProfileStore()
loadAuthProfileStore(agentDir) → loadAuthProfileStore()
upsertAuthProfile({ agentDir }) → upsertAuthProfile()
resolveAuthStorePathForDisplay(agentDir) → resolveAuthStorePathForDisplay()
Call sites that pass agentDir for auth purposes (each needs the parameter removed):
src/auto-reply/reply/get-reply-run.ts
src/agents/auth-profiles/session-override.ts (now src/auth/)
src/commands/agent.ts
src/commands/onboard-auth.credentials.ts
src/commands/agents.commands.add.ts
src/commands/doctor-auth.ts
src/commands/channels/list.ts
src/commands/configure.gateway-auth.ts
src/auth/provider-auth.ts
src/config/agent-dirs.ts (auth-related guidance message)
- Test files
4. Remove dead sync code
src/commands/onboard-auth.credentials.ts:
- Remove
syncSiblingAgents option
- Remove
resolveSiblingAgentDirs()
writeOAuthCredentials / setAnthropicApiKey etc. write to global store directly
5. Decouple from REMOTECLAW_AGENT_DIR
Auth resolution must NOT use REMOTECLAW_AGENT_DIR to find the store. The env var continues to exist for agent workspace/session purposes — just not for auth.
No migration needed
No active RemoteClaw deployments exist. Old per-agent auth files can be ignored. OpenClaw import is handled separately (#427).
Tests
- All existing auth tests updated to use global path
- Remove merge/inheritance test cases (dead behavior)
- Verify
upsertAuthProfile writes to global store
- Verify
loadAuthProfileStore reads from global store regardless of REMOTECLAW_AGENT_DIR
- Verify onboarding writes to global store
Depends on
Related
Context
Auth profiles are currently stored per-agent at
~/.remoteclaw/agents/{agentId}/agent/auth-profiles.json. With the per-agentauthconfig field (#421, done ✅), profile selection is config-driven — the store itself should be a single global file, architecturally a peer ofagents/, not a child.Current architecture (wrong)
Compensating complexity:
ensureAuthProfileStore(agentDir)merges main + agent-specific profilesmergeAuthProfileStores()resolves conflicts (agent wins)syncSiblingAgentsin onboarding copies credentials across agent dirsTarget architecture
Per-agent credential selection is handled by the
authconfig field:Scope
1. Change storage path
src/auth/paths.ts—resolveAuthStorePath():agentDirparameter~/.remoteclaw/auth-profiles.json(viaresolveStateDir())2. Simplify auth store loading
src/auth/store.ts:loadAuthProfileStore()— removeagentDirparameter, load from global path onlyensureAuthProfileStore()— remove merge logic, direct loadmergeAuthProfileStores()3. Remove
agentDirthreading through auth chainRemove
agentDirparameter from auth-related function signatures and all call sites:resolveAuthStorePath(agentDir)→resolveAuthStorePath()ensureAuthProfileStore(agentDir)→ensureAuthProfileStore()loadAuthProfileStore(agentDir)→loadAuthProfileStore()upsertAuthProfile({ agentDir })→upsertAuthProfile()resolveAuthStorePathForDisplay(agentDir)→resolveAuthStorePathForDisplay()Call sites that pass
agentDirfor auth purposes (each needs the parameter removed):src/auto-reply/reply/get-reply-run.tssrc/agents/auth-profiles/session-override.ts(nowsrc/auth/)src/commands/agent.tssrc/commands/onboard-auth.credentials.tssrc/commands/agents.commands.add.tssrc/commands/doctor-auth.tssrc/commands/channels/list.tssrc/commands/configure.gateway-auth.tssrc/auth/provider-auth.tssrc/config/agent-dirs.ts(auth-related guidance message)4. Remove dead sync code
src/commands/onboard-auth.credentials.ts:syncSiblingAgentsoptionresolveSiblingAgentDirs()writeOAuthCredentials/setAnthropicApiKeyetc. write to global store directly5. Decouple from
REMOTECLAW_AGENT_DIRAuth resolution must NOT use
REMOTECLAW_AGENT_DIRto find the store. The env var continues to exist for agent workspace/session purposes — just not for auth.No migration needed
No active RemoteClaw deployments exist. Old per-agent auth files can be ignored. OpenClaw import is handled separately (#427).
Tests
upsertAuthProfilewrites to global storeloadAuthProfileStorereads from global store regardless ofREMOTECLAW_AGENT_DIRDepends on
authconfig field (done ✅)Related