Skip to content

feat(hub): establish identity transaction kernel - #163

Open
alliscode wants to merge 6 commits into
build/hub/newfrom
alliscode-identity-transaction-kernel
Open

alliscode wants to merge 6 commits into
build/hub/newfrom
alliscode-identity-transaction-kernel

Conversation

@alliscode

@alliscode alliscode commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Hub needs a way to represent a person independently of GitHub, Entra, or any future sign-in provider. Before adding sign-in, membership, or roles, Hub must be able to map a verified external identity to one Hub user and safely record which authentication configuration is active. This changeset adds that small persistence foundation, following the architecture in github/replica-internal#48.

What changes

This changeset introduces four records:

  • A Hub user is the provider-neutral identity used by Hub.
  • An external identity links one immutable provider identity, such as a GitHub numeric user ID, to exactly one Hub user.
  • An authentication provider revision records the fingerprint of a validated provider configuration and which revision is selected. Provider credentials and configuration values are not stored here.
  • A security audit records a small, typed description of identity changes without storing tokens, secrets, or raw provider data.

The first Prisma migration creates these records and enforces their uniqueness, ownership, and immutability rules in PostgreSQL. Database tests install the committed migration so they exercise the same constraints that a running Hub will use.

Identity creation and provider-revision changes are atomic with their success audits. If concurrent requests race, the kernel retries a small fixed number of times and converges on the existing identity or provider revision instead of creating conflicting records. Provider validation and other network work happen before the database transaction.

This PR does not let anyone sign in yet. It does not add Hub sessions, membership or admission, OAuth or OIDC routes, product authorization, GitHub repository synchronization, publishing, or UI. Those capabilities build on this foundation in later changesets.

Storybook screenshots

No Storybook screenshots are required because this change does not alter UI or stories.

Copilot AI auto review requested due to automatic review settings October 29, 2026 21:47
@alliscode
alliscode requested review from a team and nshki as code owners October 29, 2026 21:47

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.

🟡 Changes recommended

Unresolved critical validation gaps allow credentials to be persisted, generated identity mappings to be overridden, and provider settings to be stored with mismatched digests.

7 open issues
What changed in this PR

Establishes Hub’s provider-neutral identity foundation for later authentication work, without adding sign-in, sessions, admission, or product access.

Changes:

  • Adds identity models and an initial migration enforcing uniqueness, immutable history, and bounded security audits.
  • Introduces focused repositories and serializable transactions for identity creation and provider revision selection.
  • Adds unit and database coverage, deploys committed migrations in database tests, and updates architecture guides.
