Skip to content

fix(credentials): one vault per user, in the config dir - #119

Merged
ralyodio merged 1 commit into
masterfrom
fix/one-vault-xdg
Aug 1, 2026
Merged

fix(credentials): one vault per user, in the config dir#119
ralyodio merged 1 commit into
masterfrom
fix/one-vault-xdg

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

The bug

defaultCredentialHome() fell back to resolve(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 — including vault/, the one place raw credential values touch disk — straight into that repo's working tree. Untracked, not gitignored, one git add -A from being committed and pushed.

This was found because two of them were sitting in unrelated repos on one machine:

/home/anthony/src/moshcoder/moshcode/.logicsrc/credentials/vault/
/home/anthony/src/profullstack/ai-ollama/.logicsrc/credentials/vault/

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:

$LOGICSRC_HOME  →  $XDG_CONFIG_HOME/logicsrc  →  ~/.config/logicsrc
  • credential store~/.config/logicsrc/credentials (never cwd; $LOGICSRC_CREDENTIAL_HOME still overrides)
  • identity~/.config/logicsrc/identity.json
  • CLI config~/.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

~/.logicsrc holds the X25519 secret key. Losing it loses access to every team vault the member was ever given, so it is moved, not abandoned:

  • moved on first use, once, if the new location does not exist
  • if the new location does exist, it wins and the legacy dir is left untouched — two directories both claiming to be the identity is how a login writes one and a read finds the other
  • a move that fails warns on stderr naming both paths, rather than silently logging someone out

Tests

New plugins/credential-sharing/src/paths.test.ts — 10 tests. Against master, 8 fail; the 2 that pass are guards for behaviour that must not change ($LOGICSRC_CREDENTIAL_HOME still 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 a chdir, that store and identity sit under one home, and all three migration paths.

plugin-credential-sharing 45 pass · cli 44 pass · npm run build:cli clean.

One thing worth a follow-up

install.sh uses LOGICSRC_HOME to 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 sets LOGICSRC_HOME to relocate their install would now land their vault inside the install tree, which the installer rm -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

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>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 36236eb into master Aug 1, 2026
4 of 5 checks passed
@ralyodio
ralyodio deleted the fix/one-vault-xdg branch August 1, 2026 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant