ADR-0005: Private CLI credential file
Context
Browser device authorization issues one opaque, scoped tv_ token that the Node.js CLI must retain between processes. Requiring one platform-specific credential service would make the development CLI unavailable on otherwise supported systems. A plain configuration file without enforced ownership, permissions, atomicity, and path-identity checks would expose the token to local disclosure or replacement attacks.
The credential boundary must work without storing Supabase or other provider sessions, printing the token, or requiring an npm release or production deployment. It must also fail without destroying a previously valid credential when it cannot prove the new file is private.
Decision
Persist one exact, bounded device-session JSON object at $XDG_CONFIG_HOME/thalix-vault/credentials.json on macOS and Linux, falling back to ~/.config. On Windows, require an explicit absolute %APPDATA% and use %APPDATA%\\thalix-vault\\credentials.json; if the value is absent or relative, fail closed without deriving another profile path. The allowed fields are kind, token, baseUrl, expiresAt, scopes, and tokenPrefix. The only accepted kind is device, the token has the opaque tv_ form, and the only accepted scope is account:read.
Use an exclusive temporary file in the same directory, flush it, and atomically replace the destination. On POSIX systems, require a 0700 directory and 0600 file. On Windows, require a protected, non-inherited private ACL, verify that the current user owns the object, and allow access only to the current user, SYSTEM, and built-in Administrators. Verify the directory and installed file after replacement.
Refuse symbolic links, Windows reparse points, and detected directory, file, or path-identity swaps. Reject oversized JSON, unknown fields, and legacy provider-session shapes. If any permission, ACL, owner, path, or replacement guarantee cannot be established and verified, fail closed with SESSION_STORE_UNAVAILABLE. Do not fall back to a less protected file or require Keychain or another operating-system credential service.
Rationale
This design gives macOS, Linux, and Windows one testable storage contract while using each platform's native private-file protections. Same-directory atomic replacement preserves the prior valid credential across pre-replacement failures. Exact schema validation limits the secret-bearing surface and prevents the removed provider-session architecture from returning through persisted data.
Consequences
- The CLI owns platform-specific permission and identity verification, including Windows ACL and reparse-point behavior.
- Windows support is conditional on successfully establishing and verifying the required owner and private ACL; failure is a hard login failure.
- File backup, roaming-profile, and endpoint-management products may copy the credential according to operating-system policy. The CLI does not claim protection from an already-compromised user or administrator account.
- Device-aware logout and token revocation remain follow-on work; local storage does not make the token independently revocable.
- Future adoption of an optional operating-system credential service requires a new decision or an amendment that preserves the same device-only schema and fail-closed security boundary.
Validation evidence
Acceptance requires real process tests on POSIX and Windows. The tests must cover exact bounded serialization, owner and permission or ACL privacy, atomic replacement and failure preservation, symbolic-link or reparse refusal, path-swap detection, and fail-closed behavior when privacy cannot be proved. This ADR records the approved boundary; a packaged release still requires the separate distribution and clean-install gates.