No description
Find a file
despiegk acffe73d73
Some checks failed
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-gnu) (push) Successful in 1m49s
Build macOS / build-macos (push) Has been cancelled
Bump version to 0.3.5, fix all clippy collapsible_if warnings
2026-01-05 11:04:07 +01:00
.cargo init 2025-12-15 09:35:48 +01:00
.claude Auto-detect init mode and use /etc/zinit for units 2025-12-31 17:45:34 +01:00
.forgejo/workflows Fix CI workflows to build zinit, zinit-server, and zinit-updater 2026-01-05 07:34:26 +01:00
_archive/.serena Fix sysinfo 0.33 API, add nix reboot feature, update install.sh for 3 binaries 2026-01-05 07:39:37 +01:00
ai_instructions Bump version to 0.3.2 2026-01-05 10:27:32 +01:00
docs Fix sysinfo 0.33 API, add nix reboot feature, update install.sh for 3 binaries 2026-01-05 07:39:37 +01:00
example install script ok 2025-12-26 18:55:05 +01:00
rhaiexamples Fix clippy collapsible_if warnings, bump to 0.3.4 2026-01-05 10:45:55 +01:00
scripts Fix sysinfo 0.33 API, add nix reboot feature, update install.sh for 3 binaries 2026-01-05 07:39:37 +01:00
specs good updates, makes specs stronger 2026-01-03 09:50:34 +01:00
tests cleanup was not good 2025-12-26 20:30:01 +01:00
zinit_client Fix more clippy collapsible_if warnings in client, update build_publish.sh 2026-01-05 10:57:14 +01:00
zinit_server Fix more clippy collapsible_if warnings in client, update build_publish.sh 2026-01-05 10:57:14 +01:00
zinit_updater Fix clippy collapsible_if warnings, bump to 0.3.4 2026-01-05 10:45:55 +01:00
.gitignore init 2025-12-15 09:35:48 +01:00
build.sh cleanup was not good 2025-12-26 20:30:01 +01:00
build_package.sh Fix sysinfo 0.33 API, add nix reboot feature, update install.sh for 3 binaries 2026-01-05 07:39:37 +01:00
build_publish.sh Fix more clippy collapsible_if warnings in client, update build_publish.sh 2026-01-05 10:57:14 +01:00
Cargo.lock Bump version to 0.3.5, fix all clippy collapsible_if warnings 2026-01-05 11:04:07 +01:00
Cargo.toml Bump version to 0.3.5, fix all clippy collapsible_if warnings 2026-01-05 11:04:07 +01:00
HEROLIB_INTEGRATION.md herolib integrated 2025-12-26 21:41:45 +01:00
install.sh intermediate step, zinit works with rhai 2025-12-17 20:34:13 +01:00
LICENSE init 2025-12-15 09:35:48 +01:00
README.md Fix sysinfo 0.33 API, add nix reboot feature, update install.sh for 3 binaries 2026-01-05 07:39:37 +01:00
run.sh improve run.sh file, is used to run zinit in usermode 2026-01-03 09:25:02 +01:00

Zinit

A lightweight process supervisor with Rhai scripting support, designed for both standalone and container deployments.

Zinit consists of three components:

  • zinit-server - The daemon that manages processes
  • zinit - The CLI client with REPL, TUI, and Rhai scripting
  • zinit-updater - Auto-updater and PID 1 init shim for containers

Features

  • Process Supervision - Automatic restarts, dependency ordering, health checks
  • Container Support - Run as PID 1 with proper signal handling and zombie reaping
  • Live Updates - Update zinit binaries without restarting services
  • On-Demand Services - Xinit proxy starts services only when connections arrive
  • Rhai Scripting - Powerful scripting for automation
  • Interactive REPL - Tab completion, syntax highlighting, command history
  • Full-screen TUI - Monitor and control services visually
  • Resource Stats - CPU and memory usage per service
  • Simple Architecture - JSON-RPC over Unix socket

Installation

Quick Install (Linux/macOS)

curl -sSL https://forge.ourworld.tf/geomind_code/zinit/raw/branch/main/scripts/install.sh | bash

From Crates.io

cargo install zinit_server
cargo install zinit
cargo install zinit-updater

From Source

