From 420aa08783b12b0dc0a0d529b1050d40f4fc5cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 24 Jul 2026 12:19:31 +0000 Subject: [PATCH] chat: run Claude un-isolated for the Super Admin (real HOME, ~/.claude parity) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single-user platform: the Super Admin's Claude process now uses the real home (HOME_DIR) instead of DATA_PATH//home, so its transcript store IS the same ~/.claude the terminal `claude` uses — platform and terminal sessions are interchangeable (native `/resume` sees them). The session reader resolves the same home. The generated container CLAUDE.md is no longer written for the Super Admin (it would pollute the personal global ~/.claude/CLAUDE.md and is stale); MCP tools still load via --mcp-config, and email/project panels inject their own prompts. Sandboxed users keep their isolated home. Co-Authored-By: Claude Opus 4.8 --- src/servers/api/chat/claude-sessions.ts | 9 ++++++--- src/servers/sidecar/claude/user-instance.ts | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/servers/api/chat/claude-sessions.ts b/src/servers/api/chat/claude-sessions.ts index 511f1438..161672b6 100644 --- a/src/servers/api/chat/claude-sessions.ts +++ b/src/servers/api/chat/claude-sessions.ts @@ -6,8 +6,11 @@ import { DATA_PATH } from '../../data-path'; // The `claude` CLI persists every session as a JSONL transcript at // $HOME/.claude/projects//.jsonl // where is the working directory with every non-alphanumeric char replaced by '-'. -// A user's claude process runs with HOME = DATA_PATH//home (see sidecar/claude/user-instance.ts), -// so we read transcripts from there. We never maintain our own copy — Claude's files are authoritative. +// The (single-user, Super Admin) platform runs Claude with no isolation — HOME is the real home +// (HOME_DIR) — so its transcripts are the same store the terminal `claude` uses. We never keep our +// own copy; Claude's files are authoritative. + +const claudeHome = (email: string): string => process.env.HOME_DIR ?? join(DATA_PATH, email, 'home'); /** Dedicated working directory for /chat sessions, so they form their own Claude "project" group. */ export const getClaudeSessionsCwd = (email: string): string => join(DATA_PATH, email, 'claude_sessions'); @@ -19,7 +22,7 @@ export const ensureClaudeSessionsCwd = (email: string): string => { return dir; }; -const claudeProjectsDir = (email: string): string => join(DATA_PATH, email, 'home', '.claude', 'projects'); +const claudeProjectsDir = (email: string): string => join(claudeHome(email), '.claude', 'projects'); /** Claude's folder name for a working directory. */ export const projectSlug = (cwd: string): string => cwd.replace(/[^a-zA-Z0-9]/g, '-'); diff --git a/src/servers/sidecar/claude/user-instance.ts b/src/servers/sidecar/claude/user-instance.ts index 79ba66d2..454b1f6f 100644 --- a/src/servers/sidecar/claude/user-instance.ts +++ b/src/servers/sidecar/claude/user-instance.ts @@ -1,5 +1,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; +import { homedir } from 'node:os'; import type { SidecarCommand, SidecarEvent } from '../protocol'; import { initPaths, loadState, flushAndSave, acquireLock, releaseLock } from './state'; import { setMcpConfigPath } from './claude-manager'; @@ -31,7 +32,11 @@ const OFFICER_AUTH_TOKEN = await sign( '30d', ); -const homeDir = join(DATA_PATH, email, 'home'); +// Single-user platform: the Super Admin runs Claude with no isolation — real HOME, real ~/.claude — +// so platform sessions have perfect parity with terminal sessions (same config, credentials, and +// transcript store, interchangeable via `claude --resume`). Any non-super-admin keeps an isolated home. +const homeDir = + dbUser.role === 'Super Admin' ? (process.env.HOME_DIR ?? homedir()) : join(DATA_PATH, email, 'home'); const globalToolsDir = join(DATA_PATH, 'tools'); const userToolsDir = join(DATA_PATH, email, 'tools'); @@ -123,10 +128,15 @@ function generateMcpConfig(): McpPaths { // ── Startup ── -try { - refreshClaudeMd(); -} catch (err) { - console.error(`[claude:${email}] failed to refresh CLAUDE.md:`, err instanceof Error ? err.message : err); +// Only sandboxed users get the generated container CLAUDE.md. For the un-isolated Super Admin, HOME is +// the real home, so writing it there would pollute the personal global ~/.claude/CLAUDE.md (loaded by +// the terminal `claude` too) — parity means running as the user, not injecting platform context. +if (dbUser.role !== 'Super Admin') { + try { + refreshClaudeMd(); + } catch (err) { + console.error(`[claude:${email}] failed to refresh CLAUDE.md:`, err instanceof Error ? err.message : err); + } } const mcpPaths = generateMcpConfig();