Crate zinit

Crate zinit 

Source
Expand description

§zinit - Process Supervisor with Dependency Management

Zinit is a lightweight process supervisor with sophisticated dependency management. It’s designed to be simpler than systemd while providing the core features needed for service management in containers, VMs, and bare-metal systems.

§Overview

Zinit manages services through:

  • State Machine: 7-state service lifecycle (Inactive, Blocked, Starting, Running, Stopping, Exited, Failed)
  • Dependency Graph: Supports after, requires, wants, and conflicts dependencies
  • Service Classes: user (bulk-controllable) and system (protected) services
  • JSON-RPC 2.0 API: Unix socket IPC with 44 RPC methods
  • Health Checks: TCP, HTTP, and exec-based health monitoring
  • Process Groups: Proper signal handling for shell-wrapped processes
  • Hot Restart: State persistence and process adoption on restart

§Architecture

The crate is organized into modules:

  • sdk - Shared types, configuration, RPC protocol, and client libraries
  • server - (with server feature) Main supervisor daemon
  • client - (with client feature) CLI interface

§Features

  • sdk - Shared types and protocol (always included in lib)
  • client - CLI client interface
  • tui - Terminal UI for interactive client
  • repl - Interactive REPL shell
  • async - Async client support with tokio
  • server - Supervisor daemon with full feature set
  • pid1 - PID 1 init process (Linux only)
  • full - Everything: client + server + pid1 + tui + repl

§Quick Start

§Blocking Client

use zinit::{ZinitClient, ServiceConfig, ServiceDef};

// Connect to supervisor
let mut client = ZinitClient::connect_default().expect("Failed to connect");

// List all services
let services = client.list().expect("Failed to list");
println!("Services: {:?}", services);

// Get service status
let status = client.status("my-service").expect("Service not found");
println!("Status: {:?}", status.state);

§Async Client

use zinit::AsyncZinitClient;

let client = AsyncZinitClient::new(
    &std::path::PathBuf::from("/var/run/zinit.sock")
).expect("Failed to create client");

let status = client.status("my-service").await.expect("Status failed");
println!("PID: {}", status.pid);

§Creating Services

use zinit::{ZinitClient, ServiceConfig, ServiceDef, ServiceClass, RestartPolicy};
use std::collections::HashMap;

let mut client = ZinitClient::connect_default().expect("Failed to connect");

// Create a service config
let mut config = ServiceConfig {
    service: ServiceDef {
        name: "my-app".to_string(),
        exec: "/usr/bin/my-app --daemon".to_string(),
        dir: Some("/opt/my-app".to_string()),
        env: HashMap::new(),
        status: "start".to_string(),
        class: ServiceClass::User,
        critical: false,
        oneshot: false,
    },
    ..Default::default()
};

// Add the service
let result = client.add(&config, true).expect("Add failed");
println!("Created service: {}", result.name);

§Managing Dependencies

# In /etc/zinit/services/app.toml
[service]
name = "app"
exec = "/usr/bin/app"

[dependencies]
requires = ["database"]
after = ["logger"]
wants = ["cache"]
conflicts = ["dev-mode"]

[lifecycle]
restart = "on-failure"
start_timeout_ms = 30000
stop_timeout_ms = 10000

§RPC Methods

The supervisor exposes 44 RPC methods over JSON-RPC 2.0:

§System

  • system.ping - Check if supervisor is alive
  • system.shutdown - Gracefully shutdown
  • system.reboot - Reboot system (PID 1 only)
  • system.prepare_restart - Save state for hot restart

§Services

  • service.list - List service names
  • service.list_full - List services with state
  • service.status - Get service status
  • service.stats - Get resource usage (PID, memory, CPU)
  • service.start / service.stop / service.restart
  • service.create / service.delete - Manage services
  • service.tree - Show dependency tree
  • service.why - Explain why service is blocked
  • service.start_all / service.stop_all - Bulk operations (user-class only)

§Logging

  • logs.get - Get log lines
  • logs.tail - Get structured logs
  • logs.filter - Filter logs by service/stream/time

§Xinet (Socket Activation)

  • xinet.create / xinet.delete - Manage proxies
  • xinet.list / xinet.status - Query proxies

See sdk::protocol for complete RPC types and docs/OPENRPC_IMPLEMENTATION.md for detailed API documentation.

§Configuration Format

Services are configured in TOML format:

[service]
name = "my-service"
exec = "/usr/bin/my-service --daemon"
dir = "/opt/my-service"
class = "user"        # or "system"
critical = false      # Fail boot if critical (PID 1 only)
oneshot = false       # Exit without restart

[dependencies]
after = ["database"]
requires = ["logger"]
wants = ["cache"]
conflicts = ["competitor"]

[lifecycle]
restart = "on-failure"  # always, on-failure, never
restart_delay_ms = 1000
start_timeout_ms = 30000
stop_timeout_ms = 10000
stop_signal = "SIGTERM"
max_restarts = 10

[health]
type = "tcp"
target = "localhost:8080"
interval_ms = 10000
timeout_ms = 5000
retries = 3

[logging]
buffer_lines = 1000
file = "/var/log/my-service.log"

§Operating Modes

§Standalone Server

zinit-server --config-dir /etc/zinit/services

§Container Mode

zinit init -c --config-dir /etc/zinit/services

§VM/Bare-metal (as PID 1)

zinit init --config-dir /etc/zinit/services --system-dir /etc/zinit/system

§Platform Support

  • Linux: Full support including PID 1 mode with systemd-style boot
  • macOS/Darwin: Client and server support (no PID 1 mode)
  • Windows: Client support (via WSL recommended)

Path handling is platform-aware:

  • Linux: /run/zinit.sock, /etc/zinit/services
  • macOS/Windows: $HOME/hero/var/zinit.sock, $HOME/hero/cfg/zinit

See sdk::socket for path configuration details.

§Examples

See the examples/ directory for complete working examples:

  • list_services.rs - Enumerate services
  • monitor_service.rs - Watch service state changes
  • dependency_graph.rs - Visualize dependencies
  • create_service.rs - Dynamically create services

§Error Handling

Most operations return Result<T, Box<dyn std::error::Error>> for convenience. For detailed error information, use the underlying error types:

§Concurrency

  • Clients are single-threaded (synchronous)
  • Server uses tokio async runtime with RwLock<ServiceGraph> for safe concurrent access
  • IPC is over Unix domain sockets with newline-delimited JSON

§See Also

  • sdk module documentation
  • server module documentation (with server feature)
  • client module documentation (with client feature)
  • docs/ directory for ADRs and specifications
  • docs/OPENRPC_IMPLEMENTATION.md for complete API reference

Re-exports§

pub use sdk::DependencyDef;
pub use sdk::FailureReason;
pub use sdk::LifecycleDef;
pub use sdk::LoggingDef;
pub use sdk::OkResponse;
pub use sdk::RestartPolicy;
pub use sdk::ServiceClass;
pub use sdk::ServiceConfig;
pub use sdk::ServiceDef;
pub use sdk::ServiceState;
pub use sdk::ServiceStats;
pub use sdk::ServiceStatus;
pub use sdk::State;
pub use sdk::Status;
pub use sdk::WhyBlocked;
pub use sdk::ZinitClient;
pub use sdk::protocol;
pub use sdk::socket;
pub use sdk::validate;
pub use sdk::xinet;

Modules§

client
zinit client module - CLI interface for the zinit process supervisor.
sdk
zinit SDK - Shared types for the zinit process supervisor.
server
zinit-server - Process supervisor with dependency management.