You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using the keychain-credentials path (no long-lived OAuth token configured), the seeded credentials work — but the agent is thrown back into Claude Code's onboarding/login flow on every boot, even though ~/.claude/.credentials.json is present and valid.
No ~/.clawk/claude-oauth-token (removed deliberately, to use the credentials path)
Host Mac logged into Claude Code interactively; SeedClaudeStateDir correctly seeds Claude Code-credentials from the keychain into the sandbox state dir
What happens
First boot: agent authenticated via the seeded .credentials.json. Works.
clawk down && clawk up
Attach → Claude Code re-runs first-run onboarding and asks to log in again — despite ~/.claude/.credentials.json persisting fine (it lives in the state-dir mount).
Cause (from source)
ClaudeJSONMarkerFile (internal/sandbox/oauth_token.go) sets hasCompletedOnboarding: trueonly when hasToken — the comment says "only when hasToken — the keychain credentials path doesn't need it".
The marker is pushed via pushHostFiles on everyup (bringUpVZ), overwriting whatever ~/.claude.json Claude wrote during the previous session (which included its own hasCompletedOnboarding and account state).
Current Claude Code does need the flag: with valid .credentials.json but a ~/.claude.json lacking hasCompletedOnboarding, it re-enters the onboarding/login flow. So on the credentials path, every boot logs the agent out.
Verified workaround
Confirms the diagnosis: after boot, re-set the flag and Claude comes up logged in with the seeded credentials, no prompt:
Set hasCompletedOnboarding: true in the marker whenever the state dir has a .credentials.json (i.e. hasToken || credentialsSeeded), not only for the token path.
Why the credentials path matters
This currently seems to be the only way to use a subscription account inside clawk.
Somewhat related: a snapshot-restored boot re-pushes the marker but (correctly) skips on up hooks, so even the hook workaround misses that path.
When using the keychain-credentials path (no long-lived OAuth token configured), the seeded credentials work — but the agent is thrown back into Claude Code's onboarding/login flow on every boot, even though
~/.claude/.credentials.jsonis present and valid.Setup
main(7c1c1d1, the env: aliases, defaults, and literals; fix profile.d perms (#4) #5 merge), macOS Apple silicon, vz provider, cwd sandbox~/.clawk/claude-oauth-token(removed deliberately, to use the credentials path)SeedClaudeStateDircorrectly seedsClaude Code-credentialsfrom the keychain into the sandbox state dirWhat happens
.credentials.json. Works.clawk down && clawk up~/.claude/.credentials.jsonpersisting fine (it lives in the state-dir mount).Cause (from source)
ClaudeJSONMarkerFile(internal/sandbox/oauth_token.go) setshasCompletedOnboarding: trueonly whenhasToken— the comment says "only when hasToken — the keychain credentials path doesn't need it".pushHostFileson everyup(bringUpVZ), overwriting whatever~/.claude.jsonClaude wrote during the previous session (which included its ownhasCompletedOnboardingand account state)..credentials.jsonbut a~/.claude.jsonlackinghasCompletedOnboarding, it re-enters the onboarding/login flow. So on the credentials path, every boot logs the agent out.Verified workaround
Confirms the diagnosis: after boot, re-set the flag and Claude comes up logged in with the seeded credentials, no prompt:
node -e 'const fs=require("fs"),p="/home/agent/.claude.json";const d=JSON.parse(fs.readFileSync(p));d.hasCompletedOnboarding=true;fs.writeFileSync(p,JSON.stringify(d,null,2))'(we run this as an
on uphook for now)Suggested fix
Set
hasCompletedOnboarding: truein the marker whenever the state dir has a.credentials.json(i.e.hasToken || credentialsSeeded), not only for the token path.Why the credentials path matters
This currently seems to be the only way to use a subscription account inside clawk.
Somewhat related: a snapshot-restored boot re-pushes the marker but (correctly) skips
on uphooks, so even the hook workaround misses that path.