# Zinit RPC API Schema
# Process supervisor and service manager with xinet socket activation proxy support
# Version: 0.3.0
# ─────────────────────────────────────────────────────────────────────────────────
# ENUMS
# ─────────────────────────────────────────────────────────────────────────────────
# Service state machine states
State = "inactive" | "blocked" | "starting" | "running" | "stopping" | "exited" | "failed"
# Restart policy
RestartPolicy = "always" | "on-failure" | "never"
# Desired service status
Status = "start" | "stop" | "ignore"
# Service class (protection level)
ServiceClass = "user" | "system"
# Dependency type
DepType = "after" | "requires" | "wants" | "conflicts"
# Log stream type
LogStream = "stdout" | "stderr" | "syslog"
# Health check type
HealthType = "tcp" | "http" | "exec"
# Failure reason type
FailureType = "exit_code" | "signal" | "start_timeout" | "stop_timeout" | "health_check_failed" | "dependency_failed" | "spawn_error" | "missing_dependency"
# ─────────────────────────────────────────────────────────────────────────────────
# SERVICE CONFIGURATION (matches TOML format)
# ─────────────────────────────────────────────────────────────────────────────────
ServiceDef = {
service: ServiceSection
dependencies?: DependencySection
lifecycle?: LifecycleSection
health?: HealthSection
logging?: LoggingSection
}
ServiceSection = {
name: str # unique identifier
exec: str # command to execute
dir?: str # working directory
oneshot?: bool # run once and exit (default: false)
env?: {str: str} # environment variables
status?: Status # desired status (default: start)
class?: ServiceClass # protection level (default: user)
critical?: bool # failure triggers emergency in PID1 mode (default: false)
}
DependencySection = {
after?: [str] # start after these services (ordering only)
requires?: [str] # requires these services running
wants?: [str] # soft dependency
conflicts?: [str] # cannot run with these
}
LifecycleSection = {
restart?: RestartPolicy # restart policy (default: on-failure)
restart_delay_ms?: u64 # initial restart delay, doubles each time (default: 1000)
restart_delay_max_ms?: u64 # max restart delay cap (default: 300000)
max_restarts?: u32 # max restart attempts, 0=unlimited (default: 10)
stability_period_ms?: u64 # time before backoff resets (default: 30000)
start_timeout_ms?: u64 # startup timeout (default: 30000)
stop_timeout_ms?: u64 # graceful stop timeout before SIGKILL (default: 10000)
stop_signal?: str # signal for graceful stop (default: SIGTERM)
}
HealthSection = {
type: HealthType # health check type
target: str # host:port (tcp), URL (http), or command (exec)
expect_status?: u16 # expected HTTP status, http only (default: 200)
interval_ms?: u64 # check interval (default: 10000)
timeout_ms?: u64 # check timeout (default: 5000)
retries?: u32 # retries before unhealthy (default: 3)
start_period_ms?: u64 # grace period before checks start (default: 0)
}
LoggingSection = {
buffer_lines?: u32 # lines to buffer in memory (default: 1000)
file?: str # log file path
forward?: str # syslog forward address
}
# ─────────────────────────────────────────────────────────────────────────────────
# SERVICE STATUS TYPES
# ─────────────────────────────────────────────────────────────────────────────────
# Simplified status (service.status)
ServiceStatus = {
name: str
state: State # state enum value
pid?: u32 # process ID (0 if not running)
exit_code?: i32 # last exit code
error?: str # error message if failed/blocked
}
# Full status with dependencies (service.status_full)
ServiceStatusFull = {
name: str
state: ServiceState # structured state
is_target: bool # true if virtual service (target)
dependencies: [DependencyInfo]
uptime_secs?: u64 # uptime in seconds if running
}
# Structured service state
ServiceState = {
state: State
pid?: u32 # for starting/running/stopping
waiting_on?: [str] # for blocked
exit_code?: i32 # for exited
reason?: FailureReason # for failed
}
# Failure reason details
FailureReason = {
type: FailureType
code?: i32 # for exit_code
signal?: i32 # for signal
attempts?: u32 # for health_check_failed
service?: str # for dependency_failed
message?: str # for spawn_error
dependency?: str # for missing_dependency
}
# Dependency information
DependencyInfo = {
name: str
dep_type: DepType
state: ServiceState
satisfied: bool
}
# Basic service info for list operations
ServiceInfo = {
name: str
state: ServiceState
is_target: bool
}
# Resource usage statistics
ServiceStats = {
pid: u32
memory_bytes: u64
cpu_percent: f32
}
# Why blocked explanation
WhyBlocked = {
name: str
blocked: bool
waiting_on: [str] # services being waited on
conflicts_with: [str] # running conflicting services
ascii: str # ASCII visualization
}
# Dependency tree
TreeResponse = {
ascii: str # ASCII tree visualization
}
# ─────────────────────────────────────────────────────────────────────────────────
# LOG TYPES
# ─────────────────────────────────────────────────────────────────────────────────
LogLine = {
timestamp_ms: u64 # Unix timestamp in milliseconds
service: str
stream: LogStream
content: str
}
# ─────────────────────────────────────────────────────────────────────────────────
# XINET SOCKET ACTIVATION PROXY
# ─────────────────────────────────────────────────────────────────────────────────
XinetDef = {
name: str # proxy name (unique identifier)
listen: str | [str] # listen address(es): host:port or unix:/path
backend: str # backend address
service: str # zinit service to start on connection
connect_timeout?: u64 # backend connect timeout in seconds (default: 30)
idle_timeout?: u64 # stop service after idle seconds, 0=never (default: 0)
single_connection?: bool # allow only one connection at a time (default: false)
}
# Simplified proxy status
XinetStatus = {
name: str
running: bool
connections: u32 # active connections
}
# Full proxy status with traffic stats
XinetStatusFull = {
name: str
listen: str # listen address(es)
backend: str
service: str
running: bool
active_connections: u32
total_connections: u64
bytes_to_backend: u64
bytes_from_backend: u64
}
# ─────────────────────────────────────────────────────────────────────────────────
# RESPONSE TYPES
# ─────────────────────────────────────────────────────────────────────────────────
PingResponse = {
version: str
}
OkResponse = {
ok: bool
}
CreateResult = {
name: str
}
AddServiceResult = {
name: str
path?: str # config file path if persisted
warnings?: [str]
}
PrepareRestartResult = {
state_path: str # path where state was saved
ready: bool
}
ReloadResult = {
added: [str]
removed: [str]
changed: [str]
config_errors?: [(str, str)] # (filename, error) pairs
}
BulkStartResult = {
started: [str]
count: u32
}
BulkStopResult = {
stopped: [str]
count: u32
}
BulkDeleteResult = {
deleted: [str]
count: u32
}
# ─────────────────────────────────────────────────────────────────────────────────
# ERROR TYPES
# ─────────────────────────────────────────────────────────────────────────────────
RpcError = {
code: i32
message: str
data?: any
}
# Standard JSON-RPC errors:
# -32700 ParseError Invalid JSON
# -32600 InvalidRequest Not a valid Request object
# -32601 MethodNotFound Method does not exist
# -32602 InvalidParams Invalid method parameters
# -32603 InternalError Internal JSON-RPC error
#
# Zinit-specific errors:
# -32000 ServiceNotFound Service does not exist
# -32001 ServiceExists Service already exists
# -32002 ServiceRunning Operation not allowed while running
# -32003 ServiceNotRunning Operation requires running service
# ─────────────────────────────────────────────────────────────────────────────────
# SERVICE: ZinitService
# ─────────────────────────────────────────────────────────────────────────────────
service ZinitService {
version: "0.3.0"
description: "Process supervisor and service manager"
# ─────────────────────────────────────────────────────────────────────────────
# DISCOVERY
# ─────────────────────────────────────────────────────────────────────────────
# Returns the OpenRPC specification
rpc.discover() -> any
# ─────────────────────────────────────────────────────────────────────────────
# SYSTEM METHODS
# ─────────────────────────────────────────────────────────────────────────────
# Check if server is alive
system.ping() -> PingResponse
# Shutdown the server and all services
system.shutdown() -> OkResponse
# Reboot the system (Linux only, requires PID 1)
system.reboot() -> OkResponse
error: RpcError
# Prepare for hot restart by saving state to disk
system.prepare_restart() -> PrepareRestartResult
# ─────────────────────────────────────────────────────────────────────────────
# SERVICE CRUD
# ─────────────────────────────────────────────────────────────────────────────
# Create a new service
service.create(config: ServiceDef, persist?: bool) -> AddServiceResult
error: RpcError
# Get service configuration
service.get(name: str) -> ServiceDef
error: RpcError
# Update service configuration
service.update(config: ServiceDef) -> CreateResult
error: RpcError
# Delete a service (stop and remove)
service.delete(name: str) -> OkResponse
error: RpcError
# List all service names
service.list() -> [str]
# List all services with state information
service.list_full() -> [ServiceInfo]
# ─────────────────────────────────────────────────────────────────────────────
# SERVICE ACTIONS
# ─────────────────────────────────────────────────────────────────────────────
# Start a service
service.start(name: str) -> OkResponse
error: RpcError
# Stop a service
service.stop(name: str) -> OkResponse
error: RpcError
# Restart a service
service.restart(name: str) -> OkResponse
error: RpcError
# Send a signal to a service
service.kill(name: str, signal?: str) -> OkResponse
error: RpcError
# ─────────────────────────────────────────────────────────────────────────────
# SERVICE STATUS
# ─────────────────────────────────────────────────────────────────────────────
# Get simplified service status
service.status(name: str) -> ServiceStatus
error: RpcError
# Get detailed service status with dependencies
service.status_full(name: str) -> ServiceStatusFull
error: RpcError
# Get service resource usage
service.stats(name: str) -> ServiceStats
error: RpcError
# Check if a service is currently running
service.is_running(name: str) -> bool
error: RpcError
# Explain why a service is blocked
service.why(name: str) -> WhyBlocked
error: RpcError
# Get ASCII dependency tree visualization
service.tree() -> TreeResponse
# ─────────────────────────────────────────────────────────────────────────────
# SERVICE ALIASES (backward compatibility)
# ─────────────────────────────────────────────────────────────────────────────
# Add a new service (alias for service.create)
service.add(config: ServiceDef, persist?: bool) -> AddServiceResult
error: RpcError
# Add an ephemeral service (not persisted)
service.monitor(config: ServiceDef) -> AddServiceResult
error: RpcError
# Add a persistent service (saved to disk)
service.register(config: ServiceDef) -> AddServiceResult
error: RpcError
# Remove a service (alias for service.delete)
service.remove(name: str) -> OkResponse
error: RpcError
# ─────────────────────────────────────────────────────────────────────────────
# CONFIG RELOAD
# ─────────────────────────────────────────────────────────────────────────────
# Reload service configurations from disk
# If name provided, reloads only that service
service.reload(name?: str) -> ReloadResult
error: RpcError
# ─────────────────────────────────────────────────────────────────────────────
# BULK OPERATIONS (user-class services only)
# ─────────────────────────────────────────────────────────────────────────────
# Start all user-class services
service.start_all() -> BulkStartResult
# Stop all user-class services
service.stop_all() -> BulkStopResult
# Delete all user-class services
service.delete_all() -> BulkDeleteResult
# ─────────────────────────────────────────────────────────────────────────────
# LOGGING
# ─────────────────────────────────────────────────────────────────────────────
# Get logs as strings (simplified)
logs.get(name?: str, lines?: u32) -> [str]
# Get structured log entries
logs.tail(name?: str, lines?: u32) -> [LogLine]
# Get filtered log entries
logs.filter(name?: str, stream?: LogStream, since?: u64, lines?: u32) -> [LogLine]
# ─────────────────────────────────────────────────────────────────────────────
# DEBUG
# ─────────────────────────────────────────────────────────────────────────────
# Get full supervisor state for debugging
debug.state() -> { output: str }
# Get process tree for a service
debug.process_tree(name: str) -> { output: str }
error: RpcError
# ─────────────────────────────────────────────────────────────────────────────
# XINET SOCKET ACTIVATION PROXY
# ─────────────────────────────────────────────────────────────────────────────
# Create an xinet proxy
xinet.create(config: XinetDef) -> OkResponse
error: RpcError
# Delete an xinet proxy
xinet.delete(name: str) -> OkResponse
error: RpcError
# List all xinet proxy names
xinet.list() -> [str]
# Get simplified proxy status
xinet.status(name: str) -> XinetStatus
error: RpcError
# Register an xinet proxy (alias for xinet.create)
xinet.register(config: XinetDef) -> OkResponse
error: RpcError
# Unregister an xinet proxy (alias for xinet.delete)
xinet.unregister(name: str) -> OkResponse
error: RpcError
# Get status of all xinet proxies
xinet.status_all() -> [XinetStatusFull]
}