Self-hosted incident-to-PR automation for GitHub repositories.
Agent Ops listens to operational signals, deduplicates incidents, runs a durable remediation workflow, asks an AI backend to prepare a fix in a guarded workspace, verifies the result, and opens a pull request for human review.
Quick start · How it works · Connect a repo · Safety model
- Turns incidents from GitHub, direct API calls, OpenSearch/Elasticsearch alerts, OpenSearch polling, and Jaeger trace polling into remediation workflows.
- Runs durable incident workflows with Temporal so retries and long-running remediation are explicit.
- Uses a Python remediation worker to clone repositories, run an AI backend, enforce path and command guards, run checks, and report results.
- Creates pull requests through a GitHub App instead of mutating production state.
- Stores repos, incidents, workflow state, and collector cursors in PostgreSQL.
- Ships with Docker Compose for local deployment and a minimal Go dashboard for operations.
- An incident arrives from a webhook, API request, log alert, trace collector, or manual trigger.
- The control plane deduplicates the incident by fingerprint and starts a Temporal workflow.
- The workflow triages the incident and requests a repository-specific remediation run.
- The remediation worker clones the repo, invokes the selected AI backend, and checks the diff.
- Configured commands run inside the guarded workspace.
- The platform pushes a fix branch and opens a pull request when verification succeeds.
| Area | What runs there |
|---|---|
cmd/control-plane |
Go HTTP API, dashboard, webhook intake, collector control |
cmd/worker |
Temporal workflow worker |
internal |
Go domain, store, intake, GitHub App, collectors, remediation client |
worker-image |
Python remediation worker and test suite |
compose |
Dockerfiles and service configuration |
examples |
Sample repo config, incidents, and alert payloads |
scripts |
Repo onboarding and smoke-test helpers |
- Docker and Docker Compose
- A GitHub App with repository content and pull request permissions
- At least one remediation backend: OpenAI-compatible API, Anthropic API, Claude CLI, or Codex CLI
cp .env.example .envSet the GitHub App values and one remediation backend in .env:
GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY_PATH=/absolute/path/to/github-app-private-key.pem
GITHUB_WEBHOOK_SECRET=replace-with-a-random-secret
REMEDIATION_PROVIDER=chatgpt_api
OPENAI_API_KEY=replace-with-your-keyFor GitHub App details, see onboarding/github-app.md.
docker compose up --buildOpen:
- Dashboard: http://localhost:18080
- Temporal UI: http://localhost:8088
If a port is already in use, change the host-side values in .env, especially CONTROL_PLANE_HOST_PORT.
curl -X POST http://localhost:18080/api/v1/repos \
-H 'content-type: application/json' \
-d @examples/repo-config.jsoncurl -X POST http://localhost:18080/api/v1/incidents \
-H 'content-type: application/json' \
-d @examples/incident.jsonThe quickest path is to generate a repo config from an existing local checkout:
python3 scripts/scaffold-repo-config.py \
--repo-path /path/to/your/repo \
--reviewer your-github-user \
--auto-run \
--remediation-provider codex_cli \
--output /tmp/agent-ops-repo.jsonIf the repo has direct observability backends, add them during scaffolding:
python3 scripts/scaffold-repo-config.py \
--repo-path /path/to/your/repo \
--reviewer your-github-user \
--auto-run \
--remediation-provider claude_api \
--logs-base-url http://localhost:9200 \
--logs-index 'my-service-logs-*' \
--trace-base-url http://localhost:16686 \
--trace-service my-service \
--output /tmp/agent-ops-repo.jsonSmoke-test the generated config:
bash scripts/project-dry-run.sh --config /tmp/agent-ops-repo.jsonRun one autonomous collector cycle:
bash scripts/project-dry-run.sh --config /tmp/agent-ops-repo.json --mode autonomousRun a safe end-to-end remediation PR test against a sandbox branch:
bash scripts/project-dry-run.sh \
--config /tmp/agent-ops-repo.json \
--mode sandbox \
--repo-path /path/to/your/repo \
--branch agent-ops-sandboxReview generated repo configs before production use. The scaffold script proposes commands, allowed paths, reviewers, labels, and optional observability sources, but it does not know your operational risk tolerance.
| Provider | Use when | Required settings |
|---|---|---|
chatgpt_api |
You want an OpenAI-compatible API backend | OPENAI_API_BASE_URL, OPENAI_API_KEY, CHATGPT_MODEL |
claude_api |
You want Anthropic's OpenAI-compatible API path | CLAUDE_API_BASE_URL, ANTHROPIC_API_KEY, CLAUDE_API_MODEL |
claude_cli |
You already use the Claude CLI locally | CLAUDE_CLI_PATH, optional CLI args/model |
codex_cli |
You already use the Codex CLI locally | CODEX_CLI_PATH, optional CLI args/model |
Set the default backend with REMEDIATION_PROVIDER, or override it per repo with remediation_provider.
When CLI backends are used through Docker Compose, the worker mounts host ~/.codex and ~/.claude read-only so existing CLI login state is available inside the container. API keys still work for CLI backends and take precedence when present.
Repositories can declare log_sources and trace_sources in repo config.
log_sourcessupport OpenSearch/Elasticsearch-compatible_searchpolling.trace_sourcessupport Jaeger Query API polling.- Collector cursors are stored in PostgreSQL so polling resumes cleanly across restarts.
- The control plane polls automatically when
COLLECTOR_ENABLED=true. - Webhook-only and manual-intake repos are still supported.
Send an OpenSearch alert payload manually:
curl -X POST http://localhost:18080/api/v1/webhooks/opensearch \
-H 'content-type: application/json' \
-d @examples/opensearch-webhook.jsonTrigger one collector cycle manually:
curl -X POST http://localhost:18080/api/v1/collectors/run \
-H 'content-type: application/json' \
-d '{"repo":"acme/example-service"}'Target repositories may define .agent-ops/repo.yaml to override:
- command sets
- model profile
- reviewers and labels
- allowed paths
remediation_providerremediation_model
Use repo-local overrides when one repository needs tighter guardrails or a different model/provider than the platform default.
Agent Ops is designed to keep automation reviewable.
- It opens pull requests, not direct commits to the default branch.
- It never auto-merges.
- Repeated incident fingerprints deduplicate to one active incident.
- Remediation runs are checked for disallowed file edits and direct commits before the platform pushes its own fix branch.
- Repo configs can restrict commands, paths, reviewers, labels, and provider selection.
Local Docker Compose is meant for development and trusted self-hosted environments. Treat these as sensitive:
.envvalues- GitHub App private keys
- API keys for AI providers
- host CLI auth directories mounted into the worker
/var/run/docker.sockmounted into the worker
Do not expose the Compose stack directly to the public internet without adding authentication, network controls, secret management, and production-grade hardening.
Run the same checks used by CI:
go test ./...
cd worker-image && python3.12 -m pytest
docker compose configOptional image build check:
docker compose build control-plane temporal-worker remediation-workerV1 is GitHub-first, single-owner, and PR-only. The core loop is intentionally conservative: detect, triage, propose, verify, and open a pull request for human review.
Automatic observability collection currently supports OpenSearch/Elasticsearch logs and Jaeger traces.
MIT. See LICENSE.