Skip to content

feat(identity): reject rebound recover after unlink+relink - #210

Closed
cursor[bot] wants to merge 25 commits into
mainfrom
cursor/bc-d3211551-7396-4ceb-a0ad-e81e5b6efdce-f467
Closed

feat(identity): reject rebound recover after unlink+relink#210
cursor[bot] wants to merge 25 commits into
mainfrom
cursor/bc-d3211551-7396-4ceb-a0ad-e81e5b6efdce-f467

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why

PR #202 lets a returning account unlink and later relink. Recover still returned whatever history reconstructed after subject lookup. Under READ COMMITTED, FOR SHARE on the unterminated link row does not block a later link-end insert, so a concurrent unlink+relink can hand back a participant now bound to another issuer-scoped subject, or an unlinked record, to the original proof.

TDD

RED accept_recovered_participant_for_authenticated_account was missing, so rebound-subject, unlinked, tenant-mismatch, and issuer-mismatch loads could not be rejected. GREEN keeps the record only when current tenant, issuer, and subject still match the still-valid Keyverse proof.

Scope

Out of scope

  • HTTP account-link transport / OpenAPI
  • Live Keyverse token verification
  • Store-wide auto-reconcile on unlink

Operator next action

After recover loads a participant, keep it only when reconstructed tenant/issuer/subject still match the current Keyverse proof. Prefer this head over #202 and #176. Do not merge #202, #192, #183, or #176 as substitutes. Do not merge this PR until exact-head checks and independent last-push approval are satisfied. Never self-approve.

Open in Web View Automation 

cursoragent and others added 23 commits August 16, 2026 15:25
A buyer who links an anonymous assessment to a Keyverse account must
still see that link after process restart. Persist assessment_participant
plus append-only link and link-end evidence, reload through the domain
lifecycle, and fail closed on conflicting replay or a subject already
bound to another participant.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Keep TRACEABILITY, ADR-0020, ERD, and as-built schema pointing at the
opened persist/reload vehicle instead of an unnamed Active PR.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
The Active PR #114 naming commit stored an empty ADR-0020. Restore the
accepted decision, including the #114 persistence status and APA 7
references, so identity-link governance is not silently deleted.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Apply each identity link and then its matching ends in one transaction so a complete in-memory unlink+relink aggregate survives restart. Cover one-shot persist, exact replay, and subject reuse after unlink.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add a tenant-scoped current-subject lookup so a returning Keyverse login can find the same product-owned participant after the anonymous session token is gone. Ended or replaced subjects stay unfindable until they are current again.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Renumber the identity-link migration so it does not collide with #113 scoring-job health indexes on 0021. Name Active PR #124 as the merge candidate over #114.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Remove the accidentally committed build tree and ignore /target so later local verification cannot leak compiler outputs into the identity-link successor.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
A missing current_participant_identity_link row no longer hides a
returning Keyverse login or lets another participant bind the same
issuer-scoped subject. Lookup and uniqueness now read append-only
link rows that have no matching end.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Name the Active PR vehicle as the successor of #124 so TRACEABILITY,
ADR-0020, and the as-built schema do not treat projection-only lookup
as the landing contract.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add a composite foreign key so a link-end or current projection cannot
point at another participant's identity-link row. Name Active PR #133
as the landing vehicle over #124 and #114.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Exact replay of the same identity-link history now reconciles the derived
current projection so operator repair cannot hide a returning login behind
a missing unique enforcer or leave a stale row after unlink. Name Active
PR #133 in TRACEABILITY instead of superseded #124.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
After dump restore the unique enforcer can be missing or stale even
though unterminated history is intact. Rebuild every current row from
that history, fail closed on two unterminated holders of the same
subject, and prefer this successor over #147.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Keep TRACEABILITY, ADR-0020, ERD, and as-built schema pointing at the
opened restore-reconcile vehicle instead of an unnamed successor of #147.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add a read-only inspect so operators can see missing or stale unique-enforcer rows after dump restore, fail closed on two unterminated links for one participant, and run reconcile only when that inspect reports drift.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point TRACEABILITY, ADR-0020, ERD, and the as-built schema at the inspect-and-reconcile landing so writers do not reopen #158 or #133.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Fold the #173 buyer path onto the #169 inspect line: a stale current row for an ended Keyverse subject must keep that account occupied until inspect reports drift and restore reconcile rebuilds the unique enforcer. After that path, a later participant can bind the freed subject. Lock ADR-0020 Implementation status to the TRACEABILITY landing PR.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Point TRACEABILITY, ADR-0020, AS_BUILT, and ERD at #178 so concurrent writers do not treat #173 or #169 as the identity-link landing vehicle.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Hosted adapters authorize both current proofs only after restore
inspect is clean, persist the append-only history, and recover the
same participant from a later valid account. Prefer this write-path
head over #160.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Record the dual-proof write successor as the identity-link landing
so concurrent writers do not treat #178 or #160 as the persist vehicle.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
A returning Keyverse account can now disconnect without the expired
anonymous session. persist_authorized_account_unlink authorizes the
current issuer-scoped proof, appends the link-end, and leaves recover
empty so the same subject can relink. Restore drift still blocks new
first-links but does not freeze this disconnect.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Prefer #202 over #192 and earlier identity-link predecessors.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Keep a loaded participant only when reconstructed tenant, issuer, and
subject still match the still-valid account proof so a concurrent
unlink+relink cannot hand back another account's current binding.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 16:36
cursoragent and others added 2 commits August 16, 2026 16:36
Point TRACEABILITY, ADR-0020, ERD, and the as-built schema at the
inspect-then-unlink head that also rejects a rebound recover load.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Reject an ended subject's proof against a rebound current binding and
replace the caller-owned participant with stored history so a stale
in-memory record cannot disconnect another account.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Verdict: persist contract is SOUND on 7860bc7

Independent review of this exact head found no remaining fail-closed grant hole on identity-link persist.

  • Recover keeps a loaded participant only when reconstructed tenant, issuer, and subject still match the still-valid Keyverse proof. A READ COMMITTED unlink+relink that would otherwise hand back a rebound participant is dropped.
  • Unlink reloads stored history before authorize_account_unlink. A stale in-memory first-link or an ended subject's proof cannot end a rebound current binding (NoCurrentBinding).
  • participant_ref is a lookup key, not a standing capability, on this persist head.

Prefer this head over #206, #202, #192, #183, #178, and #176. Do not merge those as substitutes.

Do not merge this PR until exact-head checks and an independent last-push approval are satisfied. Never self-approve. Do not fold HTTP into this slice.

Hosted account-link HTTP is the next buyer-facing gap. That adapter must call recover_participant_for_authenticated_account, re-check current binding on every later command, and must not treat a previously recovered participant_ref as a capability grant. #215 is persist/recover HTTP on the older #183/#206 write path; rebase or replace it onto this head rather than merging it as a substitute.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Copy link
Copy Markdown
Contributor

Closing as superseded, not merged. Fresh ancestry proof shows #230 head 86352add is an exact descendant of this head 7860bc71 (ahead_by=1, behind_by=0). #230 preserves this current-binding recover/unlink persistence contract and adds the persist-backed recover/unlink HTTP + OpenAPI surface, so keeping #210 open would duplicate the same identity landing lineage.

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