Skip to content

docs(agent-identity): reconcile the ToolHive work list and settle the lost-response rule - #2587

Merged
ChrisJBurns merged 1 commit into
mainfrom
docs/reconcile-work-list
Oct 28, 2026
Merged

ChrisJBurns merged 1 commit into
mainfrom
docs/reconcile-work-list

Conversation

@jhrozek

@jhrozek jhrozek commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Two follow-ups to the generic-grants design (PR #2575).

1. The ToolHive work list matches what exists in ToolHive now.

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.

  • If AS-1 never processed it, the resend succeeds.
  • If it did, check 5 refuses the resend with token_used. AS-1 also revokes the grant the first request created, when the same client reuses the jti (Revoke the grant a reused subject token created toolhive#6973, phase 2). The client then refreshes its access token and opens again.
  • A broker crash before the resend still leaves an orphan until its hard expiry. Sign out everywhere ends it.
  • The broker never presents one spent access token for a second session, because the second use would revoke the first session's grant.

This fits the comment on ErrNoResponse in serverrefusal.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

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe):

Test plan

Baseline checks

  • Linting (task lint)
  • Fast offline test suite (task test)
  • Pre-review race suite (task test:race)
  • Offline demo (go run ./cmd/mecademo)
  • Markdown changed: docs generation/link checks (task docs). Not run in full; matlatl check docs/drafts/agent-identity --strict gives the same result as on main (3 links out of the directory, 0 broken anchors). No code changed.
  • User docs/user-facing behavior changed: site build (task site:build)
  • Guarded engine API affected: compatibility check (task api:check)
  • Intentional engine API change: task api:update + engine/CHANGELOG.md

Changes

File Change
docs/drafts/agent-identity/credentials-and-lifecycle.md Step (3): check 5, the lost-response row, the crash paragraph, and the "Why" paragraph
docs/drafts/agent-identity/implementation.md Session-open tests; ToolHive work list rows

User-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:

  • "no retry with the same access token: after a lost response the open fails as a 401…" becomes: after a lost response (ErrNoResponse), resend once with the same request and a new client assertion. A success continues as usual. A token_used is reported as the 401 you already planned, so the client refreshes and opens again. Never resend for any other error.
  • In the first commit: "a retry after a lost response with a fresh access token" becomes two tests: a resend after a processed request is refused with token_used, and a resend of a request that was never processed succeeds.
  • "document … that 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 jti yet. Until it does, a refused resend leaves the orphan, which is the existing phase 1 shortcut. The test lines in implementation.md say "once replay-revoke lands" where this matters.

Unchanged for you: the reason allowlist (add revoked, parent_revoked, and not_issuable; drop superseded), 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.

… 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
ChrisJBurns merged commit bd2d858 into main Oct 28, 2026
31 checks passed
@ChrisJBurns
ChrisJBurns deleted the docs/reconcile-work-list branch October 28, 2026 19:01
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>
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>
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