minicrew runs on Mac Minis and Linux Mint XFCE boxes. A fleet of visible, unattended Claude Code sessions.
Queue-driven. Self-healing. Zero-terminal setup.
Clone the repo, open Claude Code in it, and say:
"read SETUP.md and set me up."
That's it. Claude handles the Supabase credentials prompt, venv, launchd services, and skill installation. No shell commands to memorize, ever.
Most worker-queue patterns hide the work inside a headless subprocess. minicrew does the opposite: every job opens a real, visible Terminal.app window running a full claude session — Read, Write, Bash, WebSearch, every tool Claude Code supports. You can watch work happen in real time, tail logs by eye, or intervene when debugging. When the job finishes, the window closes itself.
No human has to click anything. Between jobs, the worker polls a Supabase table, claims the next row atomically, launches the next session. When a session finishes — or gets stuck and the watchdog kills it — the worker writes the result back and loops.
┌────────────────┐ ┌─────────────────────────────────┐
│ Your project │──────▶│ Supabase │
│ (enqueues jobs)│ │ jobs table (the queue) │
└────────────────┘ └────────────┬────────────────────┘
│ atomic claim
┌────────────────────────────┼────────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Mac Mini 1 │ │ Mac Mini 2 │ │ Mac Mini N │
│ role: primary │ │ role: secondary │ │ role: secondary │
│ │ │ │ │ │
│ [term] [term] │ │ [term] [term] │ │ [term] [term] │
│ [term] [term] │ │ [term] │ │ [term] [term] │
│ │ │ │ │ │
│ up to 5 parallel│ │ up to 5 parallel│ │ up to 5 parallel│
└──────────────────┘ └──────────────────┘ └──────────────────┘
\ | /
\___________________________|__________________________/
│
Fleet-wide coordination:
atomic claim, heartbeats,
opportunistic reaper
Every job = one real Terminal.app window = one full Claude Code session with unrestricted tool access. Not a subprocess. Not a headless API call. The window is deliberately visible so engineers can audit work during dev and tail it with their eyes. It disappears when the job completes. --dangerously-skip-permissions is the intentional tradeoff — SECURITY.md covers the posture and a planned hardened-mode alternative.
Claude Code's built-in Agent tool can't use WebSearch, Bash, or any network tool from sub-agents. minicrew's fan_out mode fixes that: declare mode: fan_out with N groups in your config.yaml, and the engine launches N parallel Terminal.app windows, each a completely independent Claude Code session, then runs a final merge session that consolidates the group outputs.
Ideal for:
- Per-document analysis across a corpus (each group = subset of documents)
- Multi-region pricing sweeps (each group = a region, all with WebSearch)
- Independent classification of 30 images (split into 3 groups of 10, merge results)
- Any "N independent pieces + 1 combine" workflow
Per-group watchdogs run in parallel threads. A group that hangs is killed independently; its name is threaded into missing_groups so the merge template handles partial results.
Every job type in config.yaml can invoke any globally-installed Claude Code skill:
job_types:
analyze_contract:
skill: my-legal-plugin:analyze
model: claude-opus-4-7
thinking_budget: highThe engine prefixes the rendered prompt with /<skill> so Claude runs your skill end-to-end on the job payload. Pair with custom Anthropic-side plugins (local or published) for domain-specific tool access — your skills inherit the full Claude Code environment.
Config maps directly to Claude Code CLI flags:
| Config field | CLI flag | Values |
|---|---|---|
model |
--model |
claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5 |
thinking_budget |
--effort |
none → low, medium → medium, high → high |
Change it with a conversation: "/minicrew:tune analyze_contract, switch to sonnet and medium". Workers pick it up on their next restart.
No inter-worker messaging. No Redis. No ZooKeeper. The database is the sole coordination layer.
| Machines | Instances each | Concurrent sessions | Typical use |
|---|---|---|---|
| 1 | 1 | 1 | Dev / trial |
| 1 | 3 | 3 | Single-box production |
| 3 | 3 | 9 | Small fleet |
| 5 | 5 | 25 | Full-capacity homelab |
| 10 | 5 | 50 | At this scale consider v2 routing |
Add a new machine with /minicrew:add-machine — the skill prints the exact one-sentence bootstrap to paste on the new Mac Mini. First machine defaults to primary (polls every 5s), additional machines default to secondary (polls every 15s) so the primary wins the race on fresh jobs.
Every worker runs its own reaper thread — but a transaction-level Postgres advisory lock (pg_try_advisory_xact_lock) guarantees exactly one reaper runs per cycle across the entire fleet. No leader election, no coordination service, no single point of failure.
When a worker dies mid-job, another worker's reaper detects the stale heartbeat (default: 120 seconds), requeues the orphaned job, and increments attempt_count. Poison-pill protection: after max_attempts total runs (default 3), the job becomes failed_permanent for human review instead of cycling forever.
The claim is a single PostgREST PATCH with a status='pending' filter. Two workers racing for the same row — one wins by row count, the other gets nothing and moves on. Simple, proven, impossible to double-claim.
Recursive file-activity walk of each session's working directory. Claude writing files = session alive. No writes for 25 minutes, and no result file? The watchdog terminates the Terminal window and marks the job error. Result file exists but hasn't been touched in 15 minutes (hung post-processing)? Same. Recovery time on a stuck job drops from "2-hour job timeout" to "≤30 min."
The config contract is intentionally boring so an AI agent can edit it reliably:
- YAML + Jinja for job type declarations and prompt templates
- JSON Schema (strict,
additionalProperties: false) validates at load StrictUndefined+finalize=json.dumpsso template typos fail loudly and payload values auto-JSON-encodepayload.schema.jsonper consumer validates job payloads before launching Claude
Any agent handed your repo URL can read INTEGRATE.md in one pass and wire up a consumer project end-to-end.
With a worker running, insert a row via curl:
curl -X POST "$SUPABASE_URL/rest/v1/jobs" \
-H "apikey: $SUPABASE_SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"job_type":"summarize","payload":{"text":"Lorem ipsum..."}}'Within one poll interval a Terminal window opens on a Mac Mini, Claude summarizes the text, writes result.json, the worker PATCHes the row to status='completed', and the window closes. Full patterns for FastAPI, Next.js server actions, and Supabase Edge Functions: INTEGRATE.md.
| Layer | Defense |
|---|---|
| Secrets | .env only (chmod 0600), never in launchd plists. Worker loads .env at startup. |
| Redaction | Structured-logging filter masks any value whose source env var name is in logging.redact_env. |
| Prompt injection | Jinja finalize= callback auto-JSON-encodes non-strings. Untrusted text must pipe through ` |
| Path traversal | Config loader and result_io enforce realpath containment; JSON Schema pattern constraints on filenames. |
| Symlink attacks | read_result_safe uses O_NOFOLLOW plus fd-based containment check. |
| Stale-worker overwrites | Every job mutation filters by (id, worker_id) — a stale worker cannot clobber a reclaimed row. |
| Audit trail | Immutable claimed_at, started_at, completed_at, worker_id, worker_version on every row. |
Full threat model, key-rotation walkthrough, and RLS guidance in SECURITY.md.
minicrew/
├── worker/ Python engine (atomic claim, reaper, watchdog, launcher)
├── schema/
│ ├── template.sql Supabase DDL: jobs, workers, worker_events, RPC, view
│ └── config.schema.json Strict JSON Schema for worker-config validation
├── skills/ Eight conversational Claude Code skills
│ ├── setup.md /minicrew:setup — re-setup / reconfigure
│ ├── add-worker.md /minicrew:add-worker — add another instance
│ ├── add-machine.md /minicrew:add-machine — bootstrap another Mac Mini
│ ├── scaffold-project.md /minicrew:scaffold-project — wire a consumer repo
│ ├── add-job-type.md /minicrew:add-job-type — append a job type
│ ├── tune.md /minicrew:tune — change model / effort / timeout
│ ├── status.md /minicrew:status — fleet-wide health
│ └── teardown.md /minicrew:teardown — remove all workers
├── examples/
│ ├── minimal/ Single-session text summarization
│ └── fan-out/ Three parallel groups + merge document analysis
├── docs/ Deep-dive documentation
└── ci/ Domain-scrub patterns (CI enforces generic-template invariant)
| Audience | Start here |
|---|---|
| Setting up a Mac Mini | SETUP.md (Claude-executable) |
| Wiring a consumer project | INTEGRATE.md (agent-consumable) |
| How the engine works | docs/ARCHITECTURE.md |
| Deploying on Linux Mint XFCE | docs/LINUX.md |
| Queue priority, multi-machine, reaper | docs/QUEUEING.md |
| Single vs fan-out mode | docs/ORCHESTRATION.md |
| Tuning model + thinking budget | docs/MODEL-TUNING.md |
| Supabase schema + RLS guidance | docs/SUPABASE-SCHEMA.md |
| Config reference | docs/CONFIG-REFERENCE.md |
| Threat model | SECURITY.md |
| AI agents integrating from outside | llms.txt and INTEGRATE.md |
| OS | Required binaries | Required services |
|---|---|---|
| macOS | claude, osascript (built-in) |
launchd (built-in) |
| Linux Mint XFCE | claude, wmctrl, xdotool, xfce4-terminal, tmux |
systemd (user bus) |
- Python 3.11+ on either OS.
- Claude Code (
npm install -g @anthropic-ai/claude-code) authenticated on the machine. - A Supabase project (free tier is fine for development).
minicrew on Linux mirrors the Mac Mini pattern — same Python engine, same Supabase schema,
same atomic-claim + reaper + watchdog behavior. What changes is the platform-abstracted
backend: instead of osascript + launchd + Terminal.app, the Linux build uses xfce4-terminal
(or tmux headlessly) + wmctrl/xdotool + systemd user units. Everything OS-specific
lives behind the Platform protocol in worker/platform/; mixed-OS fleets coordinate
through the same database with no special configuration. Mint-specific setup (LightDM
auto-login, X11 session selection, systemd unit environment, logrotate with copytruncate,
MINICREW_TMPDIR for tmpfs pressure, the X11 threat model and dedicated-user
recommendation) is covered in docs/LINUX.md.
- v0.2 — Linux support (systemd, GNOME Terminal / xterm equivalent of Terminal.app)
- v0.2 — Postgres and HTTP logging sinks (schema already reserves
worker_events) - v0.3 — Tag-based routing (
requires: [heavy, vision]on jobs,capabilities: [...]on workers) - v0.3 — Config hot-reload via SIGHUP
- Later — Pluggable queue backend (Redis Streams, SQS, Cloudflare Queues)
MIT. See LICENSE.
Built for and named after the first deployment environment — a homelab of Mac Minis doing real work, headless-server style but with faces.