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 have | Use |
|---|---|
aws-vault, and you run everything through aws-vault exec | aws-vault's credential server: add --ecs-server to the command you already type |
| An IAM Identity Center (SSO) login and the AWS CLI | An SSO profile: no aws-vault needed |
aws-vault, and you want plain heph to work with no wrapper | A credential_process profile |
| A role to assume on top of any of these | Any of the three; see Assuming a role |
| An EC2 instance or container with a role | Nothing 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] profileorAWS_PROFILEordefault, 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-servercase); then instance metadata. The first that answers wins, so a[default]profile with keys in~/.aws/credentialstakes precedence over aws-vault's server. -
If the environment holds fixed credentials with
AWS_CREDENTIAL_EXPIRATIONin the past, heph refuses the request before signing it, with the message shown at the top of this chapter. A403from 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.