{"id":34547811,"url":"https://github.com/mensfeld/hookshot","last_synced_at":"2026-04-24T10:04:42.192Z","repository":{"id":329920200,"uuid":"1120961992","full_name":"mensfeld/hookshot","owner":"mensfeld","description":"A self-hosted webhook relay service built with Rails 8. Receives webhooks, filters them based on configurable rules, and dispatches to multiple target endpoints.","archived":false,"fork":false,"pushed_at":"2025-12-23T11:44:38.000Z","size":339,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-12-23T20:54:25.423Z","etag":null,"topics":["rails","ruby","webhook-receiver","webhook-relay","webhook-server","webhooks","webhooks-catcher"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mensfeld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-22T08:10:28.000Z","updated_at":"2025-12-23T11:44:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mensfeld/hookshot","commit_stats":null,"previous_names":["mensfeld/hookshot"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mensfeld/hookshot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mensfeld%2Fhookshot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mensfeld%2Fhookshot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mensfeld%2Fhookshot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mensfeld%2Fhookshot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mensfeld","download_url":"https://codeload.github.com/mensfeld/hookshot/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mensfeld%2Fhookshot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27997307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rails","ruby","webhook-receiver","webhook-relay","webhook-server","webhooks","webhooks-catcher"],"created_at":"2025-12-24T07:31:25.941Z","updated_at":"2026-04-24T10:04:42.186Z","avatar_url":"https://github.com/mensfeld.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hookshot\n\n[![CI](https://github.com/mensfeld/hookshot/actions/workflows/ci.yml/badge.svg)](https://github.com/mensfeld/hookshot/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/badge/coverage-85%25+-brightgreen)](https://github.com/mensfeld/hookshot)\n\nA self-hosted webhook relay service built with Rails 8. Receives webhooks, filters them based on configurable rules, and dispatches to multiple target endpoints.\n\n![Hookshot Screenshot](misc/screenshot.png)\n\n## Quick Start\n\n```bash\n# Clone and install\ngit clone https://github.com/mensfeld/hookshot.git\ncd hookshot\nbundle install\nnpm install\n\n# Setup database and assets\nrails db:create db:migrate\nrails tailwindcss:build\n\n# Start everything (web + background jobs)\n./bin/dev\n\n# Or start separately:\n# rails server          # Web server on port 3000\n# rails solid_queue:start  # Background job processor\n```\n\nThen:\n1. Visit `http://localhost:3000/admin/targets` (login: `admin` / `changeme`)\n2. Create a target with your destination URL\n3. Send webhooks to `http://localhost:3000/webhooks/receive`\n\n## Features\n\n- **Webhook Reception**: Accepts POST requests at `/webhooks/receive` and stores headers, payload, and metadata\n- **Multiple Targets**: Configure multiple destination endpoints for webhook delivery\n- **Filtering**: Route webhooks to specific targets based on header or payload content\n- **Background Processing**: Reliable delivery with Solid Queue, including retries with exponential backoff\n- **Admin Dashboard**: View webhooks, dispatches, and manage targets with a clean DaisyUI interface\n- **Replay**: Re-dispatch any webhook to all active targets\n- **Health Check**: `/health` endpoint for monitoring\n\n## Requirements\n\n- Ruby 3.4+\n- SQLite 3\n- Node.js (for Tailwind CSS compilation)\n\n## Setup\n\n```bash\n# Install dependencies\nbundle install\nnpm install\n\n# Setup database\nrails db:create db:migrate\n\n# Compile assets\nrails tailwindcss:build\n\n# Start the server\nrails server\n```\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `TZ` | `UTC` | Timezone for displaying timestamps (e.g., `Europe/Warsaw`, `America/New_York`) |\n| `HOOKSHOT_USER` | `admin` | HTTP Basic Auth username for admin UI |\n| `HOOKSHOT_PASSWORD` | `changeme` | HTTP Basic Auth password for admin UI |\n| `RETENTION_DAYS` | `30` | Days to retain webhook data before cleanup |\n\n### Background Jobs\n\nStart Solid Queue to process webhook deliveries:\n\n```bash\nrails solid_queue:start\n```\n\nOr run everything with Foreman/Overmind using the Procfile.\n\n## Usage\n\n### Receiving Webhooks\n\nSend any POST request to `/webhooks/receive`:\n\n```bash\ncurl -X POST http://localhost:3000/webhooks/receive \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event\": \"user.created\", \"data\": {\"id\": 123}}'\n```\n\n### Admin Dashboard\n\nAccess the admin UI at `http://localhost:3000/admin/webhooks` with HTTP Basic Auth.\n\n- **Webhooks**: View received webhooks, inspect headers/payload, replay to targets\n- **Dispatches**: Monitor delivery status, retry failed deliveries\n- **Targets**: Configure destination endpoints with filters\n- **Jobs**: Solid Queue dashboard at `/jobs`\n\n### Configuring Targets\n\nEach target has:\n\n- **Name**: Identifier for the target\n- **URL**: Destination endpoint (must be HTTPS in production)\n- **Timeout**: Request timeout in seconds (1-300)\n- **Active**: Toggle to enable/disable delivery\n- **Custom Headers**: Additional headers to send with each request\n- **Filters**: Rules to determine which webhooks to deliver\n\n### Filters\n\nFilters allow routing webhooks to specific targets. All filters must match for delivery.\n\n**Filter Types:**\n- `Header`: Match against request headers\n- `Payload`: Match against JSON payload using dot notation (e.g., `$.event`)\n\n**Operators:**\n- `Exists`: Field is present\n- `Equals`: Field equals exact value\n- `Matches`: Field matches pattern (supports `*` wildcard)\n\n**Example**: Only deliver webhooks where `$.event` equals `user.created`:\n- Type: `Payload`\n- Field: `$.event`\n- Operator: `Equals`\n- Value: `user.created`\n\n## API Endpoints\n\n| Method | Path | Auth | Description |\n|--------|------|------|-------------|\n| POST | `/webhooks/receive` | None | Receive incoming webhooks |\n| GET | `/health` | None | Health check endpoint |\n| GET | `/admin/*` | Basic | Admin dashboard |\n| GET | `/jobs` | Basic | Solid Queue dashboard |\n\n## Delivery Headers\n\nEach delivery includes these headers:\n\n- `Content-Type`: Original webhook content type\n- `X-Hookshot-Webhook-Id`: Internal webhook ID\n- `X-Hookshot-Delivery-Id`: Internal delivery ID\n- Any custom headers configured on the target\n\n## Retry Behavior\n\nFailed deliveries are retried with exponential backoff:\n\n- Up to 5 attempts\n- Increasing delay between retries\n- Client errors (4xx) are not retried\n- Server errors (5xx) and timeouts are retried\n\n## Docker Deployment\n\nSingle container runs both web server and background job processor:\n\n```bash\n# Build image\ndocker build -t hookshot .\n\n# Run with docker-compose\nSECRET_KEY_BASE=$(rails secret) HOOKSHOT_PASSWORD=your-password docker-compose up -d\n\n# Or run directly\ndocker run -d \\\n  -p 3000:3000 \\\n  -v hookshot_data:/rails/storage \\\n  -e SECRET_KEY_BASE=$(rails secret) \\\n  -e HOOKSHOT_PASSWORD=your-password \\\n  --name hookshot \\\n  hookshot\n```\n\nData is persisted in the `hookshot_data` volume.\n\n## License\n\nMIT - see [LICENSE.md](LICENSE.md) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmensfeld%2Fhookshot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmensfeld%2Fhookshot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmensfeld%2Fhookshot/lists"}