When to use Bedrock
The bedrock provider is the right choice when:
- You are on AWS and want Claude billed through Bedrock rather than the Anthropic API.
- You need SigV4 auth via the standard AWS credential chain (env vars,
~/.aws/credentials, IMDS, IRSA on EKS). - You want to keep model traffic inside a single AWS region for data-residency reasons.
- You need to route model traffic through a VPC endpoint so it never touches the public internet.
- You want cross-account access via
sts:AssumeRole.
Configuration
provider: bedrock
bedrock:
region: us-east-1
model: anthropic.claude-sonnet-4-6-20260101-v1:0
profile: default
max_tokens: 4096
| Field | Default | Effect |
|---|---|---|
region |
required | AWS region. Bedrock model availability is regional; check the AWS console. |
model |
required | Bedrock model ID. Anthropic Claude IDs follow the shape anthropic.claude-<name>-<date>-<version>:<revision>. |
profile |
empty | Credentials profile from ~/.aws/credentials. Empty falls through the standard credential chain. |
max_tokens |
SDK default | Caps output tokens per completion. |
Credential chain
The provider constructs a Bedrock client via awsconfig.LoadDefaultConfig, which walks the standard chain in order:
- Environment (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN). - Shared credentials file (
~/.aws/credentials), narrowed byprofileif set. - Shared config file (
~/.aws/config). - IAM Roles for Tasks (ECS) / IAM Roles Anywhere.
- EC2 IMDS (v2).
- IRSA — the IAM role attached to a Kubernetes service account (EKS).
None of these are configured through rousseau; the SDK handles resolution.
Required IAM permissions
The minimum policy the caller must be able to assume:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-*"
}
]
}
Scope the Resource to the specific model family you plan to invoke. Broader wildcards work but are usually overkill.
Wire format
The provider sends the standard Anthropic messages JSON body (anthropic_version, messages, system, tools, max_tokens) to bedrock:InvokeModel, and receives the same shape back. This mirrors the Anthropic direct API — tool use, stop reasons, and usage counters are the same.
Streaming uses bedrock:InvokeModelWithResponseStream with the SDK's event-stream decoder.
Streaming
The provider implements agent.StreamingProvider. Streaming is used automatically in rousseau chat.
Tool use
Tool definitions from the Registry are converted to Bedrock's tool JSON in internal/llm/bedrock/client.go. Approval policies apply.
Auth pattern by deployment
For local dev, use a named profile with SSO or long-lived keys:
aws configure sso --profile rousseau-dev
aws sso login --profile rousseau-dev
bedrock:
region: us-east-1
profile: rousseau-dev
model: anthropic.claude-sonnet-4-6-20260101-v1:0
profile: is honoured because rousseau passes awsconfig.WithSharedConfigProfile(cfg.Profile) when non-empty (see internal/llm/bedrock/client.go line 63). Omit profile to fall through the default chain.
Attach an instance profile with bedrock:InvokeModel permission (see the IAM policy below), then leave profile empty:
bedrock:
region: us-east-1
model: anthropic.claude-sonnet-4-6-20260101-v1:0
The SDK resolves credentials from IMDS v2 automatically. No env vars, no profile file needed.
IAM Roles for Service Accounts (IRSA) is the recommended pattern on EKS. Attach a role to the pod's service account:
apiVersion: v1
kind: ServiceAccount
metadata:
name: rousseau
namespace: agents
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/rousseau-bedrock
The role's trust policy binds it to the EKS OIDC provider and the service account. See Guides: Kubernetes deployment for the full example.
Rousseau lives in Account A, Bedrock lives in Account B. Configure a role assumption:
~/.aws/config:
[profile rousseau]
role_arn = arn:aws:iam::222222222222:role/rousseau-bedrock
source_profile = default
region = us-east-1
The target role in Account B has bedrock:InvokeModel on the model, and a trust policy allowing Account A's principal to assume it. Then:
bedrock:
region: us-east-1
profile: rousseau
model: anthropic.claude-sonnet-4-6-20260101-v1:0
The SDK handles the STS AssumeRole roundtrip transparently.
Least-privilege IAM policy
The minimum policy the caller must be able to assume:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-*"
}
]
}
Scope the Resource to the specific model family. Broader wildcards work but grant more than needed. For provisioned throughput, add the ARN of your provisioned model as a second resource.
Trust policy for cross-account (Account B, the model-hosting side):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:role/rousseau-caller" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId": "rousseau-prod" } }
}]
}
ExternalId is required by AWS security guidance for third-party cross-account access.
VPC endpoints
For deployments that must not reach the public internet, create an interface VPC endpoint for Bedrock in your VPC:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.us-east-1.bedrock-runtime \
--subnet-ids subnet-aaa subnet-bbb \
--security-group-ids sg-xxx
The AWS SDK will automatically resolve through the endpoint if the security group and route table allow it. No rousseau-side config change is needed — this is transparent to the provider.
Model availability by region
Availability shifts as AWS rolls out new snapshots. Snapshot as of mid-2026:
| Model | us-east-1 | us-west-2 | eu-west-2 | eu-central-1 | ap-southeast-1 | ap-northeast-1 |
|---|---|---|---|---|---|---|
| Claude Sonnet 4.6 | yes | yes | yes | yes | yes | yes |
| Claude Opus 4.6 | yes | yes | limited | limited | no | no |
| Claude Haiku 4.6 | yes | yes | yes | yes | yes | yes |
Gotchas
- Model IDs change per region.
anthropic.claude-sonnet-4-6-20260101-v1:0inus-east-1may be a different snapshot ineu-west-2. Check the Bedrock console. - Access must be granted per model. Even with IAM allowing
InvokeModel, Bedrock requires you to click through Model access > Request access in the console before the first call succeeds. - Throttling. Bedrock enforces per-account and per-model concurrency limits (tokens-per-minute and requests-per-minute). Set
max_tokensconservatively. - Provisioned throughput. If you have provisioned throughput, pass the provisioned model ID (
arn:aws:bedrock:us-east-1:<account>:provisioned-model/…) asmodel. - Streaming decoder failures. The event-stream format changed subtly between SDK versions. Pin
aws-sdk-go-v2/service/bedrockruntimeto a known-good version and re-test on every bump.
Troubleshooting
AccessDeniedException: You don't have access to the model
Two separate checks: (1) the caller's IAM policy allows bedrock:InvokeModel on the model ARN, and (2) the account has explicitly requested access to the model in the Bedrock console. Item 2 catches most first-time users.
ValidationException: The model ID isn't valid
The model ID string does not match a model available in the configured region. Copy the exact ID from the Bedrock console (Providers > Anthropic > Model catalog) rather than typing it — the date and version suffixes must match exactly.
ThrottlingException
You hit a token or request-per-minute quota. Options: (1) request a service quota increase, (2) queue calls in the caller with exponential backoff, (3) switch to provisioned throughput.
bedrock: parse response: json: — malformed JSON
The response body is not the expected Anthropic-on-Bedrock shape. Usually indicates a non-Anthropic model was passed as model; buildBedrockBody in internal/llm/bedrock/client.go only produces the Anthropic wire format.
VPC endpoint unreachable — dial tcp: no route to host
The pod/instance cannot reach the endpoint's ENIs. Check the security group on the endpoint (must allow port 443 from the caller's SG), the endpoint's subnet route table, and DNS resolution (the endpoint requires private DNS enabled on the VPC).
Related pages
- Providers: Anthropic — same wire format, direct API path.
- Guides: Kubernetes deployment — IRSA setup.
- Guides: Enterprise onboarding — platform-team checklist.
- Guides: Rate limits — throttling handbook.
- Security — trust boundaries and network egress.
Further reading
internal/llm/bedrock/client.go—Complete, message conversion, wire types.internal/config/config.go—BedrockConfigstruct.- AWS docs: Amazon Bedrock IAM permissions.
- AWS docs: Bedrock interface VPC endpoints.