Files
platform/COMMS/sidecar-app-store/42-member-cwd-is-unreachable.md
T
pastilhasandClaude Opus 5 661b3d761f 42: the first member turn ran clean, and its cwd is unreachable by the member
The privilege drop and the allowlist both held in production. Captured from /proc during the
first real member chat turn: uid 1001, parent sudo, HOME and CLAUDE_CONFIG_DIR both inside the
member's home, and exactly nine environment variables — the allowlist plus what setpriv
supplies. Zero ANTHROPIC_*, POSTGRES_URL or JWT_SECRET.

The defect is the cwd. websocket.ts:134 defaults a chat turn to the email-derived
general_chat_sessions, and confineUserTree makes every sibling of home the platform's at 0700.
So the member's turn starts in a directory it cannot enter — verified, cd fails — and every
Bash call in that turn dies instantly, which is what the owner saw as "the shell is unusable".

29 reasoned that those paths stay email-derived because they live under DATA_PATH rather than a
home. That is true and it does not follow that they are usable: platform-owned by design means
a member's turn can never run there.

Fix is a member-owned default, and I would put general_chat_sessions inside their home rather
than repointing at the home itself, since it keeps the existing shape for both parties and does
not change the owner's path at all. The sibling's 0700 should not be loosened — it holds
attachments and email_accounts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:43:01 +00:00

3.1 KiB

42 — the first member turn ran, and its cwd is a directory the member cannot enter

The privilege drop and the allowlist both held in production. Captured from /proc during the first real member chat turn:

uid=1001                parent=sudo             secrets=0
HOME              = …/jg@pertento.ai/home
CLAUDE_CONFIG_DIR = …/jg@pertento.ai/home/.claude
env = CLAUDE_CODE_ENTRYPOINT CLAUDE_CONFIG_DIR HOME LANG LOGNAME PATH SHELL TERM USER

Nine variables, exactly the allowlist plus what setpriv --init-groups --reset-env supplies. Zero ANTHROPIC_*, zero POSTGRES_URL, zero JWT_SECRET. That is the first live test of the whole design and it passed.

The defect

cwd = …/jg@pertento.ai/general_chat_sessions
      drwx------ pastilhas:pastilhas

The member cannot enter their own turn's working directory. Verified: sudo -u green … cdcan't cd to …/general_chat_sessions. Every Bash tool call in that turn fails immediately, including echo hello, which is what the owner observed — the agent reported its shell as broken and could not read its own cwd.

websocket.ts:134:

if (msg.context === 'chat') return msg.cwd?.trim() ? resolveCwd(home, msg.cwd) : ensureGeneralChatSessionsCwd(email);

The default chat cwd is email-derived, and confineUserTree:307-312 deliberately makes every sibling of home the platform's at 0700 — "Every sibling of home is the platform's. 700 means traversal alone does not open them." Correct for attachments and email_accounts; fatal as a member's cwd.

29 reasoned about this and got half of it right:

"general_chat_sessions lives under DATA_PATH/<email>, not under a home, so those paths stay email-derived. Collapsing them would have been the same class of mistake as undefined meaning 'the owner'."

The path being email-derived is true. What did not follow is that it is therefore usable — it is platform-owned by design, so a member's turn can never run there. The email/home split was right; this is the one place where the answer is neither.

Fix

A member's default chat cwd should be somewhere they own. Two options:

  • their home — one line, and it matches what an unqualified turn should mean for them
  • ~/general_chat_sessions inside their home — keeps the "chat sessions have their own directory" behaviour, member-owned, and leaves the platform's sibling untouched

I would take the second: it preserves the existing shape for both parties, and the owner's path does not change at all. The first is smaller if the directory carries no meaning for a member.

Do not loosen the sibling. It holds attachments and email_accounts, and the 0700 is doing real work.

Also observed

The member's agent reported every claude.ai connector as needing authorization. Expected: those belong to the Anthropic account they signed in with, not to us, and members get no MCP config from the platform by design. Worth knowing it presents as a wall of "needs auth" rather than as an absence.

Terminal path unaffected — green's claude in the web terminal runs fine as uid 1001 in their own home.