Skip to main content

rig

Zero-dependency, zero-daemon developer environment supervisor and process runner for multi-service repositories.

PyPI version Python Versions CI Status License: MIT Zero Dependencies

PyPI Package • Quick Start • Comparison • Socket Inheritance • Machine Supervision • AI Agent Protocol • CLI Reference


The Acute Friction

Local multi-service development across multiple git branches and checkouts is routinely broken by five recurring headaches:

Problem Traditional Workaround The rig Solution
Port Collisions lsof -i :3000 & kill -9 Dynamic & Sticky Port Leasing: Auto-assigns friendly ports (3000, 8000), increments on collision, and remembers ports across restarts.
Port Binding Race Conditions Probe port, close socket, hope child binds before another process grabs it Zero-Race Socket Inheritance (type: "fd"): Binds kernel port 0, holds the socket open, and passes descriptor directly to child processes (--fd {fd}).
Orphaned Zombie Processes killall node / pkill python Atomic File-Backed Registry: Tracks PID and PGID per instance in ~/.local/state/rig/. Cleans up orphaned services even if the directory was deleted (rm -rf).
Heavy Supervisor Daemons Docker Compose for everything, systemd, supervisord, Procfile wrappers Zero Runtime Dependencies & Zero Daemons: Standard library Python 3.10+ only. Starts instantaneously, uses kernel flock locks, and exits cleanly.
Invisible Machine State No single view of what processes or checkouts are running Machine-Wide Visibility: Run rig ps from anywhere to inspect all active checkouts, their allocated ports, health, and status across your entire machine.

Feature Comparison

How rig compares against standard development orchestrators:

Capability rig Docker Compose Foreman / Overmind systemd / supervisord
Zero Third-Party Dependencies ✅ (Standard Library) ❌ (Docker Engine) ❌ (Ruby/Go toolchains) ❌ (System-level packages)
Zero Background Daemons ✅ (File-mutex lock) ❌ (Requires dockerd) ❌ (Requires background tmux/daemon) ❌ (Requires system daemon)
Dynamic & Sticky Port Allocation ✅ Built-in ❌ Manual config ❌ Hardcoded ports ❌ Hardcoded ports
Zero-Race Socket Inheritance (type: "fd") ✅ Kernel socket pass ❌ Bridge network NAT ❌ No ⚠️ Systemd socket units only
Machine-Wide Multi-Project View ✅ rig ps ⚠️ Per-project compose ❌ Checkout-isolated only ⚠️ Global service list
Cross-Checkout Targeted Teardown ✅ rig down <slug> ❌ Must cd to folder ❌ Must cd to folder ⚠️ System unit names
Typed JSON Envelopes for AI Agents ✅ --json on every cmd ⚠️ Untyped CLI json ❌ Plain text output ❌ Plain text output
Deterministic Error Codes ✅ Typed error codes ❌ Generic 0 or 1 ❌ Generic 0 or 1 ❌ Generic exit status

The 3-Step Fast Track

1. Install Globally (User Space)

Install rig-cli from PyPI using standard, isolated tool runners:

# Recommended (pipx)
pipx install rig-cli

# Ultra-fast alternative (uv)
uv tool install rig-cli

2. Initialize Any Repository

Run rig init inside your project root. rig inspects your directory and generates a tailored rig.json. It detects Docker Compose postgres and redis services, a Django backend (manage.py), a generic Python ASGI backend such as FastAPI (pyproject.toml or requirements.txt), and a Node dev server (package.json, run with npm, pnpm, yarn, or bun as its lockfile indicates):

rig init

Preview detected services without writing to disk:

rig init --dry-run

3. Launch and Supervise

# Start all services in the active mode
rig up

# Check status of the local checkout
rig status

# Inspect service logs
rig logs backend -n 50

# Stop services in this checkout
rig down

Machine-Wide Supervision (rig ps)

rig maintains a machine-wide state registry under $XDG_STATE_HOME/rig/instances/ (~/.local/state/rig/instances/). Every project instance records its directory, PID, PGID, active mode, and allocated ports.

rig ps
PROJECT         ID        MODE     STATUS   SERVICES                          ROOT
pinpoint-leads  e9297fec  native   running  postgres:running, web:running     ~/code/pinpoint-leads
winnow-post     74376561  default  stopped  api:stopped                       ~/code/winnow-post
tertia-club     d7bfeefc  native   running  worker:running, ui:running        ~/code/tertia-club

Add -w, --wide to show the full instance identifier (<project>-<hash>) in an INSTANCE column.

Add --health to probe HTTP endpoints for real-time health checks:

rig ps --health

Targeted Teardown & Garbage Collection

Without a target, rig down stops the services of the current checkout. With a target, rig down stops a recorded instance from anywhere on your machine, even outside its checkout directory. The target must match either the project name or the full instance directory name (<project>-<hash>, as shown by rig ps --wide). Matching is case-insensitive. When a project name matches more than one instance, rig refuses with E_AMBIGUOUS and asks for the full instance identifier.

# Stop by project name
rig down pinpoint-leads

# Stop by full instance identifier
rig down pinpoint-leads-e9297fec

# Stop ALL running instances across the entire machine
rig down --all

# Clean up stale instance state files
rig prune

# Force-kill lingering orphaned processes and prune
rig prune --force

Zero-Race Socket Inheritance (type: "fd")

When a service specifies type: "fd", rig eliminates the classic time-of-check to time-of-use (TOCTOU) port race:

┌─────────┐      1. socket(AF_INET, SOCK_STREAM)
│   rig   │ ──── 2. bind("127.0.0.1", 0)  ───► OS Kernel assigns port
│         │ ──── 3. listen(128)
└────┬────┘
     │           4. subprocess.Popen(..., pass_fds=[fd])
     ▼
┌─────────┐
│ Uvicorn │ ──── 5. uvicorn.run(..., fd=sock.fileno())
└─────────┘      (Socket is NEVER closed between allocation and server startup)

Python / Uvicorn Example

import argparse
import socket
import uvicorn

parser = argparse.ArgumentParser()
parser.add_argument("--fd", type=int, default=None)
args, _ = parser.parse_known_args()

if args.fd is not None:
    sock = socket.fromfd(args.fd, socket.AF_INET, socket.SOCK_STREAM)
    uvicorn.run("main:app", fd=sock.fileno())
else:
    uvicorn.run("main:app", host="127.0.0.1", port=8000)

Multi-Stack Modes (native vs container)

Define base infrastructure and mode overlays in a single rig.json:

{
  "$schema": "https://raw.githubusercontent.com/evgesha9400/rig/main/rig.schema.json",
  "project": "my-app",
  "default_mode": "native",
  "services": {
    "db": {
      "type": "compose",
      "compose_file": "docker-compose.yml",
      "compose_service": "postgres",
      "compose_port": 5432
    }
  },
  "modes": {
    "native": {
      "services": {
        "backend": {
          "type": "fd",
          "cwd": "backend",
          "command": ".venv/bin/python -m app.main --fd {fd}",
          "healthcheck_path": "/healthz",
          "depends_on": ["db"]
        },
        "frontend": {
          "type": "port",
          "cwd": "frontend",
          "command": "npm run dev -- --port {port}",
          "healthcheck_path": "/",
          "depends_on": ["backend"],
          "env": {
            "VITE_API_PORT": "{backend_port}"
          }
        }
      }
    },
    "container": {
      "services": {
        "backend": {
          "type": "compose",
          "compose_file": "docker-compose.yml",
          "compose_service": "backend",
          "compose_port": 8000,
          "healthcheck_path": "/healthz",
          "depends_on": ["db"]
        }
      }
    }
  }
}

Collision-Safe Mode Switching

rig prevents conflicting processes from running simultaneously:

# Blocked with exit code 3 (E_MODE_CONFLICT) to prevent colliding ports/services:
rig up --mode container

# Cleanly tears down native services first and launches container mode:
rig up --mode container --switch

AI Agent & Automation Protocol

rig is built for autonomous execution by AI agents (Claude Code, AntiGravity, Codex, Cursor) and CI/CD pipelines:

Why AI Coding Assistants Use rig

  1. Deterministic Process Control: AI agents frequently lose control of background tasks or suffer port collisions when re-running test servers. rig manages the full process lifecycle with PID/PGID process groups.
  2. Predictable JSON Responses: Agents never have to scrape ANSI text or parse unpredictable terminal formatting.
  3. Actionable Resolution Hints: When an error occurs, rig returns an exact diagnostic hint for automated recovery.

Universal --json Output Envelope

Every CLI command supports --json and emits a typed, deterministic envelope:

{
  "schema": "rig.status/1",
  "ok": true,
  "data": {
    "project": "pinpoint-leads",
    "instance": "pinpoint-leads-e9297fec",
    "mode": "native",
    "generation": 1,
    "services": {
      "backend": {
        "running": true,
        "type": "fd",
        "port": 8003,
        "url": "http://127.0.0.1:8003",
        "pid": 59608
      }
    }
  }
}

Errors provide actionable resolution hints:

{
  "schema": "rig.error/1",
  "ok": false,
  "error": {
    "code": "E_MODE_CONFLICT",
    "message": "Instance is running in mode 'native'; cannot start mode 'container'",
    "hint": "Pass --switch to stop the active mode first, or run 'rig down' before starting a new mode."
  }
}

Deterministic Exit Codes

