Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bedrock Credentials

Everything heph does on Bedrock is signed with AWS credentials, and the credentials most people have are temporary: they come from a role, an SSO login, or an MFA session, and they expire after an hour or a few. A heph session outlives them. This chapter is how to give heph credentials that keep working for as long as the session does, written for someone setting this up for the first time.

If you already know the AWS credential chain, the short version: heph asks the chain for credentials when the ones it holds are a minute from expiring, so any source the chain can renew keeps heph signed in. Static credentials in the environment are the one source nothing can renew. Skip to Which to choose.

What goes wrong, and why

The simplest way to run anything with AWS credentials is to put them in the environment. That is what these do:

aws-vault exec work -- heph              # exports fixed credentials, then runs heph
eval "$(aws configure export-credentials --profile work --format env)"; heph

Both set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN to values that were true at that moment. An hour later the session token has expired, and the values in heph's environment are still the old ones. Nothing can change a running process's environment from outside, so nothing can renew them. When that happens, the next request fails:

-- turn failed: provider bedrock: AWS credentials expired at 2026-09-21T23:31:50Z:
they were exported into the environment as fixed values, which nothing can renew.
Start heph again under fresh ones, or give it credentials that renew themselves:
`aws-vault exec --ecs-server <profile> -- heph`, an SSO profile, or a profile
with `credential_process` (the book: Bedrock Credentials)

The turn's work is kept; you restart heph under fresh credentials and continue the conversation. But you would rather not have to.

The fix is to give heph not credentials but a way to get credentials. The AWS SDK inside heph knows several such ways, and asks them again whenever what it holds is about to expire. Three are practical on a developer machine. All three work with the [providers.bedrock] section as heph writes it on the first run; you change how you start heph, or one line in ~/.aws/config, not heph's config.

Which to choose

You haveUse
aws-vault, and you run everything through aws-vault execaws-vault's credential server: add --ecs-server to the command you already type
An IAM Identity Center (SSO) login and the AWS CLIAn SSO profile: no aws-vault needed
aws-vault, and you want plain heph to work with no wrapperA credential_process profile
A role to assume on top of any of theseAny of the three; see Assuming a role
An EC2 instance or container with a roleNothing to do: the instance or task role renews itself

aws-vault's credential server

aws-vault keeps your long-lived AWS keys in the OS keychain and hands out short-lived session credentials. Its usual mode, aws-vault exec profile -- command, exports those credentials into the command's environment, which is the arrangement that expires under heph. Its --ecs-server mode does something different: it starts a small HTTP server on localhost for the life of the command, and tells the SDK where it is. The SDK fetches credentials from the server when it needs them, and aws-vault renews them behind the scenes.

Setup

You need aws-vault with a profile in it. If you have been using aws-vault, you have this. If not:

aws-vault add work        # prompts for an access key id and secret; stores them in the keychain

and a profile in ~/.aws/config:

[profile work]
region = us-west-2

Running heph

aws-vault exec --ecs-server work -- heph

That is the whole change: --ecs-server (or -s) where you used to have nothing. Everything else about aws-vault exec still applies: --duration to ask for a longer session, --mfa-token if the profile needs one, and the prompt for your keychain or MFA code at the start.

What is happening

Run the same with env instead of heph and compare:

$ aws-vault exec work -- env | grep ^AWS_
AWS_ACCESS_KEY_ID=ASIA...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=...
AWS_CREDENTIAL_EXPIRATION=2026-09-22T18:31:32Z
AWS_REGION=us-west-2

$ aws-vault exec --ecs-server work -- env | grep ^AWS_
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://127.0.0.1:34567/
AWS_CONTAINER_AUTHORIZATION_TOKEN=...
AWS_REGION=us-west-2

Without --ecs-server, the credentials themselves are in the environment, with an expiry an hour out. With it, the environment holds only an address and a token for reaching aws-vault's server. The SDK's default chain includes "container credentials" as a source, originally for ECS tasks, and follows that address. Each answer from the server carries an Expiration; when heph's credentials come within a minute of it, heph asks the server again, and aws-vault, still running as heph's parent, returns fresh ones, renewing the session against AWS as needed.

The server dies with the command, so the address is useless to anything else once heph exits, and the authorization token stops anything else on the machine from asking while it runs.

Checking it works

aws-vault exec --ecs-server work -- heph wire-check
wire check passed
  provider     bedrock
  model        us.anthropic.claude-haiku-4-5-20251001-v1:0
  ...

wire-check signs and sends one small request. If it passes, a session will.

If it does not

no identity-based policy allows the bedrock:InvokeModelWithResponseStream action: the credentials work but the profile's IAM identity may not call Bedrock. Use a profile that can; often that means a role (Assuming a role).

