ProcessKit: async child-process management with a kernel-backed no-orphan guarantee

Crates.io Docs CI License: MIT OR Apache-2.0

processkit-cli

processkit-cli is the standalone command runner for ProcessKit. It executes one shell-free command inside the public processkit crate's containment boundary, preserves the child's exit code, and records a versioned JSONL lifecycle stream without requiring a Rust or Python runtime at the call site.

The runner owns the command-line and event contracts. ProcessKit-rs remains the single source of truth for containment, teardown, PID-reuse discipline, and platform lifecycle behavior. The cover above represents the wider ProcessKit family; this CLI deliberately focuses on one contained run and its control plane rather than exposing the core crate's pipelines, retries, or scheduling APIs.

For an adoption-oriented comparison with timeout, process groups, systemd, containers, Tini, and PowerShell, see Why ProcessKit CLI?. The guide explicitly identifies the cases where a smaller or wider supervisor is the better fit.

Install

Download a platform archive from the latest GitHub release, or build and install from crates.io:

cargo install processkit-cli

Release archives contain the binary, shell completions, man pages, the JSON Schema, a SHA-256 checksum, and a signed build-provenance attestation. See Installation and distribution for target selection, checksum/attestation verification, completions, man pages, and post-install preflight.

Quick start

Run a command directly, with no shell between the runner and the program:

processkit-cli run --jsonl events.jsonl -- cargo --version

Child stdout and stderr pass through unchanged. Lifecycle events go only to events.jsonl, so an adapter can consume them without parsing or contaminating the child's output.

Use a stable run id when another process needs to inspect or stop the live run:

processkit-cli run --run-id build-42 --jsonl events.jsonl -- cargo test
processkit-cli inspect --run-id build-42 --json
processkit-cli cancel --run-id build-42

The control commands address a per-user registry entry and a live local IPC endpoint, never an operating-system PID. A reused PID therefore cannot retarget an old command at an unrelated process.

Choose a run shape

You needUse
A normal CI command with live outputDefault run: closed stdin, pipe + echo stdout/stderr.
A real existing terminal--inherit-stdio (no capture or idle timeout).
Finite input--stdin-file FILE.
Durable bounded transcripts--capture-dir DIR, optionally --no-echo.
A stuck-worker detector--idle-timeout DURATION.
A recorded history of how the process tree evolved--snapshot-interval DURATION (composes with every I/O mode and with --detach).
External tools launched by an automation agentA foreground run with a unique id, finite deadlines, JSONL, and bounded capture.
Launch now, supervise from another process--detach plus a durable JSONL path and run id.
Whole-tree resource caps--max-memory, --max-processes, --cpu-quota where supported.

The Cookbook gives copyable complete invocations. The narrative guides explain why combinations are accepted or rejected.

Use from automation agents

An automation or coding agent can use this binary without a dedicated SDK. A project instruction can simply require external tools to be launched through processkit-cli run with a unique run id, finite deadlines, lifecycle JSONL, and bounded capture. The agent then has explicit inspect / cancel / wait / kill recovery operations instead of tracking a fragile PID or cleaning up by process name.

This makes agent-driven builds, tests, compilers, and long-lived workers more robust: descendant cleanup is scoped to the run, silent hangs can be bounded, diagnostics survive an interrupted agent turn, and different workloads can use different timeout, output, environment, and resource strategies. It does not pretend that disappearance of an arbitrary agent process is itself a portable cleanup signal; prefer foreground runs, terminate the runner during agent teardown, and use finite deadlines. Detached work needs a separate supervisor.

See Agent and automation workflows for a ready-to-paste agent policy and complete foreground, recovery, and escalation examples.

What the runner guarantees

  • One owned process tree. Normal completion, timeout, cancellation, and runner errors tear down the current run's ProcessKit container. Cleanup never searches by executable name.
  • Exit-code fidelity. A normal child exit is returned unchanged. Runner failures occupy the documented 100-119 band and also emit runner_exit, so a child code is never silently aliased.
  • Separated streams. Child output stays on stdout/stderr; JSONL events stay in --jsonl; runner diagnostics never enter child stdout.
  • Redacted diagnostics. Events contain a SHA-256 argv fingerprint and a classified worker hint by default. Raw arguments require --argv-raw.
  • Bounded capture. --capture-dir tees stdout and stderr into separate, size-capped transcripts with byte counts, hashes, and truncation metadata.
  • Honest platform reporting. run_started records the active containment mechanism and the real abrupt-runner-death cleanup guarantee rather than presenting every operating system as equivalent.

