A Django-based GitHub App for intelligent notification filtering. HubSnub listens to GitHub webhooks, evaluates your notification preferences, and unsubscribes you from threads you don't care about — like team-only review requests when you weren't personally asked.
GitHub's email notifications lack the granularity of the Slack integration. There's no way to distinguish between "your review was personally requested" and "a team you're on was requested." This creates noise for developers on teams auto-assigned via CODEOWNERS.
- GitHub sends webhook events to HubSnub
- HubSnub evaluates each event against your preferences
- If the event is low-priority (e.g., team-only review request), HubSnub unsubscribes you from that thread
- GitHub's own email system handles all delivery — HubSnub only controls what you're subscribed to
- Python 3.10+
- A GitHub account
- A server with a public HTTPS endpoint (for webhook delivery)
git clone https://github.com/jonocodes/HubSnub.git
cd HubSnub
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your credentials
python manage.py migrate
python manage.py createsuperuser
python manage.py runservercp .env.example .env
# Edit .env with your credentials and add your Docker host if needed
docker compose up --buildThe app will be available at http://localhost:8000. Database data is stored in the named hubsnub_data volume, and migrations run automatically when the container starts.
To create an admin user:
docker compose run --rm web python manage.py createsuperuser| Variable | Description |
|---|---|
GITHUB_APP_ID |
GitHub App ID |
GITHUB_WEBHOOK_SECRET |
Secret for verifying webhook signatures |
GITHUB_PRIVATE_KEY_PATH |
Path to GitHub App private key .pem file |
GITHUB_PAT |
Personal Access Token with notifications scope |
GITHUB_USERNAME |
Your GitHub login (e.g., jonocodes) |
DJANGO_SECRET_KEY |
Django secret key |
ALLOWED_HOSTS |
Comma-separated list of allowed hosts |
DEBUG |
Django debug mode (True for local dev) |
SQLITE_PATH |
Optional SQLite file path inside the container/app |
- Go to Settings → Developer settings → GitHub Apps → New GitHub App
- Set the webhook URL to
https://your-domain.com/webhooks/github/ - Generate a webhook secret and add it to your
.env - Set permissions:
- Pull requests: Read
- Issues: Read
- Metadata: Read
- Subscribe to events:
pull_requestpull_request_reviewpull_request_review_commentissue_commentissues
- Generate a private key, download the
.pemfile, and setGITHUB_PRIVATE_KEY_PATH - Install the app on your organization/repos
HubSnub needs a PAT with the notifications scope to manage your thread subscriptions (GitHub App tokens can't do this).
- Go to Settings → Developer settings → Personal access tokens → Tokens (classic)
- Create a token with the
notificationsscope - Add it to your
.envasGITHUB_PAT
Email notifications must be enabled. HubSnub controls your thread subscriptions — GitHub sends emails based on those.
At github.com/settings/notifications:
- Participating: Email ✅
- Watching: Email ✅
Do NOT disable team notifications at the org/team level. HubSnub needs the webhook events that team review requests generate.
- At
github.com/orgs/{org}/teams/{team}/settings→ Team notifications → keep Enabled - Code review settings → do NOT enable "Only notify requested team members" (let HubSnub handle the filtering)
Watch repos normally. HubSnub filters noise from repos in your Watched Repos list. Repos not managed by HubSnub behave as usual.
Access the admin at /admin/ to:
- Notification Preferences — configure which notification types to allow or suppress
- Watched Repos — manage which repos HubSnub filters, with optional per-repo overrides
- Notification Log — view a log of every webhook event and what HubSnub decided
- Dry Run — preview what HubSnub would do with your current notifications (no actions taken)
- Cache Viewer — inspect the in-memory thread ID cache
pip install gunicorn
gunicorn hubsnub.wsgi:application --bind 0.0.0.0:8000Use nginx or Caddy as a reverse proxy to provide HTTPS.
python manage.py test notifications