Runko Runko is a self-hosted monorepo platform on plain Git: first-class projects inside one repository, change-based review (Gerrit-style refs/for/ pushes), path ownership, affected-based CI scoping, and rebase-based landing with merge gates enforced server-side. Humans and coding agents use the same CLI/API surface; agents get stricter, server-enforced policy. Apache-2.0. Runko develops on Runko. The source of record is a public instance — browse the code, changes, and review history without an account: Code: https://runkorepo.com/runko Changes: https://runkorepo.com/runko/changes Playground: https://runkorepo.com/demo/ — the UI on sample data, entirely in your browser Clone: git clone https://runkorepo.com/o/runko/repo.git (or this mirror) This GitHub repository is the read-only mirror of that instance, updated automatically on every landing (including refs/changes/*, so any change's exact commit is fetchable here too). Git is the only storage substrate — Postgres holds a rebuildable index of trunk, never a second source of truth. Each project directory carries a README.md that is its spec surface — what it owns, its decided constraints, and a dated Decisions section; the original design spec, docs/design.md, is frozen as the historical record (see docs/README.md for the documentation model). Getting started The zero-cost look first: the playground runs the full UI on sample data entirely in your browser, and the public instance is browsable read-only with no account (joining it is invite-only — "Request an invite" on its landing page). Joining an org that already runs Runko needs no server setup at all: the whole journey is one CLI plus your org's control-plane URL (ask whoever operates it): # 1. The CLI — grab a prebuilt binary (see Installing below), or: go install github.com/saxocellphone/runko/cli/runko@latest # 2. Join your org: one command creates your account and your stored # login (signup IS login; it prompts for a password) runko auth signup --runkod-url https://runko.example.com --name val --org acme --join # 3. A workspace: your slice of the monorepo, from one shared clone. # Where it materializes is our business — you name it with -w and # work from wherever you already are. runko workspace create --name day-one --project repo # 4. Your first project — generated, opened as a change for review runko project create --name hello --type service --api rest \ --repo "$(runko workspace path day-one)" runko change push -w day-one # -> review; checks scoped to what changed runko change land -w day-one # rebase-lands once the gates are green # 5. The steady-state loop, forever after $EDITOR "$(runko workspace path day-one)"/hello/main.go runko change create -w day-one -m "hello: describe the edit" runko change push -w day-one && runko change land -w day-one Every gate you meet along the way — owners, required checks, agent policy — lives in the org's tree, and each refusal names the command that fixes it. Standing up the control plane itself (docker compose, signup flags, the two-user eval profile) is the operator's path: see Running your own. The first user on a fresh instance signs up with --create instead of --join, which genesis-seeds the org — a root manifest, OWNERS naming you, AGENTS.md — so day-one work lands by its author alone; CI replays that exact journey, refusals included, via scripts/compose-onboarding.sh. How changes land here There are no direct pushes to main and no merged PRs, anywhere. A change is pushed to refs/for/main, passes receive policy and a secret scan, gets its required checks from the PROJECT.yaml manifests it affects — each project declares its own check commands, and CI is a generic executor that runs whatever runko-ci checks resolves for the change — and lands by rebase once the gates are green. The repo is carved into projects with declared dependencies: edges; some projects declare no checks at all and are gated through the reverse-dependency closure. The self-hosting migration and everything it surfaced is recorded in docs/migration-findings.md. Status Working end-to-end and in daily use by its own development (which is the current test of record). The per-stage engineering history is docs/implementation-log.md. Implemented: the runkod daemon (smart-HTTP git, receive funnel, REST + Connect APIs, merge gates, automerge, multi-org, outbound mirror), the runko/runko-ci CLIs, workspaces (snapshot refs + sparse cones; single-use for agents), ephemeral per-task agent identities with server-enforced policy, affected computation with a Bazel build-graph adapter and per-check classes (run_when: direct | affected), immutable releases with changelogs derived from landed changes and tag governance at receive, code search (Zoekt), an MCP server, the web UI (stacked review with syntax-highlighted folding diffs, a code browser with history and blame, search), public read-only orgs, a CI watchdog, an operator admin panel, and a measured docker-compose eval loop. Expect rough edges; hardening findings land as ordinary changes. Repository layout Every project folder has a README.md — start there. docs/ the frozen design spec + histories, contract schemas (docs/spec/), the CLI output contract (docs/cli-contract.md) db/ Postgres DDL (db/migrations) and sqlc queries (db/queries) internal/ shared internals (gitstore, gitfixture test harness, sqlc output) platform/ control-plane libraries: receive, land, affected, checks, index, project templates, search, mirror, build adapters, MCP runkod/ the daemon: pre-receive processor, smart-HTTP, REST + Connect APIs; runkod/proto/ is its Connect contract (web <-> runkod), gen committed runko-bridge/ webhook -> GitHub Actions shim: turns runkod webhooks into repository_dispatch events for the CI executor runkogithubapp/ GitHub App installation-token minting — the deployment-wide credential for everything Runko does against GitHub watchdog/ runko-watchdog, the CI reconciler: force-reports finished-but-unreported runs, one rescue rerun for lost dispatches mailer/ runko-mailer, the invite-request notifier (SMTP over the operator feed) web/ web UI (React + TypeScript + Vite + Connect-ES) webadmin/ the operator admin panel — its own sign-in and failure domain, deliberately separate from web/ cli/runko/ human/agent-facing CLI cli/runko-ci/ CI-facing CLI Running your own git clone https://github.com/saxocellphone/Runko.git && cd Runko docker compose up That brings up the control plane (runkod + Postgres) at http://localhost:8080 with two eval users, alice and bob — clone through it, push a change to refs/for/main as one, approve as the other, land it. scripts/compose-smoke.sh drives that loop end to end; CI times it on every landing. The web UI runs separately for now: cd web && VITE_RUNKO_URL=http://localhost:8080 npm install && npm run dev. The CLI Everything the platform does is a command — the web UI is a view, not the interface. The daily loop: runko project create --name payments-api --type service --lang go runko workspace create --name fix-sku --project payments-api --by you # your slice, from one shared clone runko change push # -> review; checks scoped to what changed runko change requirements --json # owners + checks outstanding, machine-readable runko change land # rebase-lands once the gates are green runko change automerge --change I… # or: arm once, it lands itself when they go green Design rules, documented in docs/cli-contract.md: every command takes --json; exit codes are 0/1/2 (success / failure / usage); every refusal is a structured {code, message, suggestion} where the suggestion is a command you can type. Coding agents use the same binary, taught by a generated AGENTS.md (runko agents-md); CI uses runko-ci (affected computes impact offline from the tree, checkout does partial-clone sparse checkouts, report-check posts results with retries). Running coding agents Agents are ordinary API clients with stricter, server-enforced defaults — none of it is prompt engineering: runko agent create --task fix-sku # mints agent-fix-sku-3f2a: its own token, dead by TTL runko workspace create --name fix-sku … # one task = one workspace, enforced runko change push # per-change size caps nudge stacks, not monoliths runko change automerge --change I… # the agent never polls; the server lands when green Each agent identity is minted per task and named for the work, so authored_by, workspace ownership, and the review badge all answer "what was this agent doing" by construction. Agent workspaces are single-use and auto-close when their last change concludes; oversized changes are refused at receive with the split workflow in the message (a stack of small changes passes where the same volume as one change is refused); agents can never approve their own changes, mint other agents, or hold operator powers. This repo is built almost entirely by agents working under exactly these rules. Installing Prebuilt runko and runko-ci binaries (Linux/macOS/Windows) are on the releases page, rebuilt automatically whenever a landing affects the CLI. From source: go install github.com/saxocellphone/runko/cli/runko@latest go install github.com/saxocellphone/runko/cli/runko-ci@latest Building and testing git clone https://runkorepo.com/o/runko/repo.git runko cd runko make check # fmt + vet + test, all packages Requires Go 1.25+ and git on PATH. The test suite needs no other services — it uses throwaway local Git repositories. Optional suites, each with its own toolchain: make check-race, make check-db (live Postgres), make check-web (Node ≥ 22), make check-bazel (build graph + gazelle drift) and make check-bazel-test (the same test suite under Bazel — what CI's per-project checks run, scoped), make check-compose (Docker; the timed end-to-end eval loop). Contributing Pull requests cannot be merged on GitHub — this repo is a mirror, and a divergent main would freeze it. Issues and discussion are welcome here; code changes go through review on the Runko instance. Decisions (repo-wide) Decisions scoped to a single project are recorded in that project's README.md; entries here are the ones that cross project boundaries — and, like there, only major architectural shifts: routine work is recorded by its change description, not README edits (see docs/README.md). The record through 2026-07-16 is docs/design.md's frozen changelog. 2026-07-16 — the centralized spec is retired: docs/design.md is frozen as the historical record and each project's README.md becomes its living spec surface, with dated Decisions sections replacing the central changelog (docs/README.md documents the model).
