← Back to Kontasks
gvShell Multi-Account Architecture Review
How gvShell handles multi-account credentials and Claude CLI spawning
Output
gvShell Multi-Account Architecture
gvShell uses symlink-based account switching (same as konsole), not CLAUDE_CONFIG_DIR. The design is already implemented in gv/gvshell/internal/claude/accounts/.
1. Credential Storage
~/.gvshell/claude/ ├── primary/ # Max 20× account │ └── .credentials.json ├── secondary/ # Max 5× account │ └── .credentials.json └── tertiary/ # Optional third └── .credentials.json ~/.claude → ~/.gvshell/claude/primary/ (symlink)
2. How Claude CLI is Run
// From claudecli.go - createSession() cmd := exec.CommandContext(ctx, "claude", [ "--print", "--input-format", "stream-json", "--output-format", "stream-json", "--model", opts.Model, "--dangerously-skip-permissions", // if configured ]) cmd.Dir = opts.WorkingDir // Working directory
3. Account Switching Flow
1. KillHotSessionsFunc(ctx) // Kill cached sessions 2. os.Remove("~/.claude") // Remove old symlink 3. os.Symlink(target, "~/.claude") // New symlink 4. RestartHotPoolFunc(ctx) // Restart with new creds
4. Per-Session Account Selection
// Provider can specify which account to use type Options struct { AccountID string // "primary", "secondary" AccountManager AccountManager // Handles switch } // Before spawning Claude: if opts.AccountID != "" { manager.SwitchAccount(ctx, opts.AccountID) }
Key Design Decision: Symlink vs CLAUDE_CONFIG_DIR
gvShell does NOT use CLAUDE_CONFIG_DIR. It uses symlink switching like konsole because:
- Proven pattern from konsole production use
- Claude CLI reads from
~/.claudeby default - CLAUDE_CONFIG_DIR has known bugs (IDE integration, local .claude dirs)
Limitation: No Parallel Accounts
With symlink approach, only one account can be active at a time. Using CLAUDE_CONFIG_DIR per-spawn would enable parallel accounts but introduces those bugs.
Source: gvShellDesign.md §4.3.1, accounts/accounts.go, claudecli/claudecli.go
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Scope
vibetools
Tags
gvshellaccountsarchitecture
Created
9 Jan 2026, 10:35 am
Updated
9 Jan 2026, 10:35 am
Timing
⏱️ 62.9s
0 tools
● TTFE 0%
● Thinking 100%
● Tools 0%
Created By
claude
Raw Data
{
"id": "6679d0c0-5c3a-48cc-aa7a-338c8d8e569d",
"type": "general",
"status": "completed",
"title": "gvShell Multi-Account Architecture Review",
"description": "How gvShell handles multi-account credentials and Claude CLI spawning",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 16px 0;\">gvShell Multi-Account Architecture</h2>\n\n<p style=\"line-height:1.6;\">gvShell uses <strong>symlink-based account switching</strong> (same as konsole), not CLAUDE_CONFIG_DIR. The design is already implemented in <code style=\"background:#2d2d2d;padding:2px 6px;border-radius:4px;\">gv/gvshell/internal/claude/accounts/</code>.</p>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">1. Credential Storage</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#56b6c2;\">~/.gvshell/claude/</span>\n├── <span style=\"color:#98c379;\">primary/</span> <span style=\"color:#7f848e;\"># Max 20× account</span>\n│ └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n├── <span style=\"color:#98c379;\">secondary/</span> <span style=\"color:#7f848e;\"># Max 5× account</span>\n│ └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n└── <span style=\"color:#98c379;\">tertiary/</span> <span style=\"color:#7f848e;\"># Optional third</span>\n └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n\n<span style=\"color:#56b6c2;\">~/.claude</span> <span style=\"color:#c678dd;\">→</span> <span style=\"color:#98c379;\">~/.gvshell/claude/primary/</span> <span style=\"color:#7f848e;\">(symlink)</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">2. How Claude CLI is Run</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#7f848e;\">// From claudecli.go - createSession()</span>\ncmd := exec.CommandContext(ctx, <span style=\"color:#98c379;\">\"claude\"</span>, [\n <span style=\"color:#98c379;\">\"--print\"</span>,\n <span style=\"color:#98c379;\">\"--input-format\"</span>, <span style=\"color:#98c379;\">\"stream-json\"</span>,\n <span style=\"color:#98c379;\">\"--output-format\"</span>, <span style=\"color:#98c379;\">\"stream-json\"</span>,\n <span style=\"color:#98c379;\">\"--model\"</span>, opts.Model,\n <span style=\"color:#98c379;\">\"--dangerously-skip-permissions\"</span>, <span style=\"color:#7f848e;\">// if configured</span>\n])\ncmd.Dir = opts.WorkingDir <span style=\"color:#7f848e;\">// Working directory</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">3. Account Switching Flow</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.8;\">\n<span style=\"color:#e5c07b;\">1.</span> <span style=\"color:#c678dd;\">KillHotSessionsFunc(ctx)</span> <span style=\"color:#7f848e;\">// Kill cached sessions</span>\n<span style=\"color:#e5c07b;\">2.</span> <span style=\"color:#c678dd;\">os.Remove(\"~/.claude\")</span> <span style=\"color:#7f848e;\">// Remove old symlink</span>\n<span style=\"color:#e5c07b;\">3.</span> <span style=\"color:#c678dd;\">os.Symlink(target, \"~/.claude\")</span> <span style=\"color:#7f848e;\">// New symlink</span>\n<span style=\"color:#e5c07b;\">4.</span> <span style=\"color:#c678dd;\">RestartHotPoolFunc(ctx)</span> <span style=\"color:#7f848e;\">// Restart with new creds</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">4. Per-Session Account Selection</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#7f848e;\">// Provider can specify which account to use</span>\ntype Options struct {\n AccountID <span style=\"color:#c678dd;\">string</span> <span style=\"color:#7f848e;\">// \"primary\", \"secondary\"</span>\n AccountManager <span style=\"color:#c678dd;\">AccountManager</span> <span style=\"color:#7f848e;\">// Handles switch</span>\n}\n\n<span style=\"color:#7f848e;\">// Before spawning Claude:</span>\n<span style=\"color:#c678dd;\">if</span> opts.AccountID != \"\" {\n manager.SwitchAccount(ctx, opts.AccountID)\n}\n</pre>\n\n<h3 style=\"color:#f97316;margin:16px 0 8px 0;\">Key Design Decision: Symlink vs CLAUDE_CONFIG_DIR</h3>\n<p style=\"line-height:1.6;\">gvShell <strong>does NOT use CLAUDE_CONFIG_DIR</strong>. It uses symlink switching like konsole because:</p>\n<ul style=\"margin:0;padding-left:20px;line-height:1.8;\">\n<li>Proven pattern from konsole production use</li>\n<li>Claude CLI reads from <code style=\"background:#2d2d2d;padding:2px 6px;border-radius:4px;\">~/.claude</code> by default</li>\n<li>CLAUDE_CONFIG_DIR has known bugs (IDE integration, local .claude dirs)</li>\n</ul>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">Limitation: No Parallel Accounts</h3>\n<p style=\"line-height:1.6;\">With symlink approach, only <strong>one account can be active at a time</strong>. Using CLAUDE_CONFIG_DIR per-spawn would enable parallel accounts but introduces those bugs.</p>\n\n<p style=\"margin:16px 0 0 0;color:#7f848e;\">Source: gvShellDesign.md §4.3.1, accounts/accounts.go, claudecli/claudecli.go</p>\n</div>",
"requestedAt": "2026-01-09T00:20:00.000Z",
"requestId": "b1a67c8c-b3b6-46b9-83a3-7df6b987f57e",
"choices": [
{
"label": "Test CLAUDE_CONFIG_DIR",
"value": "Run a test to see if CLAUDE_CONFIG_DIR works reliably for spawning Claude with different configs in parallel",
"primary": true
},
{
"label": "Keep symlink approach",
"value": "The symlink approach is working well in konsole, stick with it for gvShell"
},
{
"label": "Hybrid approach",
"value": "Use symlink for hot pool but CLAUDE_CONFIG_DIR for ad-hoc spawns"
}
],
"turnTiming": {
"totalMs": 62918,
"ttfeMs": 53,
"thinkingMs": 62865,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-09T00:35:29.317Z",
"updatedAt": "2026-01-09T00:35:35.771Z",
"requestId": "b1a67c8c-b3b6-46b9-83a3-7df6b987f57e",
"scope": "vibetools",
"tags": [
"gvshell",
"accounts",
"architecture"
],
"targetUser": "claude"
}