One static binary
Pure Go, no cgo. All it needs is a data dir and its environment variables. GET /healthz answers ok.
Engineering agent for software & cloud projects
underling picks up Sentry alerts, sends Claude Code into a sandboxed clone of your repo to find and fix the bug, then hands you a draft pull request. You stay the boss. It does the digging.
How it works
A Sentry issue alert hits POST /sentry/callback. underling verifies the HMAC signature and records the culprit, exception and stack frames as an event.
The Sentry project maps to a GitHub repo. underling clones it onto a new branch, underling/sentry-<issue>. One investigation per issue, even across restarts.
Headless Claude Code, with the Codex and auto-task plugins, investigates and fixes the bug inside a sandbox, committing to the branch and nothing else.
underling pushes the branch and opens a draft PR that links the Sentry issue and includes Claude's summary. Review it, merge it, or close it.
Safety
The agent that writes the fix never holds the keys to ship it. Every boundary is deliberate.
Claude Code runs with its sandbox enabled and file writes limited to the clone. underling won't ship with the sandbox off.
The Claude process gets only PATH, a temporary HOME and model credentials. It never sees the GitHub token or the Sentry secret.
Claude commits locally. underling pushes with its own token, which goes to git through the environment and never lands in .git/config.
Every change arrives as a draft PR against your default branch. A human always decides what merges.
PR opened, no changes, skipped or failed (with the stage and reason): each run ends in an event. A failure never blocks the next alert.
Alerts older than INVESTIGATION_MAX_AGE are skipped as stale, so a first deploy doesn't start working through history.
Under the hood
Pure Go, no cgo. All it needs is a data dir and its environment variables. GET /healthz answers ok.
An embedded bbolt event store keeps every alert, investigation and outcome, with checkpoints so handlers pick up where they left off.
Each feature is one package: a state change (trigger → command → event) or an automation (event → reaction). Slices meet only through events.
OpenTofu provisions an ECS service behind an ALB. Claude Code and Codex reach Bedrock through the task role, so there are no model API keys to manage.
Map each Sentry project id to a GitHub repo, give it a token, and run it.
# map Sentry project ids to GitHub repos
SENTRY_GITHUB_REPOS=4507=acme/checkout,4512=acme/api
SENTRY_CLIENT_SECRET=…
GITHUB_TOKEN=…
CLAUDE_PLUGIN_DIRS=/opt/plugins/codex,/opt/plugins/auto-task
INVESTIGATION_TIMEOUT=30m
$ underling -data-dir ./data -addr :8080