fix(credentials): one vault per user, in the config dir - #119
Merged
Conversation
The credential store resolved its base directory against process.cwd(). Running the CLI from inside a git checkout wrote `.logicsrc/credentials` into that repo's working tree — a directory containing `vault/`, the one place raw credential values touch disk — untracked, unignored, and one `git add -A` from being committed. Two such directories were sitting in unrelated repos on the machine this was found on. A per-directory store is also the wrong shape for what the store is for. It is the record of what was rotated and what the prior values were, and a record that forks per project folder is several records that disagree. There is one user, one identity, one vault. Everything now hangs off a single logicsrcHome(): $LOGICSRC_HOME, else $XDG_CONFIG_HOME/logicsrc, else ~/.config/logicsrc. The credential store, the identity and the CLI config all read it rather than each deriving their own answer — three separate derivations is how the vault ended up somewhere the config never was. ~/.logicsrc is migrated rather than abandoned. It holds the X25519 secret key, and losing that loses access to every team vault the member was ever given, so it is moved on first use; a move that fails says so on stderr instead of leaving someone silently logged out with a key still on disk somewhere they were not told about. If the new directory already exists it wins and the old one is left untouched, because two directories both claiming to be the identity is how a login writes one and a read finds the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
defaultCredentialHome()fell back toresolve(process.cwd(), ".logicsrc", "credentials").So the credential store was wherever you were standing when you ran the CLI. Run it inside a git checkout and it writes a
.logicsrc/credentials/tree — includingvault/, the one place raw credential values touch disk — straight into that repo's working tree. Untracked, not gitignored, onegit add -Afrom being committed and pushed.This was found because two of them were sitting in unrelated repos on one machine:
Both untracked, neither ignored. The vault files happened to be empty, so nothing leaked — that was luck, not design.
A per-directory store is also just the wrong shape. The store is the record of what was rotated and what the prior values were; a record that forks per project folder is several records that disagree.
The fix
One resolver,
logicsrcHome(), used by everything:~/.config/logicsrc/credentials(never cwd;$LOGICSRC_CREDENTIAL_HOMEstill overrides)~/.config/logicsrc/identity.json~/.config/logicsrc/config.json(was~/.logicsrc/config.json)They now share the resolver rather than each deriving their own answer — three separate derivations is how the vault ended up somewhere the config never was.
Migration
~/.logicsrcholds the X25519 secret key. Losing it loses access to every team vault the member was ever given, so it is moved, not abandoned:Tests
New
plugins/credential-sharing/src/paths.test.ts— 10 tests. Againstmaster, 8 fail; the 2 that pass are guards for behaviour that must not change ($LOGICSRC_CREDENTIAL_HOMEstill honoured, no-op when there is no legacy dir).Covers: the default,
XDG_CONFIG_HOME,LOGICSRC_HOME, that the store never resolves under cwd, that it is the same store after achdir, that store and identity sit under one home, and all three migration paths.plugin-credential-sharing45 pass ·cli44 pass ·npm run build:cliclean.One thing worth a follow-up
install.shusesLOGICSRC_HOMEto mean the install dir (~/.logicsrc-cli), while the code uses it to mean the config dir. Nothing exports it at runtime today, so there is no live conflict — but anyone who setsLOGICSRC_HOMEto relocate their install would now land their vault inside the install tree, which the installerrm -rfs on update. Worth renaming one of them; out of scope here.Versions bumped to 0.1.1 on the two changed packages.
🤖 Generated with Claude Code