RequestStateCodec's wire format is rs1.<base64url(expiry ‖ payload)>.<base64url(tag)>.
The rs1 prefix versions the format.
Nothing in the token identifies the key that produced the tag.
A server that rotates its signing key therefore has two options, and both are unpleasant:
- Rotate and break. Every
requestState in flight becomes unverifiable the moment the new
key is installed. For a multi-round tool call that is every dialogue a user has open.
- Trial-open. Keep the previous key and attempt
open_with against each key in turn. This
works — it is what this demo does — but it costs an extra HMAC verification per request for
every key in the window, and it obliges every server author to rediscover the pattern.
Why it matters here
MRTR makes the codec's key a deployment-wide shared secret: every replica that might
continue a dialogue must hold it. That is precisely the population most likely to need staged
rotation, and least able to coordinate an atomic switch.
JWS solves this with a kid header, JWE likewise. rs1. has no equivalent field.
Suggested fix
Add an optional key identifier to the format — rs1.<kid>.<body>.<tag>, or a length-prefixed
kid inside the authenticated region — and let RequestStateCodec hold a small keyring keyed
by it. Opening then selects one key instead of trying all of them, and a server can publish a
new key before it starts signing with it.
If a format change is unacceptable, documenting the trial-open pattern in RequestStateCodec's
type-level docs would at least stop each author from working it out alone.
RequestStateCodec's wire format isrs1.<base64url(expiry ‖ payload)>.<base64url(tag)>.The
rs1prefix versions the format.Nothing in the token identifies the key that produced the tag.
A server that rotates its signing key therefore has two options, and both are unpleasant:
requestStatein flight becomes unverifiable the moment the newkey is installed. For a multi-round tool call that is every dialogue a user has open.
open_withagainst each key in turn. Thisworks — it is what this demo does — but it costs an extra HMAC verification per request for
every key in the window, and it obliges every server author to rediscover the pattern.
Why it matters here
MRTR makes the codec's key a deployment-wide shared secret: every replica that might
continue a dialogue must hold it. That is precisely the population most likely to need staged
rotation, and least able to coordinate an atomic switch.
JWS solves this with a
kidheader, JWE likewise.rs1.has no equivalent field.Suggested fix
Add an optional key identifier to the format —
rs1.<kid>.<body>.<tag>, or a length-prefixedkidinside the authenticated region — and letRequestStateCodechold a small keyring keyedby it. Opening then selects one key instead of trying all of them, and a server can publish a
new key before it starts signing with it.
If a format change is unacceptable, documenting the trial-open pattern in
RequestStateCodec'stype-level docs would at least stop each author from working it out alone.