The project is in a healthy, maintained state
Adds a fully-featured, customisable bug and feature reporting flow to any Rails app: a schema-driven form, screenshot uploads, a my-reports list, and signed closure webhooks from the companion bug-reports API which files GitHub issues.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 8.0
 Project Readme

Bug Reports

A small, self-hostable bug-reporting service for teams running multiple apps. Your applications submit bug reports and feature requests to this API; it files them as GitHub issues on the right repository, and calls the source app back with a signed webhook when the issue is closed - so reporters can be told "your report has been resolved".

Built with Ruby on Rails 8.1 as an API-only application. Pairs with bug_reports_client - a mountable Rails engine that gives any Rails app a polished, schema-driven report form, screenshot uploads, a my-reports page and resolved-report alerts with a few lines of setup.

Your app (bug_reports_client gem)          This API                 GitHub
  submit form ── POST /api/bug_reports ──▶ persist + resolve repo
                                           CreateGithubIssueJob ──▶ issue created
                                                                    issue closed
                                           POST /api/webhooks ◀──── webhook
  signed callback ◀── NotifySourceAppJob ─ mark closed
  "your report was resolved" banner

Requirements

  • Ruby 3.3+ (see .ruby-version), PostgreSQL 14+
  • A GitHub App (recommended) or a personal access token that can create issues on your repositories

Setup

bundle install
cp .env.example .env       # then fill in the GitHub credentials
bin/rails db:prepare       # creates the database and a demo API key in dev
bin/rails server -p 3002

GitHub credentials

Two options, configured via environment variables (see .env.example):

  1. GitHub App (recommended) - issues are created by a bot identity, not a personal account. Create an app under your organisation (Settings -> Developer settings -> GitHub Apps) with Issues: read & write repository permission and the Issues webhook event, install it on every repository that should receive issues, then set GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID and GITHUB_APP_PRIVATE_KEY (the PEM contents).
  2. Personal access token - set GITHUB_TOKEN (fine-grained, Issues read/write on the target repos). Used automatically when no app is configured.

Point the GitHub (App or repository) webhook at https://<your-host>/api/webhooks for Issues events, and set the same secret in GITHUB_WEBHOOK_SECRET - inbound webhooks are verified against X-Hub-Signature-256.

Onboarding an application

Each consuming app is one ApiKey record - no config files, no deploys:

ApiKey.create!(name: "myapp", github_repo: "my-org/myapp")

The record carries everything the app needs: token (Bearer auth), webhook_secret (verifying closure callbacks) and the GitHub repository its reports are filed on. An app may only create/update reports whose source matches its own key name.

If the app is Rails, install the bug_reports_client gem and set BUG_REPORT_API_URL, BUG_REPORT_API_KEY, BUG_REPORT_WEBHOOK_SECRET and APP_HOST - everything else is provided.

API

All endpoints are under /api. Bearer token auth everywhere except the GitHub webhook receiver.

Method Path Auth Purpose
POST /api/bug_reports Bearer token Create a report (returns 202 + id)
GET /api/bug_reports Bearer token List reports
GET /api/bug_reports/:id Bearer token Show a report
PATCH /api/bug_reports/:id Bearer token Update a report (re-syncs the issue)
POST /api/error_reports Bearer token Automatic 500 capture, deduplicated by fingerprint
POST /api/webhooks GitHub HMAC Receive GitHub issue events

Error reports (report_type: "error") are machine-generated 500 captures from the client engine: no reporter details or callback URL, deduplicated by source + fingerprint - repeats of an open error bump its occurrence_count rather than filing duplicate issues, and a recurrence after the issue is closed files a fresh one. Deleting an issue on GitHub (rather than closing it) is also treated as terminal: the report closes and a recurring error files a fresh issue.

Issue types and labels

Issues are created with a GitHub issue type matching the report (bug/feature), plus labels: bug-report/feature-request/ error-report for provenance, severity:* for bugs and errors, and external-user when the reporter is outside your team.

GitHub validates issue types against your organisation's configured list (Settings -> Issue types) and rejects unknown values, so error captures default to the bug type - the error-report label distinguishes them. If you'd like errors as a first-class type: create one in your organisation's issue-type settings (e.g. "Error"), then set GITHUB_ERROR_ISSUE_TYPE=Error and error captures will use it.

curl -X POST http://localhost:3002/api/bug_reports \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "bug_report": {
      "title": "Login page returns 500",
      "description": "Markdown body for the GitHub issue.",
      "severity": "high",
      "report_type": "bug",
      "source": "myapp",
      "reporter_email": "someone@example.com",
      "reporter_name": "Someone",
      "callback_url": "https://myapp.example.com/bug_reports/webhook"
    }
  }'

severity: low|medium|high|critical (required for bugs). report_type: bug|feature. callback_url must be public HTTPS - it is validated at submission time and again (with DNS resolution, blocking private/loopback addresses) before every callback.

Closure callbacks

When an issue is closed on GitHub, the source app's callback_url receives a JSON POST signed with that app's webhook_secret:

  • X-Timestamp - unix seconds, and
  • X-Signature-Timestamped: sha256=<hex> - HMAC-SHA256 over "<timestamp>.<body>" (receivers should reject stale timestamps), plus
  • X-Signature: sha256=<hex> - legacy HMAC over the body alone, kept for older receivers.

The bug_reports_client engine implements the receiving side, including replay protection.

Development

  • GitHub dry-run: in development (or with GITHUB_DRY_RUN=true), issue creation/updates are logged instead of sent - nothing touches GitHub. Inspect the exact would-be payload for stored reports with bin/rails bug_reports:preview (LIMIT=25 or ID=3).
  • Local webhooks: gh extension install cli/gh-webhook, then gh webhook forward --repo=<org>/<repo> --events=issues --secret=$GITHUB_WEBHOOK_SECRET --url=http://localhost:3002/api/webhooks.
  • Tests: bin/rails test (COVERAGE=1 for SimpleCov). The client engine has its own suite: cd client && bundle exec rake test.
  • Linting/scanning: bin/rubocop, bin/brakeman.

Stack

Rails 8.1 API-only, PostgreSQL, Solid Queue/Cache/Cable, Octokit. Background jobs retry 5 times with polynomial backoff. This repository also contains the bug_reports_client engine gem under client/.

Deployment

Any Rails-friendly host works. For Fly.io, copy fly.toml.example to fly.toml (gitignored - deployment config is per-installation), set your app name, add the environment variables as secrets, and fly deploy.

Licence

MIT - see LICENSE.