12 KiB
sloptrap
sloptrap runs the OpenAI Codex CLI inside a container with a predictable and locked-down filesystem view. The launcher script (sloptrap) resolves the project manifest, builds the support image directly, and starts Codex with the requested target (defaults to run). Hardened parsing blocks container escapes via manifest or ignore directives, verifies the downloaded Codex binary, and keeps the runtime environment minimal.
Dependencies
- Podman ≥ 4 (sloptrap refuses to run without it unless you explicitly override
SLOPTRAP_CONTAINER_ENGINE). - GNU
bash,curl,tar,sha256sum,realpath(from GNU coreutils), andjqon the host.
Tip: set
SLOPTRAP_CONTAINER_ENGINE=<engine>if you need to override the default Podman requirement.
macOS setup
sloptrap targets GNU userland. On macOS, install the GNU tools via Homebrew and the launcher will prepend their gnubin paths automatically:
brew install coreutils gnu-tar jq
Quick Start
- Place
sloptrapsomewhere on your PATH/shared drive (the helper Dockerfile and Codex binary are bundled and downloaded automatically). - (Optional) Create a project-specific manifest and ignore file:
cat > path/to/project/.sloptrap <<'EOF' name=path/to/project packages_extra=make EOF cat > path/to/project/.sloptrapignore <<'EOF' .git/ secrets/ EOF - Run
./sloptrap path/to/project. On the first invocation sloptrap:- builds
path/to/project-sloptrap-imageif missing, - verifies the Codex binary hash,
- creates
${HOME}/.codex, prepares a per-project state directory, and runsloginif${HOME}/.codex/auth.jsonis missing or empty.
- builds
Use
./sloptrap path/to/project shellto enter a troubleshooting shell inside the container or./sloptrap path/to/project cleanto remove cached images and state.
How It Works
- The project directory mounts at
/workspace; project-scoped Codex state mounts at/codexfrom${HOME}/.codex/sloptrap/state/<project-hash>, and shared auth mounts from${HOME}/.codex/auth.jsonto/codex/auth.json. .sloptrapignoreentries (if present in your project) are overlaid by tmpfs (for directories) or empty bind mounts (for files) so Codex cannot read the masked content.- sloptrap launches containers on an isolated network (
bridgeon Docker,slirp4netnson Podman) with--cap-drop=ALL,--security-opt no-new-privileges, a read-only root filesystem, and tmpfs-backed/tmp,/run, and/run/lock. Projects that explicitly setallow_host_network=truein their manifest opt into--network host. - The helper Dockerfile is embedded inside
sloptrap; setSLOPTRAP_DOCKERFILE_PATH=/path/to/custom/Dockerfileif you need to supply your own recipe. The default image installscurl,bash,ca-certificates,libstdc++6,git,ripgrep,xxd, andfile, so most debugging helpers are already available without addingpackages_extra. - The container user matches the host UID/GID (
--userns=keep-idon Podman or--user UID:GIDon Docker). - The runtime environment is fixed to HOME/XDG variables pointing at
/codex; manifest-controlled environment injection is disabled.
.sloptrap Manifest Reference
The manifest is optional. When absent, sloptrap derives:
name = basename(project directory)packages_extra = ""(none)capabilities = ""(none) If a build is requested and no.sloptrapexists, sloptrap prompts to create one interactively.
Supported keys when the manifest is present:
| Key | Default | Notes |
|---|---|---|
name |
project directory name | Must match ^[A-Za-z0-9_.-]+$. Used for image/container naming. |
packages_extra |
empty | Additional Debian packages installed during docker/podman build. Tokens must be alphanumeric plus +.-. |
capabilities |
empty | Optional privileged features. Supported values are apt-install, packet-capture, and nested-podman. |
allow_host_network |
false |
true opts into --network host; keep false unless the project absolutely requires direct access to host-local services. |
Values containing $, `, or newlines are rejected to prevent command injection. Setting illegal keys or malformed values aborts the run before containers start.
sloptrap always runs Codex with --sandbox danger-full-access --ask-for-approval never. codex_args is deprecated and rejected if present.
Capability trust is local state, not part of the repository. Builds for manifests that request capabilities require either an interactive trust confirmation or --trust-capabilities. Trusted capabilities can then be activated per run with --enable-capability <name>.
.sloptrapignore
- Parsed using gitignore-style globbing with support for
!negation. - Entries must stay within the project root after resolving symlinks; attempts to reference
./.., absolute paths, or symlink escapes raise errors. - Directory matches become
--mount type=tmpfs,target=/workspace/<path>. File matches bind to empty files within.sloptrap-ignores/session-<pid>/. - The helper directory is removed automatically on exit or during
./sloptrap <project> clean.
CLI Reference
./sloptrap [--dry-run] [--print-config] [--trust-capabilities] [--enable-capability <name> ...] <code-directory> [target ...]
Options:
--dry-run— print the container/engine commands that would run without executing them.--print-config— output the resolved manifest values, defaults, and ignore list.--trust-capabilities— trust the manifest's requested capabilities for the current build flow.--enable-capability <name>— enable a trusted runtime capability for this invocation. Repeat for multiple capabilities.-h, --help— display usage.--— stop option parsing; remaining arguments are treated as targets.
Behaviour:
- Missing manifests are treated as default configuration; when a build is requested, sloptrap runs the interactive wizard if a TTY is available, otherwise it warns and continues with defaults.
SLOPTRAP_CONTAINER_ENGINEoverrides engine auto-detection.- If
${HOME}/.codex/auth.jsonis absent or empty, sloptrap prepends a login run before executing your targets. - Fresh interactive
runsessions receive a launcher-generated startup prompt telling the agent it is inside sloptrap, summarising the resolved manifest/runtime state, and pointing it at/workspace/.sloptrapfor exact project configuration.resumedoes not inject that prompt again. - Exit status mirrors the last target executed; errors in parsing or setup abort early with a message.
--print-config fields include manifest_present=true|false, requested/enabled capability lists, trust status, resolved paths, and the sanitised ignore mount roots so you can confirm what will be hidden inside the container.
Regression Suite
make regress(ortests/run_tests.sh) runsshellcheckagainstsloptrapand then executes every scenario intests/run_tests.sh, including the container build path check.- The suite must pass cleanly; ShellCheck diagnostics or scenario regressions cause a non-zero exit and should be fixed before shipping changes.
Built-in Targets
Targets are supplied after the code directory. When omitted, sloptrap defaults to run.
| Target | Description |
|---|---|
build |
Download Codex (if missing), verify SHA-256, and build the container image. |
build-if-missing |
No-op when the image already exists; otherwise delegates to build. |
rebuild |
Rebuild the image from scratch (--no-cache). |
run |
Default goal. Runs the container with Codex using sloptrap's built-in runtime flags. |
resume <session-id> |
Continues a Codex session by running codex resume <session-id> inside the container (builds if needed). |
login |
Starts Codex in login mode to bootstrap shared ${HOME}/.codex/auth.json credentials. |
shell |
Launches /bin/bash inside the container for debugging. |
wizzard |
Creates or updates .sloptrap interactively (no build); rerun build or rebuild afterward. |
stop |
Best-effort stop of the running container (if any). |
clean |
Removes .sloptrap-ignores, deletes the container/image, and stops the container if necessary. |
The launcher executes targets sequentially, so ./sloptrap repo build run performs an explicit rebuild before invoking Codex. Extra targets may be added in the future; unknown names fail fast.
Capability Helpers
When a trusted capability is enabled for a run, the container includes helper commands:
slop-apt install <package...>for session-scoped package installation.slopcap capture --interface <iface> [--filter <expr>] [--output <path>] [--stdout]for packet capture.sloppodman <pull|build|tag|run|ps|logs|stop|rm|inspect> ...for nested Podman workflows.buildcontexts and Dockerfiles must remain inside/workspace, and pushes are not supported.
Execution Environment
- Container engine: Podman or Docker with identical command lines. Podman uses
--userns=keep-id; Docker receives the equivalent--user UID:GIDfor standard runs. - Filesystem view: the project directory mounts at
/workspace;${HOME}/.codex/sloptrap/state/<project-hash>mounts at/codex;${HOME}/.codex/auth.jsonmounts at/codex/auth.json. - Ignore filter:
.sloptrapignoreentries are overlaid with tmpfs directories or empty bind mounts so data remains unavailable to Codex. - Network: isolated networking is used by default;
allow_host_network=trueopts into--network host. - Process context: standard runs drop capabilities, set
no-new-privileges, use a read-only root filesystem, and keep scratch paths (/tmp,/run,/run/lock) on tmpfs. Capability-enabled runs may selectively add the runtime options required for the requested capability. - Codex configuration: runtime flags are fixed to
--sandbox danger-full-access --ask-for-approval never. Persistent Codex state is project-scoped under${HOME}/.codex/sloptrap/state/, while credentials are shared via${HOME}/.codex/auth.json.
Threat Model and Limits
- Outbound disclosure: prompts and referenced data travel from the container to the configured LLM endpoint. Any file content within
/workspaceor environment data exposed to the process can appear in that traffic. - Shared storage:
/workspace, project-scoped/codex, and/codex/auth.jsonare host mounts. Files written to these locations become visible on the host and to the LLM provider through prompts. - Environment surface: the container receives a minimal fixed environment (HOME/XDG paths,
CODEX_HOME). The manifest no longer allows injecting additional environment variables. - Process isolation: standard runs keep a read-only root filesystem and no extra Linux capabilities. Capability-enabled runs deliberately relax specific runtime controls for the enabled feature, so they should be treated as a stronger trust decision than a default session.
- Networking stance: traffic is unrestricted once it leaves the container. sloptrap does not enforce an allowlist or DNS policy. Host networking is opt-in per manifest; if you require an offline or firewalled workflow, sloptrap is not an appropriate launcher.
- Persistence: Codex history and logs accumulate per project under
${HOME}/.codex/sloptrap/state/. Sensitive prompts recorded on disk remain on the host after the session. Because.git/is ignored inside the container, any historical secrets in Git objects stay outside the LLM context unless explicitly surfaced in the working tree. - Codex cache hygiene: per-project state mounts remain writable by the container and hold prompts/history/state, while
${HOME}/.codex/auth.jsonholds shared credentials. Rotate credentials regularly and protect both locations. - Secret scanning: sloptrap does not perform secret discovery or redaction; any credentials present in the project remain available to Codex and the upstream provider.
- Local model exception: pointing Codex at a local or self-hosted model keeps data within the host network boundary, but the filesystem and environment exposure described above is unchanged.
These constraints focus on limiting host data exposure to the Codex session while acknowledging that any material introduced into the context window may leave the environment through the upstream API.