AI 日报hiw3c.com

Show HN: Corral – Kill every command your agent starts

Hacker News Top github.com 网页快照
正文为英文,可一键机器翻译(仅首次需要等待)

Notifications You must be signed in to change notification settings

Latest commit

History

Folders and files

Repository files navigation

corral

corral runs a command with a time limit. When corral returns, no process that the command started is still alive. corral verifies this before it returns. If it cannot prove it, corral exits with code 120.

AI coding agents and CI jobs run shell commands that they did not write. A typical runner signals its child process or the child's process group, and then waits until the output pipes close. This model fails in three common cases:

A daemon forks twice and calls setsid() . It is then in a different process group and session, and its parent is init, so the signal does not reach it.

A background process keeps stdout or stderr open. The runner never reads end-of-file, so it hangs after the command exits.

A process ignores SIGTERM and continues to run.

Leftover processes keep ports, file locks, and CPU, and the next run can fail because of them.

corral controls the full process tree, not only its direct child. In enforced mode, the command runs in its own cgroup v2 group, and one write to cgroup.kill stops every process in the group. Without a cgroup, corral is a child subreaper, so orphaned processes become its children. It finds the remaining processes through /proc and signals them through pidfds. In both modes, the run ends when the command exits, not when the pipes close.

corral is not a security sandbox. It does not limit file access, network access, or privileges.

Install

corral needs Linux 5.11 or later, and 5.14 or later for enforced mode.

The prebuilt binary is for x86-64 with glibc 2.36 or later (for example Ubuntu 22.10, Debian 12, or Fedora 37, or a later release):

curl -LO https://github.com/Cardinal44/corral/releases/latest/download/corral-linux-x86_64.tar.gz tar -xzf corral-linux-x86_64.tar.gz sudo cp corral-linux-x86_64/corral corral-linux-x86_64/corral-enforced /usr/local/bin/

To build from source, you need glibc 2.36, CMake 3.22, Ninja, and GCC 11 or Clang 14. The tests need Python 3.10.

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release cmake --build build ctest --test-dir build --output-on-failure

Use

corral --wall 30s -- ./build.sh corral --wall 5m --max-output 1M --json run.json -- make test corral-enforced --wall 30s --mem 512M --pids 64 -- ./build.sh

Enforced mode puts the command in its own cgroup v2 group. The kernel keeps every descendant in that group, and one write to cgroup.kill stops all of them. This mode also applies --mem and --pids . It needs a delegated cgroup. scripts/corral-enforced starts one with systemd-run .

Fallback mode needs no cgroup. corral finds the processes by parent, process group, and session, and scans /proc until nothing is left.

The default, --cgroup-mode=auto , uses enforced mode when it can.

A DURATION is a number with ms , s , m , or h . A SIZE is a number of bytes with an optional K , M , or G .

With --json , corral writes one JSON record for each run. The record gives the mode, the limits, and why the run ended. It also gives the time of each step and the pids of any process that did not stop.

Exit codes

How it works

The command starts in a new session, so a signal to its group never reaches corral.

corral is a child subreaper. Orphaned processes become children of corral, so corral can stop them.

corral signals a process that is not its child only through a pidfd, after it checks the process again. A reused pid never gets the signal.

The run ends when the command exits, not when its output pipes close.

After each run, corral stops and reaps processes until none are left. In enforced mode, the kernel must also report the group empty, and rmdir of the group must succeed.

Results

bench/compare.py runs ten test programs from faults/ with four runners:

NV: a Python runner that kills only its child

Each run has a 2 s time limit. Each cell shows the exit status and the largest number of processes that stayed alive, over 5 runs.

corral left no process alive in any test. CE also stopped F9 at its 64 MiB limit in 0.12 s. TO left a process alive in F7 in 1 of 5 runs. On this machine, timeout is from uutils coreutils 0.8.0, not GNU coreutils.

bench/bench.py measured these times (P50 / P95):

Test machine: Hetzner cloud server, 2 shared vCPUs (Intel Xeon, Skylake), Ubuntu 26.04, kernel 7.0. The full data and a terminal recording of scripts/demo.sh are in bench/results/ .

Limits

corral cannot see work that the command starts outside its process tree, for example with systemd-run , D-Bus, or at .

In enforced mode, a process can leave the group if it writes to cgroupfs itself.

In fallback mode, corral cannot signal a setuid child. It then exits with 120.

If you stop corral with SIGKILL, only the direct child is sure to stop.

corral has no PTY support and no CPU time limit, and it runs only on Linux.

License

About

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages