Architecture Overview
Runix is a single-binary process manager built in Go. It uses a supervisor pattern to manage long-running processes, communicating through a lightweight daemon layer when running in background mode.
System Architecture
Dual-Mode Execution
Every CLI command follows the same dual-mode pattern:
- Daemon mode — When the daemon is running, CLI commands send HTTP requests to
~/.runix/runix.sock. The daemon holds the singleSupervisorinstance and all process state. - Direct mode — When the daemon is not running, commands create a temporary
Supervisorinstance and execute locally. State is loaded from disk.
Key Design Principles
| Principle | Implementation |
|---|---|
| Single binary | No CGO, fully static compilation. CGO_ENABLED=0 everywhere. |
| Lock-free state | Process state uses atomic.Value + CompareAndSwap — no mutexes on the hot path. |
| Process group isolation | Setpgid: true + Kill(-pid, signal) ensures clean termination of entire process groups. |
| Dependency root | pkg/types is the shared kernel — imported by everything, imports nothing from internal/. |
| Explicit initialization | No init() functions. All setup via New*() constructors. |
| Structured logging | zerolog throughout. Errors are returned, never logged-and-forgotten. |
Module Map
| Module | Location | Responsibility |
|---|---|---|
| CLI | cmd/runix/ | 28+ cobra commands, thin wrappers around internal packages |
| Supervisor | internal/supervisor/ | Core engine — process lifecycle, state machine, restart/backoff |
| Daemon | internal/daemon/ | HTTP-over-Unix-socket IPC, fork/exec lifecycle, PID management |
| Runtime | internal/runtime/ | Adapters for Go, Python, Node.js, Bun, Deno, Ruby, PHP |
| Config | internal/config/ | Config loading via viper, defaults, extends, hot-reload diffing |
| Hooks | internal/hooks/ | Lifecycle hook execution (sh -c), hook chains |
| Metrics | internal/metrics/ | /proc-based metrics collector, Prometheus exposition format |
| Scheduler | internal/scheduler/ | Cron job scheduling via robfig/cron with seconds support |
| Watcher | internal/watcher/ | fsnotify file watching with debounce and ignore patterns |
| Healthcheck | internal/healthcheck/ | HTTP, TCP, and command health checks with retry logic |
| Events | internal/events/ | Pub/sub event bus with persistent JSON log store |
| Secrets | internal/secrets/ | Secret resolution from env vars, files, and (planned) Vault |
| Auth | internal/auth/ | Authentication — basic, token, and local-only modes |
| cgroups | internal/cgroups/ | Linux cgroups v2 resource limits (CPU quota, memory cap) |
| Log Rotation | internal/logrot/ | Size-based log rotation with count and age pruning |
| Updater | internal/updater/ | Self-update via GitHub Releases API with SHA256 verification |
| MCP | internal/mcp/ | Model Context Protocol server (stdio + HTTP transports) |
| Web UI | internal/web/ | Chi HTTP server + WebSocket for live dashboard |
| TUI | internal/tui/ | BubbleTea terminal UI with process table, log view, help |
| SDK | sdk/ | Embeddable Go SDK for in-process process management |
| Version | internal/version/ | Version and BuildTime injected via ldflags |
| Types | pkg/types/ | Shared types — ProcessConfig, State, hooks, health checks, etc. |
Data Flow
What's Next
- Supervisor — How the core engine manages process lifecycle
- Process State Machine — The 7-state machine with atomic transitions
- Daemon — IPC layer and dual-mode execution details
- Runtime Adapters — How runtimes are detected and used
- Dependency Graph — Module import relationships