Context
Follow-up to #628 and #640. The unified identity core still stores the external subject in the legacy untyped identity_binding.subject column, which cannot safely represent typed aliases or concurrent migration.
Authoritative design: docs/21-unified-identity-federation-design.md, PR 2.
Scope
- Add
identity_binding_subject with typed primary/alias subjects, status and timestamps.
- Preserve legacy reads and dual writes during the mixed-version window.
- Backfill legacy bindings and reject unsafe or ambiguous rows before contract activation.
- Enforce exactly one active primary subject per active binding in a separate Contract migration.
- Fail closed when aliases resolve to different bindings or legacy and typed lookups disagree.
- Cover concurrent first login, rolling upgrade and rollback to the PR 1-compatible version.
Rollout
This issue intentionally requires two ordered PRs:
- Expand + backfill/preflight.
- Contract gate after Expand validation and old-Pod retirement checks.
Acceptance
- Real PostgreSQL migration and concurrency tests pass repeatedly.
- Existing OAuth bindings upgrade without changing their legacy subject.
- Mixed-version reads/writes remain compatible during Expand.
- Contract rejects zero or multiple active primary subjects.
- Rollback evidence proves the PR 1-compatible image can read Expand data before revocation semantics are used.
- Both PRs are validated through
big-main; neither goes directly to main.
Context
Follow-up to #628 and #640. The unified identity core still stores the external subject in the legacy untyped
identity_binding.subjectcolumn, which cannot safely represent typed aliases or concurrent migration.Authoritative design:
docs/21-unified-identity-federation-design.md, PR 2.Scope
identity_binding_subjectwith typed primary/alias subjects, status and timestamps.Rollout
This issue intentionally requires two ordered PRs:
Acceptance
big-main; neither goes directly tomain.