From ca0d2e989a88187a06fc99a5976243974512a51f Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 15:24:02 +0000 Subject: [PATCH 1/6] docs: add GitHub webhook to Discord integration guide (closes #83) --- .github/workflows/notify-discord.yml | 35 ++++++++++++ docs/github-webhook-integration.md | 79 ++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 .github/workflows/notify-discord.yml create mode 100644 docs/github-webhook-integration.md diff --git a/.github/workflows/notify-discord.yml b/.github/workflows/notify-discord.yml new file mode 100644 index 000000000..bba5618fd --- /dev/null +++ b/.github/workflows/notify-discord.yml @@ -0,0 +1,35 @@ +name: Notify Discord + +on: + pull_request: + types: [opened, reopened] + issues: + types: [opened] + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Send to Discord + env: + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + TITLE="${{ github.event.pull_request.title }}" + URL="${{ github.event.pull_request.html_url }}" + AUTHOR="${{ github.event.pull_request.user.login }}" + NUM="${{ github.event.pull_request.number }}" + TYPE="pr_opened" + LABEL="PR #${NUM}" + else + TITLE="${{ github.event.issue.title }}" + URL="${{ github.event.issue.html_url }}" + AUTHOR="${{ github.event.issue.user.login }}" + NUM="${{ github.event.issue.number }}" + TYPE="issue_opened" + LABEL="Issue #${NUM}" + fi + + curl -s -H "Content-Type: application/json" \ + -d "{\"content\":\"[GH-EVENT] repo:${{ github.repository }} action:${TYPE} ${LABEL}\\n**${TITLE}**\\nby ${AUTHOR}\\n${URL}\"}" \ + "$DISCORD_WEBHOOK_URL" diff --git a/docs/github-webhook-integration.md b/docs/github-webhook-integration.md new file mode 100644 index 000000000..1d80f93cc --- /dev/null +++ b/docs/github-webhook-integration.md @@ -0,0 +1,79 @@ +# GitHub Webhook to Discord — Agent Trigger Pattern + +## Overview + +Agent Broker (AB) only listens to Discord events. It does not accept external webhooks directly. To trigger agents from GitHub events (PR, Issue, etc.), we route through Discord as the single entry point. + +## Architecture + +``` +GitHub (PR/Issue event) + → GitHub Actions workflow + → Discord Webhook (formatted message to channel) + → Agent Broker detects message + → Routes to target agent + → Agent performs action (review, comment, notify) +``` + +## Setup + +### 1. Discord Webhook + +Create a webhook in your Discord server for the target channel/topic: +- Server Settings → Integrations → Webhooks → New Webhook +- Copy the webhook URL + +### 2. GitHub Secret + +Add the webhook URL as a repository secret: +- Repo → Settings → Secrets and variables → Actions +- Name: `DISCORD_WEBHOOK_URL` +- Value: the webhook URL from step 1 + +### 3. GitHub Actions Workflow + +Add `.github/workflows/notify-discord.yml` to your repo. See the workflow file for the full implementation. + +Triggers: +- `pull_request`: opened, reopened +- `issues`: opened + +## Message Format Convention + +Messages use a structured prefix so AB can identify GitHub events: + +``` +[GH-EVENT] repo:{owner/repo} action:{event_type} {PR/Issue} #{number} +**{title}** +by {author} +{url} +``` + +Example: +``` +[GH-EVENT] repo:thepagent/agent-broker action:pr_opened PR #42 +**Add webhook integration docs** +by obrutjack +https://github.com/thepagent/agent-broker/pull/42 +``` + +## Open Questions + +- **Bot message handling**: Does AB currently ignore messages from bots/webhooks? If so, webhook sources need to be allowlisted. +- **Routing**: How does AB determine which agent handles a `[GH-EVENT]` message? +- **Loop prevention**: If an agent replies in the same channel, could it re-trigger events? Recommend using a dedicated channel and filtering by `[GH-EVENT]` prefix only. + +## Best Practices + +- Use a dedicated channel or thread for webhook events +- Stick to the `[GH-EVENT]` prefix convention for all GitHub-sourced messages +- Validate webhook sources on the Discord side (restrict channel permissions) +- Avoid agents posting back to the same webhook channel to prevent loops +- Start minimal (PR + Issue notifications), expand as needed + +## Future Considerations + +- Extend pattern to other sources: Jira, Slack, PagerDuty, etc. +- Agent-to-agent invocation during review workflows +- Event filtering and deduplication at the AB level +- Richer payloads using Discord embeds instead of plain text From 36c6107233f22d7e5b27645f05c956fd02376c4a Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 15:38:32 +0000 Subject: [PATCH 2/6] feat: add GH_TOKEN to k8s secret and deployment --- k8s/deployment.yaml | 5 +++++ k8s/secret.yaml | 1 + 2 files changed, 6 insertions(+) diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index a3574ea79..ed5dd8132 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -26,6 +26,11 @@ spec: secretKeyRef: name: agent-broker-secret key: discord-bot-token + - name: GH_TOKEN + valueFrom: + secretKeyRef: + name: agent-broker-secret + key: gh-token - name: HOME value: /home/agent volumeMounts: diff --git a/k8s/secret.yaml b/k8s/secret.yaml index 2fd3e91e3..318b2c8fc 100644 --- a/k8s/secret.yaml +++ b/k8s/secret.yaml @@ -5,3 +5,4 @@ metadata: type: Opaque stringData: discord-bot-token: "REPLACE_ME" + gh-token: "REPLACE_ME" From 1734461e1b6a52ea38c0c87a2b888a625bed0b35 Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 15:39:17 +0000 Subject: [PATCH 3/6] docs: add GitHub token setup guide for agents --- docs/github-token-setup.md | 104 +++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/github-token-setup.md diff --git a/docs/github-token-setup.md b/docs/github-token-setup.md new file mode 100644 index 000000000..6fc1f39ee --- /dev/null +++ b/docs/github-token-setup.md @@ -0,0 +1,104 @@ +# GitHub Token Setup for Agents + +Step-by-step guide to give your agent secure access to GitHub via `gh` CLI. + +## Overview + +Agents sometimes need to interact with GitHub — push branches, open PRs, comment on issues. The recommended approach is to store a GitHub fine-grained personal access token in a Kubernetes secret and inject it as an environment variable. + +## 1. Create a Fine-Grained Personal Access Token + +1. Go to [GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens](https://github.com/settings/tokens?type=beta) +2. Click **Generate new token** +3. Configure: + - **Token name**: e.g. `agent-broker-masami` + - **Expiration**: set a reasonable expiry (e.g. 90 days) + - **Repository access**: select only the repos the agent needs + - **Permissions**: + - Contents: Read and write (push branches) + - Pull requests: Read and write (create/comment on PRs) + - Issues: Read and write (comment on issues) + - Workflows: Read and write (if the agent needs to modify workflows) +4. Click **Generate token** and copy it immediately + +## 2. Store the Token in Kubernetes Secret + +Add the token to `k8s/secret.yaml`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: agent-broker-secret +type: Opaque +stringData: + discord-bot-token: "your-discord-bot-token" + gh-token: "github_pat_xxxxxxxxxxxx" +``` + +Apply it: + +```bash +kubectl apply -f k8s/secret.yaml +``` + +Or create it directly: + +```bash +kubectl create secret generic agent-broker-secret \ + --from-literal=discord-bot-token="your-discord-token" \ + --from-literal=gh-token="github_pat_xxxxxxxxxxxx" +``` + +## 3. Inject as Environment Variable + +In `k8s/deployment.yaml`, the `GH_TOKEN` env var is already configured: + +```yaml +env: + - name: GH_TOKEN + valueFrom: + secretKeyRef: + name: agent-broker-secret + key: gh-token +``` + +The `gh` CLI automatically picks up `GH_TOKEN` — no additional auth setup needed. + +## 4. Install `gh` CLI in the Agent Container + +Ensure `gh` is available in your Dockerfile: + +```dockerfile +RUN apt-get update && apt-get install -y gh && rm -rf /var/lib/apt/lists/* +``` + +## 5. Verify + +Once the agent pod is running: + +```bash +# Check auth status +gh auth status + +# Should show: +# ✓ Logged in to github.com as your-agent-user (GH_TOKEN) +``` + +The agent can now run `gh` commands: `gh pr create`, `gh issue comment`, `gh repo fork`, etc. + +## Security Best Practices + +- **Fine-grained tokens only** — avoid classic tokens; fine-grained tokens limit access to specific repos and permissions +- **Least privilege** — only grant the permissions the agent actually needs +- **Set expiration** — rotate tokens regularly; don't use non-expiring tokens +- **One token per agent** — if you run multiple agents, give each its own token with its own GitHub account +- **Never log tokens** — ensure your agent doesn't echo `$GH_TOKEN` in responses or logs +- **Dedicated GitHub account** — create a bot account (e.g. `masami-agent`) rather than using a personal account + +## Troubleshooting + +- **`gh auth status` fails** — check that `GH_TOKEN` env var is set: `echo ${GH_TOKEN:+exists}` +- **Permission denied on push** — the token's repo access doesn't include the target repo, or write permission is missing +- **403 on PR create** — the token needs Pull requests: Read and write permission +- **Token expired** — generate a new one and update the k8s secret From 0036ae717c057be2c29ee844cb7a8b9fa45ebf3c Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 15:59:53 +0000 Subject: [PATCH 4/6] fix: revert k8s/* changes, use Helm chart envFrom instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per neilkuan's review — k8s/* should not be modified directly. Updated docs to use charts/agent-broker envFrom pattern. --- docs/github-token-setup.md | 50 +++++++++++++++----------------------- k8s/deployment.yaml | 5 ---- k8s/secret.yaml | 1 - 3 files changed, 20 insertions(+), 36 deletions(-) diff --git a/docs/github-token-setup.md b/docs/github-token-setup.md index 6fc1f39ee..95ab4f0dd 100644 --- a/docs/github-token-setup.md +++ b/docs/github-token-setup.md @@ -21,48 +21,38 @@ Agents sometimes need to interact with GitHub — push branches, open PRs, comme - Workflows: Read and write (if the agent needs to modify workflows) 4. Click **Generate token** and copy it immediately -## 2. Store the Token in Kubernetes Secret +## 2. Store the Token in a Kubernetes Secret -Add the token to `k8s/secret.yaml`: - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: agent-broker-secret -type: Opaque -stringData: - discord-bot-token: "your-discord-bot-token" - gh-token: "github_pat_xxxxxxxxxxxx" -``` - -Apply it: - -```bash -kubectl apply -f k8s/secret.yaml -``` - -Or create it directly: +Create a dedicated secret for the GitHub token: ```bash -kubectl create secret generic agent-broker-secret \ - --from-literal=discord-bot-token="your-discord-token" \ +kubectl create secret generic gh-token-secret \ --from-literal=gh-token="github_pat_xxxxxxxxxxxx" ``` -## 3. Inject as Environment Variable +## 3. Inject via Helm Chart -In `k8s/deployment.yaml`, the `GH_TOKEN` env var is already configured: +Use `envFrom` in your Helm values to inject the token as `GH_TOKEN`: ```yaml +# values.yaml +envFrom: + - secretRef: + name: gh-token-secret + env: - - name: GH_TOKEN - valueFrom: - secretKeyRef: - name: agent-broker-secret - key: gh-token + GH_TOKEN: "" # or use envFrom above ``` +Or pass it directly during install: + +```bash +helm install agent-broker charts/agent-broker \ + --set env.GH_TOKEN="github_pat_xxxxxxxxxxxx" +``` + +> **Recommended**: Use `envFrom` with a separate secret rather than `--set`, so the token doesn't appear in shell history. + The `gh` CLI automatically picks up `GH_TOKEN` — no additional auth setup needed. ## 4. Install `gh` CLI in the Agent Container diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index ed5dd8132..a3574ea79 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -26,11 +26,6 @@ spec: secretKeyRef: name: agent-broker-secret key: discord-bot-token - - name: GH_TOKEN - valueFrom: - secretKeyRef: - name: agent-broker-secret - key: gh-token - name: HOME value: /home/agent volumeMounts: diff --git a/k8s/secret.yaml b/k8s/secret.yaml index 318b2c8fc..2fd3e91e3 100644 --- a/k8s/secret.yaml +++ b/k8s/secret.yaml @@ -5,4 +5,3 @@ metadata: type: Opaque stringData: discord-bot-token: "REPLACE_ME" - gh-token: "REPLACE_ME" From afe828ddc21fcbb379d8cbc9d4db3d152d80f7c9 Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 16:03:28 +0000 Subject: [PATCH 5/6] fix: remove workflow from doc-only PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per thepagent's review — issue #83 is doc-only. Workflow changes should go in a separate PR. --- .github/workflows/notify-discord.yml | 35 ---------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/notify-discord.yml diff --git a/.github/workflows/notify-discord.yml b/.github/workflows/notify-discord.yml deleted file mode 100644 index bba5618fd..000000000 --- a/.github/workflows/notify-discord.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Notify Discord - -on: - pull_request: - types: [opened, reopened] - issues: - types: [opened] - -jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Send to Discord - env: - DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - TITLE="${{ github.event.pull_request.title }}" - URL="${{ github.event.pull_request.html_url }}" - AUTHOR="${{ github.event.pull_request.user.login }}" - NUM="${{ github.event.pull_request.number }}" - TYPE="pr_opened" - LABEL="PR #${NUM}" - else - TITLE="${{ github.event.issue.title }}" - URL="${{ github.event.issue.html_url }}" - AUTHOR="${{ github.event.issue.user.login }}" - NUM="${{ github.event.issue.number }}" - TYPE="issue_opened" - LABEL="Issue #${NUM}" - fi - - curl -s -H "Content-Type: application/json" \ - -d "{\"content\":\"[GH-EVENT] repo:${{ github.repository }} action:${TYPE} ${LABEL}\\n**${TITLE}**\\nby ${AUTHOR}\\n${URL}\"}" \ - "$DISCORD_WEBHOOK_URL" From 244431e32c056fd08a60de4d84df09518c0f2dbb Mon Sep 17 00:00:00 2001 From: Masami Date: Mon, 6 Apr 2026 16:11:10 +0000 Subject: [PATCH 6/6] fix: use placeholder per review --- docs/github-token-setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/github-token-setup.md b/docs/github-token-setup.md index 95ab4f0dd..4a07613ae 100644 --- a/docs/github-token-setup.md +++ b/docs/github-token-setup.md @@ -27,7 +27,7 @@ Create a dedicated secret for the GitHub token: ```bash kubectl create secret generic gh-token-secret \ - --from-literal=gh-token="github_pat_xxxxxxxxxxxx" + --from-literal=gh-token="" ``` ## 3. Inject via Helm Chart @@ -48,7 +48,7 @@ Or pass it directly during install: ```bash helm install agent-broker charts/agent-broker \ - --set env.GH_TOKEN="github_pat_xxxxxxxxxxxx" + --set env.GH_TOKEN="" ``` > **Recommended**: Use `envFrom` with a separate secret rather than `--set`, so the token doesn't appear in shell history.