git clone https://forge.ourworld.tf/geomind_code/zinit
cd zinit
cargo build --release

# Install binaries
cp target/release/zinit-server /usr/local/bin/
cp target/release/zinit /usr/local/bin/
cp target/release/zinit-updater /usr/local/bin/

Quick Start

1. Start the Server

# Run in background (daemon mode)
zinit-server -bg

# Or run in foreground
zinit-server

2. Use the Client

# Check connection
zinit ping

# List services
zinit list

# Start interactive REPL
zinit repl

# Start full-screen TUI
zinit tui

Container / PID 1 Mode

For container deployments, use zinit-updater as your entrypoint. It acts as a proper init process (PID 1) and manages zinit-server as a child:

ENTRYPOINT ["/zinit-updater"]

Architecture

PID 1: zinit-updater
  │
  ├── Reaps orphan zombie processes
  ├── Forwards signals to zinit-server
  ├── Auto-restarts zinit-server on crash
  ├── Periodically checks for updates
  │
  └── zinit-server (child process)
        └── services...

Signal Handling

Signal Behavior
SIGTERM Full shutdown - stops all services, then exits
SIGINT Same as SIGTERM
SIGUSR1 Soft restart - zinit-server restarts, services keep running

Environment Variables

Variable Default Description
ZINIT_UPDATE_INTERVAL 300 Seconds between update checks

Testing Init Mode

# Test without being PID 1
zinit-updater --init

# In another terminal
pgrep -a zinit

# Test soft restart
kill -USR1 $(pgrep -x zinit-server)

# Test shutdown
kill -TERM $(pgrep -f 'zinit-updater.*--init')

Live Updates

When zinit-updater detects a new version:

  1. Sends SIGUSR1 to zinit-server (soft restart)
  2. zinit-server exits without stopping services
  3. Downloads and installs new binaries
  4. Spawns new zinit-server
  5. New server rediscovers running services via /proc

Services continue running throughout the update process.

Usage

Server Commands

zinit-server              # Run in foreground
zinit-server -bg          # Run in background (daemon mode)
zinit-server --port 9123  # Also listen on TCP port
zinit-server --help       # Show help

Updater Commands

zinit-updater check       # Check for updates
zinit-updater update      # Download and apply updates
zinit-updater watch [s]   # Poll for updates every N seconds
zinit-updater add <url>   # Track a Rhai script URL
zinit-updater remove <url> # Stop tracking a script
zinit-updater list        # List tracked scripts
zinit-updater --init      # Run as init (for testing)

Client Commands

# Service management
zinit list                # List all services
zinit status <name>       # Get service status
zinit start <name>        # Start a service
zinit stop <name>         # Stop a service
zinit restart <name>      # Restart a service
zinit delete <name>       # Delete a service
zinit kill <name> [sig]   # Send signal to service
zinit stats <name>        # Get resource usage

# Logs
zinit logs                # Show all logs
zinit logs <name>         # Show logs for service

# Scripting
zinit script.rhai         # Run a Rhai script file
zinit scripts/            # Run all .rhai files in directory
zinit -c 'zinit_ping()'   # Execute inline script
zinit -i                  # Read script from stdin

# Interactive
zinit repl                # Interactive REPL
zinit tui                 # Full-screen TUI
zinit ping                # Check server connection

Rhai Scripting

Inline Scripts

# Simple ping
zinit -c 'zinit_ping()'

# List services
zinit -c 'print(zinit_list())'

# Create and start a service
zinit -c '
let svc = new_service("myapp")
    .exec("python3 -m http.server 8080")
    .oneshot(false);
start(svc, 5);
'

From Stdin

echo 'zinit_list()' | zinit -i

zinit -i << 'EOF'
let status = zinit_status("myapp");
print("State: " + status.state);
print("PID: " + status.pid);
EOF

Script Files

// myservice.rhai
let svc = new_service("webserver")
    .exec("python3 -m http.server 8080")
    .dir("/var/www")
    .env("PORT", "8080")
    .after("database")
    .oneshot(false);

start(svc, 10);  // Start and wait up to 10s

print("Webserver started!");
print("State: " + zinit_status("webserver").state);
zinit run myservice.rhai

Service Builder API

