Self-host the hub: Cloudflare setup

Self-host the hub

Deploy to Cloudflare

Create the D1 database, R2 bucket, Durable Objects, and cron trigger, set secrets, and deploy the hub Worker.

This page creates everything the hub needs in your Cloudflare account. You will come back to set the GitHub and Slack secrets after creating those apps.

Prepare

  1. Clone the repository and install dependencies. The hub lives in the hub/ directory.

    git clone https://github.com/KCuppens/hookci.git
    cd hookci/hub
    npm ci
  2. Log in to Cloudflare.

    npx wrangler login
  3. Create the D1 database. Wrangler prints a database_id. Copy it.

    npx wrangler d1 create hookci-hub
  4. Create the R2 bucket for logs.

    npx wrangler r2 bucket create hookci-logs

Configure wrangler.toml

Check hub/wrangler.toml in the repository for the exact file. It must define these bindings. Names the code relies on are DB, LOGS, AGENT_DO, and SLACK_DO. Resource names such as hookci-hub and hookci-logs are your choice.

name = "hookci-hub"

[[d1_databases]]
binding = "DB"
database_name = "hookci-hub"
database_id = "<your-database-id>"

[[r2_buckets]]
binding = "LOGS"
bucket_name = "hookci-logs"

[durable_objects]
bindings = [
  { name = "AGENT_DO", class_name = "AgentDO" },
  { name = "SLACK_DO", class_name = "SlackDO" },
]

[triggers]
crons = ["*/5 * * * *"]

Durable Object classes need a migration entry the first time they are deployed. If the repository’s wrangler.toml already has a [[migrations]] block for AgentDO and SlackDO, keep it unchanged. Wrangler’s new_sqlite_classes versus new_classes choice must match the repository, so do not change it.

The cron trigger runs every 5 minutes. Keep the schedule as shipped.

Apply database migrations

Migrations are SQL files in the repository. Check the migrations directory name in wrangler.toml (migrations_dir) if it is not the default.

npx wrangler d1 migrations apply hookci-hub --remote

Run the same command after every upgrade. See Operations.

Secrets

Set each secret with wrangler secret put NAME. Wrangler prompts for the value.

Secret What it is Where it comes from
GITHUB_APP_ID GitHub App ID GitHub App settings page
GITHUB_APP_PRIVATE_KEY App private key (PEM) Generated on the GitHub App page
GITHUB_CLIENT_ID App OAuth client ID GitHub App settings page
GITHUB_CLIENT_SECRET App OAuth client secret GitHub App settings page
GITHUB_WEBHOOK_SECRET Webhook signing secret Value you chose when creating the app
SLACK_CLIENT_ID Slack OAuth client ID Slack App Basic Information
SLACK_CLIENT_SECRET Slack OAuth client secret Slack App Basic Information
SLACK_SIGNING_SECRET Slack request signing secret Slack App Basic Information
TOKEN_ENC_KEYS Key ring for encrypting stored tokens Generate yourself
STATE_SIGNING_KEY Key for signing OAuth state Generate yourself
LOG_SIGNING_KEYS Key ring for signing log links Generate yourself

Generate the random values with openssl.

openssl rand -base64 32

TOKEN_ENC_KEYS and LOG_SIGNING_KEYS are key rings, so they hold more than one key to allow rotation. Check the hub source (search for the variable names in hub/src) for the exact list format before setting a value. Operations covers rotation.

npx wrangler secret put TOKEN_ENC_KEYS
npx wrangler secret put STATE_SIGNING_KEY
npx wrangler secret put LOG_SIGNING_KEYS

The GitHub and Slack values do not exist until you create those apps. A Worker deploy does not require secrets to exist, so deploy first, then create the apps, then set their secrets.

Deploy

npx wrangler deploy

Wrangler prints the Worker URL, for example https://hookci-hub.<your-subdomain>.workers.dev.

Custom domain

A stable hostname is better than workers.dev because the GitHub App, the Slack App, and every agent store it. Add a custom domain or a route in the Cloudflare dashboard (Workers, your Worker, Settings, Domains and Routes), or declare it in wrangler.toml.

routes = [
  { pattern = "hub.example.com", custom_domain = true },
]

The domain’s zone must be on your Cloudflare account. Use that URL, for example https://hub.example.com, as the hub URL in the next steps.

Continue with GitHub App.