> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub Webhook Setup for Issues Management

> Purpose: Configure the GitHub repository webhook that feeds the github-webhook Edge Function for spec-tracking automation (auto-label from [CORE-##], optional…

**Purpose:** Configure the GitHub repository webhook that feeds the `github-webhook` Edge Function for spec-tracking automation (auto-label from `[CORE-##]`, optional project add, optional sync table).

**Related:** [GITHUB\_PROJECT\_SPEC\_TRACKING\_PLAN.md](./GITHUB_PROJECT_SPEC_TRACKING_PLAN.md) (labels, project board, workflow).

***

## 1. Edge Function Endpoint

* **URL:** `https://<supabase-project-ref>.supabase.co/functions/v1/github-webhook`
* **Method:** `POST` only; `OPTIONS` supported for CORS.
* Replace `<supabase-project-ref>` with your Supabase project reference (Dashboard → Project Settings → General).

***

## 2. GitHub Repository Configuration

1. In the GitHub repo: **Settings → Webhooks → Add webhook**.
2. **Payload URL:** The Edge Function URL above.
3. **Content type:** `application/json`.
4. **Secret:** Generate a random string (e.g. `openssl rand -hex 32`) and set it in both:
   * GitHub: **Secret** field.
   * Supabase: **Edge Function secrets** → `GITHUB_WEBHOOK_SECRET` = same value.
5. **SSL verification:** Enable (default).
6. **Which events?** Choose **Let me select individual events**, then enable:
   * **Issues**
   * **Issue comments**
   * **Pull requests** (optional; for future “Fixes #N” handling)
7. Save; GitHub will send a `ping` event. The function responds with `200` and `{ "received": true, "event": "ping" }`.

***

## 3. Supabase Secrets

Set in **Supabase Dashboard → Edge Functions → github-webhook → Secrets** (or via CLI):

| Secret                  | Required          | Description                                                                                                                                                               |
| ----------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GITHUB_WEBHOOK_SECRET` | Yes (recommended) | Same value as the webhook **Secret** in GitHub. If unset, signature verification is skipped (not recommended in production).                                              |
| `GITHUB_TOKEN`          | No                | GitHub PAT or app token with `repo` / `issues:write` (and `project` if adding issues to Projects). Used for auto-label and “add to project” when enabled in the function. |

***

## 4. Behavior Summary

* **Verification:** Request body is verified with `X-Hub-Signature-256` using HMAC-SHA256 and `GITHUB_WEBHOOK_SECRET`. Invalid or missing signature (when secret is set) returns `401`.
* **Idempotency:** `X-GitHub-Delivery` is stored in `pf_github_webhook_events`. Duplicate delivery IDs receive `200` and are not processed again.
* **Events:**
  * `ping` → `200` immediately.
  * `issues` → Parsed for `action`, `issue`, `repository`; `[CORE-##]` in title is parsed for optional auto-label/sync.
  * `issue_comment` → Logged; optional bot comment or notify can be added later.
  * Other events → Logged and acknowledged with `200`.

***

## 5. Optional: Auto-Label and Add to Project

To have the function add labels and/or add issues to a GitHub Project:

1. Set `GITHUB_TOKEN` in Edge Function secrets (token with `repo` and, if needed, `project` scope).
2. In code, the handler already parses `[CORE-##]` from the issue title; you can extend `handleIssues` to:
   * Ensure label `core:{CORE}` exists (e.g. GitHub API `GET /repos/:owner/:repo/labels`, create if missing).
   * Add the label to the issue (`POST /repos/:owner/:repo/issues/:number/labels`).
   * If label `spec-tracking` is present, add the issue to the project (Projects v2 API).

See [GitHub Webhook events and payloads](https://docs.github.com/en/webhooks/webhook-events-and-payloads) and [GitHub REST API](https://docs.github.com/en/rest) for endpoints.

***

## 6. Optional: Sync Table for Dashboards

To show “open spec issues” or “recent activity” inside the app:

1. Add a migration that creates `pf_github_issue_sync` (e.g. `organization_id`, `repo_owner`, `repo_name`, `issue_number`, `issue_url`, `title`, `state`, `spec_id`, `labels`, `updated_at`, `last_event_type`) with RLS as needed.
2. In the `issues` handler, upsert a row keyed by `(repo_owner, repo_name, issue_number)` on each `issues` event.

This is optional and can be implemented in a follow-up.

***

## 7. Testing

### 7.1 Unit tests (no deployment)

* **Vitest (CI):** From project root run:
  ```bash theme={null}
  npx vitest run tests/unit/platform/monitoring/github-webhook.test.ts
  ```
  Tests signature verification (valid / invalid / tampered) and `[CORE-##]` parsing from issue titles.
* **Deno (edge function):** With [Deno](https://deno.land/) installed, from project root run:
  ```bash theme={null}
  npm run test:functions
  ```
  Or from `supabase/functions`: `deno test --allow-env --allow-read github-webhook/index.test.ts`
  These cover the same logic as implemented in the Edge Function.

### 7.2 Local endpoint (Supabase Functions serve)

1. Start local Supabase (if you need the DB for idempotency): `npx supabase start` then `npx supabase db reset`.
2. Serve the function (no JWT for webhooks):
   ```bash theme={null}
   npx supabase functions serve github-webhook --no-verify-jwt
   ```
   Functions are available at **[http://localhost:54321/functions/v1/github-webhook](http://localhost:54321/functions/v1/github-webhook)** (see [Supabase Edge Functions local development](https://supabase.com/docs/guides/functions/local-development)).
3. Optional: set `GITHUB_WEBHOOK_SECRET` via env file so signature verification is tested:
   ```bash theme={null}
   npx supabase functions serve github-webhook --no-verify-jwt --env-file .env.local
   ```
   Use the same secret value you will configure in GitHub (or a test secret for local only).

### 7.3 Manual curl (ping and issues)

Without a secret (verification skipped):

```bash theme={null}
# Ping (expect 200, {"received":true,"event":"ping"})
curl -s -X POST http://localhost:54321/functions/v1/github-webhook \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: ping" \
  -H "X-GitHub-Delivery: test-delivery-$(date +%s)" \
  -d '{"zen":"test"}'

# Issues opened (expect 200, {"received":true,"event":"issues"})
curl -s -X POST http://localhost:54321/functions/v1/github-webhook \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: issues" \
  -H "X-GitHub-Delivery: test-issues-$(date +%s)" \
  -d '{"action":"opened","issue":{"number":1,"title":"[HR-05] Time & Attendance","state":"open","html_url":"https://github.com/org/repo/issues/1","body":null,"labels":[],"assignees":[]},"repository":{"id":1,"full_name":"org/repo","name":"repo","owner":{"login":"org"}}}'
```

With a secret, compute `X-Hub-Signature-256` (e.g. `echo -n '<body>' | openssl dgst -sha256 -hmac '<secret>' -binary | xxd -p -c 256` then prefix with `sha256=`).

### 7.4 Forward real webhooks with GitHub CLI (development only)

To receive **real** GitHub webhook deliveries (issues, issue\_comment, ping) on your local endpoint while you develop:

1. **Install the webhook extension:** [Using the GitHub CLI to forward webhooks](https://docs.github.com/en/webhooks/testing-and-troubleshooting-webhooks/using-the-github-cli-to-forward-webhooks-for-testing)
   ```bash theme={null}
   gh extension install cli/gh-webhook
   ```
2. **Start your local handler:** Run `npx supabase functions serve github-webhook --no-verify-jwt` so the function is listening at `http://localhost:54321/functions/v1/github-webhook`.
3. **Forward repo webhooks to local:** In a second terminal (replace `OWNER/REPO` with your repo, e.g. `myorg/northsight-health-OS`):
   ```bash theme={null}
   gh webhook forward --repo=OWNER/REPO --events=issues,issue_comment,ping --url="http://127.0.0.1:54321/functions/v1/github-webhook"
   ```
   Leave this running; it forwards the selected events to your local URL. Only one forward can be active per repository at a time.
4. **Trigger events:** Create or edit an issue, or add a comment, in the repo; you should see the request in the terminal where `supabase functions serve` is running.

**Note:** Webhook forwarding is for **testing and development only**, not for production. The forwarded request will include GitHub’s signature; for verification to succeed locally, set `GITHUB_WEBHOOK_SECRET` in the environment used by `supabase functions serve` to the same value as the webhook secret configured for that repo in GitHub.

***

## 8. Troubleshooting

* **401 Signature required / Invalid signature:** Ensure `GITHUB_WEBHOOK_SECRET` matches the webhook secret in GitHub and that the raw body is used for verification (no parsing before verification).
* **Duplicate delivery:** Expected when GitHub retries; the function returns `200` and skips processing.
* **Logs:** Use Supabase Edge Function logs (or `createLogger` output) to inspect `event`, `action`, and `issue_number`; never log secrets or full request bodies.
* **gh webhook forward:** If you get "Hook already exists", only one forward can be active per repo; stop another client’s forward or use a different repo. For org webhooks, use `--org=ORG` and ensure `gh auth refresh --scopes admin:org_hook` if needed.