Command surface

CommandPurpose
runStart one contained, shell-free command and write lifecycle JSONL.
inspectSnapshot a live run and its current members.
cancelRequest soft stop, wait through the grace window, then hard-kill survivors.
killHard-kill the run's whole container immediately.
waitWait for one run, or a snapshot of all live runs, to finish.
eventsRead a run's JSONL lifecycle stream back: render, follow, pass through, or validate it.
listDiscover live, stale, and unprobed registry entries.
pruneRemove only entries confirmed stale.
probeVerify the binary's versioned compatibility surface before launch.

Run processkit-cli <command> --help for the complete flag set. The integration guide shows a fail-closed adapter workflow from preflight through cleanup.

Platform behavior

PlatformPreferred mechanismAbrupt runner death
WindowsJob ObjectWhole tree is reaped by kernel kill-on-close.
Linuxcgroup v2, with process-group fallbackDirect child only when parent-death signaling is available.
macOS / other UnixPOSIX process groupNo automatic whole-tree guarantee after an uncatchable runner death.

Every ordinary teardown still uses the active container on every supported platform. The last column is intentionally narrower: it describes only a crash, SIGKILL, or comparable event that prevents the runner from executing its own cleanup path. See the architecture and troubleshooting guide for the exact caveats.

Guides

GuideCovers
Installation and distributionArchives, package-manager manifests, target selection, checksums, attestations, Cargo, completions, man pages.
CookbookTask → command recipes for common foreground, detached, capture, control, and container workflows.
Agent and automation workflowsA drop-in agent instruction, bounded execution strategies, recovery, and honest agent-stop guarantees.
Running commandsShell-free argv, cwd, environment, run ids, foreground lifecycle, and flag interactions.
Standard I/O and captureDefault pipes, inherited handles, stdin files, no-echo, bounded transcripts, TTY caveats.
Detached runsStartup proof, changed launcher exit semantics, recovery, and out-of-band supervision.
Timeouts and cancellationOverall/idle clocks, grace, signals, cancel vs kill, and platform soft-stop behavior.
Resource limitsWhole-tree memory/process/CPU caps and fail-closed enforcement.
Platform supportRelease targets, mechanisms, abrupt cleanup, capability and CI matrices.
Running in containersmusl/glibc images, PID 1, signals, writable paths, cgroup delegation, outer limits.
Integration guideProbe, launch, event consumption, supervision, and housekeeping for adapters.
Compatibility and upgradesSurface tokens, schema/exit-band pinning, rolling upgrades, and acceptance policy.
Live-run control planeIPC transport, inspect/cancel/kill semantics, and safe targeting.
Run registryPer-user records, liveness probing, ambiguity, waiting, and pruning.
JSONL event schemaThe normative schema_version = 1 contract and golden fixtures.
Exit-code contractChild-code fidelity and the reserved runner failure band.
TroubleshootingSymptom-to-cause diagnosis for operators and CI.
Threat modelTrusted boundaries, hostile inputs, local IPC, and supply chain.
ArchitectureModule map and the data flow of one run.

The 60-second tour

# 1. Prove the installed runner supports the contract your caller needs.
processkit-cli probe --json \
  --require-schema-version 1 \
  --require-exit-code-band 100-119 \
  --require-surface run:--capture-dir

# 2. Start one shell-free command with a stable id and bounded transcripts.
processkit-cli run --run-id demo --capture-dir ./demo-output \
  --jsonl demo.jsonl -- cargo test

# 3. While it is live, inspect or cancel it from another process.
processkit-cli inspect --run-id demo --json
processkit-cli cancel --run-id demo

# 4. Read its lifecycle story back — live, or long after it finished.
processkit-cli events --run-id demo --follow
processkit-cli events --file demo.jsonl

# 5. Discover or clean up registry state after an orchestrator restart.
processkit-cli list --json
processkit-cli prune --dry-run --json

The JSONL file is the durable lifecycle record. The registry and control endpoint exist only while a run is live (or as detectable stale leftovers after an abrupt runner death).

Source, release history, and contribution guidance live in the GitHub repository.