[feature] GSTACK_CODEX_NO_SANDBOX env gate — Codex /review + /ship silently no-op in containers (bwrap can't create user namespaces)
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## Summary
Request a `GSTACK_CODEX_NO_SANDBOX=1` opt-in env override that makes the Codex passes in `/review` and `/ship` run with Codex's OS sandbox bypassed, for environments where Codex's `bubblewrap` sandbox cannot create user namespaces (containers / devcontainers / Flatpak / WSL / Replit).
This mirrors the existing **`GSTACK_CHROMIUM_NO_SANDBOX=1`** escape hatch that already exists for the `/qa` Chromium sandbox on AppArmor/root Linux — the Codex adversarial review has the exact same class of problem with no equivalent knob today.
## Environment
- gstack `1.55.0.0`
- codex-cli `0.136.0`
- Linux devcontainer on OrbStack (kernel `7.0.5-orbstack`); unprivileged user namespaces are **not** permitted
- `bwrap` not on `PATH` → Codex falls back to its bundled bubblewrap, which fails the same way
## The problem
Every Codex pass in `/review` (always-on adversarial + the 200+ line structured review) and `/ship` (Step 11 adversarial) runs Codex with `-s read-only`. On Linux, `-s read-only` wraps **every shell command Codex executes** in `bubblewrap`, which needs unprivileged user namespaces. In a locked-down container these aren't available, so every command Codex runs (`git diff`, file reads) fails instantly:
```
bwrap: No permissions to create a new namespace, likely because the kernel
does not allow non-privileged user namespaces.
/bin/bash -lc 'git status --short' exited 1 in 0ms
```
Confirming the kernel side:
```
$ unshare --user true
unshare: unshare failed: Operation not permitted
$ cat /proc/sys/kernel/unprivileged_userns_clone # knob doesn't exist on this kernel
```
### The insidious part: it fails *silently* and looks like a clean review
- A trivial Codex prompt with **no shell commands** succeeds (the model replies fine), which masks the problem.
- But the review prompt instructs Codex to run `git diff` to see the changes. That command — and every command after it — `exited 1 in 0ms` with the bwrap error, so Codex produces output like *"Could not run the requested commands … No repository status available"* — i.e. a non-review.
- Critically, **`codex` exits 0**, and the current error handling in `ship/sections/adversarial.md` / `review/SKILL.md` only treats stderr as an error when it matches `auth` / `login` / `unauthorized` / `API key`, a timeout, or empty output. A `bwrap` / `namespace` failure matches none of those, so gstack presents the empty "review" **verbatim as a success**. No gate, no warning. Users believe they got cross-model adversarial coverage when they got nothing.
## Repro
```bash
# Works (no shell commands → sandbox never engaged) — this is the false reassurance
codex exec "Reply with exactly: PROBE_OK" -C "$PWD" -s read-only < /dev/null
# Fails (the real review path: Codex must run git/file reads)
codex exec "Run 'git status --short' then read README.md, summarize." \
-C "$PWD" -s read-only -c 'model_reasoning_effort="low"' < /dev/null
# → bwrap: No permissions to create a new namespace; every command exits 1 in 0ms
```
## Proposed solution
Add a `GSTACK_CODEX_NO_SANDBOX` opt-in (off by default; identical ergonomics to `GSTACK_CHROMIUM_NO_SANDBOX`). When set to `1`, the Codex invocations in `codex/SKILL.md`, `review/SKILL.md`, and `ship/sections/adversarial.md` swap their sandbox flag:
- `codex exec` passes: `-s read-only` → `--dangerously-bypass-approvals-and-sandbox`
- `codex review` (the 200+ line structured pass): it **rejects** `-s` and (per `codex review --help`) does not appear to accept `--dangerously-bypass-approvals-and-sandbox` — so use the config form `-c sandbox_mode="danger-full-access"` (please verify the exact accepted mechanism for the `review` subcommand).
The rationale is exactly what Codex's own flag help states: *"Skip all confirmation prompts and execute commands without sandboxing. EXTREMELY DANGEROUS. Intended solely for running in environments that are externally sandboxed."* A container/devcontainer **is** that externally-sandboxed environment, so Codex's nested OS sandbox is redundant there.
### Secondary ask (independent of the env gate): stop the silent failure
Even without the env gate, the error handling should treat a sandbox/namespace failure as a Codex failure. Suggest adding `bwrap` / `namespace` / `sandbox failed` to the stderr error patterns so the review reports *"Codex sandbox failed — set GSTACK_CODEX_NO_SANDBOX=1 if this environment is already externally sandboxed"* instead of presenting an empty review as success. This is arguably the more important half — a silent no-op is worse than a loud skip.
## Prior art / references
- gstack's own `GSTACK_CHROMIUM_NO_SANDBOX=1` for the analogous Chromium-sandbox-in-container case (the accepted pattern this request mirrors).
- Upstream Codex, same `bwrap: No permissions to create a new namespace` class, with `danger-full-access` repeatedly confirmed as the working path:
- openai/codex#16211 — *codex-cli in docker: bwrap: No permissions to create a new namespace* (closed)
- openai/codex#15678 — *workspace-write sandbox fails … while danger-full-access works*
- openai/codex#14794 — *VS Code extension sandbox makes writable devcontainer workspace appear read-only*
- openai/codex#14976 — *Bubblewrap sandbox fails inside container*
- OpenAI Codex sandboxing docs (prerequisites): https://developers.openai.com/codex/concepts/sandboxing
## Affected files
- `codex/SKILL.md` / `codex/SKILL.md.tmpl`
- `review/SKILL.md`
- `ship/sections/adversarial.md` (+ `.tmpl`)
Contributor guide
Assessment
This issue has not been assessed yet.