Skip to content

Webhook Relay — Fly.io Setup

The Hall only sees events from its own repository. The webhook relay extends it to the entire org: a lightweight HTTP server on Fly.io receives GitHub App webhooks and forwards them to invoke.yml via workflow_dispatch.

Once deployed, any repo in the org can use Hall agents by labeling issues — no need to open issues in hall-of-automata manually.


Why the App webhook, not an org webhook?

The Hall GitHub App is already installed org-wide. GitHub App webhooks fire for every repository where the app is installed — exactly the same scope as a manually configured org webhook, but without the extra setup. You configure the relay URL once in the App settings and reuse the App's webhook secret (WEBHOOK_SECRET) that you already have.

No separate org-level webhook is needed.


Architecture

GitHub App webhook (any repo where App is installed)
  └─▶ hall-relay.fly.dev/webhook
        ├─ validate HMAC-SHA256 signature (App webhook secret)
        ├─ generate GitHub App installation token (hall-of-automata[bot])
        ├─ filter hall: label events
        └─▶ POST /repos/MockaSort-Studio/hall-of-automata/actions/workflows/invoke.yml/dispatches
              └─▶ [Hall] Invoke Agent (agent, repo-owner, repo-name, issue-number)

Events forwarded:

GitHub event Condition Result
issues.labeled label starts with hall: dispatch agent
issue_comment.created issue has hall:<agent> label, sender is human re-dispatch (awaiting-input reply)

Prerequisites

  • flyctl installed and authenticated (fly auth login)
  • A Fly.io account (free tier is sufficient)
  • The Hall GitHub App APP_ID, APP_PRIVATE_KEY, and App webhook secret (the secret set in the App's Webhook settings page)

1. Create the relay app

The relay code lives in deploy/relay/ in the Hall repo — no extra files to create.

cd deploy/relay
fly launch --name hall-relay --region lhr --no-deploy --copy-config

The folder already contains index.js, package.json, and fly.toml. The app listens on port 3000 and authenticates as hall-of-automata[bot] by generating short-lived installation tokens from the App credentials — no personal token required.


2. Set secrets

fly secrets set \
  WEBHOOK_SECRET="<App webhook secret from GitHub App settings>" \
  APP_ID="<Hall GitHub App ID>" \
  APP_PRIVATE_KEY="$(cat path/to/hall-of-automata.private-key.pem)" \
  HALL_OWNER="MockaSort-Studio" \
  HALL_REPO="hall-of-automata"

WEBHOOK_SECRET is the secret you set (or will set) in GitHub App → Settings → Webhook → Secret. APP_ID and APP_PRIVATE_KEY are the same values stored as secrets in the Hall repo. The relay generates 1-hour installation tokens and refreshes them automatically (5 min before expiry).


3. Deploy

# From repo root — checks for missing secrets before deploying
bash deploy/relay/deploy.sh

Or manually from deploy/relay/:

fly deploy

Note the URL printed at the end: https://hall-relay.fly.dev.


4. Configure the GitHub App webhook

Go to GitHub → Developer settings → GitHub Apps → hall-of-automata → Edit:

Field Value
Webhook URL https://hall-relay.fly.dev/webhook
Webhook secret a strong random secret (save this — you'll need it for step 2 above)
Events Issues + Issue comments
Active

The App already has the right installation scope — no org webhook needed.

To generate a webhook secret if you don't have one:

openssl rand -hex 32

5. Ensure the GitHub App is installed on target organization

The Hall App needs permission to push branches and open PRs in each target repo. Go to:

https://github.com/apps/hall-of-automata → Configure → Repository access

Add each repo, or select All repositories for org-wide access. Repos without the app installed will not receive webhook events.


Cross-repo flow after setup

User opens issue in other-repo
  → applies hall:dispatch-automaton (or uses the issue template)
  → App webhook fires (because app is installed on other-repo)
  → relay validates signature, generates App token, extracts repo + issue
  → workflow_dispatch → [Hall] Invoke Agent
  → Old Major reads issue, picks specialist, applies hall:<agent>
  → specialist dispatched with repo-owner=other-repo, repo-name=other-repo
  → agent pushes branch + opens PR in other-repo

Monitoring

# Live logs
fly logs --app hall-relay

# App status
fly status --app hall-relay

Failed dispatches are logged with status code and response body. The relay is stateless — a redeploy restores full operation.