Skip to content

refactor(auth): relocate auth store from per-agent to global path #438

@alexey-pelykh

Description

@alexey-pelykh

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.tsresolveAuthStorePath():

  • 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions