zinit/
lib.rs

1//! # zinit - Process Supervisor with Dependency Management
2//!
3//! Zinit is a lightweight process supervisor with sophisticated dependency management.
4//! It's designed to be simpler than systemd while providing the core features needed
5//! for service management in containers, VMs, and bare-metal systems.
6//!
7//! ## Overview
8//!
9//! Zinit manages services through:
10//! - **State Machine**: 7-state service lifecycle (Inactive, Blocked, Starting, Running, Stopping, Exited, Failed)
11//! - **Dependency Graph**: Supports `after`, `requires`, `wants`, and `conflicts` dependencies
12//! - **Service Classes**: `user` (bulk-controllable) and `system` (protected) services
13//! - **JSON-RPC 2.0 API**: Unix socket IPC with 44 RPC methods
14//! - **Health Checks**: TCP, HTTP, and exec-based health monitoring
15//! - **Process Groups**: Proper signal handling for shell-wrapped processes
16//! - **Hot Restart**: State persistence and process adoption on restart
17//!
18//! ## Architecture
19//!
20//! The crate is organized into modules:
21//!
22//! - [`sdk`] - Shared types, configuration, RPC protocol, and client libraries
23//! - [`server`] - (with `server` feature) Main supervisor daemon
24//! - [`client`] - (with `client` feature) CLI interface
25//!
26//! ## Features
27//!
28//! - `sdk` - Shared types and protocol (always included in lib)
29//! - `client` - CLI client interface
30//! - `tui` - Terminal UI for interactive client
31//! - `repl` - Interactive REPL shell
32//! - `async` - Async client support with tokio
33//! - `server` - Supervisor daemon with full feature set
34//! - `pid1` - PID 1 init process (Linux only)
35//! - `full` - Everything: client + server + pid1 + tui + repl
36//!
37//! ## Quick Start
38//!
39//! ### Blocking Client
40//!
41//! ```no_run
42//! use zinit::{ZinitClient, ServiceConfig, ServiceDef};
43//!
44//! // Connect to supervisor
45//! let mut client = ZinitClient::connect_default().expect("Failed to connect");
46//!
47//! // List all services
48//! let services = client.list().expect("Failed to list");
49//! println!("Services: {:?}", services);
50//!
51//! // Get service status
52//! let status = client.status("my-service").expect("Service not found");
53//! println!("Status: {:?}", status.state);
54//! ```
55//!
56//! ### Async Client
57//!
58//! ```no_run
59//! # #[cfg(feature = "async")]
60//! # async fn example() {
61//! use zinit::AsyncZinitClient;
62//!
63//! let client = AsyncZinitClient::new(
64//!     &std::path::PathBuf::from("/var/run/zinit.sock")
65//! ).expect("Failed to create client");
66//!
67//! let status = client.status("my-service").await.expect("Status failed");
68//! println!("PID: {}", status.pid);
69//! # }
70//! ```
71//!
72//! ### Creating Services
73//!
74//! ```no_run
75//! use zinit::{ZinitClient, ServiceConfig, ServiceDef, ServiceClass, RestartPolicy};
76//! use std::collections::HashMap;
77//!
78//! let mut client = ZinitClient::connect_default().expect("Failed to connect");
79//!
80//! // Create a service config
81//! let mut config = ServiceConfig {
82//!     service: ServiceDef {
83//!         name: "my-app".to_string(),
84//!         exec: "/usr/bin/my-app --daemon".to_string(),
85//!         dir: Some("/opt/my-app".to_string()),
86//!         env: HashMap::new(),
87//!         status: "start".to_string(),
88//!         class: ServiceClass::User,
89//!         critical: false,
90//!         oneshot: false,
91//!     },
92//!     ..Default::default()
93//! };
94//!
95//! // Add the service
96//! let result = client.add(&config, true).expect("Add failed");
97//! println!("Created service: {}", result.name);
98//! ```
99//!
100//! ### Managing Dependencies
101//!
102//! ```toml
103//! # In /etc/zinit/services/app.toml
104//! [service]
105//! name = "app"
106//! exec = "/usr/bin/app"
107//!
108//! [dependencies]
109//! requires = ["database"]
110//! after = ["logger"]
111//! wants = ["cache"]
112//! conflicts = ["dev-mode"]
113//!
114//! [lifecycle]
115//! restart = "on-failure"
116//! start_timeout_ms = 30000
117//! stop_timeout_ms = 10000
118//! ```
119//!
120//! ## RPC Methods
121//!
122//! The supervisor exposes 44 RPC methods over JSON-RPC 2.0:
123//!
124//! ### System
125//! - `system.ping` - Check if supervisor is alive
126//! - `system.shutdown` - Gracefully shutdown
127//! - `system.reboot` - Reboot system (PID 1 only)
128//! - `system.prepare_restart` - Save state for hot restart
129//!
130//! ### Services
131//! - `service.list` - List service names
132//! - `service.list_full` - List services with state
133//! - `service.status` - Get service status
134//! - `service.stats` - Get resource usage (PID, memory, CPU)
135//! - `service.start` / `service.stop` / `service.restart`
136//! - `service.create` / `service.delete` - Manage services
137//! - `service.tree` - Show dependency tree
138//! - `service.why` - Explain why service is blocked
139//! - `service.start_all` / `service.stop_all` - Bulk operations (user-class only)
140//!
141//! ### Logging
142//! - `logs.get` - Get log lines
143//! - `logs.tail` - Get structured logs
144//! - `logs.filter` - Filter logs by service/stream/time
145//!
146//! ### Xinet (Socket Activation)
147//! - `xinet.create` / `xinet.delete` - Manage proxies
148//! - `xinet.list` / `xinet.status` - Query proxies
149//!
150//! See [`sdk::protocol`] for complete RPC types and [`docs/OPENRPC_IMPLEMENTATION.md`](https://github.com/geomind_code/zinit/blob/main/docs/OPENRPC_IMPLEMENTATION.md) for detailed API documentation.
151//!
152//! ## Configuration Format
153//!
154//! Services are configured in TOML format:
155//!
156//! ```toml
157//! [service]
158//! name = "my-service"
159//! exec = "/usr/bin/my-service --daemon"
160//! dir = "/opt/my-service"
161//! class = "user"        # or "system"
162//! critical = false      # Fail boot if critical (PID 1 only)
163//! oneshot = false       # Exit without restart
164//!
165//! [dependencies]
166//! after = ["database"]
167//! requires = ["logger"]
168//! wants = ["cache"]
169//! conflicts = ["competitor"]
170//!
171//! [lifecycle]
172//! restart = "on-failure"  # always, on-failure, never
173//! restart_delay_ms = 1000
174//! start_timeout_ms = 30000
175//! stop_timeout_ms = 10000
176//! stop_signal = "SIGTERM"
177//! max_restarts = 10
178//!
179//! [health]
180//! type = "tcp"
181//! target = "localhost:8080"
182//! interval_ms = 10000
183//! timeout_ms = 5000
184//! retries = 3
185//!
186//! [logging]
187//! buffer_lines = 1000
188//! file = "/var/log/my-service.log"
189//! ```
190//!
191//! ## Operating Modes
192//!
193//! ### Standalone Server
194//! ```bash
195//! zinit-server --config-dir /etc/zinit/services
196//! ```
197//!
198//! ### Container Mode
199//! ```bash
200//! zinit init -c --config-dir /etc/zinit/services
201//! ```
202//!
203//! ### VM/Bare-metal (as PID 1)
204//! ```bash
205//! zinit init --config-dir /etc/zinit/services --system-dir /etc/zinit/system
206//! ```
207//!
208//! ## Platform Support
209//!
210//! - **Linux**: Full support including PID 1 mode with systemd-style boot
211//! - **macOS/Darwin**: Client and server support (no PID 1 mode)
212//! - **Windows**: Client support (via WSL recommended)
213//!
214//! Path handling is platform-aware:
215//! - Linux: `/run/zinit.sock`, `/etc/zinit/services`
216//! - macOS/Windows: `$HOME/hero/var/zinit.sock`, `$HOME/hero/cfg/zinit`
217//!
218//! See [`sdk::socket`] for path configuration details.
219//!
220//! ## Examples
221//!
222//! See the `examples/` directory for complete working examples:
223//! - `list_services.rs` - Enumerate services
224//! - `monitor_service.rs` - Watch service state changes
225//! - `dependency_graph.rs` - Visualize dependencies
226//! - `create_service.rs` - Dynamically create services
227//!
228//! ## Error Handling
229//!
230//! Most operations return `Result<T, Box<dyn std::error::Error>>` for convenience.
231//! For detailed error information, use the underlying error types:
232//!
233//! - [`sdk::protocol::RpcError`] - RPC protocol errors
234//! - [`sdk::validate::ValidationError`] - Configuration validation errors
235//!
236//! ## Concurrency
237//!
238//! - Clients are single-threaded (synchronous)
239//! - Server uses tokio async runtime with `RwLock<ServiceGraph>` for safe concurrent access
240//! - IPC is over Unix domain sockets with newline-delimited JSON
241//!
242//! ## See Also
243//!
244//! - [`sdk`] module documentation
245//! - [`server`] module documentation (with `server` feature)
246//! - [`client`] module documentation (with `client` feature)
247//! - `docs/` directory for ADRs and specifications
248//! - `docs/OPENRPC_IMPLEMENTATION.md` for complete API reference
249
250// SDK module - always available
251pub mod sdk;
252
253// Re-export commonly used types from SDK at crate root
254pub use sdk::{
255    DependencyDef,
256    FailureReason,
257    LifecycleDef,
258    LoggingDef,
259    OkResponse,
260    RestartPolicy,
261    ServiceClass,
262    // Config types
263    ServiceConfig,
264    ServiceDef,
265    // State types
266    ServiceState,
267    ServiceStats,
268    // Response types
269    ServiceStatus,
270    State,
271    Status,
272    WhyBlocked,
273    // Client
274    ZinitClient,
275    // Modules
276    protocol,
277    socket,
278    validate,
279    xinet,
280};
281
282// Async client (when async feature enabled)
283#[cfg(feature = "async")]
284pub use sdk::AsyncZinitClient;
285
286// Server module (when server feature enabled)
287#[cfg(feature = "server")]
288pub mod server;
289
290// Client module (when client feature enabled)
291#[cfg(feature = "client")]
292pub mod client;