Repository navigation
fix(stt): sign a Transcribe stream with its opening credentials - #17924
amadsalmon wants to merge 1 commit into
Conversation
amazon_transcribe asks the credential resolver again for every audio event, and each event's signature chains from the opening request's. _ProfileCredentialResolver asked the boto3 session every time, so a profile whose credentials refreshed mid-stream signed the next event with a different key, and Transcribe refused it with "the request signature we calculated does not match". Resolve once per resolver. One is built per live stream and per voice memo. A live stream is capped at five minutes, inside the ten minutes botocore leaves on any credentials it hands out. Part of kirodotdev#17842
f6267fa to
76f0b16
Compare
|
The workflow runs for the current head are waiting for maintainer approval (fork PR, all at |
|
Status for head
A fork author cannot rerun jobs. Once the rate limit has reset, could a maintainer run: |
Opus 5.5 Review (fork) — ✅ no blocking findingsReviewed |
|
Taking this over in #19550 since it has been idle; you are credited as co-author on the commits. Thanks for the groundwork. |
@buluoray hi Ray, this PR is not idle. It is simply pending maintainer action. I opened it only five days ago, but the issue is that the CI checks are so flaky and unreliable that it makes it hard to keep up with them and get them to pass to green. I would appreciate some more support from the maintainers of this package to make it easier to pass the CI checks, without that support being taking over my PRs completely. |
…s credentials (#19550) * fix(stt): end dictation when Transcribe drops the stream Amazon Transcribe can end the result stream mid-dictation (a refused event signature, 15 s without audio). The gateway only awaited the result stream at teardown, so the socket stayed open and the dashboard kept showing Listening while every later word was lost. A watcher task now waits on the result stream. If it ends before the client stops, it sends an stt_session_failed error frame and closes the socket, the way the apple relay already does. The duration cap and the watcher share one first-claimant record, so only one frame goes out and stt_stream_end records the cause that came first. The new tests bound their reads with default_timeout() from kiro_crew.testing.wait, which replaced _AUDIT_WAIT_TIMEOUT_SECS on main. Supersedes #17925. Co-authored-by: Amad Salmon <amad.salmon@icloud.com> * fix(stt): sign a Transcribe stream with its opening credentials The streaming SDK asks the credential resolver again for every audio event, and each event's signature chains from the opening request's. _ProfileCredentialResolver asked the boto3 session every time, so a profile whose credentials refreshed mid-stream signed the next event with a different key and Transcribe refused it with "signature does not match". The resolver, built once per stream, now resolves once and returns the same credentials on every later call. botocore refreshes credentials with under ten minutes left before handing them out, so the pinned ones outlive the five-minute stream cap. Supersedes #17924. Co-authored-by: Amad Salmon <amad.salmon@icloud.com> --------- Co-authored-by: Amad Salmon <amad.salmon@icloud.com>
|
Thank you, @amadsalmon. This fix is now on main: #19550 (commit 97beb72) carries "fix(stt): sign a Transcribe stream with its opening credentials" with you as co-author, and its message says it supersedes #17924. Closing this one as already merged. If anything here did not make it in, please reopen. |
Problem / Motivation
Goal: A live Transcribe dictation on a named AWS profile is not cut off because the profile's credentials refreshed mid-stream.
Amazon Transcribe ended several live dictations 40 to 138 seconds in with "The request signature we calculated does not match the signature you provided." The streaming SDK asks the credential resolver again for every audio event, and each event's signature chains from the previous one, starting at the opening request.
_ProfileCredentialResolverasked the boto3 session every time, so a profile whose credentials refreshed during the stream signed the next event with a different key.Why it matters
The stream dies part-way through and the rest of the dictation is lost. Profiles backed by
credential_processor SSO refresh on their own schedule, so this can hit any session that happens to straddle a refresh.Not a goal
What changed (motivation → approach → change)
A stream should be signed with one key from start to end, so
_ProfileCredentialResolver(transcribe.py) now resolves the profile's credentials once and returns the same ones on every later call. One resolver is built per live stream and per voice memo, so a new dictation still picks up fresh credentials. A live stream is capped at five minutes, and botocore refreshes credentials that have less than ten minutes left before handing them out, so the pinned credentials normally outlive the stream. The stt-streaming spec states this for the named-profile path.Backwards compatibility
Compatible: the resolver's interface and callers are unchanged; it returns the same credentials it would have returned on the first call.
Removes nothing: no field, setting or reader goes away. A stream whose credentials were already close to expiry at open fails at expiry, where before it failed at the first refresh with a signature error.
Tests
test_a_stream_keeps_the_credentials_it_opened_with: real botocoreRefreshableCredentialsinside the refresh window, where every read refreshes to new keys; the resolver keeps returning the opening keys and asks the session once.test_none_credentials_raisesnow builds the resolver through its constructor withboto3patched, instead of__new__plus a hand-set attribute.prove.py: PROVEN (the new test fails with the fix reverted). The two resolver tests passed 20/20 at-n0.Manual verification
N/A — unit coverage sufficient: the test drives botocore's real refresh logic with an injected clock, which is the exact mechanism that changes the key.
Related Issues
Part of #17842
Pattern harvest
Not generalizable: specific to an SDK that re-resolves credentials per event while chaining signatures across events.
Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)