5.3 KiB
Configuration
Secrets
WebHooker requires several secrets to function. For local development, store them in .dev.vars. For production, use Cloudflare Worker Secrets.
Required Secrets
| Variable | Description |
|---|---|
GITHUB_WEBHOOK_SECRET |
Webhook secret from your GitHub App settings |
GITHUB_APP_ID |
Numeric ID of your GitHub App |
GITHUB_PRIVATE_KEY |
App private key (PEM format, with \n escapes) |
GITHUB_CLIENT_ID |
OAuth client ID from App settings |
GITHUB_CLIENT_SECRET |
OAuth client secret from App settings |
DISCORD_TOKEN |
Discord bot token |
Optional Secrets
| Variable | Description | Default |
|---|---|---|
BASE_URL |
Public URL for OAuth callbacks | http://localhost:8787 |
ADMIN_USER_IDS |
Comma-separated GitHub user IDs (or logins) allowed to access the Web UI | Disabled |
DISCORD_GATEWAY_ENABLED |
Set to true to connect the Discord Gateway (bot online status); messaging works without it via REST |
false |
Web UI
WebHooker ships with a built-in config console at /admin for managing routes in the browser. It is protected by GitHub OAuth plus an admin whitelist.
Setup
- Configure
ADMIN_USER_IDSwith the GitHub user IDs allowed to manage routes. Logins are also accepted, e.g.ADMIN_USER_IDS=12345,RhenCloud. If unset, the console is disabled. - Open
/adminand sign in with GitHub. - Only users in the whitelist receive a session cookie; everyone else gets
403.
Endpoints
| Endpoint | Description |
|---|---|
GET /admin |
Config console UI |
GET /admin/login |
Start GitHub OAuth sign-in |
GET /admin/logout |
Destroy session |
GET /admin/api/routes |
List routes (admin only) |
PUT /admin/api/routes |
Replace routes (admin only) |
The console lets you add, edit, delete, and toggle routes. Saved routes are written to KV config:routes immediately and the config cache is invalidated so the webhook pipeline picks them up on the next run.
Routes
Routes define which events get forwarded to which Discord channels. They are stored in Cloudflare KV under the key config:routes as a JSON array.
There are no default routes — each route must define its own target. If no routes are configured, no events are forwarded.
Route Schema
{
"id": "unique-route-id",
"name": "Human-readable name",
"enabled": true,
"filters": [
{ "type": "event", "match": "push" },
{ "type": "repo", "match": "org/repo", "exclude": false }
],
"target": {
"channelId": "REQUIRED_CHANNEL_ID",
"threadId": "OPTIONAL_THREAD_ID"
}
}
target.channelId is required and used as-is; there is no fallback to a default channel.
Custom Route Example
[
{
"id": "backend-prs",
"name": "Backend PRs",
"enabled": true,
"filters": [
{ "type": "repo", "match": "myorg/backend" },
{ "type": "event", "match": "pull_request" },
{ "type": "actor", "match": "[bot]", "exclude": true }
],
"target": {
"channelId": "1234567890",
"threadId": "9876543210"
}
}
]
Filter Types
| Type | Matches | Example |
|---|---|---|
event |
GitHub event name | push, pull_request, issues |
repo |
Repository full name | org/repo |
actor |
Sender login | username, [bot] |
action |
Event action | opened, closed, published |
branch |
Branch name | main, feature/* |
keyword |
Text in payload body | deploy, /fix\s+\d+/ (regex) |
Filter Behavior
- All filters in a route must match for the route to trigger (AND logic)
- Set
"exclude": trueon any filter to invert it (NOT logic) keywordfilter supports regex patterns — falls back to substring match if regex is invalidbranchfilter works for push, pull_request, create/delete, workflow_run, and code_scanning_alert events
Match Values
Filters accept either a single string or an array of strings:
{ "type": "event", "match": "push" }
{ "type": "event", "match": ["push", "pull_request"] }
KV Storage Layout
| Key Pattern | Value | TTL |
|---|---|---|
config:routes |
JSON array of routes | Permanent |
session:{id} |
Admin session { userId, login } |
7 days |
token:{userId} |
{ accessToken, expiresAt } |
Until expiry |
state:{hex} |
{ userId, createdAt } |
600 seconds |