Unattended, one-shot AI coding agents. Give Maxions a task and a target repo — it clones, implements, lints, builds, fixes errors, commits, pushes, and opens a pull request with no human in the loop.
Inspired by Stripe's Minions, where over 1,300 PRs merge autonomously every week. Built on top of isol8 — an isolated code execution platform for AI agents.
Maxionalisa (@maxionalisa) is a GitHub App powered by this platform. Here it is opening a real pull request autonomously on the isol8 repo:
Illusion47586/isol8#111 — cloned the repo, made the change, committed with a conventional commit message, and opened the PR. No human code.
Maxions is a self-hosted platform for running one-shot coding agents as a queue of jobs. You submit a plain-English task and a target GitHub repo. The platform:
- Spins up a fresh, isolated Docker container for the job
- Clones the repo and checks out a new branch
- Runs the
picoding agent (GitHub Copilot) to implement the task - Runs lint and build checks deterministically
- If they fail, runs the agent again to fix the errors (up to 2 rounds)
- Commits with a conventional commit message and pushes
- Opens a pull request via the
ghCLI - Streams every log line to the web dashboard in real time
The result is a PR ready for human review.
One-shot coding agents need three things to be reliable:
- An isolated environment — each run must not affect other runs or the host system, and must start from a clean state every time.
- A deterministic blueprint — the agent should only do the parts that require judgment (write code, fix errors). Linting, building, committing, and opening PRs are deterministic and should not be left to the agent's discretion.
- Bounded retries — lint/build failures are expected on first attempt. A fix loop with a hard cap (2 rounds) handles them without infinite token burn.
isol8 solves #1. Maxions implements #2 and #3 on top of it.
Each job runs inside a single persistent DockerIsol8 container. All pipeline steps share the same container filesystem, so the repo cloned in setup is still there for implement, lint, fix, and commit.
Task
│
▼
[setup] clone repo, checkout branch, install deps
(bash, inside container, before agent starts)
│
▼
[implement] pi agent reads the repo and writes the code
(runtime: "agent", code = task prompt)
│
▼
[lint] bun run lint:check ─┐
[build] bun run build ─┤ allowed to fail
│ ─┘
▼ (if either failed)
[fix] pi agent fixes lint/build errors
re-runs lint + build, up to 2 rounds
│
▼
[commit] pi writes commit message → /tmp/commit-msg.txt
git add -A && git commit -F /tmp/commit-msg.txt && git push
│
▼
[pr] pi writes title + body → /tmp/pr-title.txt, /tmp/pr-body.md
gh pr create --body-file /tmp/pr-body.md
For a full walkthrough of this architecture, see the isol8 guide: One-shot coding agents.
isol8 is the execution engine that provides:
- Isolated Docker containers — read-only root filesystem, non-root
sandboxuser, seccomp syscall filtering - Persistent sessions — a single container reused across all steps, with shared
/sandboxfilesystem - The
agentruntime — runspiinside the container; thecodefield is the LLM prompt - Secret masking — credentials in
secretsare automatically redacted from all output setupScript— bash that runs inside the container before the agent receives any prompt
Maxions uses DockerIsol8 in mode: "persistent" with network: "host" and the isol8:agent image (which has pi, gh, git, and bun pre-installed).
The container receives two separate GitHub tokens:
| Variable | Token type | Used by |
|---|---|---|
GITHUB_TOKEN |
GitHub App installation token (short-lived, repo-scoped) | git clone, git push, gh CLI |
COPILOT_GITHUB_TOKEN |
Personal Access Token with Copilot access | pi (checks this env var before GITHUB_TOKEN) |
GitHub App installation tokens are rejected by the Copilot LLM API — they are server-to-server tokens, not user tokens. A PAT is required for Copilot. The split keeps both integrations working.
A fresh installation token is minted per run (via @octokit/auth-app) — tokens expire in 1 hour, so they must not be cached across jobs.
| Layer | Tech |
|---|---|
| Monorepo | Turborepo + Bun |
| API server | Hono (Bun) |
| Web dashboard | Next.js 15 + shadcn/ui + Tailwind CSS |
| Database | SQLite + Drizzle ORM |
| Agent sandbox | @isol8/core — DockerIsol8 persistent mode |
| Coding agent | pi (@mariozechner/pi-coding-agent) via runtime: "agent" |
| LLM | GitHub Copilot (github-copilot/gpt-5-mini) |
| GitHub auth | GitHub App — installation tokens via @octokit/auth-app |
| Queue | p-queue (concurrency: 3) |
| Linting | Ultracite (Biome-based) |
apps/
api/ Hono API — job queue, SSE live streaming, REST routes
web/ Next.js dashboard — job list, detail view, live log terminal
packages/
orchestrator/ The blueprint: all pipeline steps, DockerIsol8 engine wiring
db/ Drizzle schema, client, SQLite migrations
ui/ Shared React components — StatusBadge, LogTerminal, StepTimeline
- Bun 1.2+
- Docker running locally, with access to
/var/run/docker.sock - The
isol8:agentimage (pre-built from@isol8/core):docker build --target agent -t isol8:agent node_modules/@isol8/core/docker/
- A GitHub App installed on the target repo with Contents (read/write) and Pull Requests (read/write) permissions
- A GitHub PAT with Copilot access (for the
piagent)
bun install
cp .env.example .env
# Fill in .env — see below
bun run db:migrate
bun run dev- Dashboard: http://localhost:3002
- API: http://localhost:3000
# GitHub App — mints short-lived installation tokens for git + gh CLI
GITHUB_APP_ID=
GITHUB_APP_PRIVATE_KEY= # PEM, with literal \n between lines
GITHUB_APP_INSTALLATION_ID=
# GitHub PAT with Copilot access — pi uses this for the Copilot LLM API
COPILOT_GITHUB_TOKEN=
# SQLite database path
DATABASE_URL=file:./maxions.db
# Server ports
API_PORT=3000
WEB_PORT=3002
# Default target repo (overridable per-task via the API)
TARGET_REPO=owner/repodocker compose up --buildThe API container mounts /var/run/docker.sock to spawn sandbox containers as siblings on the host Docker daemon (Docker-outside-of-Docker). The isol8:agent image must be built on the host before starting.
| Method | Path | Description |
|---|---|---|
POST |
/maxions |
Create and enqueue a new job |
GET |
/maxions |
List all jobs (newest first) |
GET |
/maxions/:id |
Get a single job |
POST |
/maxions/:id/kill |
Kill a queued or running job |
DELETE |
/maxions/:id |
Delete a completed job and its logs |
GET |
/maxions/:id/logs |
Full log history |
GET |
/maxions/:id/stream |
SSE live event stream |
Create a job:
curl -X POST http://localhost:3000/maxions \
-H "Content-Type: application/json" \
-d '{"task": "Add a dark mode toggle to the settings page", "repo": "owner/repo"}'A few non-obvious things discovered while building this:
for awaitdeadlocks Bun when consuming Docker TCP streams. The stream consumer inpackages/orchestrator/src/blueprint.tsuses.then()+setImmediatechaining instead offor awaitorawait iter.next()— this keeps the Bun event loop free between iterations so Docker TCPdataevents can fire.git commitwithout-mopens an interactive editor and hangs in a non-interactive container. The commit step instructs the agent to write the message to/tmp/commit-msg.txtand rungit commit -F /tmp/commit-msg.txt.gh pr create --body "..."breaks when the body contains backticks or$(...)— they are interpreted as shell command substitution. The PR body is written to/tmp/pr-body.mdand passed via--body-file.- Bun SSE connections drop after 10 seconds without
idleTimeout: 0on the Bun server export — Bun's default idle timeout kills long-lived connections before the SSE heartbeat runs. X-Accel-Buffering: nois required on SSE responses when behind nginx — without it, nginx buffers the entire response body and the client sees nothing until the connection closes.git checkout -bfails on retry withset -eactive if the branch already exists. The|| git checkout ${branch}fallback in the setup script is load-bearing.
- isol8 — One-shot coding agents guide — the architectural patterns this project is built on
- Stripe's Minions — the original inspiration
- isol8 repo — the sandbox engine