GitHub App setup
Bloom uses a GitHub App to create and manage repositories. One App (owned by the
hidden-claw org) serves both cases:
- Bloom-hosted (default): the App is installed on
hidden-claw; Bloom creates each project's repo there. - Client-owned (opt-in): the client installs the same App on their org; Bloom creates the repo there, so the client owns the code and can revoke access at any time.
Bloom never stores a long-lived credential for any account - it mints a short-lived installation token per action from the App's private key.
One-time registration (operator)
-
Create the App at
https://github.com/organizations/hidden-claw/settings/apps/new:- Name:
bloom-po(the slug becomes part of the install URL). - Homepage URL: your Bloom URL.
- Setup URL:
https://<your-bloom-host>/webhooks/github/setupand tick "Redirect on update". Bloom links the new installation to the project here. - Webhook URL:
https://<your-bloom-host>/webhooks/githubwith a secret (reused asBLOOM_GITHUB_WEBHOOK_SECRET). Subscribe to Pull request and Issues events (Issues events drive the blocked-work and milestone-completion notifications, FR-10).
- Name:
-
Permissions (Repository) - what the running flows actually use:
- Administration: Read & write (create repositories)
- Issues: Read & write (tickets, labels, milestones)
- Pull requests: Read & write (the engine opens PRs; Bloom reviews and merges them)
- Contents: Read & write (branch pushes,
PRD.md/README commits) - Variables: Read & write (M19-4: publish the non-secret
INFISICAL_*deploy coordinates; without it, delivery provisioning 403s - #293) - Metadata: Read (default)
Changing permissions on an existing App requires the org to accept the updated permissions on its installation (GitHub prompts the installation admin).
-
Generate a private key (
.pem) and download it. Store it as a secret - never commit it. -
Install the App on
hidden-clawand note the installation id (visible in the installation URL:.../installations/<id>).
Configuration (env)
Set these (see .env.example):
BLOOM_GITHUB_APP_ID=<app id>
BLOOM_GITHUB_APP_SLUG=bloom-po
BLOOM_GITHUB_APP_PRIVATE_KEY_PATH=/run/secrets/bloom-app.pem # or BLOOM_GITHUB_APP_PRIVATE_KEY
BLOOM_GITHUB_DEFAULT_ORG=hidden-claw
BLOOM_GITHUB_DEFAULT_INSTALLATION_ID=<hidden-claw installation id>
BLOOM_GITHUB_WEBHOOK_SECRET=<same secret as the App webhook>
BLOOM_PUBLIC_BASE_URL=https://<your-bloom-host>
When BLOOM_GITHUB_APP_ID and a private key are present, Settings.github_app_configured
is true and Bloom wires up GitHub. Without them, GitHub steps are skipped and the rest of the
app runs normally.
Client-owned flow (what the founder does)
- In Telegram the client sends "use my own org" (or
/useownorg). - Bloom replies with a one-tap install link
(
https://github.com/apps/bloom-po/installations/new?state=<thread>). - The client installs the App on their org and approves the permissions.
- GitHub redirects to
…/webhooks/github/setup; Bloom records the installation as the project's repo target and confirms in Telegram. - On plan approval, Bloom creates the repo in the client's org.
Choose client-owned before approving the plan - once the repo is created in hidden-claw,
moving it to the client's org requires a GitHub repository transfer.
Adopting an existing repository
The same App powers repository adoption (M44): instead of Bloom creating
the repo at plan approval, a project can bind a pre-existing repository as its target via the
optional repo: "owner/name" field on the create call. Install the App on the account that
owns the repository and grant it access to that repo; the bind resolves the covering
installation and read-preflights it before the project is created. Personal accounts work
for adoption - the organizations-only rule below applies to repository creation.
Notes
- Organizations only. Repo creation targets an org (
POST /orgs/{org}/repos); installing on a personal account is rejected with guidance to use an org. - Rotation. To rotate the key, generate a new private key in the App settings and update the secret; installation tokens are short-lived and refresh automatically.