-Adding a provider requires an API key. The base URL is optional.
+Adding a provider usually requires an API key. AWS Bedrock can also use
+ambient AWS credentials. The base URL is optional.
-### Provider API keys and security
+## Configuring AWS Bedrock
-Provider API keys are stored encrypted in the Coder database. They are never
-exposed to workspaces, developers, or the browser after initial entry. The
-dashboard shows only whether a key is set, not the key itself.
+AWS Bedrock supports two credential modes for Agents providers:
+
+- **Bearer token mode**: Enter a Bedrock-compatible bearer token in the
+ **API key** field when you add the provider.
+- **Ambient AWS credentials mode**: Leave the **API key** field empty. The
+ Coder server resolves credentials from the standard AWS SDK credential chain,
+ including IAM instance roles and `AWS_ACCESS_KEY_ID` /
+ `AWS_SECRET_ACCESS_KEY` environment variables.
+
+Region comes from the standard AWS SDK configuration. In most deployments, set
+`AWS_REGION` on the Coder server. Bearer token mode falls back to `us-east-1`
+when no region is configured. Ambient credentials require a region from the
+standard AWS SDK chain, for example `AWS_REGION`.
+
+The **Base URL** field overrides the Bedrock runtime endpoint. Use it for
+custom endpoints or VPC endpoints.
+
+> [!NOTE]
+> Agents Bedrock provider configuration is separate from AI Gateway Bedrock
+> flags (`CODER_AIBRIDGE_BEDROCK_*`). AI Gateway and Agents use independent
+> credential paths.
+
+## Provider credentials and security
+
+Provider API keys entered in the dashboard are stored encrypted in the Coder
+database. They are never exposed to workspaces, developers, or the browser
+after initial entry. The dashboard shows only whether a key is set, not the
+key itself.
+
+When a provider uses ambient credentials, Coder resolves them from the server
+environment at request time instead of storing a secret in the database.
Because the agent loop runs in the control plane, workspaces never need direct
access to LLM providers. See
[Architecture](./architecture.md#no-api-keys-in-workspaces) for details
on this security model.
-### Key policy
+## Key policy
-Each provider has three policy flags that control how API keys are sourced:
+Each provider has three policy flags that control how provider credentials are
+sourced:
-| Setting | Default | Description |
-|-------------------------|---------|-----------------------------------------------------------------------------------------------------|
-| Central API key | On | The provider uses a deployment-managed API key entered by an administrator. |
-| Allow user API keys | Off | Developers may supply their own API key for this provider. |
-| Central key as fallback | Off | When user keys are allowed, fall back to the central key if a developer has not set a personal key. |
+| Setting | Default | Description |
+|-------------------------|---------|--------------------------------------------------------------------------------------------------------------------------|
+| Central API key | On | The provider uses deployment-managed credentials configured by an administrator. For most providers, this is an API key. |
+| Allow user API keys | Off | Developers may supply their own API key for this provider. |
+| Central key as fallback | Off | When user keys are allowed, fall back to deployment-managed credentials if a developer has not set a personal key. |
At least one credential source must be enabled. These settings appear in the
provider configuration form under **Key policy**.
@@ -87,10 +117,11 @@ to a given developer:
| On | On | On | No | Uses central key |
When a developer's personal key is present, it always takes precedence over
-the central key. When user keys are required and fallback is disabled,
-the provider is unavailable to developers who have not saved a personal key —
-even if a central key exists. This is intentional: it enforces that each
-developer authenticates with their own credentials.
+deployment-managed credentials. When user keys are required and fallback is
+disabled, the provider is unavailable to developers who have not saved a
+personal key, even if deployment-managed credentials exist. This is
+intentional: it enforces that each developer authenticates with their own
+credentials.
## Models
@@ -196,14 +227,15 @@ fields appear dynamically in the admin UI when you select a provider.
> [!NOTE]
> Azure OpenAI uses the same options as OpenAI. AWS Bedrock uses the same
-> options as Anthropic.
+> model configuration options as Anthropic (thinking budget, reasoning
+> effort).
## How developers select models
Developers see a model selector dropdown when starting or continuing a chat on
the Agents page. The selector shows only models from providers that have valid
-API keys configured. Models are grouped by provider if multiple providers are
-active.
+credentials configured. Models are grouped by provider if multiple providers
+are active.
The model selector uses the following precedence to pre-select a model:
@@ -232,17 +264,17 @@ developers can supply their own API key from the Agents settings page.
1. Enter your API key and click **Save**.
Personal API keys are encrypted at rest using the same database encryption
-as deployment-managed keys. The dashboard never displays a saved key — only
-whether one is set.
+used for deployment-managed provider secrets. The dashboard never displays a
+saved key, only whether one is set.
### How key selection works
-When you start a chat, the control plane resolves which API key to use for
-each provider:
+When you start a chat, the control plane resolves which credential source to
+use for each provider:
1. If you have a personal key for the provider, it is used.
1. If you do not have a personal key and central key fallback is enabled,
- the deployment-managed key is used.
+ deployment-managed credentials are used.
1. If you do not have a personal key and fallback is disabled, the provider
is unavailable to you. Models from that provider will not appear in the
model selector.
@@ -251,8 +283,8 @@ each provider:
Click **Remove** on the provider card in the API Keys settings tab. If
central key fallback is enabled, subsequent requests will use the shared
-deployment key. If fallback is disabled, the provider becomes unavailable
-until you add a new personal key.
+deployment-managed credentials. If fallback is disabled, the provider becomes
+unavailable until you add a new personal key.
## Using an LLM proxy
diff --git a/docs/ai-coder/ai-gateway/setup.md b/docs/ai-coder/ai-gateway/setup.md
index 06a0e2637a..0df93376d6 100644
--- a/docs/ai-coder/ai-gateway/setup.md
+++ b/docs/ai-coder/ai-gateway/setup.md
@@ -61,6 +61,10 @@ If both are set, `CODER_AIBRIDGE_BEDROCK_BASE_URL` takes precedence.
- `CODER_AIBRIDGE_BEDROCK_MODEL` or `--aibridge-bedrock-model`
- `CODER_AIBRIDGE_BEDROCK_SMALL_FAST_MODEL` or `--aibridge-bedrock-small-fast-model`
+> [!NOTE]
+> These Bedrock settings configure AI Gateway only. To configure Bedrock as an
+> Agents provider, see [Configuring AWS Bedrock](../agents/models.md#configuring-aws-bedrock).
+
**Optional:**
- `CODER_AIBRIDGE_BEDROCK_ACCESS_KEY` or `--aibridge-bedrock-access-key`
diff --git a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx
index a0c28b4807..61f6cca95e 100644
--- a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx
+++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx
@@ -813,6 +813,158 @@ export const ProviderInvalidCredentialState: Story = {
},
};
+export const ProviderFormBedrockAmbientCredentials: Story = {
+ args: {
+ section: "providers" as ChatModelAdminSection,
+ providerConfigsData: [
+ createProviderConfig({
+ id: nilProviderConfigID,
+ provider: "bedrock",
+ display_name: "AWS Bedrock",
+ source: "supported",
+ enabled: false,
+ }),
+ ],
+ modelCatalogData: { providers: [] },
+ },
+ play: async ({ canvasElement, args }) => {
+ const body = within(canvasElement.ownerDocument.body);
+ await userEvent.click(
+ await body.findByRole("button", { name: /AWS Bedrock/i }),
+ );
+
+ const apiKeyInput = await body.findByLabelText(/^API Key$/i);
+ const createButton = body.getByRole("button", {
+ name: "Create provider config",
+ });
+
+ await expect(apiKeyInput).not.toBeRequired();
+ await expect(apiKeyInput).toHaveAttribute(
+ "placeholder",
+ "Enter bearer token",
+ );
+ await expect(
+ body.findByText(
+ "Bearer token for Bedrock authentication. Leave empty to use ambient AWS credentials.",
+ ),
+ ).resolves.toBeInTheDocument();
+ await expect(
+ body.findByText(
+ /Overrides the Bedrock runtime endpoint\.\s+Set AWS_REGION on\s+the Coder server to select the target region\./i,
+ ),
+ ).resolves.toBeInTheDocument();
+ await expect(createButton).toBeEnabled();
+
+ await userEvent.click(createButton);
+ await waitFor(() => {
+ expect(args.onCreateProvider).toHaveBeenCalledTimes(1);
+ });
+ const createProviderMock = args.onCreateProvider as ReturnType