rightkit-process
Ownership-safe process lifecycle, restart, and health primitives for Right
Suite desktop apps. It owns child spawn with tree ownership, tree termination,
restart scheduling (RestartTracker), and health thresholds (HealthTracker).
It contains no app protocol, probe implementation, or business message.
Use it wherever an app starts a child process that must not outlive the app, or must be restarted by policy.
Do not use it to sandbox a process (use rightkit-sandbox), to adopt and kill arbitrary PIDs, or to define an app's health probe.
Features
None. The crate has no Cargo features. Platform dependencies: nix (Unix) and windows (Windows).
Platform support
- Unix: children lead a new process group (or a session with
UnixContainment::Session). Termination signals the group. - Windows: children are created suspended, assigned to a Job Object, then resumed. Termination uses the Job Object.
- The crate has
cfg(unix),cfg(windows),cfg(target_os = "macos"), andcfg(target_os = "linux")branches acrosssrc/owned.rs,src/process_handle.rs,src/process_unix.rs,src/process_windows.rs, andsrc/spawn_windows.rs.
Published version: see INDEX.md at the repository root. This crate has no CHANGELOG.md.
Ownership boundary
OwnedCommandis the only way to create anOwnedChild. Ordinary spawns own their tree; explicitly detached spawns leave lifetime with the caller.AdoptedProcesscan only report an existing PID and whether it is running. It deliberately has no terminate method. There is no publickill_pidAPI.- Dropping an ordinary
OwnedChildterminates and reaps its tree. A failure after OS spawn also cleans up the partial child.
Windows children are created suspended, assigned to a Job Object, then
resumed. Children are hidden by default (CREATE_NO_WINDOW); windows_hide() is a no-op kept for compatibility.
Explicit termination uses the Job Object, so descendants die while unrelated
sibling processes survive. Unix children lead a new process group;
wait_or_kill sends SIGTERM, waits for the bounded grace period, then sends
SIGKILL to the group and reaps it.
Native std children retain their group/job identity independently of direct-child exit. Windows resumes the suspended primary thread only after assignment decisions.
Also (0.3.0)
OwnedChild::child()borrows stdChildon Unix orWindowsChildon Windows through a lock guard, including stdin/stdout/stderr and wait/try_wait; existing stream-taking APIs remain.unix_containment(UnixContainment::Session)runssetsid; Windowsjob_assignment_mode(JobAssignmentMode::BestEffort)retains non-fatal errors injob_assignment_failures(). Strict assignment remains default.terminate_tree_with_options(TerminationOptions)sends Unix TERM immediately, then KILL after configurable tree-widegrace(zero means immediate escalation). Retained group/job also works after parent exit;windows_exit_codeaccepts 1067.terminate_tree_bounded_with_options(timeout, options)bounds grace plus reap.spawn_uncontained_detached()skips owner containment and survives owner drop. Windows requires parent-job breakaway permission and fails without fallback. Unix starts a separate session and reaps asynchronously; configure stdio so EOF or closed pipes do not request application shutdown.process_handle()returns an independentProcessHandleexit reader/waiter. Windows implementsAsHandlewith an owned duplicate and exposes FILETIMEcreation_time_ticks();creation_time()reports native time on Windows/macOS, spawn observation time on other Unix hosts. Unix readers share std's cached reap state. Releasechild()guards before calling another accessor or reader.partial_spawn_cleanup_timeout(Duration::from_secs(5))bounds failed-spawn cleanup; default remains unbounded. Timed-out children go to a background reaper.
Example and usage
Illustrative; not compiled as a doctest. The compiled checks are tests/api.rs, tests/supervision_primitives.rs, and tests/restart_health.rs.
OwnedCommand::inherit_handles_only restricts inheritance to caller handles/fds plus stdio; Windows requires windows_spawn_config for opaque stdio, environment-inheritance & raw-argument settings.
use ;
use ;
let mut command = new;
command.command_mut.stdout;
command.windows_hide;
let mut child = command.spawn?;
let stdout = child.take_stdout;
// The app sends its own graceful protocol message before this call on Windows.
match child.wait_or_kill?
# Ok::
RestartTracker is a pure rolling-window/backoff state machine. Restart
permits are single-use scheduling authorities: issuing a replacement permit,
starting a new generation, or shutting down invalidates every older permit.
RestartMode is Automatic, OnDemand, or Disabled.
HealthTracker is also pure: callers provide monotonic Duration timestamps
and probe outcomes, making startup timeout and consecutive-failure thresholds
deterministic in tests.
Verification
Run through the RightKit wrapper, per the workspace's docs/rules/rightkit.md:
rightkit cargo test -p rightkit-process --all-targets
rightkit cargo fmt --check
rightkit cargo clippy -p rightkit-process --all-targets -- -D warnings
rightkit cargo package -p rightkit-process --allow-dirty --no-verify --list
tests/windows_process_tree.rs covers parent and grandchild termination, sibling
survival, bounded grace, kill-on-drop, and partial-spawn cleanup on Windows.
tests/unix_process_group.rs covers Unix process-group behavior. Run both suites on
their own host platforms.
Windows
- Spawn:
CreateProcessWwithCREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, plusCREATE_NO_WINDOW(the default) orCREATE_BREAKAWAY_FROM_JOB(detached). Inherited handles are limited by aPROC_THREAD_ATTRIBUTE_HANDLE_LISTbuilt from the caller's handles and stdio. - Ownership order: a caller job from
windows_job()is assigned first, then a new job withJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. The primary thread is resumed by ID from a Toolhelp thread snapshot, so the child does not run outside its job. job_assignment_mode(BestEffort)records caller-job and owned-job errors injob_assignment_failures(). Strict, the default, fails the spawn.- Termination:
terminate_treecallsTerminateJobObjecton the owned job, thenTerminateProcesson the direct child. An ACCESS_DENIED from a child that is already exiting gets a 2 s wait. Processes outside the job are untouched. - Detached spawns (
spawn_uncontained_detached) need the parent job to allow breakaway. The spawn fails without fallback when it does not, and a detached spawn cannot take a caller job. - Errors: a
HRESULT_FROM_WIN32value (0x8007xxxx) is returned as the raw OS error. Other failures areio::Error::other. - Dependency:
windows0.61 undercfg(windows), with featuresWin32_Foundation,Win32_Security,Win32_System_Diagnostics_ToolHelp,Win32_System_JobObjectsandWin32_System_Threading. Consumers enable no Windows features. - The staged 0.62 bump (
tasks/handoffs/2026-10-08/inventory/windows-0.62.patch) is not applied at HEAD. - The code has no session or desktop check. Nothing here was run on Windows for this section.
Licensed under either MIT or Apache-2.0, at your option.