- Rust 86.8%
- Shell 5.9%
- Python 5%
- HCL 1.2%
- Dockerfile 0.7%
- Other 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| crates | ||
| docs | ||
| scripts | ||
| templates | ||
| terraform | ||
| tests/fixtures | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| .gitleaks.toml | ||
| Cargo.lock | ||
| Cargo.toml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
gitlab-cursor-webhook (GitLab Cloud Host)
Filter GitLab webhooks and provision ephemeral Hetzner agent VMs from golden snapshots. Each VM runs the Cursor CLI headless agent against the project workspace.
Components
| Binary | Purpose |
|---|---|
gchcontroller |
HTTP server: GitLab webhooks, job API, VM provisioning via Terraform |
gchconfig |
SQLite CLI: projects, tags, prompts, doctor, dry-run |
| Crate | Purpose |
|---|---|
gch-core |
Shared filter logic, dedup, database, prompt rendering |
Routes
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness — {"status":"ok"} |
POST |
/webhook |
GitLab webhook ingress (Standard Webhooks HMAC signature) |
GET |
/api/jobs/{job_id} |
Agent VM fetches job (Bearer JOB_RUNTIME_TOKEN) |
POST |
/api/jobs/{job_id}/heartbeat |
Agent heartbeat (every 60s) |
POST |
/api/jobs/{job_id}/status |
Optional progress callback |
POST |
/api/jobs/{job_id}/complete |
Agent finished — triggers VM destroy |
GET |
/api/admin/jobs |
List in-memory jobs (Bearer GCH_ADMIN_TOKEN) |
GET |
/api/admin/jobs/{job_id} |
Job details (Bearer GCH_ADMIN_TOKEN) |
Flow
- GitLab sends MR or issue webhook → filter/validate/dedup (unchanged rules)
gchcontrollerresolves project + tag from SQLite (securityfor MRs; issue labelsagent:{tag})- Renders prompt template, creates job, runs isolated Terraform apply (one state file per job)
- VM boots from golden snapshot, cloud-init injects secrets and runs project
gch-cloud-init.sh - VM calls
GET /api/jobs/{id}, runsagent -p ... --force --trust, sends heartbeats - On complete, timeout (3h), or stale heartbeat (5m), controller destroys the VM
Build
cargo build --release
# Binaries: target/release/gchcontroller, target/release/gchconfig
Configuration
Controller environment
See .env.example. Required:
ALLOWED_USERS— comma-separated GitLab usernames (fail-closed if empty)HCLOUD_TOKEN,GCH_FIREWALL_ID,GCH_CONTROLLER_URLGCH_SSH_KEY_IDS— SSH keys for agent VMs (numeric id, key name, or fingerprint; see.env.example)CURSOR_API_KEY,GITLAB_TOKEN
SQLite (gchconfig)
gchconfig project add --gitlab plasticdigits/yieldomega --workspace /home/agent/workspace
gchconfig project set-signing-token --gitlab plasticdigits/yieldomega --token whsec_...
gchconfig tag add --project yieldomega --name security --snapshot 12345678
gchconfig prompt set --project yieldomega --tag security --file prompts/security.md
gchconfig setting controller_url https://gch.example.com
gchconfig doctor
gchconfig jobs list # requires GCH_ADMIN_TOKEN + controller_url
gchconfig jobs list --active # provisioning or running only
gchconfig jobs show <job-uuid>
Migrate legacy env-based project paths:
gchconfig import-env # reads PROJECT_WEBHOOKS_* for project paths only
Dry-run (no VM)
gchconfig dry-run --fixture tests/fixtures/mr_open.json
Filter behavior
Merge requests
security(default): forwards onopen, orupdatewith new commits (oldrevset); user inALLOWED_USERS; project configured.- Other tags (e.g.
fix_conflicts,fix_security,fix_bugfix): forwards when anagent:{tag}label is on the MR at open, or newly added onupdate— same label rules as issues. Deduped perproject_id:iid:tag(issue dedup TTL).
Issues → any configured tag via agent:{tag} label
Forwards when an agent:{tag} label is present on open, or newly added on update (e.g. agent:verify, agent:gap_analysis). The {tag} must match a gchconfig tag for that project. Reserved: security is MR-only — agent:security on issues is ignored. When multiple agent labels fire, priority is implement > verify > others alphabetically.
Dedup: MR by project_id:iid:commit_sha:tag; issues by project_id:iid:tag (each tag/flow dedupes independently).
Golden images
See docs/admin-golden-image.md.
Project runbooks (controller + webhook + gchconfig):
Example project scripts:
- docs/examples/yieldomega/ — EVM / Foundry / Rabby
- docs/examples/cl8y-dex-terraclassic/ — Terra / Keplr
Copy gch-cloud-setup.sh, gch-cloud-init.sh, gch-cloud-init-runner.sh, gch-agent-idle-wrap.py, gch-golden-image-finalize.md, and prompts/ into each project repo before building snapshots.
Terraform
- Firewall (one-time): terraform/firewall/
- Agent VM module: terraform/modules/agent-vm/
Each job gets its own directory under GCH_JOBS_DIR with a separate terraform.tfstate.
Docker / Coolify
docker compose up --build
Migrating from a bare-metal systemd deployment (SQLite + env): see docs/docker-deploy.md.
Development
cp .env.example .env
# Configure GCH_DB_PATH, disable terraform in tests via GCH_PROVISION_ENABLED=false
cargo test
cargo clippy -- -D warnings
License
AGPL-3.0-or-later — see LICENSE.