diff --git a/COMMS/sidecar-app-store/42-member-cwd-is-unreachable.md b/COMMS/sidecar-app-store/42-member-cwd-is-unreachable.md new file mode 100644 index 00000000..e93b9aa9 --- /dev/null +++ b/COMMS/sidecar-app-store/42-member-cwd-is-unreachable.md @@ -0,0 +1,67 @@ +# 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 … cd` → +`can'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`: + +```ts +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/`, 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.