The github-bot service (services/github-bot/) receives GitHub webhooks and creates Netclode sessions to handle them. It reuses the same GitHub App as repo access (see github-integration.md).
Comment @netclode <request> on any PR or issue. The bot:
- Verifies the commenter has write/admin access to the repo
- Posts a "looking into it" comment
- Creates a sandbox session with the repo cloned
- Sends a prompt with PR diff (or issue body), comment thread, and the user's request
- Updates the comment with the agent's response
- Deletes the session
Works on PR comments, issue comments, and PR review comments.
When Dependabot or Renovate opens/updates a PR, the bot automatically:
- Creates a sandbox session
- Sends a prompt instructing the agent to:
- Inspect what changed in the dependency (module cache, git diff between tags)
- Find impacted code paths in the repo
- Check CI status (or run tests locally if no CI)
- Render a verdict: Safe to merge, Needs review, or Issues found
- Posts the review as a comment
Triggered by dependabot[bot] and renovate[bot]/renovate authors only.
Comment @netclode /review-dep-bump on any PR to manually trigger the dependency review workflow, regardless of PR author.
Environment variables (set in k8s deployment):
| Variable | Default | Description |
|---|---|---|
CONTROL_PLANE_URL |
(required) | Control-plane endpoint (Connect protocol over h2c) |
REDIS_URL |
(required) | Redis for dedup + session tracking |
GITHUB_APP_ID |
(required) | GitHub App ID |
GITHUB_APP_PRIVATE_KEY |
(required) | PEM-encoded private key (raw, not base64) |
GITHUB_INSTALLATION_ID |
(required) | GitHub App installation ID |
GITHUB_WEBHOOK_SECRET |
(required) | Webhook signature verification |
MODEL |
claude-opus-4-6 |
LLM model |
SDK_TYPE |
claude |
SDK: claude, opencode, copilot, codex |
MAX_CONCURRENT_GITHUB_SESSIONS |
5 |
Max concurrent sessions |
SESSION_TIMEOUT |
10m |
Per-session timeout |
PORT |
8080 |
HTTP listen port |
GitHub webhook
│
▼
github-bot (POST /webhook)
│
├── Verify signature
├── Dedup check (Redis)
├── Access control (GitHub API permission check)
├── Return 200 immediately
│
└── Async workflow:
├── Post "thinking" comment
├── Create session via control-plane (Connect bidi stream, h2c)
│ └── initial_prompt sends the prompt automatically
├── Stream until RUNNING → READY transition
├── Collect last assistant message (discard intermediate narration)
├── Update comment with response
└── Delete session
Redis is used for:
- Webhook deduplication: prevents processing the same delivery twice
- In-flight session tracking: maps
deliveryID → {sessionID, owner, repo, number, commentID}for crash recovery
On startup, the bot recovers any orphaned sessions from Redis.
The bot reuses the existing GitHub App. Additional requirements:
- Enable webhooks in the app settings
- Set webhook URL to
https://netclode-github-bot-ingress.tail527cb.ts.net/webhook - Set webhook secret (same as
GITHUB_WEBHOOK_SECRET) - Add permissions:
issues:write(for posting comments) - Subscribe to events:
issue_comment,pull_request,pull_request_review_comment
After adding new event subscriptions, the installation owner must accept updated permissions at https://github.com/settings/installations.
The webhook endpoint is exposed publicly via Tailscale Funnel. The control-plane connection uses h2c (HTTP/2 cleartext) for in-cluster Connect protocol bidi streaming.
See the Tailscale Funnel section in the Ansible README for ACL requirements.
# After CI builds the image
make rollout-github-botTo update the k8s manifest (env vars, resource limits):
cd infra/ansible
DEPLOY_HOST=your-server ansible-playbook playbooks/k8s-only.yaml