File Description
hub/​test/​database/​identity-foundation/​index.db.test.ts Tests migration constraints and immutable history.
hub/​test/​database/​global-setup/​index.ts Deploys committed migrations during test setup.
hub/​server/​utils/​normalize-secret-free-json/​types.ts Defines normalized JSON and validation limits.
hub/​server/​utils/​normalize-secret-free-json/​index.ts Normalizes bounded JSON and rejects credentials.
hub/​server/​utils/​normalize-secret-free-json/​index.test.ts Tests JSON validation boundaries.
hub/​server/​repositories/​users/​create-user/​types.ts Defines user creation inputs.
hub/​server/​repositories/​users/​create-user/​index.ts Inserts Hub users.
hub/​server/​repositories/​security-audits/​create-security-audit/​types.ts Defines audit inputs and context values.
hub/​server/​repositories/​security-audits/​create-security-audit/​index.ts Validates and inserts security audits.
hub/​server/​repositories/​security-audits/​create-security-audit/​index.test.ts Tests audit context safeguards.
hub/​server/​repositories/​external-identities/​find-external-identity/​types.ts Defines external identity lookup tuples.
hub/​server/​repositories/​external-identities/​find-external-identity/​index.ts Finds mappings by identity tuple.
hub/​server/​repositories/​external-identities/​create-external-identity/​types.ts Defines identity mapping creation inputs.
hub/​server/​repositories/​external-identities/​create-external-identity/​index.ts Inserts external identity mappings.
hub/​server/​repositories/​authentication-providers/​select-authentication-provider-revision/​types.ts Defines revision selection inputs.
hub/​server/​repositories/​authentication-providers/​select-authentication-provider-revision/​index.ts Updates the selected provider revision.
hub/​server/​repositories/​authentication-providers/​find-authentication-provider/​types.ts Defines provider lookup database access.
hub/​server/​repositories/​authentication-providers/​find-authentication-provider/​index.ts Loads providers with selected revisions.
hub/​server/​repositories/​authentication-providers/​create-authentication-provider/​types.ts Defines provider creation inputs.
hub/​server/​repositories/​authentication-providers/​create-authentication-provider/​index.ts Inserts authentication providers.
hub/​server/​repositories/​authentication-providers/​create-authentication-provider-revision/​types.ts Defines revision persistence inputs.
hub/​server/​repositories/​authentication-providers/​create-authentication-provider-revision/​index.ts Inserts provider revisions.
hub/​server/​domains/​identity/​run-provider-revision-transaction/​types.ts Defines transaction-scoped provider repositories.
hub/​server/​domains/​identity/​run-provider-revision-transaction/​index.ts Runs serializable provider transactions.
hub/​server/​domains/​identity/​run-identity-creation-transaction/​types.ts Defines transaction-scoped identity repositories.
hub/​server/​domains/​identity/​run-identity-creation-transaction/​index.ts Runs serializable identity transactions.
hub/​server/​domains/​identity/​resolve-or-create-external-identity/​types.ts Defines identity creation dependencies and results.
hub/​server/​domains/​identity/​resolve-or-create-external-identity/​index.ts Resolves or atomically creates audited identities.
hub/​server/​domains/​identity/​resolve-or-create-external-identity/​index.db.test.ts Tests identity persistence, rollback, and concurrency.
hub/​server/​domains/​identity/​resolve-external-identity/​types.ts Defines identity resolution results.
hub/​server/​domains/​identity/​resolve-external-identity/​index.ts Validates and resolves identity tuples.
hub/​server/​domains/​identity/​persist-validated-provider-revision/​types.ts Defines provider persistence dependencies and results.
hub/​server/​domains/​identity/​persist-validated-provider-revision/​index.ts Enforces audited provider revision transitions.
hub/​server/​domains/​identity/​persist-validated-provider-revision/​index.db.test.ts Tests revision selection, rejection, and concurrency.
hub/​server/​domains/​identity/​get-selected-provider-revision/​types.ts Defines selected revision lookup dependencies.
hub/​server/​domains/​identity/​get-selected-provider-revision/​index.ts Loads and checks selected revision ownership.
hub/​server/​domains/​identity/​errors.ts Defines identity and revision failure types.
hub/​server/​domains/​identity/​create-validated-provider-revision/​types.ts Defines validated provider revision data.
hub/​server/​domains/​identity/​create-validated-provider-revision/​index.ts Normalizes settings and computes configuration digests.
hub/​server/​domains/​identity/​create-validated-provider-revision/​index.test.ts Tests digest stability and settings safeguards.
hub/​schema/​user.prisma Adds the Hub user model.
hub/​schema/​security-audit.prisma Adds the security audit model.
hub/​schema/​security-audit-outcome.enum.prisma Defines success, denial, and failure outcomes.
hub/​schema/​migrations/​migration_lock.toml Declares PostgreSQL as the migration provider.
hub/​schema/​migrations/​20261029203000_identity_foundation/​migration.sql Creates identity tables, constraints, and triggers.
hub/​schema/​external-identity.prisma Models unique external identity mappings.
hub/​schema/​authentication-provider.prisma Models providers and selected revisions.
hub/​schema/​authentication-provider-revision.prisma Models versioned provider settings.
hub/​docs/​testing-and-operations.md Documents migration-backed database test setup.
hub/​docs/​database.md Describes the identity schema and migrations.
hub/​docs/​backend-architecture.md Documents transaction and audit boundaries.
hub/​docs/​architecture.md Updates foundation capabilities and non-goals.