Code Constant Meaning
0 EXIT_OK Success.
1 EXIT_OP_FAILED Service failure, healthcheck timeout, or teardown error.
2 EXIT_USAGE Invalid CLI arguments or schema validation error.
3 EXIT_MUTEX_CONFLICT Instance lock busy (checkout.lock) or unswitched mode conflict.
4 EXIT_NOT_FOUND Target project, service, or instance not found.
5 EXIT_REFUSED Destructive action refused without confirmation.
6 EXIT_EXTERNAL_TOOL Missing system binary (docker, compose).
130 EXIT_INTERRUPTED Interrupted by signal (SIGINT, SIGTERM).

CLI Command Reference

Command Arguments Description
rig init [--dry-run] [--force] [--up] Scans repository and generates a validated rig.json.
rig up [--mode MODE] [--scope SCOPE] [--switch] Starts services in dependency order with healthchecks.
rig down [target] [--all] [--scope SCOPE] Gracefully stops services (SIGTERM ➜ SIGKILL).
rig status [--json] Shows tabular or JSON status of services in the current checkout.
rig ps [--health] [-w, --wide] [--json] Lists all active and stopped rig projects machine-wide.
rig logs [service] [-n TAIL] [--mode MODE] Tails service logs from .local-run/logs/.
rig check [--mode MODE] Validates manifests, working directories, and binary execution.
rig prune [--force] [--json] Reclaims stale or orphaned instance metadata across the machine.
rig schema [--json] Prints the formal JSON Schema for rig.json.
rig -v, --version Displays current installed version (rig 1.1.0).

Configuration Reference (rig.json)

To inspect or validate the JSON Schema directly:

rig schema
Property Type Description
project string Required. Slug identifier for instance isolation and Compose project naming.
default_mode string Mode to boot when --mode is omitted. When unset, rig uses the first key declared in modes.
services object Base services active across all modes.
modes object Named mode configurations (native, container, etc.).
type "fd" | "port" | "compose" Required. Port allocation and execution strategy.
command string | string[] Command line to execute. Supports {fd}, {port}, {<service>_port}.
cwd string Working directory relative to repository root (defaults to .).
healthcheck_path string Root-relative HTTP path to poll for readiness (e.g. /healthz). health is an accepted alias.
healthcheck_timeout number Seconds to wait for the healthcheck to pass (defaults to 45).
preferred_port integer Preferred local port (1 to 65535). rig tries this port first.
depends_on string[] Upstream services that must pass healthchecks before boot.
compose_file string Relative path to Docker Compose file (for type: "compose").
compose_service string Service name within the Docker Compose file.
compose_port integer Container port whose published host port rig records (for type: "compose").

Zero Runtime Dependencies

rig is committed to zero third-party runtime dependencies. It relies exclusively on the Python standard library (socket, subprocess, os, signal, json, fcntl, shlex, dataclasses, pathlib).

  • No background daemons (systemd, dockerd, supervisord) required to orchestrate native processes.
  • No Node.js or Ruby runtimes required.
  • Instantaneous startup with file-backed atomic state.

Symmetrical Makefile Integration

Include examples/rig.mk in your root Makefile (include rig.mk), or inline its targets:

# Standard local dev targets for rig.
# Include this file in your project's Makefile: `include rig.mk` (or inline it).

.PHONY: up down local-up local-down backend-up backend-down ui-up ui-down status logs

RIG ?= rig
# Service for `make logs`, e.g. `make logs SERVICE=backend`.
SERVICE ?=

up:
	@$(RIG) up --scope full

down:
	@$(RIG) down --scope full

local-up:
	@$(RIG) up --scope local

local-down:
	@$(RIG) down --scope local

backend-up:
	@$(RIG) up --scope backend

backend-down:
	@$(RIG) down --scope backend

ui-up:
	@$(RIG) up --scope ui

ui-down:
	@$(RIG) down --scope ui

status:
	@$(RIG) status

logs:
	@$(RIG) logs $(SERVICE)


License

MIT © 2026 Evgeny Aleshin

Release files for rig-cli 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rig-cli 1.1.0
File Size Uploaded
rig_cli-1.1.0.tar.gz 157.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rig-cli 1.1.0
File Interpreter ABI Platform
rig_cli-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 237.5 kB

Release files / rig_cli-1.1.0.tar.gz

Download URL rig_cli-1.1.0.tar.gz
Size 157.3 kB
Tags Source
SHA-256 checksum
How to use checksums
85caf267656658626d067019763f0439083cd6fc9406d983eabd93d530622645
BLAKE2b-256 checksum
How to use checksums
3b5909d9b433df3a4c24f3f039e5562ce809ab7881e0f16c5cb0a87afa92dc73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / rig_cli-1.1.0-py3-none-any.whl

Download URL rig_cli-1.1.0-py3-none-any.whl
Size 80.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0a2e471275b9b466218ea3415be04528357891be8c29c649bb2b0edd473a7ee6
BLAKE2b-256 checksum
How to use checksums
df63a485a0afe86da25a7f4ef567f23b4fe8373adfa200b200a1f48d972ad0c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page