Small process orchestration for host/child IPC workflows in Rust.
pork helps you start child processes, establish a bootstrap handshake, exchange raw IPC messages, and shut children down gracefully using a shared control protocol. The workspace also includes pork-proto, a companion crate that provides the shared control-plane protocol types and codec implementations.
Use pork when you want to:
- supervise one or more child processes,
- connect parent and child over IPC without building the handshake yourself,
- send application-defined payloads between host and child,
- and keep framework-level shutdown behavior consistent.
This repository uses a workspace-first layout:
- repository root — shared workspace files, Nix development setup, and top-level documentation
pork/— mainporklibrary cratepork-proto/— shared protocol crateexamples/pork-comms/— end-to-end host/child example showing typed messages, codec selection, and child status reporting
This keeps the workspace root focused on coordination while each crate owns its own manifest, source tree, and tests.
pork— high-level orchestration API for starting, tracking, messaging, restarting, and stopping managed child processes through explicit modules such asorchestrator,spec,child, anderror.pork-proto— shared protocol definitions inprotocolplus feature-gated codec implementations incodecs.
A typical setup has two sides:
- A host process creates a
pork::orchestrator::ProcessOrchestratorand starts a child from apork::orchestrator::spec::ProcessSpec. - A child process reads bootstrap information from the environment and connects back to the host with
pork::child::bootstrap. - Both sides exchange typed
pork::types::DataPayloadvalues throughpork. - Shared control messages, encoded
pork::types::ControlPayloadvalues, typed IPC envelopes, and codec selection live inpork_proto::protocol.
If you only need process orchestration and raw byte transport, depend on pork.
If you want typed IPC payloads and the shared codec helpers, depend on both pork and pork-proto.
pork uses feature flags to enable host-side and child-side APIs:
host(default) — host APIs for process managementclient(default: off) — child APIs for bootstrap and connection
With both features enabled, pork establishes two IPC channels:
- Data channel — application payloads
- Control channel — codec-encoded framework messages (
GracefulShutdown,Restart, status updates)
On the child side, ChildBootstrap::connect provides one API surface with two independent
receive workers (recv_data and recv_control). Heavy data traffic therefore cannot block
control-plane reception.
ProcessSpecBuilder configures the bootstrap environment-variable names and optional
managed child name before producing an immutable ProcessSpec:
- builder setters:
data_bootstrap_env(...),control_bootstrap_env(...), andmanaged_name(...) ProcessSpecaccessors:data_bootstrap_env_ref(),control_bootstrap_env_ref(), andmanaged_name()
The orchestrator reads those values when spawning the child, and ChildBootstrap::from_env
expects both bootstrap variable names. In the common case, use the default ProcessSpecBuilder
settings on the host and ChildBootstrap::from_default_env() on the child.
See pork/src/orchestrator.rs, pork/src/orchestrator/spec.rs, and pork/src/child/bootstrap.rs for the primary API documentation.
For the actual API, examples, and behavior details, use the crate documentation:
porkcrate docs: seepork/src/lib.rspork::orchestratorfor host-side process managementpork::orchestrator::specfor child process configurationpork::child::bootstrapfor child-side bootstrap helpers
pork-protocrate docs: seepork-proto/src/lib.rspork_proto::protocolfor protocol models, shared control messages, and typed IPC envelopespork_proto::codecsforJsonCodecandPostcardCodec
If you are browsing locally, the crate-level docs are the best starting point because they include the intended usage flow and focused examples for the namespaced API.
You need:
- Rust toolchain (this workspace targets Rust
1.98) - Cargo
- Unix-like local IPC support for the current process model
- Optional: Nix with flakes enabled if you want the provided development shell
Using Nix (recommended for reproducible developer shells):
nix develop -c cargo build --workspace
nix develop -c cargo test --workspace --all-targets
nix develop -c cargo test --workspace --all-features --all-targetsWithout Nix (plain Cargo):
cargo build --workspace
cargo test --workspace --all-targets
cargo test --workspace --all-features --all-targetsIf you prefer an interactive shell, enter it first and then run the same Cargo commands inside that shell:
nix develop
cargo test --workspace --all-features --all-targetsA typical workflow has three parts:
- define how the child process should be started with
pork::orchestrator::spec::ProcessSpec - start and manage the child from
pork::orchestrator::ProcessOrchestrator - connect from the child side with
pork::child::bootstrap::ChildBootstrap
For a complete typed example, see examples/pork-comms/.
For heartbeat-based liveness reporting, configure a heartbeat interval on the host-side
ProcessSpec. Bootstrap will propagate that interval to the child and the child will send
periodic Heartbeat messages automatically. These messages refresh the timestamp of the
cached child-reported status on the host but never overwrite the lifecycle state. Child
code should still report meaningful lifecycle transitions with report_status, such as
Running or Stopping; the heartbeat is only a liveness ping. See
docs/child-lifecycle.md for a detailed guide.
Host side sketch:
use pork::orchestrator::ProcessOrchestrator;
use pork::orchestrator::spec::ProcessSpec;
async fn run_host() -> Result<(), pork::error::OrchestratorError> {
let orchestrator = ProcessOrchestrator::new();
let child = orchestrator
.start_process(
ProcessSpec::builder("./child-binary")
.managed_name("worker")
.log_output("./worker.log")
.build(),
)
.await?;
child.send("ping")?;
let _status = orchestrator.graceful_shutdown_process(child.process_id()).await?;
Ok(())
}Child side sketch:
use pork::child::bootstrap::ChildBootstrap;
async fn run_child() -> Result<(), pork::error::OrchestratorError> {
let channels = ChildBootstrap::from_default_env()?.connect().await?;
channels.send_data("ready")?;
while let Some(payload) = channels.recv_data().await {
let _ = payload;
}
Ok(())
}Run these checks before making a release or merging large changes. These match what CI enforces. When available, prefer the nix develop -c variants because they are the canonical repository workflow.
Formatting and lints
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warningsTests and docs
cargo test --workspace
cargo test --workspace --doc
cargo test --workspace --all-features --all-targetsProduction-readiness notes also live in docs/production-readiness.md.
Security & license checks
# install tools if you don't have them already
cargo install --locked cargo-audit cargo-deny
# run the checks
cargo audit
cargo deny checkMSRV (verify compilation on minimum supported Rust)
rustup toolchain install 1.98.0
rustup run 1.98.0 cargo check --workspaceNix-based validation
nix flake checkThis repository uses cargo-audit and cargo-deny to enforce advisories and license policies in CI. The cargo-deny configuration lives at deny.toml in the workspace root. CI runs cargo audit and cargo deny check as part of the security job; run the same commands locally before releasing.
The deny.toml file also contains any temporary advisory suppressions that have been reviewed and accepted with a plan to remediate (for example, a transitive unmaintained crate that currently has no safe upgrade path). Treat suppressions as temporary and track follow-up work to remove them.
The examples/pork-comms/ crate demonstrates a small end-to-end setup with:
- a host binary
- a child binary
- typed messages encoded with
pork-proto - coverage for both JSON and Postcard codec flows
- child-to-host status reporting over the control channel
Use that example when you want a concrete reference before integrating pork into your own application.
Note: example crates are publish = false in their Cargo.toml to avoid accidental publishing.
This workspace is prepared for the 2.0.0 release line: the main orchestration API lives in the pork crate, while shared protocol details live in pork-proto.
Before publishing, ensure CI is green and perform these validation steps locally (see the Validate locally section above).
The publish order is:
pork-protopork
The workspace currently uses a local path dependency from pork to pork-proto together with the matching published version requirement. Validate both crates with dry runs first, then publish in that order.
Recommended pre-publish commands
cargo package --manifest-path pork-proto/Cargo.toml
cargo publish --dry-run -p pork-proto
cargo package --manifest-path pork/Cargo.toml
cargo publish --dry-run -p porkCI is defined in .github/workflows/ci.yml and runs the following gates on PRs and pushes to release branches:
- formatting (
cargo fmt --all -- --check) - clippy (
cargo clippy --workspace --all-targets -- -D warnings) - workspace tests (
cargo test --workspace) - all-features and all-targets tests (
cargo test --workspace --all-features --all-targets) - feature-matrix checks for
porkandpork-proto - documentation tests (
cargo test --workspace --all-features --doc) - MSRV compile check (Rust 1.98)
- security and license checks (
cargo audit,cargo deny check) nix flake check
- See
pork/src/lib.rsandpork-proto/src/lib.rsfor crate-level documentation and examples. - Look at
.github/workflows/ci.ymlfor the exact CI jobs and expected checks.