Review effort: Max (auto)

Comment thread hub/server/domains/identity/persist-validated-provider-revision/index.ts Outdated
Comment thread hub/server/domains/identity/resolve-or-create-external-identity/index.ts Outdated
Comment thread hub/server/utils/normalize-bounded-json/index.ts
Comment thread hub/server/utils/normalize-bounded-json/index.ts
Comment thread hub/server/domains/identity/resolve-or-create-external-identity/index.ts Outdated

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.

🟡 Changes recommended

Valid multibyte identity tuples can exceed PostgreSQL’s B-tree entry limit and roll back creation, while copied transaction runners prevent race tests from validating production isolation and conflict handling.

2 open issues
7 resolved since last review

Review effort: Max (auto)

Comment thread hub/server/domains/identity/resolve-or-create-external-identity/index.db.test.ts Outdated
Ben Thomas and others added 6 commits October 30, 2026 09:58
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

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.

🔵 Needs a closer look

Identity fields still accept NULs and lone surrogates, risking database failures and wrong-user resolution, while credential-filtering, retry-exhaustion, and final-audit rollback tests remain incomplete.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

High severity Reject NUL characters and lone surrogates in issuer and subject

hub/​server/​domains/​identity/​parse-external-identity-tuple/​index.ts:13

issuer and subject accept NUL characters and lone UTF-16 surrogates. NUL causes database failures, while UTF-8 encoding maps distinct subjects such as x\uD800 and x\uDC00 to identical bytes, risking resolution to the wrong user. Reject both at this boundary and test those rejections while still allowing valid surrogate pairs.

Medium severity Test rollback when the final success-audit write fails

hub/​server/​domains/​identity/​resolve-or-create-external-identity/​index.ts:41

The rollback tests stop before the success-audit write, so they do not cover failure at the final step of either transaction. Add migration-backed cases through the production runners that fail this audit and verify that new state rolls back, any previous provider selection is preserved, and exactly one terminal failure audit is added. Same gap in hub/server/domains/identity/validate-and-persist-provider-revision/index.ts:123.

Medium severity Test provider revision retry exhaustion and failure audit

hub/​server/​domains/​identity/​validate-and-persist-provider-revision/​index.ts:138

No provider-revision test exhausts the retry budget, so neither the ProviderRevisionConflictError conversion nor the terminal persist_failed audit with reason retry_exhausted is covered. Add a database case whose transaction always throws IdentityTransactionConflictError, and assert three attempts, that error type, exactly one failure audit with the expected reason, and no new revision.

Medium severity Test credential rejection and safe metadata acceptance

hub/​server/​utils/​normalize-bounded-json/​index.ts:54

The utility's tests cover size, type, and depth limits but never exercise credential-shaped values or secret-bearing keys. Add representative rejection cases and acceptance cases for ordinary issuer URLs and safe metadata such as tokenEndpoint, so changes to these checks cannot silently expose credentials or block valid audits.

2 resolved since last review

Review effort: Max (auto)

@alliscode
alliscode force-pushed the alliscode-identity-transaction-kernel branch from 8d1de33 to c65562b Compare October 30, 2026 17:09
@alliscode
alliscode requested an auto review from Copilot October 30, 2026 17:09

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.

🟡 Changes recommended

Issuer and subject parsing accepts U+0000, which PostgreSQL cannot store, causing both the identity transaction and its failure audit to fail without leaving an audit record.

3 open issues

Review effort: Max (auto)

Comment on lines +10 to +11
issuer: z.string().min(1).max(500),
subject: z.string().min(1).max(500),
issuer: tuple.issuer,
subject: tuple.subject,
})
await repositories.createSecurityAudit({
Comment on lines +52 to +54
if (sensitiveValuePatterns.some((pattern) => pattern.test(value))) {
throw new Error("JSON values cannot contain credential or key material.")
}
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