feat(identity): persist, recover, and unlink account links over HTTP - #230
feat(identity): persist, recover, and unlink account links over HTTP#230cursor[bot] wants to merge 29 commits into
Conversation
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>
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>
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>
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>
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>
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>
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>
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>
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>
Add POST /v1/account-links, /recover, and /unlink on the #210 write path. Recover and unlink re-check the current binding from the Keyverse proof. Unlink rejects a client participant_ref so a recovered identifier is not a capability grant. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
There was a problem hiding this comment.
Queue
Do not merge this HTTP head over #236 / #222.
src/account_link_write.rs on 86352ad has persist/recover/unlink and no grant_account_linked_capability / accept_account_linked_capability. Recovering a participant_ref is not an account grant. A later same-subject relink under a new link_event_ref must fail closed on the pre-unlink grant.
Prefer #236 2d4676e for that unique event-bind proof. Prefer #222 for the grant/accept introduction. Prefer #206 for persist unlink. Prefer this head over #215 only for HTTP persist/recover/unlink shape after it is rebased onto #236 and calls accept_account_linked_capability in the same transaction as the privileged command.
Live Keyverse verification stays out. Anonymous assessment sessions stay on participant_ref.
Do not merge until exact-head checks and independent last-push approval are satisfied. Never self-approve.
Sent by Cursor Automation: Fix Issues
The RED framing test showed accept closed after headers. Read progress now continues until the declared body arrives, so a fragmented recover POST is classified instead of dropping the connection.
Processing integration_consumption rows must carry a database-authoritative claim deadline so restore fixtures satisfy the protected-main check constraint.


Why
A buyer still cannot link, return, or disconnect over HTTP. Persist landing is #210. #215 adds persist/recover HTTP on the older #183/#206 write path and omits unlink plus the #210 recover-binding check.
TDD
RED
tests/account_link_http.rsclassified persist, recover, and unlink, and rejected a clientparticipant_refon unlink. GREEN addssrc/account_link_http.rsandopenapi/account-links.yaml. REDtests/postgres_account_link_http.rsposts both current proofs, recovers the sameparticipant_ref, unlinks from the current proof, and proves an ended subject cannot recover after relink.Scope
POST /v1/account-links,POST /v1/account-links/recover,POST /v1/account-links/unlink.participant_refso a previously recovered identifier is not a capability grant.Out of scope
Operator next action
Review the HTTP routes on the #210 write path. Do not merge #215 as a substitute. Do not merge this PR until exact-head checks and independent last-push approval are satisfied. Never self-approve.