// Create and register a service
let svc = zinit_service_new()
    .name("myapp")                       // Service name
    .exec("python3 -m http.server 8080") // Required: command to run
    .test_cmd("curl -s localhost:8080")  // Command-based health check
    .test_tcp("localhost:8080")          // TCP port health check
    .test_http("http://localhost:8080/") // HTTP health check
    .tcp_kill()                          // Kill processes on port before starting
    .oneshot(false)                      // Restart on exit (default)
    .after("database")                   // Wait for dependency
    .env("PORT", "8080")                 // Environment variable
    .dir("/var/www")                     // Working directory
    .log("ring")                         // Logging: "ring", "stdout", "none"
    .shutdown_timeout(10)                // Seconds before SIGKILL
    .signal_stop("SIGTERM")              // Stop signal
    .register();                         // Register with zinit

// Wait for service to start
svc.wait(30);  // Wait up to 30 seconds

Xinit: On-Demand Service Startup

Xinit is a socket proxy feature that enables on-demand service startup. Instead of running services continuously, xinit creates a proxy socket that starts the service only when a client connects.

How It Works

  1. When zinit starts, services with xinit.enabled: true don't auto-start
  2. Xinit creates a proxy socket at the listen address
  3. When a client connects to the proxy, xinit:
    • Starts the backend service
    • Waits for the backend socket to become available
    • Forwards the connection to the backend
  4. After idle_timeout seconds with no connections, the service stops

Configuration

Add xinit configuration to your service YAML:

# ~/hero/cfg/zinit/postgres.yaml
exec: "postgres -D /var/lib/postgresql/data"

# Health check - used as default backend if xinit.backend not set
health:
  tcp: "localhost:5432"

# Xinit configuration
xinit:
  enabled: true
  listen: "/tmp/pg-proxy.sock"   # Proxy socket (Unix or TCP)
  backend: "localhost:5432"      # Backend address (optional, defaults to health.tcp)
  connect_timeout: 30            # Seconds to wait for backend after service start
  idle_timeout: 300              # Stop service after N seconds of inactivity (0=never)
  single_connection: false       # Allow only one connection at a time

Examples

Unix Socket Proxy (PostgreSQL):

exec: "postgres -D /var/lib/postgresql/data"
health:
  tcp: "localhost:5432"
xinit:
  enabled: true
  listen: "/tmp/pg-proxy.sock"
  idle_timeout: 300

Clients connect to /tmp/pg-proxy.sock instead of localhost:5432.

TCP Proxy (Redis):

exec: "redis-server --port 6379"
health:
  tcp: "localhost:6379"
xinit:
  enabled: true
  listen: "0.0.0.0:16379"       # External proxy port
  backend: "localhost:6379"     # Internal backend
  idle_timeout: 600

Clients connect to port 16379, which proxies to Redis on 6379.

Xinit Builder API (Rhai)

// 1. First, create the backend service (not started yet)
zinit_service_new()
    .name("echo_server")
    .exec("socat TCP-LISTEN:9999,reuseaddr,fork EXEC:cat")
    .test_tcp("localhost:9999")
    .register();

// 2. Create xinit proxy that starts the service on demand
xinit_proxy_new()
    .name("echo_proxy")
    .listen("unix:///tmp/echo.sock")    // Listen on Unix socket or "tcp://0.0.0.0:8080"
    .backend("tcp://127.0.0.1:9999")    // Forward to backend
    .service("echo_server")              // Service to start on connection
    .idle_timeout(60)                    // Stop after 60s idle (0=never)
    .connect_timeout(10)                 // Wait 10s for backend
    .reset()                             // Delete existing proxy first
    .register();

// Proxy management
xinit_list();                   // List all proxies
xinit_status("echo_proxy");     // Get proxy status
xinit_unregister("echo_proxy"); // Remove proxy

Use Cases

  • Development environments: Start heavy services only when needed
  • Resource-constrained systems: Run services on-demand to save memory
  • Rarely-used services: Start database only when accessed
  • Cold start testing: Simulate service startup latency

Rhai Functions

