Skip to content

feat(hub): add installation membership administration - #121

Open
Copilot wants to merge 2 commits into
copilot/hub-identity-behaviorfrom
copilot/hub-identity-api
Open

Copilot wants to merge 2 commits into
copilot/hub-identity-behaviorfrom
copilot/hub-identity-api

Conversation

Copilot AI commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Why

GitHub authentication proves who a person is, but it should not decide whether that person may enter a Hub installation. Hub needs its own durable membership boundary so installation access and administration remain stable when repository permissions change.

What changes

Hub now admits a signed-in identity only when it has an active membership, matches a usable invitation, or completes authorized bootstrap or recovery enrollment. Administrators can invite immutable provider identities, assign member or administrator roles, and move memberships through active, suspended, and removed states.

Suspending or removing a member ends that person's Hub sessions. Hub prevents concurrent changes from removing the final active administrator, supports one-use bootstrap and no-administrator recovery, and records successful and denied administration outcomes as structured security audits. State-changing APIs enforce the Hub origin, CSRF protection, membership checks, and private no-store responses.

The minimal interface adds first-administrator enrollment, recovery, invitation management, and membership administration. This layer does not add prototype authorization, GitHub membership synchronization, artifact ingestion, or production OIDC.

Storybook screenshots

Hub/Pages/Identity/Membership administration / Default — dark theme, 1280 × 720

Hub membership administration default in dark theme at 1280 by 720

Hub/Pages/Identity/Sign in / Signed Out — enrollment and recovery expanded, dark theme, 1280 × 720

Hub sign-in signed-out state with enrollment and recovery expanded in dark theme at 1280 by 720

Hub/Pages/Identity/Sign in / Signed In — dark theme, 1280 × 720

Hub sign-in signed-in state in dark theme at 1280 by 720

Hub/Pages/Identity/Sign in / Unconfigured — dark theme, 1280 × 720

Hub sign-in unconfigured state in dark theme at 1280 by 720

Copilot AI requested review from a team and nshki as code owners October 26, 2026 23:31
Copilot AI max review requested due to automatic review settings October 26, 2026 23:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because there is no eligible user to bill.

An enterprise owner can allow the organization to be billed for reviews like this by enabling Copilot code review for users without a license.

Copilot AI added this pull request to stack #122 October 26, 2026 23:31
Ben Thomas and others added 2 commits October 28, 2026 11:28
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@alliscode
alliscode force-pushed the copilot/hub-identity-api branch from 1a04887 to c3e12ce Compare October 28, 2026 18:34
@alliscode alliscode changed the title feat(hub): expose the GitHub identity API feat(hub): add installation membership administration Oct 28, 2026
@alliscode
alliscode requested a max review from Copilot October 28, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</Button>
<Button
type="button"
variant="danger"
pkceVerifier: string
nonce: string | null
purpose: "sign-in" | "verify" | "link-identity"
purpose: "sign-in" | "verify" | "link-identity" | "bootstrap" | "recovery"
Comment on lines +131 to +132
const data = (await response.json()) as { error?: string }
throw new Error(data.error ?? "Administration request failed")
Comment on lines +131 to +132
const data = (await response.json()) as { error?: string }
throw new Error(data.error ?? "Administration request failed")
const [provider, setProvider] = useState("github")
const [issuer, setIssuer] = useState("https://github.com")
const [subject, setSubject] = useState("")
const [role, setRole] = useState<"member" | "administrator">("administrator")
Comment on lines +253 to +255
context: {
reason: nextStatus !== target.status ? `status_${nextStatus}` : `role_${nextRole}`,
},
Comment on lines +53 to +55
if (attempt.purpose === "sign-in") {
policyAdmitted = await admissionPolicy.admit(profile)
}
Comment on lines +115 to +116
membership?.status !== "suspended" &&
!policyAdmitted
return (await database.installationMembership.findMany({
include: { user: { include: { externalIdentities: true } } },
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
take: 100,
): Promise<MembershipInvitationRecord[]> {
return (await database.membershipInvitation.findMany({
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
take: 100,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants