Skip to content

Local dev cannot register a GitHub App installation #396

Description

@andreykeycee

What happened

I was setting up local development to try Facility on a real repository. pnpm dev seeds a demo org, user and membership, and nothing in the local flow ever writes the two rows a GitHub App needs — the identity and the installation. So after creating the App, setting all four GITHUB_APP_* values and installing it, project creation still told me no installation was visible.

The App itself was fine: I called GET /app and GET /app/installations myself, both returned 200 and the installation lists my repository. The advice in that message — install the App and reload, or type owner/name — can't work either, because the list is read from the database rather than from GitHub.

The only writer of github_installations in the repository is facility instance bootstrap, which the self-host docs describe, so I ran that. It refused with Database already contains a different Facility instance — because of the demo seed, which is the part I did not expect. I got unblocked by inserting the row by hand; my instance still has zero identity rows and is still on the demo org.

How to reproduce

Two commands on a clean database: the seed exactly as pnpm dev runs it, then bootstrap

FACILITY_SEED_DEMO=1 node packages/db/dist/bin/seed.js

node packages/cli/bin/facility.mjs instance bootstrap \
  --org-name Acme --org-slug acme --owner-email dev@example.com --owner-name Dev \
  --github-user-id 1 --github-login dev --github-account-id 1 \
  --github-account-login dev --github-installation-id 999 --github-account-type user

Result: Database already contains a different Facility instance

How are you running Facility?

Local development (pnpm dev)

Version

main at 11df0f9

Evidence

Nothing in the local flow writes the installation. seedLocalDemo in packages/db/src/seed.ts:49-73 writes the org, the user and the membership and stops there, and scripts/dev.mjs never mentions GitHub at all. After the demo seed the database holds orgs=1 users=1 members=1 identities=0 installations=0 — three of the five rows a working GitHub setup needs.

Nothing else fills the gap. routes/v1/github-installations.ts:50-58 lists from the database and never asks GitHub, so installing the App and reloading changes nothing. outes/webhooks.ts:61 drops deliveries from unknown installations, so installation.created can't self-register. routes/v1/projects.ts:284-290 requires the same row, so the type owner/name fallback fails with github_installation_required.

And the one command that can write it refuses. At packages/cli/src/instance.mjs:41, hasInstanceData is true if any of those five counts is non-zero — the demo seed makes three of them non-zero. The query that then decides whether the existing instance matches mine inner-joins user_identities and github_installations, both empty, so it returns no rows, identical is false, and it throws at line 69. The guard is meant to catch a conflicting instance; a demo-seeded org isn't one, because it has no binding to conflict with. It is incomplete, not conflicting — but the result is that the state can neither be adopted nor repaired.

The same check is strict beyond this case. exactlyOneBinding requires every count to equal 1, so re-running bootstrap with the identical values it was first given fails on any instance that has grown past one user or one installation. The command otherwise implements idempotency — it returns created: false and prints "already bootstrapped" — and #336 would make re-running routine via a Compose profile.

Related: #336 also touches instance.mjs, but only how bootstrap reads its input; it leaves this guard unchanged.

Activity

  1. dinsan commented on Oct 6, 2026

    @dinsan

    I reproduced this on my macOS local dev, and traced it through the code.

    the demo seed leaves 1 org, 1 user, 1 membership, 0 identities, 0 installations. In instance.mjs, the "existing instance" query inner-joins user_identities and github_installations, so it returns no rows, identical is false, and it throws "Database already contains a different Facility instance"

    apps/web/app/(app)/(org)/projects/new/page.tsx 368 shows "No GitHub App installation is visible yet. Install the Facility GitHub App on your org…" whenever the installations list comes back empty without an error. That also happens when the App is installed on GitHub but not registered in the instance, so the advice points the wrong way.

    Image

    I tried treating the exact demo-seed shape (1/1/1/0/0) as an unbound instance and having bootstrap bind it. On a seeded database the counts went from 1/1/1/0/0 to 1/1/1/1/1, and sign-in worked after logging out and back in.

    if bootstrap renames the demo org, the next pnpm dev fails with duplicate key ... orgs_pkey (org_local). seedLocalDemo in packages/db/src/seed.ts only handles conflicts on the slug and email so a renamed org isn't recognized! Any fix has to either keep those keys or make the seed tolerate an adopted org.

    i am happy to turn this into a PR. and i have two questions
    should bootstrap rewrite the demo org/owner or only the GitHub rows ?
    should the seed change be in the same PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions