Skip to content

Commit 1aee684

Browse files
Mossakaclaude
andcommitted
fix: ensure HOME directory has correct ownership in chroot mode
When Docker bind-mounts subdirectories (e.g., ~/.claude, ~/.config), it creates parent directories as root:root. This means $HOME itself becomes root-owned, preventing CLI tools from writing directly to $HOME (e.g., creating lock files, temp files, or new subdirectories). Fix HOME ownership before Docker compose runs to ensure the agent user can write to the home directory. This likely fixes the Smoke Claude timeout where Claude Code hangs after accessing the API key. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2308287 commit 1aee684

1 file changed

Lines changed: 17 additions & 3 deletions

File tree

src/docker-manager.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -968,15 +968,29 @@ export async function writeConfigs(config: WrapperConfig): Promise<void> {
968968
logger.debug(`MCP logs directory permissions fixed at: ${mcpLogsDir}`);
969969
}
970970

971-
// Ensure chroot home subdirectories exist with correct ownership before Docker
972-
// bind-mounts them. If a source directory doesn't exist, Docker creates it as
973-
// root:root, making it inaccessible to the agent user (e.g., UID 1001).
971+
// Ensure chroot home directory and subdirectories exist with correct ownership
972+
// before Docker bind-mounts them. If a source directory doesn't exist, Docker
973+
// creates it as root:root, making it inaccessible to the agent user (e.g., UID 1001).
974974
// This is critical for CLI tools that need writable home subdirectories
975975
// (e.g., Claude Code needs ~/.claude, npm needs ~/.npm).
976+
// We also ensure $HOME itself has correct ownership because Docker's creation of
977+
// subdirectory mounts creates parent directories as root:root, which prevents
978+
// tools from creating new files/directories directly in $HOME.
976979
if (config.enableChroot) {
977980
const effectiveHome = getRealUserHome();
978981
const uid = parseInt(getSafeHostUid(), 10);
979982
const gid = parseInt(getSafeHostGid(), 10);
983+
984+
// Ensure $HOME exists and is owned by the agent user
985+
// (Docker creates parent directories as root when bind-mounting subdirectories)
986+
if (fs.existsSync(effectiveHome)) {
987+
const stats = fs.statSync(effectiveHome);
988+
if (stats.uid !== uid || stats.gid !== gid) {
989+
fs.chownSync(effectiveHome, uid, gid);
990+
logger.debug(`Fixed ownership of ${effectiveHome} to ${uid}:${gid}`);
991+
}
992+
}
993+
980994
const chrootHomeDirs = [
981995
'.copilot', '.cache', '.config', '.local',
982996
'.anthropic', '.claude', '.cargo', '.rustup', '.npm',

0 commit comments

Comments
 (0)