aws-vault: error: exec: running in an existing aws-vault subshell: you are already inside an aws-vault exec shell ($AWS_VAULT is set). Run from a plain shell, or unset AWS_VAULT first.

The SDK ignores the server and complains about missing credentials: the container provider needs AWS_CONTAINER_CREDENTIALS_FULL_URI, which aws-vault sets. If something in your shell startup clears AWS_* variables, it clears this too.

In tmux

Start the heph pane with the wrapped command and leave it: aws-vault exec --ecs-server work -- heph. The keychain or MFA prompt happens once, when the pane starts. A convenient alias:

alias heph-work='aws-vault exec --ecs-server work -- heph'

An SSO profile

If your organization signs in to AWS through IAM Identity Center (the service formerly called AWS SSO), the AWS CLI can hold that login for you and the SDK inside heph can turn it into credentials, renewing them as long as the login lasts. No aws-vault is involved.

Setup

You need the AWS CLI v2. Configure a profile for the SSO login:

aws configure sso

It asks for a session name, the start URL your administrator gave you (it looks like https://d-xxxxxxxxxx.awsapps.com/start or https://yourcompany.awsapps.com/start), the SSO region, then opens a browser for you to sign in, then lists the accounts and roles you may use. Pick one, and it writes two sections to ~/.aws/config:

[sso-session mycompany]
sso_start_url = https://d-xxxxxxxxxx.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access

[profile work]
sso_session = mycompany
sso_account_id = 123456789012
sso_role_name = DeveloperAccess
region = us-west-2

The sso-session is the login; the profile is one account and role reached through it. You may have several profiles on one session. Any profile of this shape, however it was written, works.

Running heph

Sign in, then run heph with the profile named:

aws sso login --profile work
AWS_PROFILE=work heph

or put the profile in heph's config so you can type heph alone:

# ~/.config/heph/config.toml
[providers.bedrock]
profile = "work"

aws sso login opens a browser; you approve the device code it shows, and it writes an access token to ~/.aws/sso/cache/. It does not touch the environment, so nothing is fixed at the moment you ran it.

What is happening

Each time heph needs credentials, the SDK reads the cached SSO token and asks Identity Center for role credentials, which come with an expiry, usually an hour. When those near expiry heph asks again, and gets a new hour, for as long as the token is valid. The token itself lasts as long as your administrator configured the session to: eight hours, a day, longer.

When the token lapses, requests fail with a message about loading the cached SSO token. Run aws sso login --profile work again, in any pane; you do not restart heph. The next credential refresh finds the new token.

Checking it works

AWS_PROFILE=work heph wire-check

If it does not

failed to load the cached SSO token: you have not run aws sso login for this session, or the token has expired. If you normally sign in through aws-vault (aws-vault login), note that aws-vault keeps its own token store; the SDK reads only ~/.aws/sso/cache/, so aws sso login is still needed.

The SSO session associated with this profile has expired or is otherwise invalid from aws sso login: run it again; it will open the browser.

Sign-in on a machine without a browser: aws sso login --profile work --no-browser prints the URL and a code:

Browser will not be automatically opened.
Please visit the following URL:

https://oidc.us-east-1.amazonaws.com/device?user_code=XXXX-XXXX

Open it anywhere you have a browser, on another machine if need be, and enter the code.

The profile has no sso_session, only sso_start_url in the profile itself: that is the older (legacy) SSO form. The SDK reads it too, but its tokens cannot be refreshed, only re-issued by logging in again; running aws configure sso to make a session-based profile is worth the two minutes.

A credential_process profile

The AWS config file can say "to get credentials for this profile, run this program". The SDK runs it, reads credentials and an expiry from its output, and runs it again when they near expiry. This is credential_process, and it lets plain heph with no wrapper renew through aws-vault, or through any tool that can print credentials.

With aws-vault

Add a profile to ~/.aws/config whose credentials come from aws-vault export:

[profile work]
region = us-west-2

[profile work-heph]
credential_process = aws-vault export --format=json work
region = us-west-2

work is your existing aws-vault profile; work-heph is a second profile that gets its credentials by asking aws-vault for work's. Then tell heph to use it:

# ~/.config/heph/config.toml
[providers.bedrock]
profile = "work-heph"

Now heph alone works, with no aws-vault exec around it. The first request of the session runs aws-vault export; when the credentials near expiry, an hour later, the SDK runs it again. aws-vault answers from the session it keeps in your keychain, so this is normally silent. If it has to prompt, for a keychain passphrase or an MFA code, the prompt lands under heph's screen where you cannot answer it; run aws-vault exec work -- true in another pane first, which stores a session, and heph's request then finds it.

A credential_process profile cannot also be the profile it exports: [profile work] must not name a credential_process that exports work, or aws-vault and the SDK call each other forever. Hence the second profile.

With the AWS CLI

The AWS CLI can be the program too. aws configure export-credentials prints credentials for a profile in exactly the format credential_process expects (its default --format is process), so a profile can borrow another profile's credentials, an SSO profile's included:

[profile work-heph]
credential_process = aws configure export-credentials --profile work
region = us-west-2

This is useful when work is an SSO profile and you want the profile = "work-heph" indirection for some other reason; with an SSO profile you can also just name it directly, as An SSO profile shows.

With any program

The program must print one JSON object to stdout:

{
  "Version": 1,
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "SessionToken": "...",
  "Expiration": "2026-09-22T18:31:32Z"
}

Version is always 1. Expiration is RFC 3339, UTC. Leave Expiration out and the SDK treats the credentials as permanent and never asks again, which is right for a long-lived key and wrong for anything temporary. The program runs with the environment heph has, in an unspecified directory; give it absolute paths.

A script that reads from a password manager, or from a company tool that issues credentials, is a few lines:

#!/bin/sh
# ~/bin/aws-creds-from-op: credentials kept in 1Password as a JSON document
exec op read "op://Private/aws-work-session/credential"
[profile work-heph]
credential_process = /home/you/bin/aws-creds-from-op
region = us-west-2

Checking it works

heph wire-check

To see the chain decide, turn on the SDK's own logging:

RUST_LOG=aws_config=debug heph wire-check 2>&1 | grep -o '"provider":"[A-Za-z]*"'

prints each source tried and the one that answered, Profile for this arrangement.

If it does not

Error retrieving credentials from external process, could not parse response: the program's output is not the JSON above. Run it by hand and look; a common cause is an empty Expiration because the source it read from was not set, or extra text printed before the JSON.

external process exited with code exit status: 1. Stderr: aws-vault: error: export: profile 'work' not found: aws-vault reads ~/.aws/config through the same AWS_CONFIG_FILE variable the SDK does. If you point that variable at another file for testing, both profiles must be in it.

The request hangs, or the screen shows stray characters: the program is prompting under heph's screen. Interrupt the turn with Esc, y, run aws-vault exec work -- true in another pane to satisfy the prompt there, and send the prompt again; heph's next request finds the stored session.

Assuming a role

Often the identity that may call Bedrock is a role, not your user. All three arrangements handle that in ~/.aws/config, the same way the AWS CLI does:

[profile work]
region = us-west-2

[profile bedrock-dev]
source_profile = work
role_arn = arn:aws:iam::123456789012:role/BedrockDeveloper
region = us-west-2

Then use bedrock-dev wherever the examples above say work: aws-vault exec --ecs-server bedrock-dev -- heph, profile = "bedrock-dev", credential_process = aws-vault export --format=json bedrock-dev. The role's credentials expire on their own schedule (an hour by default) and renew the same way. For an SSO profile, the role is sso_role_name; there is nothing extra to assume.

If the role needs MFA, add mfa_serial = arn:aws:iam::123456789012:mfa/you to the profile; aws-vault prompts for the code when it first needs it.

What heph does with credentials

For completeness, the behavior all of this relies on:

  • Heph asks the SDK's default credential chain for credentials when it holds none, and again when the ones it holds are within one minute of their stated expiry. Credentials that state no expiry are asked for again after fifteen minutes. These are the AWS SDK's own margins.

  • Between refreshes, every round of every turn signs with the credentials in hand; a refresh is one call to the chain, not one per round.

  • The chain is consulted in the SDK's standard order: the environment; then the profile named by [providers.bedrock] profile or AWS_PROFILE or default, whether it holds static keys, SSO, credential_process, or a role over any of those; then a web identity token; then container credentials (the --ecs-server case); then instance metadata. The first that answers wins, so a [default] profile with keys in ~/.aws/credentials takes precedence over aws-vault's server.

  • If the environment holds fixed credentials with AWS_CREDENTIAL_EXPIRATION in the past, heph refuses the request before signing it, with the message shown at the top of this chapter. A 403 from Bedrock about an expired security token is reported the same way.

  • Heph never writes credentials anywhere: not to its database, not to its logs, not to config.

Checking any of them

heph wire-check

passes only if credentials resolve, sign, and are accepted by Bedrock for the coding tier's model. Run it once when you set an arrangement up, and again if a session fails in a way you do not understand; its error is the SDK's, in full.

RUST_LOG=aws_config=debug heph wire-check 2>&1 | grep -o '"provider":"[A-Za-z]*"'

shows each source of the chain as it is tried and which one supplied the credentials: Environment, Profile, EcsContainer, and so on.