Skip to content

Mount .claude.json so container logins persist; allow isolating state - #4

Open
systemjack wants to merge 1 commit into
gherlein:mainfrom
systemjack:fix-claude-json-mount
Open

Mount .claude.json so container logins persist; allow isolating state#4
systemjack wants to merge 1 commit into
gherlein:mainfrom
systemjack:fix-claude-json-mount

Conversation

@systemjack

Copy link
Copy Markdown

Claude Code keeps state in two places and both are required for a session to
survive a restart:

~/.claude/ credentials, history, settings, plugin cache and registry
~/.claude.json FILE at HOME root: account linkage, onboarding, project state

The launchers mounted only the directory. So .credentials.json persisted fine,
but .claude.json was absent on every start, Claude Code saw no linked account,
and prompted for login each launch -- with a perfectly good credential sitting in
the mounted directory unused.

Mounting the pair fixes that. Both mounts now come from one shared function in
localdev-mounts.sh rather than a copy in each launcher, so the three cannot
drift apart.

Also adds LOCALDEV_CLAUDE_STATE to choose which host directory supplies that
pair, defaulting to $HOME (existing shared behaviour). Sharing has a real cost
that mounting .claude.json increases: host and container run different Claude
Code builds against one mutable registry and reset each other's state. A
container session was observed emptying the host's installed_plugins.json,
disabling the org's managed plugins on the host. Pointing
LOCALDEV_CLAUDE_STATE at a container-only directory gives the container its own
credential and plugin state and removes that failure mode, at the cost of one
extra login.

A nonexistent .claude.json is seeded with '{}' before mounting: podman creates a
directory for a missing bind-mount source, which would hand Claude Code a
directory where it expects a JSON file.

Verified across all three launchers: shared mode mounts the dir and the json from
$HOME; isolated mode mounts both from LOCALDEV_CLAUDE_STATE and nothing from
$HOME, seeding the directory and file on first use.

Documents both in README: that Claude Code state spans a directory and a file,
and the LOCALDEV_CLAUDE_STATE choice with the tradeoff behind it. Without that,
the variable is discoverable only by reading the library source.

Claude Code keeps state in two places and both are required for a session to
survive a restart:

  ~/.claude/       credentials, history, settings, plugin cache and registry
  ~/.claude.json   FILE at HOME root: account linkage, onboarding, project state

The launchers mounted only the directory. So .credentials.json persisted fine,
but .claude.json was absent on every start, Claude Code saw no linked account,
and prompted for login each launch -- with a perfectly good credential sitting in
the mounted directory unused.

Mounting the pair fixes that. Both mounts now come from one shared function in
localdev-mounts.sh rather than a copy in each launcher, so the three cannot
drift apart.

Also adds LOCALDEV_CLAUDE_STATE to choose which host directory supplies that
pair, defaulting to $HOME (existing shared behaviour). Sharing has a real cost
that mounting .claude.json increases: host and container run different Claude
Code builds against one mutable registry and reset each other's state. A
container session was observed emptying the host's installed_plugins.json,
disabling the org's managed plugins on the host. Pointing
LOCALDEV_CLAUDE_STATE at a container-only directory gives the container its own
credential and plugin state and removes that failure mode, at the cost of one
extra login.

A nonexistent .claude.json is seeded with '{}' before mounting: podman creates a
directory for a missing bind-mount source, which would hand Claude Code a
directory where it expects a JSON file.

Verified across all three launchers: shared mode mounts the dir and the json from
$HOME; isolated mode mounts both from LOCALDEV_CLAUDE_STATE and nothing from
$HOME, seeding the directory and file on first use.

Documents both in README: that Claude Code state spans a directory and a file,
and the LOCALDEV_CLAUDE_STATE choice with the tradeoff behind it. Without that,
the variable is discoverable only by reading the library source.
@systemjack

Copy link
Copy Markdown
Author

This PR maybe not ready for prime time. The refactor for common function is probably okay but I have mixed feelings about the LOCALDEV_CLAUDE_STATE bit. Not sure it actually works like I want.

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