ADR-0003 — Credentials at rest: 0600 files / 0700 dirs under XDG, atomic writes
Context and Problem Statement
Per-account OAuth blobs and the registry must persist on disk. Claude Code itself stores OAuth credentials as a 0600 file (~/.claude/.credentials.json on Linux), so the filesystem model is what the ecosystem already practices.
Decision Drivers
- Linux-only scope (non-goal: macOS keychain).
- No dbus/Secret Service dependency for a tool that must run in minimal environments.
- A torn write must never leave a partial credential or registry: multi-process safety needs atomic rename + flock.
Considered Options
- Secret Service / dbus keyring
keyringlibrary- 0600/0700 files under
$XDG_DATA_HOME/claude-acc-manager, atomic tmp+rename
Decision Outcome
Chosen option: "0600/0700 files under $XDG_DATA_HOME/claude-acc-manager, atomic tmp+rename", because it matches Claude Code's own credential model, carries zero dependencies, and is fully testable under a tmp HOME.
All state lives under $XDG_DATA_HOME/claude-acc-manager (default ~/.local/share/claude-acc-manager): accounts/<name>/ dirs (each a real CLAUDE_CONFIG_DIR), registry.json, usage-cache.json, settings.json, and a single .lock flock. Dirs are 0700, files 0600, every write is atomic tmp+rename through src/claude_acc_manager/shared/fsio.py, the single implementation. Secret Service can be added later behind the storage port without changing the use cases.
Consequences
Mode enforcement and atomicity are centralized: a unit test asserts them on every credential write path, so a second write path must go through fsio, not around it. ensure_private_dir tightens only directories it creates: an earlier defect chmod'd the first existing ancestor (would have made ~/.local/share 0700); the regression test pins the corrected walk.
Confirmation
Mode and atomicity are test-enforced: test/unit/shared/test_fsio.py asserts 0600/0700 and tmp+rename behavior hermetically.
Pros and Cons of the Options
| Option | Pro | Con |
|---|---|---|
| Secret Service / dbus keyring | OS-managed secret store | dbus dependency; headless hosts often lack a running service; over-scope for v1 |
keyring library |
One API over backends | Runtime dep + backend variance; same over-scope |
0600/0700 files under $XDG_DATA_HOME/claude-acc-manager, atomic tmp+rename |
Matches Claude Code's own model; zero deps; fully testable under tmp-HOME | We own mode enforcement and atomicity discipline |