invincible

module
v1.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT

README

Invincible

Proof of concept. Invincible is an experiment, not a finished product. APIs, config format, and behaviour WILL change without notice.

A process manager for local development, written in Go.

Keep services alive, restart them on crash, assign free ports automatically, wire up port dependencies, and optionally watch files for auto-rebuild — no Docker required. Comes with a terminal UI for humans and an HTTP API for agents.

Dashboard

Logs

Invincible is Not

  • A replacement for make
  • A replacement for a full process manager like systemd or supervisor
  • Intended for production use

Installation

Requires Go 1.26 or later.

# Install to $GOPATH/bin
go install github.com/saintedlama/invincible/cmd/invincible@latest

Building from source

# Or build from source
git clone https://github.com/saintedlama/invincible
cd invincible
go build -o invincible ./cmd/invincible

Quick start

# Create a starter config in the current directory
invincible init

# Edit invincible.toml, then run
invincible

Configuration

Invincible looks for invincible.toml in the current directory by default.

[project]
name    = "myapp"
# api_addr = ":7777"   # override the HTTP API port (default: path-derived offset from 7777)

[[process]]
name          = "api"
cmd           = "go run ./cmd/api"
cwd           = "./backend"   # working directory for this process
port          = 8080          # hint; Invincible finds the next free port if taken
# port_env    = "PORT"        # env var injected with this process's port (default: PORT)
# no_port     = true          # disable port assignment for this process
# depends_on  = ["worker"]    # restart this process if a dependency changes port
# restart_delay = "500ms"     # wait before restarting after a crash (default: 500ms)
# shutdown_timeout = "500ms"  # SIGTERM grace period before SIGKILL
# env         = { QUEUE = "default" }  # extra static env vars

[[process]]
name = "worker"
cmd  = "go run ./cmd/worker"
# port omitted → Invincible assigns an arbitrary free port
Port assignment

Every process that needs a port gets one automatically:

  • If port is set, Invincible searches upward from that hint until it finds a free port.
  • If port is omitted, the OS assigns an arbitrary free port.
  • Set no_port = true to opt out entirely.
Port environment variables

Before starting, Invincible injects into every process:

Variable Value
PORT (or port_env) This process's assigned port
<PEER_NAME>_PORT One variable per sibling that has a port, e.g. API_PORT=8080
Dependencies
[[process]]
name       = "frontend"
cmd        = "..."
depends_on = ["api"]

If api crashes and gets a new port, frontend is automatically restarted with the updated API_PORT. If the port doesn't change, frontend is left alone.

Cycles are detected at startup — Invincible exits with an error if a cycle exists.

Working directory

Set cwd to run a command from a specific directory:

[[process]]
name = "frontend"
cmd  = "npm run dev"
cwd  = "./frontend"
port = 5173
File watching + auto-rebuild (opt-in)

When watch and build are both configured for a process, Invincible watches the specified directories for file changes, runs the build command, and restarts the process on success. If the build fails, the old binary keeps running.

Watch directories are relative to cwd when set, otherwise relative to the project root.

[[process]]
name  = "api"
cmd   = "./bin/api"
cwd   = "./backend"

# Rebuild and restart on file changes
build           = "go build -o ./bin/api ./cmd/api"
watch           = ["."]                      # directories to watch
watch_include   = ["*.go", "*.templ"]       # file globs that trigger rebuild (default: all)
watch_exclude   = ["vendor", ".git", "tmp"] # subdirectories to skip
watch_debounce  = "500ms"                   # quiet period before triggering (default: 500ms)

The TUI detail panel shows watch on for processes with active file watching. Build output and watch events appear in the process logs with the invincible source.

Running

invincible [flags]

Flags:
  --config    path to config file           (default: invincible.toml)
  --api-addr  preferred HTTP API address    (default: path-derived offset from :7777; falls back to config api_addr)
  --no-tui    run headless, print API URL to stdout
TUI key bindings

Invincible has two screens — Dashboard (process list + details) and Logs (full-width). Press Tab to switch between them.

Key Action
Tab Toggle between Dashboard and Logs screens
/ k Select previous process
/ j Select next process
s Start selected process
x Stop selected process
r Restart selected process
S (Shift+s) Start all processes
X (Shift+x) Stop all processes
R (Shift+r) Restart all processes
f Cycle log filter (ALL → STDERR → STDOUT → INVINCIBLE)
Shift+↑ / PgUp Scroll logs up
Shift+↓ / PgDn Scroll logs down
q / Ctrl+C Quit
Mouse support
Action Area
Scroll wheel Dashboard: over process list → select next/previous process
Scroll wheel Logs screen: scroll logs

CLI commands

invincible init

Create a starter invincible.toml in the current directory. Exits with an error if one already exists.

invincible skill

Print a quick guide telling you what to paste into an AI agent session to install the Invincible skill.

invincible skill-spec

Print the full skill description (API reference, process schema, workflows) for an agent to consume. Invoke this after the agent has been told to install the skill via invincible skill.

Working with agents across worktrees

When you run Invincible in multiple git worktrees simultaneously, each instance needs a way to find the right API port. Invincible handles this in two ways:

.invincible.port file

On startup, Invincible writes the bound API address (e.g. 127.0.0.1:12583) to .invincible.port in the project root. The file is removed on clean shutdown. Agents can discover the correct instance by reading this file from the worktree they are operating in:

cat .invincible.port
# → 127.0.0.1:12583
Path-derived port offset

When no explicit api_addr is configured (flag or config file), Invincible derives a deterministic port from the project directory path by hashing the absolute path and adding the result as an offset to the base port 7777. Each worktree gets a different preferred port, so instances rarely collide:

/projects/app/main      → offset 1234 → tries :9011
/projects/app/feature-x → offset 7891 → tries :15668

If the preferred port happens to be taken, Invincible falls back to an OS-assigned ephemeral port — and still records the actual address in .invincible.port.

Run invincible skill to see how to install the Invincible agent skill, then invincible skill-spec for the full API reference.

HTTP API

The API binds to 127.0.0.1 and is only accessible locally.

Method Path Description
GET /processes List all processes
GET /processes/{name} Get one process
GET /processes/{name}/logs Get recent logs (?n=100&format=text)
POST /processes/{name}/start Start a process
POST /processes/{name}/stop Stop a process
POST /processes/{name}/restart Restart a process
GET /openapi.json OpenAPI 3.0 spec
Process object
{
  "Name":       "api",
  "State":      "running",
  "PID":        1234,
  "Cmd":        "go run ./cmd/api",
  "Cwd":        "./backend",
  "Port":       8080,
  "PortEnv":    "PORT",
  "DependsOn":  ["worker"],
  "Restarts":   0,
  "StartedAt":  "2026-06-04T08:00:00Z",
  "Watching":   true,
  "Env":        { "QUEUE": "default" }
}

States: stopped · starting · probing · running · building · crashed

probing means the process has started but its port has not yet accepted a connection. Once the port is reachable the state transitions to running.

Log entry object
{ "time": "2026-06-04T08:22:01Z", "line": "server started on :8080", "source": "stdout" }

Source values: stdout, stderr, or invincible (system events / build output).

Use ?format=text for plain newline-separated output (no metadata).

Directories

Path Synopsis
cmd
invincible command
internal
api
tui

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL