A real-time GitHub repository activity monitor built with Cloudflare Agents. This example demonstrates how to handle webhooks with Agents, verify signatures, store events in SQLite, and stream updates to connected clients.
- Webhook Handling - Receive and process GitHub webhooks
- Signature Verification - HMAC-SHA256 verification of webhook payloads
- Agent-per-Repository - Each repo gets its own isolated agent instance
- Real-time Updates - WebSocket connection streams events as they arrive
- Event History - Events stored in SQLite for persistence
- Beautiful Dashboard - Dark-themed UI with live event feed
npm installCopy .dev.vars.example to .dev.vars and set your webhook secret:
cp .dev.vars.example .dev.varsEdit .dev.vars:
GITHUB_WEBHOOK_SECRET=your-secret-here
npm startSince GitHub needs to reach your webhook endpoint, use a tool like ngrok:
ngrok http 5173Copy the ngrok URL (e.g., https://abc123.ngrok.io).
- Go to your GitHub repository → Settings → Webhooks
- Click Add webhook
- Configure:
- Payload URL:
https://your-ngrok-url.ngrok.io/webhooks/github/owner/repo - Content type:
application/json - Secret: Same value as
GITHUB_WEBHOOK_SECRET - Events: Select which events to receive (or "Send me everything")
- Payload URL:
- Click Add webhook
Open http://localhost:5173 in your browser, enter your repository name (e.g., cloudflare/agents), and click Connect.
GitHub → POST /webhooks/github/owner/repo → Worker → RepoAgent (Durable Object)
↓
Browser ← WebSocket ← Agent broadcasts state updates ←─────┘
-
Webhook Routing
// Route webhooks to the right agent based on repository const agentName = sanitizeRepoName(payload.repository.full_name); const agent = await getAgentByName(env.RepoAgent, agentName); return agent.fetch(request);
-
Signature Verification
// Verify GitHub's HMAC-SHA256 signature const key = await crypto.subtle.importKey( "raw", secret, { name: "HMAC", hash: "SHA-256" }, false, ["sign"] ); const signature = await crypto.subtle.sign("HMAC", key, payload);
-
Event Storage in SQLite
this.sql`INSERT INTO events (id, type, title, ...) VALUES (...)`;
-
Real-time State Broadcasting
- When a webhook arrives, the agent updates its state
- Connected clients receive the update via WebSocket
| Event Type | Description |
|---|---|
push |
Commits pushed to a branch |
pull_request |
PR opened, closed, merged, etc. |
issues |
Issue opened, closed, labeled, etc. |
issue_comment |
Comment on an issue or PR |
star |
Repository starred/unstarred |
fork |
Repository forked |
release |
Release published |
ping |
Webhook configured |
npm run deployAfter deploying:
-
Set the webhook secret in Cloudflare:
wrangler secret put GITHUB_WEBHOOK_SECRET
-
Update your GitHub webhook URL to your deployed worker URL
Ideas for enhancements:
- AI PR Summaries - Use OpenAI to summarize PR diffs
- Slack Notifications - Forward important events to Slack
- Multi-Repo Dashboard - Monitor all your repos in one view
- Custom Alerts - Schedule reminders for stale PRs
- Webhook Replay - Re-send events for testing
examples/github-webhook/
├── src/
│ ├── server.ts # RepoAgent + webhook routing
│ ├── client.tsx # React dashboard
│ ├── github-types.ts # TypeScript types for GitHub payloads
│ └── styles.css # Dashboard styles
├── public/
│ └── normalize.css
├── index.html
├── wrangler.jsonc
└── package.json