cloudflare / cloudflare/sandbox-sdk
Support `user` flag for sessions
- Dominant language
- TypeScript
- Stars
- 1.1k
- Forks
- 114
- Avg merge
- 22h 42m
- Merged PRs (30d)
- 14
Description
There's no clean way to run a session's commands as a non-root user today.
- The container's control plane runs as root (legitimately — it appends the CF runtime CA cert to the system bundle, manages FUSE mounts, etc.).
- `Bun.spawn` for the persistent bash shell inherits root.
- Every `session.exec()` framing script runs as root.
User code that wants to run unprivileged (e.g. Claude Code's `--permission-mode bypassPermissions` refuses under root) has no good options:
- `exec runuser -u claude bash` as the first scripted command **hangs**: `exec` replaces bash before the framing script writes its exit-code file, so `session.exec()` polls forever.
- `runuser -u claude bash` (no `exec`) spawns a child shell, blocking the parent root bash until the child exits — same hang.
- Per-command `runuser -u -- bash -lc ` wrappers work but pollute every call site and lose the persistent shell state across commands (cwd, env, functions defined in earlier commands).
This is related to #677.
## Proposal
Add `user?: string` to `SessionOptions`. When set, the container resolves it to a uid/gid at session boot and spawns the persistent bash shell under that user via `Bun.spawn`'s `uid`/`gid` options. Control plane stays root;
only the session shell drops privileges.
```ts
const session = await sandbox.createSession({ user: 'claude' });
await session.exec('whoami'); // → 'claude'
```
Persistent shell state still works because privilege drop happens once at spawn, not per command.
Contributor guide
Assessment
This issue has not been assessed yet.