Scope check
Due diligence
What problem does this solve?
Problem
RubyLLM’s Bedrock configuration currently centers on static credential values:
bedrock_api_key
bedrock_secret_key
bedrock_session_token
That works for long-lived credentials, but it is fragile for normal AWS production environments that use refreshable credentials, especially EKS/IRSA, ECS task roles, instance profiles, SSO, process credentials, or assume-role chains.
In our EKS/IRSA deployment, Bedrock requests started failing after the STS token lifetime elapsed:
RubyLLM::ForbiddenError: The security token included in the request is expired
The AWS SDK credential provider can refresh these credentials, but RubyLLM’s Bedrock integration snapshots the current values into static config strings. After expiration, RubyLLM continues signing requests with stale credentials.
Desired behavior
Proposed solution
RubyLLM should support Bedrock authentication through refreshable AWS credentials, not only static access-key strings.
A user should be able to configure Bedrock in one of these ways:
RubyLLM.configure do |config|
config.bedrock_region = "us-west-2"
# Option A: let RubyLLM resolve AWS credentials through the AWS SDK
config.bedrock_use_aws_credentials_provider = true
end
or:
RubyLLM.configure do |config|
config.bedrock_region = "us-west-2"
# Option B: provide an explicit AWS SDK credentials provider
config.bedrock_credentials_provider = Aws::CredentialProviderChain.new.resolve
end
Static credentials should remain supported for existing users.
Requirements
- Bedrock signing must work with refreshable AWS SDK credential providers.
- RubyLLM should not require users to manually copy
access_key_id, secret_access_key, and session_token into static config values when using AWS-managed credentials.
- A single Bedrock request signature must use one internally consistent credential snapshot: access key, secret key, and session token must come from the same provider read.
- Existing static credential configuration should remain backward compatible.
- The feature should work with common AWS SDK providers including IRSA/web identity, ECS credentials, instance profile credentials, SSO credentials, process credentials, and assume-role credentials.
- The feature should not require app-level timers, reinitializing RubyLLM, or manual credential refresh logic.
Why this matters
AWS SDK credential providers already know how to refresh credentials. For example, Aws::AssumeRoleWebIdentityCredentials includes AWS SDK refresh behavior and refreshes when credentials approach expiration.
RubyLLM currently loses that behavior because the provider is resolved once and its current fields are copied into strings. This makes Bedrock unusable in long-running processes with expiring AWS credentials.
Important correctness concern
A naive implementation might make bedrock_api_key, bedrock_secret_key, and bedrock_session_token dynamic getters that each call credentials_provider.credentials.
That improves freshness, but it can still be incorrect. RubyLLM’s Bedrock signer reads those fields separately while building one SigV4 signature. If the provider refreshes between reads, one signature could mix:
- session token from credential set A
- access key from credential set B
- secret key from credential set B or C
The implementation should instead ensure each signed request uses one credential snapshot.
This could be done by using AWS’s own signing machinery, by snapshotting the provider once per request internally, or by another design that provides the same guarantee.
Possible API shapes
These are suggestions, not requirements.
config.bedrock_use_aws_credentials_provider = true
This would let RubyLLM resolve credentials internally through the AWS SDK default provider chain.
config.bedrock_credentials_provider = Aws::CredentialProviderChain.new.resolve
This would let callers provide an explicit provider, useful for custom chains or tests.
Both could coexist.
Suggested test coverage
-
Bedrock can be configured with AWS-provider mode enabled and no static access-key strings.
-
Bedrock can be configured with an explicit object responding to #credentials.
-
Missing bedrock_region still fails configuration.
-
Existing static credential configuration continues to work.
-
One Bedrock signature reads credentials once, or otherwise proves that access key, secret key, and session token come from the same credential snapshot.
-
A fake refreshable provider returning different credentials on each call does not produce a mixed signature.
-
Configuration survives RubyLLM.context / config duplication if that is expected to work for provider config.
-
Signing works for both Bedrock runtime requests and Bedrock model-listing requests.
Research notes
Observed with:
ruby_llm 1.14.1
aws-sdk-core 3.244.0
aws-sigv4 1.12.1
Relevant RubyLLM areas:
lib/ruby_llm/providers/bedrock.rb
lib/ruby_llm/providers/bedrock/auth.rb
Relevant AWS SDK behavior:
Aws::CredentialProviderChain can resolve refreshable providers.
Aws::AssumeRoleWebIdentityCredentials supports refreshable IRSA credentials.
- AWS signing APIs can sign from a credentials provider and avoid manually handling separate credential fields.
Operational caveat
AWS’s provider chain checks some static credential sources before web identity. If an environment injects static AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN, the resolved provider may still be static. That is normal AWS SDK behavior, but RubyLLM should not force static credentials when refreshable AWS provider credentials are available.
Why this belongs in RubyLLM
The only way to correct this problem at present seems to be monkey patching, therefore it must either become a part of RubyLLM or be abstracted in such a way to allow it to happen without monkey patching.
Scope check
Due diligence
What problem does this solve?
Problem
RubyLLM’s Bedrock configuration currently centers on static credential values:
bedrock_api_keybedrock_secret_keybedrock_session_tokenThat works for long-lived credentials, but it is fragile for normal AWS production environments that use refreshable credentials, especially EKS/IRSA, ECS task roles, instance profiles, SSO, process credentials, or assume-role chains.
In our EKS/IRSA deployment, Bedrock requests started failing after the STS token lifetime elapsed:
The AWS SDK credential provider can refresh these credentials, but RubyLLM’s Bedrock integration snapshots the current values into static config strings. After expiration, RubyLLM continues signing requests with stale credentials.
Desired behavior
Proposed solution
RubyLLM should support Bedrock authentication through refreshable AWS credentials, not only static access-key strings.
A user should be able to configure Bedrock in one of these ways:
or:
Static credentials should remain supported for existing users.
Requirements
access_key_id,secret_access_key, andsession_tokeninto static config values when using AWS-managed credentials.Why this matters
AWS SDK credential providers already know how to refresh credentials. For example,
Aws::AssumeRoleWebIdentityCredentialsincludes AWS SDK refresh behavior and refreshes when credentials approach expiration.RubyLLM currently loses that behavior because the provider is resolved once and its current fields are copied into strings. This makes Bedrock unusable in long-running processes with expiring AWS credentials.
Important correctness concern
A naive implementation might make
bedrock_api_key,bedrock_secret_key, andbedrock_session_tokendynamic getters that each callcredentials_provider.credentials.That improves freshness, but it can still be incorrect. RubyLLM’s Bedrock signer reads those fields separately while building one SigV4 signature. If the provider refreshes between reads, one signature could mix:
The implementation should instead ensure each signed request uses one credential snapshot.
This could be done by using AWS’s own signing machinery, by snapshotting the provider once per request internally, or by another design that provides the same guarantee.
Possible API shapes
These are suggestions, not requirements.
This would let RubyLLM resolve credentials internally through the AWS SDK default provider chain.
This would let callers provide an explicit provider, useful for custom chains or tests.
Both could coexist.
Suggested test coverage
Bedrock can be configured with AWS-provider mode enabled and no static access-key strings.
Bedrock can be configured with an explicit object responding to
#credentials.Missing
bedrock_regionstill fails configuration.Existing static credential configuration continues to work.
One Bedrock signature reads credentials once, or otherwise proves that access key, secret key, and session token come from the same credential snapshot.
A fake refreshable provider returning different credentials on each call does not produce a mixed signature.
Configuration survives
RubyLLM.context/ config duplication if that is expected to work for provider config.Signing works for both Bedrock runtime requests and Bedrock model-listing requests.
Research notes
Observed with:
ruby_llm1.14.1aws-sdk-core3.244.0aws-sigv41.12.1Relevant RubyLLM areas:
lib/ruby_llm/providers/bedrock.rblib/ruby_llm/providers/bedrock/auth.rbRelevant AWS SDK behavior:
Aws::CredentialProviderChaincan resolve refreshable providers.Aws::AssumeRoleWebIdentityCredentialssupports refreshable IRSA credentials.Operational caveat
AWS’s provider chain checks some static credential sources before web identity. If an environment injects static
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, andAWS_SESSION_TOKEN, the resolved provider may still be static. That is normal AWS SDK behavior, but RubyLLM should not force static credentials when refreshable AWS provider credentials are available.Why this belongs in RubyLLM
The only way to correct this problem at present seems to be monkey patching, therefore it must either become a part of RubyLLM or be abstracted in such a way to allow it to happen without monkey patching.