Skip to content

Add Bedrock Mantle with native IAM credentials and GPT/Claude routing - #35

Open
edwardsb wants to merge 1 commit into
pgrundev:mainfrom
cavenine:main
Open

Add Bedrock Mantle with native IAM credentials and GPT/Claude routing#35
edwardsb wants to merge 1 commit into
pgrundev:mainfrom
cavenine:main

Conversation

@edwardsb

@edwardsb edwardsb commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Adds PGBOT_AI_PROVIDER=bedrock (alias mantle) for AWS Bedrock Mantle, with bearer-token authentication or native IAM credentials from the AWS SDK credential chain, including AWS_PROFILE.

  • Routes openai.* models through the existing Responses client and anthropic.* models through the Messages client.
  • Defaults to openai.gpt-5.6-terra. Region selection uses AWS_REGION, then AWS_DEFAULT_REGION, then the SDK configuration for IAM authentication, then us-east-1.
  • Accepts PGBOT_AI_API_KEY or AWS_BEARER_TOKEN_BEDROCK; otherwise derives short-lived bearer tokens locally from AWS credentials. IAM authentication restricts requests to the configured regional Mantle HTTPS endpoint, and redirects are disabled.
  • Recognizes the openai. model prefix for reasoning models, omits temperature for those models, and retains the upstream Responses token-budget floor and store=false behavior.
export PGBOT_AI_PROVIDER=bedrock
export AWS_PROFILE=my-profile
export AWS_REGION=us-east-1
export PGBOT_AI_MODEL=openai.gpt-5.6-terra
pgbot ask "What needs attention?" --url "$DATABASE_URL"

Validation: go test ./..., go test -race ./..., go vet ./..., and a static CLI build. Tests cover Responses request shape, IAM profile credentials, token signing and expiry, endpoint restrictions, redirect handling, and GPT/Claude routing.

The AWS SDK credential chain is intentional: native profile, SSO, and workload-role resolution and refresh support the AWS deployment use case without a separate credential-export step. Supplied bearer tokens bypass the chain. The SDK dependency tradeoff remains for maintainer approval.

Auth headers are selected from the model family, including when the base URL is overridden. Regression tests cover Anthropic without an /anthropic path and OpenAI with one. The provider list, privacy documentation (including credential-service calls), changelog, and unknown-provider error include Bedrock/Mantle.

@edwardsb

edwardsb commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

hey @alexshapalov , I work mostly in AWS and I am in the progress of standing up pgbot mcp server in https://github.com/obot-platform/obot using their MCP Gateway. I needed a way for pgbot to use AWS Bedrock via IAM. This is working in my environment, so let me know what you think. Thanks.

@alexshapalov

Copy link
Copy Markdown
Contributor

Code review

Found 3 issues, plus one decision that is the maintainer's rather than a review finding.

  1. The auth header is chosen from the request path, not the model family. bedrockModel decides anthropic from the model prefix, but the transport re-decides by strings.HasPrefix(req.URL.Path, "/anthropic/"). With PGBOT_AI_BASE_URL set to the allow-listed host without the /anthropic suffix and an anthropic.* model, the request passes the host check and goes out with Authorization: Bearer instead of x-api-key (reproduced). Pass the family into bedrockAuth instead of inferring it from the path.

}
clone := req.Clone(req.Context())
if strings.HasPrefix(req.URL.Path, "/anthropic/") {
clone.Header.Set("x-api-key", token)
} else {
clone.Header.Set("Authorization", "Bearer "+token)
}
return a.next.RoundTrip(clone)

  1. Docs and comments are only partly updated: the Provider() enum comment and the README environment table still list four providers with no bedrock, the README privacy section still names five providers and does not say that IAM resolution may call STS, SSO, or instance metadata, and there is no CHANGELOG entry (repo convention).

Generate(ctx context.Context, c Call) (*Response, error)
Provider() string // "gemini" | "openai" | "anthropic" | "xai"
Model() string // resolved model id — shown in the AI banner

  1. Minor: the new keyVarsFor branch for bedrock/mantle is unreachable because the bedrock case returns before the shared missing-key check, and the mantle alias is absent from the "unknown provider" message.

case "bedrock", "mantle":
if key == "" {
key = firstEnv("AWS_BEARER_TOKEN_BEDROCK")
}
return bedrockModel(model, base, key, httpc)

Verified as correct: the bearer-token construction matches AWS's reference token generator field for field (and the golden-signature test proves it), the 15-minute TTL is clamped to credential expiry, redirects are refused, the host is re-checked on every request, credential retrieval is lazy so no AWS network call happens before the consent prompt, and nothing secret reaches error messages.

For the maintainer. This PR adds aws-sdk-go-v2 plus its config, credentials, STS, SSO, OIDC, and IMDS modules (14 in go.mod) and grows the binary from 26.9 MB to 30.0 MB on darwin/arm64, and it rewrites the package doc that said "no vendor SDKs" and the resolve doc that said keys come only from the environment. That rule was set in the commit that introduced the OpenAI provider, restated with measurements in #29 by this PR's own author, and issue #23 records "do not require AWS credentials, CLI, or an SDK" for the Aurora work. The author's earlier #33 took the SDK-free route (user supplies AWS_BEARER_TOKEN_BEDROCK) and was closed the same day. The alternative that keeps the rule is a stdlib SigV4 presigner (about 100 lines) with credentials from AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN, which aws configure export-credentials --format env produces for any profile or SSO login; what you give up is the SDK's automatic profile, SSO, and instance-role resolution. Either is workable; it's a policy call, not a defect.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@alexshapalov

Copy link
Copy Markdown
Contributor

Correction to item 1 above: the head commit already carries an anthropic flag on the transport and picks the header from the model family, so that finding does not apply to this PR as it stands. The rest holds. Per the maintainer's decision the dependency is not accepted, so a review branch is being prepared that keeps this PR's routing, endpoint checks, token format, and tests, and replaces the AWS SDK with a standard-library SigV4 presigner reading AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN; the golden-signature test from this PR pins the two implementations to the same token. Link to follow.

@alexshapalov

Copy link
Copy Markdown
Contributor

The SDK-free rework is up as #37: your commit 2998a37 unchanged plus one commit replacing aws-sdk-go-v2 with a standard-library SigV4 presigner over environment credentials (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN, bounded by AWS_CREDENTIAL_EXPIRATION; a profile or SSO login exports them with aws configure export-credentials --format env). Your golden-signature test pins both implementations to the same token, and your routing, endpoint restriction, redirect refusal, and tests are kept as-is. Merging #37 lands this PR's commit and marks it merged. Thanks for the careful work on the token format; it made the swap mechanical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants