2026-09-05 11:15:30 +10:00

forgebot

K8s operator + API for AI agent dispatch from git forges.

Architecture

  • API server (cmd/api) — REST API backed by PostgreSQL. Receives webhooks from Gitea, manages task lifecycle, and orchestrates the review workflow.
  • Operator (cmd/operator) — Kubernetes controller that watches for pending tasks, creates Jobs via AgentPool/AgentTask CRDs, and reports completion back to the API.
  • TUI (cmd/tui) — Terminal kanban board for viewing and managing tasks.

Task Lifecycle

Tasks follow a kanban workflow with automated review:

                    +-----------+
                    |   Todo    |
                    +-----+-----+
                          |
                   agent picks up
                          |
                    +-----v-------+
                    | In Progress |
                    +-----+-------+
                          |
                   agent completes
                          |
              +-----------+-----------+
              |                       |
        auto-create               error?
        review task            back to Todo
              |
        +-----v------+
        |  In Review  |<-----+
        +-----+------+      |
              |              |
       human decision    reviewer
              |          suggests
         +----+----+    changes
         |         |        |
    +----v--+  +---v----+   |
    | Done  |  | Wontdo |   |
    +-------+  +--------+   |
                             |
              (new fix task in Todo)

Only humans can move tasks from In Review to Done/Wontdo.

Quick Start

API Server

export DBHOST=localhost DBUSER=forgebot DBPASS=secret DBNAME=forgebot
export GITEA_URL=https://git.unkin.net GITEA_TOKEN=<token>
make build
./bin/forgebot-api

Operator

./bin/forgebot-operator --api-url http://forgebot-api:8000
# or
FORGEBOT_API_URL=http://forgebot-api:8000 ./bin/forgebot-operator

TUI

./bin/forgebot-tui -api http://localhost:8000
# or
FORGEBOT_API_URL=http://forgebot-api:8000 ./bin/forgebot-tui

TUI Key Bindings

Key Action
h/l or arrows Move between columns
j/k or arrows Move within column
Enter Task detail view
e Edit task in $EDITOR
n Create new task
d Mark done (in review only)
w Mark wontdo (in review only)
r Refresh
/ Filter by repository
? Toggle help
q Quit

API

Method Path Description
GET /health Health check
GET /api/v1/tasks List tasks (?status=, ?repository=)
POST /api/v1/tasks Create task
GET /api/v1/tasks/{id} Get task
PATCH /api/v1/tasks/{id} Update task status
POST /api/v1/tasks/{id}/complete Agent completion callback (triggers review workflow)
POST /api/v1/tasks/{id}/comment Post comment to forge
POST /api/v1/webhook/gitea Gitea webhook receiver

Schema

The SQL lives in migrations/, is embedded in the API binary and applied at startup before the server listens, so there is no mirrored copy in the deployment to drift out of sync. The runner is golib/pg's pg.NewMigrated — forgebot owns the SQL, the shared library owns the mechanics.

Each start takes pg_advisory_lock on a fixed key (FNV-1a/64 of the lock name forgebot-migrations), creates schema_migrations (version, applied_at) if missing, and applies every embedded file whose filename is not yet recorded — in lexical (version) order, each file's SQL and its tracking row in one transaction — then unlocks. Replicas starting together queue on the lock and then find nothing to do.

A file absent from schema_migrations is re-run even where the live database already has the schema, which is how a database created by the pre-golib runner is adopted. Migrations are therefore IF NOT EXISTS-guarded and their data fixups re-runnable.

CRDs

  • AgentPool — Configuration for a pool of AI agents (model, concurrency, image, resources)
  • AgentTask — A task dispatched to a pool for execution
  • ProviderQueue — Polls the API for pending tasks and creates AgentTask CRs
  • RepositoryBinding — Links a repository to a queue and pool with access controls

Building

make build          # all binaries to bin/
make test           # run tests
make lint           # go vet
make fmt            # gofmt
make generate       # regenerate CRDs and RBAC
make docker-api     # build API container image
make docker-operator # build operator container image

GOPRIVATE

forgebot depends on git.unkin.net/unkin/golib, which is served by Gitea and is unknown to proxy.golang.org / sum.golang.org. Module resolution therefore needs:

export GOPRIVATE=git.unkin.net

The Makefile exports it for every target, and both Dockerfiles and the woodpecker Go steps set it themselves, so make build|test|lint and CI work on a clean checkout. Only bare go commands run outside make need it in your shell — set it there rather than with go env -w, which is machine state this repo cannot carry.

S
Description
K8s operator + API for AI agent dispatch from git forges
Readme 156 KiB
Languages
Go 97.8%
Makefile 2.2%