Everything you need to try Cowboy, run it for your team and tune its rules. These docs cover 0.6.0-preview, the latest preview release.
Cowboy sorts every pull request into a pen, and tags the ones your AI coding agents opened:
It picks the pen with fixed rules, so the same pull request always lands in the same pen, and it writes down why. It also flags doubles (two agents doing the same work) and strays (an agent that went quiet after you asked for changes).
It looks back at what merged, to show which rules and habits actually went with less trouble in your repo, and it can turn your rules into instructions your coding agents follow.
Cowboy runs on your own server. It only reads your pull requests and adds labels, one comment and review requests. It never merges, closes or reviews code, and your code isn't sent anywhere except an optional model server you run yourself.
The demo runs Cowboy on a made-up sample repo, with nothing to set up and no account needed.
curl -fsSL https://github.com/sgracia13/Cowboy/releases/latest/download/install.sh | sh cowboy demo
The install script downloads the right file for your computer, checks it against the published checksums and puts it in /usr/local/bin, or ~/.local/bin if that isn't writable. The macOS builds aren't signed yet; the script clears the download quarantine for you.
Download cowboy-windows-amd64.exe from the releases page and run cowboy-windows-amd64.exe demo.
go run github.com/sgracia13/Cowboy/cmd/cowboy@latest demo
Open http://localhost:8080 straight away. The sample pull requests arrive one every 4 seconds and drop into their pens without a refresh. One webhook is skipped on purpose, so you can watch the catch-up sweep find that pull request a few seconds later. Click any card to see its reasons and the comment Cowboy would post.
Want everything at once? cowboy demo --drip 0. Just a table in the terminal? cowboy demo --once.
Then open outcomes: a replay of real vercel/next.js history (about 2,500 merged pull requests, author names removed), so you can see what Cowboy finds after merge and how the pens compare with simple alternatives there.
cowboy
With no arguments, Cowboy starts on this machine only and opens your browser on a setup page:
When the pens look sensible, press start labelling on the setup page. Cowboy first checks the login can write labels on every repo, by adding the cowboy:easy label, and tells you if it can't. The login, repos and mode are saved, so next time it's just cowboy. Sign out or swap the token on the same page.
On a laptop GitHub can't send webhooks to Cowboy, so it checks for new pull requests every 5 minutes instead. For a server your team shares, see the next section.
On GitHub, Cowboy can set itself up: it creates a private GitHub App for your server, and you pick the repos. On Forgejo and Gitea, or if you'd rather use a token, follow the steps further down.
docker run -d --name cowboy -p 8080:8080 -v cowboy-data:/data \ -e UI_PASSWORD=a-dashboard-password \ -e PUBLIC_URL=https://cowboy.your-domain.com \ ghcr.io/sgracia13/cowboy:latest
That's all: no token, no webhook and no secret to copy. Repos you add or remove on GitHub later are picked up automatically.
This setup is new. It's tested end to end against a stand-in for GitHub's API, but hasn't yet been run against github.com.
You need a server your git host can reach over HTTPS, a token Cowboy can use, and a webhook. This uses GitHub; for Forgejo and Gitea see the next section.
In GitHub, go to Settings › Developer settings › Fine-grained tokens, choose the repos Cowboy should sort, and give it these permissions:
| Permission | Access | Used for |
|---|---|---|
| Pull requests | Read and write | reading pull requests, diffs and reviews; asking code owners for a review |
| Issues | Read and write | labels and the comment (GitHub handles these for pull requests through issues) |
| Contents | Read | reading cowboy.json and CODEOWNERS from the default branch |
| Checks | Read | knowing whether checks pass |
| Metadata | Read | required by GitHub |
A classic token with the repo scope also works.
With Docker:
docker run -d --name cowboy -p 8080:8080 -v cowboy-data:/data \ -e FORGE=github \ -e FORGE_TOKEN=github_pat_... \ -e WEBHOOK_SECRET=a-long-random-string \ -e UI_PASSWORD=a-dashboard-password \ -e REPOS=your-org/your-repo \ ghcr.io/sgracia13/cowboy:latest
Or with the binary, using the same settings as environment variables:
export FORGE=github FORGE_TOKEN=github_pat_... WEBHOOK_SECRET=... UI_PASSWORD=... export REPOS=your-org/your-repo DATA=/var/lib/cowboy/cowboy.json cowboy serve
When it starts, the log says whether the token can read each repo in REPOS, then Cowboy sorts the pull requests that are already open.
In the repo (or the whole organization), go to Settings › Webhooks › Add webhook:
| Field | Value |
|---|---|
| Payload URL | https://cowboy.your-domain.com/webhook/github |
| Content type | application/json (the default form type won't work) |
| Secret | the same value as WEBHOOK_SECRET |
| Events | Let me select: Pull requests, Check suites and Check runs |
Webhooks are how Cowboy hears about pull requests the moment they change. If one is lost, the catch-up sweep picks the pull request up within 5 minutes.
Go to your Cowboy address and sign in with UI_PASSWORD. New pull requests appear in their pens as they're sorted.
GitHub needs an HTTPS address for webhooks. The simplest way is a reverse proxy such as Caddy, which gets a certificate for you:
# Caddyfile
cowboy.your-domain.com {
reverse_proxy 127.0.0.1:8080
}
To know how many files depend on a changed file, Cowboy builds an import graph of the repo. It does this by itself: there's nothing to set up.
The rules page on the dashboard shows how blast radius is working for each repo. To read a local copy instead, set REPO_PATHS; to turn downloads off, set BLAST_RADIUS=off.
# optional: use a checkout you keep up to date yourself export REPO_PATHS=your-org/your-repo=/srv/cowboy/your-repo
For the binary on Linux, a minimal systemd unit:
# /etc/systemd/system/cowboy.service [Unit] Description=cowboy After=network-online.target [Service] EnvironmentFile=/etc/cowboy.env ExecStart=/usr/local/bin/cowboy serve Restart=on-failure User=cowboy [Install] WantedBy=multi-user.target
On stop, Cowboy finishes the work already queued before it exits.
GitLab.com and self-managed GitLab both work. Merge requests are sorted, labelled and commented on like pull requests, and after-merge tracking reads the default branch, releases and pipelines.
FORGE=gitlab FORGE_URL=https://gitlab.com FORGE_TOKEN=glpat-... WEBHOOK_SECRET=... \ REPOS=your-group/your-project cowboy serve
Limits: strays need a GitLab version that records "requested changes" on reviewers. If GitLab refuses several reviewers (a paid GitLab feature), Cowboy asks the first code owner only. Tested against a stand-in for GitLab's API, not yet against gitlab.com itself.
Cowboy works the same way on Forgejo and Gitea. This support is covered by tests but hasn't been run against a live server yet, so please report anything odd.
One difference: on Forgejo and Gitea, changing a cowboy: label by hand isn't read back as a correction yet. Use the move buttons on the dashboard instead.
Cowboy is configured with environment variables. On a server you need a way to sign in (UI_PASSWORD or AUTH_PROXY_HEADER). With the GitHub App, add PUBLIC_URL; with a token, add FORGE_TOKEN and WEBHOOK_SECRET. Everything else has a default.
| Variable | What it does |
|---|---|
| FORGE | github (default), gitlab, forgejo or gitea |
| FORGE_TOKEN | a token (required on Forgejo and Gitea; leave it empty on GitHub to use the GitHub App) |
| WEBHOOK_SECRET | the secret your webhooks are signed with (required with a token; the GitHub App brings its own) |
| PUBLIC_URL | the HTTPS address GitHub can reach Cowboy at, e.g. https://cowboy.your-domain.com. Used for the GitHub App's webhooks and links in the weekly tally |
| GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY_FILE | only for a GitHub App you created yourself instead of with the setup button |
| UI_PASSWORD | the dashboard password, one shared login for the team (or use AUTH_PROXY_HEADER) |
| AUTH_PROXY_HEADER | instead of a password: the header your sign-in proxy sets, e.g. X-Forwarded-User |
| UI_OPEN | 1 runs the dashboard with no login. Only if nothing else can reach it |
| UI_SESSION_DAYS | how long a sign-in lasts (default 14) |
| FORGE_URL | your Forgejo or Gitea server, or the API address of GitHub Enterprise |
| REPOS | owner/repo,owner/repo: repos to sort on start and to sweep |
| BLAST_RADIUS | off stops Cowboy downloading repos for blast radius (default on) |
| BLAST_RADIUS_MAX_MB | the biggest repo archive to download (default 300) |
| REPO_PATHS | owner/repo=/path,...: read blast radius from local copies instead of downloading |
| DRY_RUN | 1 sorts and shows, but writes nothing to your git host |
| CATCHUP_MINUTES | how often the catch-up sweep runs (default 5) |
| DATA | where Cowboy keeps its data (default ./data/cowboy.json; /data/cowboy.json in Docker). The databases sit next to it as cowboy.db and outcomes.db |
| ADDR | the address to listen on (default :8080) |
| MODEL_URL | your model server, e.g. http://localhost:11434 (optional) |
| MODEL_NAME | the model to use (default qwen2.5-coder:7b) |
| MODEL_KEY | only if your model server asks for one |
| OUTCOMES | off turns off after-merge tracking (default on) |
| OUTCOME_WINDOW_DAYS | how long after merge trouble counts (default 30) |
| SENTRY_CLIENT_SECRET, PAGERDUTY_SECRET, INCIDENT_SECRET | turn on incident webhooks from Sentry, PagerDuty or any tool |
| INCIDENT_REPOS | project=owner/repo,...: when a tool names a project or service, not the repo |
| TALLY_SLACK_WEBHOOK | a Slack incoming webhook for the weekly tally (optional; see the dashboard) |
| TALLY_EMAIL_TO | addresses for the weekly tally by email, comma-separated (optional) |
| SMTP_HOST, SMTP_PORT | your mail server for the tally email (port defaults to 587; 465 also works) |
| SMTP_USER, SMTP_PASSWORD | the mail server login, if it needs one |
| SMTP_FROM | the sender address (defaults to SMTP_USER) |
| TALLY_DAY, TALLY_HOUR | when the tally goes out (default monday at 9) |
| TZ | the time zone for TALLY_HOUR, e.g. America/New_York (default: the server's) |
If the dashboard would be reachable from other machines and no login is set, Cowboy refuses to start and tells you what to set.
Every pull request goes through the same rules, whoever wrote it: the rules look at the code, not the author. Agent pull requests also get an agent tag (see Recognizing agents).
A pull request with no agent mark gets a pen label like cowboy:easy and nothing else: no agent label, never a double or stray label, and a comment only when it lands in careful (where a heads-up is worth it). Change that with "people": {"comment": "always"} or "never" in cowboy.json, or sort agent pull requests only with "detect": {"sort_all": false}.
Why sort everything? Agent work that nobody marked can't be spotted reliably, so sorting every pull request is how nothing risky goes unsorted.
Everything else.
Some things are shown on the pull request as notes but never change the pen: warning silencers (like eslint-disable or # type: ignore), newly skipped tests, comments that admit special-casing for tests, and pull requests with 400 or more lines of source, which get a "too big to review well" note.
Not proven yet. On about 28,000 merged pull requests from eleven public repos, tested with the claims written down first, the careful pen didn't beat “largest first” or chance at finding pull requests that were later reverted. The easy pen mostly held up: it was no riskier than the same share of the smallest pull requests. So read careful as “a closer look might be worth it”, not “this will break”, and check the pens on your own repo with the comparison on the outcomes page. The evidence page has every test.
Put a cowboy.json (or .cowboy.json) on the repo's default branch to change the rules for that repo. Cowboy always reads it from the default branch, so a pull request can't loosen its own rules. Anything you leave out keeps its default.
{
"agents": { "build-bot": "our-agent" },
"careful": {
"paths": ["auth/**", "billing/**", "infra/**"],
"min_fan_in": 20,
"top_fan_in_percent": 1,
"shortcuts": ["tests_removed", "catch_all", "ci_loosened"]
},
"easy": { "max_lines": 50, "max_dirs": 2, "max_fan_in": 3 },
"notes": { "big_source_lines": 400 },
"strays": { "nudge_after_days": 3, "suggest_archive_after_days": 7 },
"fan_in_ignore": ["src/utils/index.ts"],
"people": { "comment": "careful" },
"detect": { "enabled": false, "sort_all": true }
}
| Key | What it does |
|---|---|
| agents | bot accounts (or co-author names) and the agent name to label them with |
| careful.paths | sensitive paths; replaces the default list |
| careful.min_fan_in | the fewest importing files that can make a change careful (default 20) |
| careful.top_fan_in_percent | a changed file must also be in this top share of the repo's most-imported files (default 1; 100 turns the extra bar off) |
| careful.shortcuts | which shortcuts make a pull request careful. The others become notes. Options: tests_removed, catch_all, ci_loosened, suppressions, skips, special_case_comments |
| careful.min_source_lines | make big pull requests careful again (off by default) |
| easy.* | the limits for easy |
| notes.big_source_lines | when the "too big to review well" note appears (default 400) |
| shortcuts | turn a shortcut check off entirely, e.g. "shortcuts": {"skips": false} |
| strays.* | days before the stray label and the suggestion to close |
| fan_in_ignore | files never counted for blast radius, like a shared utils file everything imports |
| people.comment | when pull requests from people get a comment: careful (default), always or never. They always get a pen label |
| detect.sort_all | true (default) sorts every pull request; false sorts agent pull requests only |
| detect.* | the experimental check for unmarked pull requests; see Recognizing agents |
The rules page on the dashboard shows the rules each repo is actually using.
For careful pull requests, Cowboy asks the matching people in CODEOWNERS (in .github/, the repo root, docs/ or .gitea/) for a review. It skips the pull request's own author, and in this preview it skips teams like @org/team.
Cowboy labels a pull request as agent work when it finds one of these:
Built in: Claude Code, GitHub Copilot, Codex, Devin, Cursor and Jules. These are only recognized when the agent leaves one of those marks; an agent run on someone's laptop that leaves no mark looks like a person. Add your own bots under agents in cowboy.json.
The label is agent:<name>, for example agent:claude-code.
Cowboy can also score pull requests that carry no mark, comparing each one to that author's own history: commit timing, writing style and code shape. If even the low end of its estimate reaches 80, the pull request gets agent:likely and is treated like other agent work. Either way it's sorted into a pen.
Cowboy can post the tally to a Slack channel, email it, or both, every Monday at 9:00 by default. Nothing is sent unless you set a destination.
# Slack: create an incoming webhook for the channel (Mattermost webhooks work too) TALLY_SLACK_WEBHOOK=https://hooks.slack.com/services/... # Email TALLY_EMAIL_TO=team@your-co.com,lead@your-co.com SMTP_HOST=smtp.your-co.com SMTP_USER=... SMTP_PASSWORD=... SMTP_FROM=cowboy@your-co.com # Optional TALLY_DAY=friday TALLY_HOUR=16 TZ=Europe/Berlin PUBLIC_URL=https://cowboy.your-domain.com
The post covers the 7 days before it goes out: pull requests per agent and for people, by pen, with doubles, strays and how often your team moved one. In Slack it's a short summary and a table; by email it's a small html table with a plain-text copy.
The first post goes out at the next scheduled time. To try it straight away, run cowboy tally --send with the same settings. The tally page on the dashboard says where it's sent and when the next one goes out.
Cowboy remembers what it sent in tally-sent.json next to the data file, so a restart never posts twice. If a send fails, it tries that destination again every 30 minutes; a post more than a day late (the server was off) is skipped instead of sent stale.
On GitHub you can also move a pull request by changing its cowboy:easy, cowboy:normal or cowboy:careful label yourself; Cowboy remembers your choice.
Dashboard buttons only accept requests from the dashboard itself, and the pages send strict browser security headers.
Cowboy follows every merged pull request for 30 days and ties what happened back to it. The outcomes page shows what went wrong, and whether the pens, or any simpler rule, would have pointed at it. It's on by default; set OUTCOMES=off to turn it off.
| Outcome | How Cowboy knows | Counts |
|---|---|---|
| Reverted | A revert on the default branch names the pull request: GitHub's Revert button, git revert, a revert branch, or the pull request's title | Straight away |
| Broke main | Checks failed on the merge commit, though the pull request itself was green | Straight away |
| Needed a fix | Within two weeks, a pull request titled as a fix names this one, or rewrites three or more lines it added | Once a person confirms |
| Incident | An alert from Sentry, PagerDuty or any tool, matched by release and by the files in the error | Once a person confirms |
Anything that counts can be marked "not a problem". Cowboy also records who approved each pull request, which release it shipped in, and how much of its code is still there after 30 and 90 days. It measures that from fingerprints of a sample of the added lines; no code is kept.
Careful pull requests also get more attention, so a pen's rate shows what happened, not what the pen caused.
A pen that takes a quarter of your pull requests will catch some trouble by luck. So the outcomes page gives the same share of pull requests to simple alternatives and counts how many of the pull requests that had trouble each one caught:
Each result has a likely range (95%, from 2,000 resamples) and is said in words: outperformed (the whole range clears the line), no demonstrated advantage (it doesn't, which isn't the same as useless), or did worse. Until a repo has 20 pull requests with trouble, it says insufficient evidence and nothing else. It's the same calculation as the published evidence; here, every kind of trouble counts, not only reverts. Run cowboy evaluate for the same thing in a terminal, or cowboy evaluate --json for all the numbers.
On a fresh install the outcomes page offers rate the last 90 days (or run cowboy backfill --days 90). Cowboy sorts the pull requests that already merged, the way it would have, and checks what happened to them. Nothing is written to your git host: no labels, no comments. It uses today's rules and today's import graph, and these pull requests stay out of the tally.
Each is optional. Point the tool at your Cowboy address:
| Tool | Setting | Address |
|---|---|---|
| Sentry | SENTRY_CLIENT_SECRET: the client secret of an internal integration with a webhook, used from an issue alert's action | /webhook/sentry |
| PagerDuty | PAGERDUTY_SECRET: a v3 webhook subscription for incident.triggered | /webhook/pagerduty |
| Anything else | INCIDENT_SECRET: sign the JSON body (X-Cowboy-Signature: sha256=…) or send Authorization: Bearer <secret> | /webhook/incident |
{"id": "INC-12", "title": "Checkout errors", "severity": "sev2",
"started_at": "2026-10-19T14:02:00Z", "url": "https://…",
"repo": "acme/web", "release": "v2.3.0", "files": ["src/pay/api.ts"]}
Only title is required. If a tool names a project or service rather than the repo, map it with INCIDENT_REPOS=web-frontend=acme/web; with one repo, nothing is needed. For each incident Cowboy suggests up to three likely causes: shipped in that release (or just before it started), and changed files that appear in the error. It never uses the pen to pick a cause, so the pens are judged fairly. You can also link an incident to a pull request by hand.
Every sort, move, merge, approval, outcome and confirmation is added to audit.jsonl next to the data file. Each entry carries the hash of the one before, so cowboy audit verify finds any entry that was changed or removed. The weekly tally post includes the latest fingerprint, which gives you a copy outside the server to check against. Export it with cowboy audit export --format csv, or from the outcomes page.
cowboy benchmark export writes counts only: pens, agents, rules and kinds of outcome, with no repo names, file paths, titles or people. Read it, and share it only if you want to. cowboy benchmark report a.json b.json pools several teams' files into one report. Cowboy never sends it anywhere.
On two years of Sentry, Grafana and n8n history, Cowboy traced 98.8%, 87% and 70% of reverts to the pull request they undid; the rest name no pull request, title or commit. Replayed over 7,721 real merged pull requests from the same repos, the reverts it found matched an independent earlier count on 213 pull requests (8 found only there, 5 only here), and, weighted back to Sentry's real mix of pull requests, 1.42% of careful pull requests were reverted within 30 days against 0.81% of easy ones. Those repos are the ones the rules were tuned on; on Supabase, which the rules never saw, careful PRs were reverted about as often as any other, and on three fresh repos (next.js, Airflow, Kibana) neither revised rules nor rules fitted to each repo beat chance (the evidence page has all three rounds). Suggested fixes looked right in about 7 in 10 of a sample, which is why a person confirms them. Everything else has been tested against stand-ins for GitHub, Sentry and PagerDuty, not yet on a live repo for 30 days. See the check.
The Habits page shows, for each repo, how often merged pull requests keep five habits, and whether each went with less trouble after merge in that repo:
Each habit shows its share over the last 90 days and the 90 days before, and a verdict from all rated history: holds here, did worse here, no demonstrated difference or not enough evidence yet (fewer than 20 pull requests with trouble). Size is held fixed, and a verdict needs the whole 95% range to clear the line. When a habit shows no difference and the range is tight (0.8× to 1.25×), the page says a rule requiring it may be a ritual worth questioning; when the range is wide, it says it can't tell yet.
Numbers are for the whole team, never per person. Across nine public repos no habit went with fewer reverts everywhere, which is why each repo gets its own answer.
Coding agents follow an instructions file in the repo (AGENTS.md, CLAUDE.md or .github/copilot-instructions.md). Cowboy writes a section for it: the paths and files that get a careful review, the size that gets the quickest review, the shortcuts it flags (only the checks you have on), this team's habits, and how to say an agent helped.
cowboy guide --repo acme/app # print it cowboy guide --repo acme/app --write AGENTS.md # put it in the file
The section sits between <!-- cowboy:guide start --> and <!-- cowboy:guide end -->, so running it again replaces only that part. It reads the repo's cowboy.json from the current folder (or --config). The Habits page has the same text with Copy and Download buttons. Cowboy never commits it for you.
That's all. It never merges, closes, approves or pushes code.
Cowboy talks only to your git host (including downloading the default branch for blast radius) and, if you set them, your own model server (MODEL_URL) and where the weekly tally goes (your Slack webhook or mail server). The tally holds counts only, no code. No telemetry, no accounts, nothing sent to us.
An open-weight model on your own server can add notes to each pull request it sorts: whether the change stayed on task, a three-line brief, and where to look first. The model never picks the pen; the same pull request gets the same pen with or without it. Notes that mention a file the pull request didn't change are dropped.
It works with anything that speaks the OpenAI chat API: Ollama, vLLM, llama.cpp server, LM Studio or LocalAI. With Ollama on the same machine:
ollama pull qwen2.5-coder:7b export MODEL_URL=http://localhost:11434 MODEL_NAME=qwen2.5-coder:7b cowboy check-model # is it answering? cowboy serve
Labels go on first and the notes follow when the model finishes. Without MODEL_URL, everything else works with rules only. The notes have only been tried against a stand-in model so far, so check the wording with your own model before relying on it.
| Command | What it does |
|---|---|
| cowboy | start on this machine and open the browser: connect GitHub, pick repos and choose a mode on the setup page |
| cowboy demo | the sample repo on localhost:8080. Flags: --drip 0 (all at once), --once (print and exit), --model-url, --model-name, --no-model, --agent-check (turn on the experimental check), --addr |
| cowboy serve | run on a server, configured by the settings (a login saved on the setup page is used too; environment variables win) |
| cowboy score | sort one pull request offline from files: --diff pr.diff --meta meta.json, plus --repo ./checkout for blast radius and --json for full output |
| cowboy tally | print this week's tally as a markdown table. --send sends it to Slack and/or email now |
| cowboy sign | print the webhook signature for a file, for testing: --secret S body.json |
| cowboy check-model | check your model server answers |
| cowboy outcomes | after merge, by pen. --check reads the git host now |
| cowboy evaluate | do the pens beat largest-first, most-files and chance? --json for every number |
| cowboy guide | instructions for coding agents from this repo's rules and habits: --repo owner/repo, --write AGENTS.md to put them in a file, --days 90 |
| cowboy audit verify | check the audit record wasn't changed |
| cowboy audit export | the audit record as CSV or JSON: --format csv --since 2026-01-01 --repo owner/repo |
| cowboy benchmark export | the counts-only summary, to share if you want. benchmark report a.json b.json pools several |
| cowboy version | print the version |
The dashboard also serves /healthz for health checks, outside the login, and /api/decisions as JSON for signed-in users.
The webhook secret doesn't match WEBHOOK_SECRET, or the content type isn't application/json. Fix it in the webhook settings and use GitHub's Redeliver button. Missed pull requests are caught by the sweep within 5 minutes either way.
Set UI_PASSWORD, or AUTH_PROXY_HEADER behind a sign-in proxy. See Settings.
Open the rules page: it says how blast radius is working for each repo, and why not if it isn't (for example, an archive over the size limit). See Blast radius.
Cowboy waits out short GitHub rate limits by itself. For longer ones it gives up on that call and the next catch-up sweep tries again.
The builds aren't signed yet. The install script handles this; if you downloaded the file yourself, run xattr -d com.apple.quarantine ./cowboy-darwin-arm64 once.
The link back from GitHub works once, within an hour. Go to /setup and press the button again. If GitHub already created an app, delete it in GitHub's settings first or give the new one a different name.
The app isn't installed on any repos yet. Open /setup and press pick repos.
Open an issue on GitHub with the log lines around the problem.
Cowboy is free and open source under the AGPL-3.0 license. Whatever's free today stays free.