Repository navigation
docs(agent-identity): reconcile the ToolHive work list and settle the lost-response rule - #2587
Merged
Merged
Conversation
… lost-response rule Work list, to match what exists in ToolHive now: - the child-grant row points to the generic-grant issue, because the schedule issue was closed as covered by it; - the revocation row points to the RFC 7009 endpoint issue; grant storage can already revoke, only the endpoint is missing; - the issuer-as-audience row says v0.58.0 and drops "needs a release"; - a new row for clock skew on client assertions, which fosite does not allow. Lost response at session open: the broker resends once with a new client assertion, instead of failing at once. If AS-1 never processed the request, the resend succeeds. If it did, AS-1 refuses it with token_used and, when the same client reuses the source jti, also revokes the grant that jti created, so no orphan is left. A broker crash before the resend still leaves an orphan until its hard expiry. The broker never presents one spent access token for two sessions, because the second use would revoke the first session's grant. Co-Authored-By: mecatl <noreply@mecatl.dev>
ChrisJBurns
approved these changes
Oct 28, 2026
ChrisJBurns
added a commit
that referenced
this pull request
Oct 28, 2026
#2587 settled the lost-response rule (resend once with a new client assertion), so this branch drops its own token_used rule for lost responses and 502/504, and the step (5) exception it implied. The token, stream, and owner rules stay. Co-authored-by: Mecatl <noreply@mecatl.dev>
2 tasks done
ChrisJBurns
added a commit
that referenced
this pull request
Oct 28, 2026
Design PR #2587 makes the broker resend a lost session-open request once, with the same access token and a new client assertion. The ErrNoResponse note applies at session open again, and the lost-response tests become two cases: a resend after a processed request gets token_used, and a resend of a request AS-1 never processed converts. DropNextExchange injects the second case. Co-authored-by: Mecatl <noreply@mecatl.dev>
This was referenced Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two follow-ups to the generic-grants design (PR #2575).
1. The ToolHive work list matches what exists in ToolHive now.
expandiatexactly, so check 5 in step (5) describes behaviour ToolHive does not have yet.2. The lost-response rule at session open. The design said: after a lost response, do not resend the access token; refresh it and open again. That leaves an orphan grant, valid until its hard expiry. It also treats two different cases alike: the request that never reached AS-1, and the request AS-1 processed but whose response was lost.
Now the broker resends once, with the same request and a new client assertion.
token_used. AS-1 also revokes the grant the first request created, when the same client reuses thejti(Revoke the grant a reused subject token created toolhive#6973, phase 2). The client then refreshes its access token and opens again.This fits the comment on
ErrNoResponseinserverrefusal.go: the server may or may not have processed the request, and callers may retry once with a new client assertion.Until stacklok/toolhive#6973 lands, a refused resend leaves the orphan: that is the existing phase 1 shortcut, so nothing changes for phase 1.
Issue relationship
Relates to #2451, #2492, #2543, #2570
Relates to stacklok/toolhive#6973, stacklok/toolhive#6972, stacklok/toolhive#6966, stacklok/toolhive#6954
Type of change
Test plan
Baseline checks
task lint)task test)task test:race)go run ./cmd/mecademo)task docs). Not run in full;matlatl check docs/drafts/agent-identity --strictgives the same result as on main (3 links out of the directory, 0 broken anchors). No code changed.task site:build)task api:check)task api:update+engine/CHANGELOG.mdChanges
docs/drafts/agent-identity/credentials-and-lifecycle.mddocs/drafts/agent-identity/implementation.mdUser-facing change
None. Design drafts only.
Special notes for reviewers
@ChrisJBurns, what this means for your work:
"Convert the sign-in at session open" (#2492) has three lines to change, and nothing else:
ErrNoResponse), resend once with the same request and a new client assertion. A success continues as usual. Atoken_usedis reported as the 401 you already planned, so the client refreshes and opens again. Never resend for any other error.token_used, and a resend of a request that was never processed succeeds.ErrNoResponse's retry note does not apply to session open" is no longer needed. The note applies, once.The "Done when" line "a lost response (a 401, and a fresh token opens again)" follows the same change.
The open question in that ticket about the reopen after registration now has an answer: do not open a second session with a spent token in the same request; return the 401.
Phase 1 note: replay-revoke is phase 2 in ToolHive, so the harness's AS-1 fake does not need to revoke on a reused
jtiyet. Until it does, a refused resend leaves the orphan, which is the existing phase 1 shortcut. The test lines inimplementation.mdsay "once replay-revoke lands" where this matters.Unchanged for you: the reason allowlist (add
revoked,parent_revoked, andnot_issuable; dropsuperseded), the harness's AS-1 fake, and everything else in the ticket's first commit. I can edit the ticket text myself if you would rather not.Review focus: the "Why" paragraph of step (3), which now explains the resend and the one rule that keeps it safe.