feat: add request-state key rotation (#1128)

* feat: add request-state key rotation

* docs: streamline request-state codec documentation

* fix: harden request-state fallback verification

* refactor: refine request-state keyring API

* docs: make request-state rotation guidance self-contained

* docs: streamline request-state keyring rustdocs

* test: streamline request-state keyring coverage

* docs: restore request-state key rotation doctest

* chore: remove manual changelog entry
This commit is contained in:
camillelawrence 2026-08-28 09:19:23 -04:00 • committed by GitHub
commit 3ef34e64e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 897 additions and 61 deletions

View file

@ -1363,11 +1363,21 @@ async fn call_tool(&self, request: CallToolRequestParams, _ctx: RequestContext<R
}
```
> **`requestState` is untrusted.** The client echoes it back verbatim, so a
> stateless server that stores meaningful data in it MUST verify integrity
> first. Enable the `request-state` feature and use `RequestStateCodec` to seal
> and open it (HMAC-tagged), or keep state server-side and use `requestState`
> only as an opaque handle.
> **`requestState` is untrusted.** [SEP-2322 requires servers to validate
> it](https://modelcontextprotocol.io/seps/2322-MRTR#protocol-requirements-for-ephemeral-workflow)
> because the client echoes it back verbatim. A stateless server that stores
> meaningful data in it MUST verify integrity first. Enable the `request-state`
> feature and use `RequestStateCodec` to seal and open it (HMAC-tagged), or keep
> state server-side and use `requestState` only as an opaque handle.
For multi-replica deployments, use `RequestStateCodec::new_with_keyring` to
rotate signing keys without invalidating in-flight requests:
1. Deploy the old and new keys everywhere, continuing to emit `rs1` with the
old key via `with_rs1_signing("old")`.
2. Start emitting `rs2` with the new key while retaining the old key via
`with_rs1_fallback("old")`.
3. After the maximum `requestState` lifetime has elapsed, remove the old key.
### Client-side

File diff suppressed because it is too large Load diff