Service Control

  • zinit_start(name) - Start a service
  • zinit_stop(name) - Stop a service
  • zinit_restart(name) - Restart a service
  • zinit_delete(name) - Delete a service
  • zinit_kill(name, signal) - Send signal
  • .register() - Register service (builder method)
  • .wait(secs) - Wait for service to start (builder method)

Service Info

  • zinit_list() - Array of service names
  • zinit_status(name) - Map: name, pid, state, target
  • zinit_stats(name) - Map: pid, memory_usage, cpu_usage
  • zinit_is_running(name) - Boolean

Logs

  • zinit_logs() - Array of all log lines
  • zinit_logs_filter(name) - Logs for specific service

Batch Operations

  • zinit_start_all() - Start all services
  • zinit_stop_all() - Stop all services
  • zinit_shutdown() - Shutdown server

System

  • zinit_ping() - Check server connection

Xinit (On-Demand Proxy)

  • xinit_list() - Array of proxy names
  • xinit_register(name, listen, backend, service) - Register a proxy
  • xinit_unregister(name) - Unregister a proxy
  • xinit_status(name) - Proxy status (connections, bytes, etc.)
  • xinit_status_all() - Status of all proxies

Utilities

  • print(msg) - Print output
  • sleep(secs) - Sleep seconds
  • sleep_ms(ms) - Sleep milliseconds
  • get_env(key) - Get environment variable
  • set_env(key, val) - Set environment variable
  • file_exists(path) - Check if path exists
  • check_tcp(addr) - Check TCP port
  • check_http(url) - Check HTTP endpoint
  • kill_port(port) - Kill process on port

Paths

Path Purpose
~/hero/cfg/zinit/ Service YAML files
~/hero/var/zinit.sock Unix socket
~/hero/cfg/zinit_latest.toml Updater state

Service States

State Description
Running Process is running
Success Oneshot completed successfully
Failed Process exited with error
Blocked Waiting for dependencies
Spawned Process started, not yet confirmed
Stopped Manually stopped

Interactive REPL

$ zinit repl
╔═══════════════════════════════════════════════════════════╗
║           Zinit Interactive Shell                         ║
╠═══════════════════════════════════════════════════════════╣
║  Tab: completion | Up/Down: history | Ctrl+D: exit        ║
║  Type /help for commands, /functions for API              ║
╚═══════════════════════════════════════════════════════════╝

Connected to zinit server: pong v0.3.0

zinit> zinit_list()
=> ["myapp", "database"]

zinit> zinit_status("myapp")
=> #{"name": "myapp", "pid": 1234, "state": "Running", "target": "Up"}

zinit> /quit
Goodbye!

REPL Commands

  • /help - Show help
  • /functions - List all functions
  • /tui - Switch to TUI mode
  • /clear - Clear screen
  • /quit - Exit

Architecture

Standalone Mode

┌─────────────┐     JSON-RPC      ┌──────────────┐
│   zinit     │ ◄───────────────► │ zinit-server │
│  (client)   │   Unix Socket     │   (daemon)   │
└─────────────┘                   └──────────────┘
     │                                   │
     ├── CLI commands                    ├── Process management
     ├── REPL                            ├── Service lifecycle
     ├── TUI                             ├── Dependency ordering
     └── Rhai scripting                  └── Resource monitoring

Container Mode (PID 1)

┌────────────────────────────────────────────────────────────┐
│                     Container                              │
│  ┌──────────────┐                                          │
│  │zinit-updater │ ◄── PID 1 (reaps zombies, handles sigs) │
│  │  (init shim) │                                          │
│  └──────┬───────┘                                          │
│         │ spawns/monitors                                  │
│         ▼                                                  │
│  ┌──────────────┐     JSON-RPC      ┌─────────────┐       │
│  │ zinit-server │ ◄───────────────► │   zinit     │       │
│  │   (daemon)   │   Unix Socket     │  (client)   │       │
│  └──────┬───────┘                   └─────────────┘       │
│         │ manages                                          │
│         ▼                                                  │
│  ┌──────────────┐                                          │
│  │  services... │                                          │
│  └──────────────┘                                          │
└────────────────────────────────────────────────────────────┘

Testing

# Run all tests
cargo test --workspace

# Run init mode tests (requires release build)
cargo build --release
cargo test -p zinit-updater --test init_mode -- --test-threads=1

License

Apache-2.0