diff --git a/README.md b/README.md
index b6d6e85..73e73d6 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,34 @@
Troutbot is the final solution to protecting the trout population. It's
environmental protection incarnate!
-Well in reality, it's a GitHub webhook bot that analyzes issues and pull
-requests using real signals such as CI check results, diff quality, and body
-structure and then posts trout-themed comments about the findings. Now you know
-whether your changes hurt or help the trout population.
+Well, in reality, it is a GitHub bot that analyzes issues and pull requests
+using real signals such as CI check results, diff quality, and body structure
+and then posts trout-themed comments about the findings. Now you know whether
+your changes hurt or help the trout population.
+
+## Operation Modes
+
+Troutbot supports two operation modes:
+
+### Webhook Mode (Real-time)
+
+GitHub sends webhook events to troutbot when issues/PRs are opened or updated.
+Troutbot responds immediately. Best for:
+
+- Single or few repositories
+- You have admin access to configure webhooks
+- You can expose a public endpoint
+
+### Polling Mode (Periodic)
+
+Troutbot periodically polls configured repositories for `@troutbot` mentions in
+comments. Best for:
+
+- Monitoring dozens of repositories without webhook setup
+- Running behind a firewall or on dynamic IPs
+- Simplified deployment without webhook secrets
+
+Both modes use the same analysis engine and produce the same results.
## Quick Start
@@ -17,18 +41,17 @@ $ npm install
# Populate the environment config
$ cp .env.example .env
-# Set up application confg
+# Set up application config
cp config.example.ts config.ts
-# Edit .env and config.ts, then to start:
-npm run build && npm start
+# Edit .env and config.ts, then build and start.
+# If `.env` is not populated, Troutbot will start in dry-run mode.
+pnpm run build && pnpm start
```
## How It Works
-Troutbot has three analysis backends ran against each incoming webhook event.
-They are the primary decisionmaking logic behind whether your changes affect the
-trout population negatively, or positively.
+Troutbot has three analysis backends that analyze issues and PRs:
### `checks`
@@ -71,6 +94,75 @@ checks 0.4, diff 0.3, quality 0.3). Backends that return zero confidence (e.g.,
no CI checks found yet) are excluded from the average. If combined confidence
falls below `confidenceThreshold`, the result is forced to neutral.
+## Webhook Mode
+
+In webhook mode, troutbot receives real-time events from GitHub.
+
+### GitHub Webhook Setup
+
+1. Go to your repository's **Settings > Webhooks > Add webhook**
+2. **Payload URL**: `https://your-host/webhook`
+3. **Content type**: `application/json`
+4. **Secret**: Generate with `openssl rand -hex 32` and set as `WEBHOOK_SECRET`
+5. **Events**: Select **Issues**, **Pull requests**, and optionally **Check
+ suites** (for re-analysis when CI finishes)
+
+If you enable **Check suites** and set `response.allowUpdates: true` in your
+config, troutbot will update its comment on a PR once CI results are available.
+
+### Webhook Security
+
+- **`WEBHOOK_SECRET` is strongly recommended.** Without it, anyone who can reach
+ the `/webhook` endpoint can trigger analysis and post comments. Always set a
+ secret and configure the same value in your GitHub webhook settings.
+
+## Polling Mode
+
+In polling mode, troutbot periodically checks configured repositories for
+`@troutbot` mentions in comments.
+
+### Configuration
+
+Enable polling in your `config.ts`:
+
+```typescript
+polling: {
+ enabled: true,
+ intervalMinutes: 5, // Check every 5 minutes
+ lookbackMinutes: 10, // Look back 10 minutes for new comments
+}
+```
+
+### How It Works
+
+1. On startup, troutbot fetches recent comments from all configured repositories
+2. It scans each comment for `@troutbot` mentions
+3. When found, it analyzes the associated issue/PR and posts a response
+4. Processed comments are tracked to avoid duplicate responses
+5. The cycle repeats every `intervalMinutes`
+
+### On-Demand Analysis
+
+Users can trigger analysis by mentioning `@troutbot` in any comment:
+
+```plaintext
+Hey @troutbot, can you take a look at this?
+```
+
+The bot will analyze the issue/PR and respond with a trout-themed assessment.
+
+### Rate Limiting
+
+Polling uses the GitHub REST API and respects rate limits. The default settings
+(5 min interval, 10 min lookback) are conservative and work well within GitHub's
+5000 requests/hour limit for personal access tokens.
+
+### Requirements
+
+- `GITHUB_TOKEN` with read access to all watched repositories
+- Repositories configured in `config.repositories`
+- Write access to post comments
+
## GitHub Account & Token Setup
Troutbot is designed to run as a dedicated bot account on GitHub. Create a
@@ -89,8 +181,7 @@ The bot account needs access to every repository it will comment on:
- **For organization repos**: Invite the bot account as a collaborator with
**Write** access, or add it to a team with write permissions.
- **For personal repos**: Add the bot account as a collaborator under
- \*\*Settings
- > Collaborators\*\*.
+ `Settings > Collaborators`.
The bot needs write access to post comments. Read access alone is not enough.
@@ -98,10 +189,10 @@ The bot needs write access to post comments. Read access alone is not enough.
Log in as the bot account and create a fine-grained PAT:
-1. Go to **Settings > Developer settings > Personal access tokens > Fine-grained
- tokens**
+1. Go to
+ `Settings > Developer settings > Personal access tokens > Fine-grained tokens`
2. Click **Generate new token**
-3. Set a descriptive name (e.g., `troutbot-webhook`)
+3. Set a descriptive name (e.g., `troutbot-production`)
4. Set **Expiration** - pick a long-lived duration or no expiration, since this
runs unattended
5. Under **Repository access**, select the specific repositories the bot will
@@ -121,19 +212,7 @@ Set this as the `GITHUB_TOKEN` environment variable.
> `repo` scope. Fine-grained tokens are recommended because they follow the
> principle of least privilege.
-### 4. Generate a webhook secret
-
-Generate a random secret to verify webhook payloads:
-
-```bash
-openssl rand -hex 32
-```
-
-Set this as the `WEBHOOK_SECRET` environment variable, and use the same value
-when configuring the webhook in GitHub (see
-[GitHub Webhook Setup](#github-webhook-setup)).
-
-## Configuration
+## Configuring Troutbot
### Environment Variables
@@ -142,7 +221,7 @@ when configuring the webhook in GitHub (see
| Variable | Description | Required |
| ---------------- | ----------------------------------------------------- | ---------------------------- |
| `GITHUB_TOKEN` | Fine-grained PAT from the bot account (see above) | No (dry-run without it) |
-| `WEBHOOK_SECRET` | Secret for verifying webhook signatures | No (skips verification) |
+| `WEBHOOK_SECRET` | Secret for verifying webhook signatures | No (only for webhook mode) |
| `PORT` | Server port (overrides `server.port` in config) | No |
| `CONFIG_PATH` | Path to config file | No (defaults to `config.ts`) |
| `LOG_LEVEL` | Log level override (`debug`, `info`, `warn`, `error`) | No |
@@ -156,10 +235,11 @@ default-exports a `Config` object - full type checking and autocompletion in
your editor.
```typescript
-import type { Config } from "./src/types";
+import type { Config } from './src/types';
const config: Config = {
server: { port: 3000 },
+ repositories: [{ owner: 'myorg', repo: 'myrepo' }],
engine: {
backends: {
checks: { enabled: true },
@@ -169,6 +249,11 @@ const config: Config = {
weights: { checks: 0.4, diff: 0.3, quality: 0.3 },
confidenceThreshold: 0.1,
},
+ polling: {
+ enabled: true,
+ intervalMinutes: 5,
+ lookbackMinutes: 10,
+ },
// ...
};
@@ -180,28 +265,13 @@ pre-compilation needed.
See `config.example.ts` for the full annotated reference.
-## GitHub Webhook Setup
-
-1. Go to your repository's **Settings > Webhooks > Add webhook**
-2. **Payload URL**: `https://your-host/webhook`
-3. **Content type**: `application/json`
-4. **Secret**: Must match your `WEBHOOK_SECRET` env var
-5. **Events**: Select **Issues**, **Pull requests**, and optionally **Check
- suites** (for re-analysis when CI finishes)
-
-If you enable **Check suites** and set `response.allowUpdates: true` in your
-config, troutbot will update its comment on a PR once CI results are available.
-
## Production Configuration
When deploying troutbot to production, keep the following in mind:
-- **`WEBHOOK_SECRET` is strongly recommended.** Without it, anyone who can reach
- the `/webhook` endpoint can trigger analysis and post comments. Always set a
- secret and configure the same value in your GitHub webhook settings.
-- **Use a reverse proxy with TLS.** GitHub sends webhook payloads over HTTPS.
- Put nginx, Caddy, or a cloud load balancer in front of troutbot and terminate
- TLS there.
+- **Use a reverse proxy with TLS.** If using webhook mode, GitHub sends payloads
+ over HTTPS. Put nginx, Caddy, or a cloud load balancer in front of troutbot
+ and terminate TLS there. Polling mode doesn't require a public endpoint.
- **Set `NODE_ENV=production`.** This is set automatically in the Docker image.
For standalone deployments, export it in your environment. Express uses this
to enable performance optimizations.
@@ -218,22 +288,19 @@ When deploying troutbot to production, keep the following in mind:
## Deployment
-
-Standalone (Node.js)
+### Standalone (Node.js)
```bash
npm ci
npm run build
export NODE_ENV=production
export GITHUB_TOKEN="ghp_..."
-export WEBHOOK_SECRET="your-secret"
+# Only needed for webhook mode:
+# export WEBHOOK_SECRET="your-secret"
npm start
```
-
-
-
-Nix
+### Nix
**Flake** (NixOS or flake-enabled systems):
@@ -248,8 +315,8 @@ npm start
{
services.troutbot = {
enable = true;
- environmentFile = "/path/to/.env"; # use Agenix if possible
- configPath = "/path/to/config.ts" # use Agenix if possible
+ environmentFile = "/path/to/.env";
+ configPath = "/path/to/config.ts";
};
}
];
@@ -264,10 +331,7 @@ npm start
nix run github:notashelf/troutbot
```
-
-
-
-Docker
+### Docker
```bash
docker build -t troutbot .
@@ -275,7 +339,6 @@ docker run -d \
--name troutbot \
-p 127.0.0.1:3000:3000 \
-e GITHUB_TOKEN="ghp_..." \
- -e WEBHOOK_SECRET="your-secret" \
-v $(pwd)/config.ts:/app/config.ts:ro \
--restart unless-stopped \
troutbot
@@ -283,17 +346,14 @@ docker run -d \
Multi-stage build, non-root user, built-in health check, `STOPSIGNAL SIGTERM`.
-
-
-
-Docker Compose
+### Docker Compose
```yaml
services:
troutbot:
build: .
ports:
- - "127.0.0.1:3000:3000"
+ - '127.0.0.1:3000:3000'
env_file: .env
volumes:
- ./config.ts:/app/config.ts:ro
@@ -305,20 +365,17 @@ services:
logging:
driver: json-file
options:
- max-size: "10m"
- max-file: "3"
+ max-size: '10m'
+ max-file: '3'
```
-
-
-
-systemd
+### Systemd
Create `/etc/systemd/system/troutbot.service`:
```ini
[Unit]
-Description=Troutbot GitHub Webhook Bot
+Description=Troutbot GitHub Bot
After=network.target
[Service]
@@ -345,10 +402,9 @@ sudo systemctl daemon-reload
sudo systemctl enable --now troutbot
```
-
+### Reverse Proxy (nginx)
-
-Reverse Proxy (nginx)
+Only needed for webhook mode:
```nginx
server {
@@ -369,7 +425,7 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
}
- # Optional: nginx-level rate limiting
+ # Optional: nginx-level rate limiting for webhooks
# limit_req_zone $binary_remote_addr zone=webhook:10m rate=10r/s;
# location /webhook {
# limit_req zone=webhook burst=20 nodelay;
@@ -378,21 +434,23 @@ server {
}
```
-
-
## API Endpoints
+
+
| Method | Path | Description |
| -------- | ------------- | ---------------------------------------------------------------------------------------- |
| `GET` | `/health` | Health check - returns `status`, `uptime` (seconds), `version`, `dryRun`, and `backends` |
-| `POST` | `/webhook` | GitHub webhook receiver (rate limited) |
+| `POST` | `/webhook` | GitHub webhook receiver (rate limited, webhook mode only) |
| `GET` | `/dashboard` | Web UI dashboard with status, events, and config editor |
| `GET` | `/api/status` | JSON status: uptime, version, dry-run, backends, repo count |
-| `GET` | `/api/events` | Recent webhook events from the in-memory ring buffer |
+| `GET` | `/api/events` | Recent events from the in-memory ring buffer |
| `DELETE` | `/api/events` | Clear the event ring buffer |
| `GET` | `/api/config` | Current runtime configuration as JSON |
| `PUT` | `/api/config` | Partial config update: deep-merges, validates, and applies in-place |
+
+
## Dashboard & Runtime API
Troutbot ships with a built-in web dashboard and JSON API for monitoring and
@@ -405,9 +463,8 @@ running). The dashboard provides:
- **Status card** - uptime, version, dry-run state, active backends, and repo
count. Auto-refreshes every 30 seconds.
-- **Event log** - table of recent webhook events showing repo, PR/issue number,
- action, impact rating, and confidence score. Keeps the last 100 events in
- memory.
+- **Event log** - table of recent events showing repo, PR/issue number, action,
+ impact rating, and confidence score. Keeps the last 100 events in memory.
- **Config editor** - read-only JSON view of the current runtime config with an
"Edit" toggle that lets you modify and save changes without restarting.
@@ -441,8 +498,8 @@ original config remains unchanged if validation fails.
### Event Buffer API
-The event buffer stores the last 100 processed webhook events in memory. Events
-are lost on restart.
+The event buffer stores the last 100 processed events in memory (from both
+webhooks and polling). Events are lost on restart.
```bash
# List recent events