Problem
backfillOrbInstallations (src/orb/installations.ts:50-65) unconditionally clears the suspension flag
on every install GitHub returns:
ON CONFLICT(installation_id) DO UPDATE SET
account_login = excluded.account_login, …, suspended_at = NULL, removed_at = NULL,
last_event_at = CURRENT_TIMESTAMP
listOrbAppInstallations never parses the installation's suspended_at — it reads only id, account,
and repository_selection — so there is no value to write through even if the upsert wanted one.
GET /app/installations does return suspended installations, carrying suspended_at/suspended_by.
A suspension recorded by the installation.suspend webhook (installations.ts:33-35) is therefore
erased by the next backfill, with no unsuspend event ever having arrived.
Trigger
An account owner suspends the ORB App (GitHub's native "pause this app"). An operator later runs
POST /v1/internal/orb/installations/backfill to recover missed installs. The suspension silently
disappears from the registry.
Impact
brokerOrbToken's eligibility check install.suspended_at !== null (src/orb/broker.ts:167-171) — the
only registry-side representation of "the account owner revoked consent" — becomes structurally
always-passing. Concretely this converts a clean 403 installation_not_eligible into a 503 broker_error (GitHub itself rejects the mint), so the container retries and DLQs instead of stopping,
and every operator-facing view reports the install as active. src/orb/oauth.ts:105's "Installation not
active" check is defeated the same way, re-opening self-enrollment on a suspended install.
Consent withdrawal is the one signal that should never be silently discarded.
Requirements
- Parse
suspended_at in listOrbAppInstallations and write it through in the backfill upsert instead
of hardcoding NULL.
- Apply the same reasoning to
removed_at — confirm the backfill should be resurrecting removed installs
at all, or scope the clear to installs GitHub actually reports as active.
- Add a test that a suspended install survives a backfill.
Test Coverage Requirements
99%+ patch coverage, branch-counted; both arms (suspended / active) through the backfill.
Links & Resources
maintainer-only — consent and eligibility state.
Problem
backfillOrbInstallations(src/orb/installations.ts:50-65) unconditionally clears the suspension flagon every install GitHub returns:
listOrbAppInstallationsnever parses the installation'ssuspended_at— it reads onlyid,account,and
repository_selection— so there is no value to write through even if the upsert wanted one.GET /app/installationsdoes return suspended installations, carryingsuspended_at/suspended_by.A suspension recorded by the
installation.suspendwebhook (installations.ts:33-35) is thereforeerased by the next backfill, with no
unsuspendevent ever having arrived.Trigger
An account owner suspends the ORB App (GitHub's native "pause this app"). An operator later runs
POST /v1/internal/orb/installations/backfillto recover missed installs. The suspension silentlydisappears from the registry.
Impact
brokerOrbToken's eligibility checkinstall.suspended_at !== null(src/orb/broker.ts:167-171) — theonly registry-side representation of "the account owner revoked consent" — becomes structurally
always-passing. Concretely this converts a clean
403 installation_not_eligibleinto a503 broker_error(GitHub itself rejects the mint), so the container retries and DLQs instead of stopping,and every operator-facing view reports the install as active.
src/orb/oauth.ts:105's "Installation notactive" check is defeated the same way, re-opening self-enrollment on a suspended install.
Consent withdrawal is the one signal that should never be silently discarded.
Requirements
suspended_atinlistOrbAppInstallationsand write it through in the backfill upsert insteadof hardcoding
NULL.removed_at— confirm the backfill should be resurrecting removed installsat all, or scope the clear to installs GitHub actually reports as active.
Test Coverage Requirements
99%+ patch coverage, branch-counted; both arms (suspended / active) through the backfill.
Links & Resources
src/orb/installations.ts~33-35, ~50-65;src/orb/app-auth.ts~47-49;src/orb/broker.ts~167-171;src/orb/oauth.ts~105;src/api/routes.ts~4546maintainer-only — consent and eligibility state.