Secrets as environment variables¶
Your agent already reads its configuration from environment variables
(DATABASE_URL, LLM_GATEWAY_KEY...), and the values live in AWS Secrets
Manager. [env] is not the way: it refuses anything that looks like a secret,
and AgentCore has no secret gateway.
secret_env and secret_env_from put those values in the agent's environment
at startup, without touching its code, and the project only holds names:
the variable's and the secret's, never the value (ADR-033 D4).
Cost and activation
- By default: off. Without
[secret_env],[[secret_env_from]],--secret-envor--secret-env-from, the SDK builds no Secrets Manager client and the plan, the image and the runtime environment are the usual ones. - Turns it on:
[secret_env]or[[secret_env_from]]inrayito-agent.toml,--secret-envor--secret-env-fromonrayito agent deploy, orsecret_env=/secretEnvonAgentProject.--create-secrets-from(orcreate_secrets_from/createSecretsFrom) also creates a secret. - AWS resources and calls: one
GetSecretValueper secret when each session starts (AgentCore, with the runtime role) or each VM starts (MicroVMs, with your credentials). With--create-secrets-from, oneCreateSecret(orPutSecretValuewhen it already exists) of the secretrayito/agents/<name>/env. - Approximate cost (us-east-1, checked 2026-09-30,
pricing): $0.05 per
10,000 calls; the secret that
--create-secrets-fromcreates, $0.40 per month until you delete it. - IAM: on AgentCore, Rayito gives the runtime role
secretsmanager:GetSecretValueon those secrets only; on MicroVMs, the same permission on your credentials (RayitoSecretsReader). For--create-secrets-from,secretsmanager:CreateSecret,secretsmanager:DescribeSecretandsecretsmanager:PutSecretValue. - How to turn it off: drop the tables or the flags; delete the created
secret (
SecretStore(prefix="").destroy(...)or the Secrets Manager console) when you no longer use it.
Declaring them¶
In the project file, each variable names a secret by its full name (without
the rayito/ prefix of SecretStore, like --readable-secret) or by its ARN;
key takes one key of a JSON secret and version_stage, another version label
(by default, AWSCURRENT):
[secret_env]
DATABASE_URL = "app/db-url"
LLM_GATEWAY_KEY = { secret = "arn:aws:secretsmanager:us-east-1:123456789012:secret:llm-AbCdEf", key = "api_key" }
[[secret_env_from]]
secret = "app/config"
prefix = "APP_"
keys = ["DB_HOST", "DB_USER"]
[[secret_env_from]] takes every key of a JSON secret as a variable, with
prefix in front. With keys it only takes those (and they must exist);
without keys, every key of the secret must be a valid variable name: none is
dropped silently.
From the CLI, the same two pieces, repeatable, add to what the file has:
rayito agent deploy ./mi-agente --target agentcore \
--secret-env DATABASE_URL=app/db-url \
--secret-env 'LLM_GATEWAY_KEY=arn:aws:secretsmanager:us-east-1:123456789012:secret:llm-AbCdEf#api_key' \
--secret-env-from 'app/config,prefix=APP_,keys=DB_HOST|DB_USER'
# separates the JSON key because Secrets Manager does not allow it in names
or ARNs. The rules are those of the file:
- the variable name must be an identifier and not a reserved name (
PATH,HOME,PYTHONPATH,NODE_OPTIONS, anything starting withAWS_,LD_orRAYITO_, and those the runtime already sets); - the secret must be a name or an ARN, never the pasted value
(
secret_value); - a variable cannot be both in
[env]and in[secret_env](conflict); - at most 20 secrets and 262144 bytes across all values.
From the SDK, with the project you already have:
AgentProject(secret_env=..., secret_env_from=...) (TypeScript secretEnv,
secretEnvFrom) takes strings, SecretEnvRef and SecretEnvSource.
How they reach the agent¶
On AgentCore the runtime only receives the RAYITO_SECRET_ENV variable,
with the variable names and the secret names or ARNs (never a value). When each
session starts, before loading your code, the agent host takes it out of its
environment, reads each secret with the runtime role (a botocore tool in the
same venv the image already ships for credentials), sets the variables and only
logs secret_env_loaded count=N. As each session is a new microVM, a new
session sees the current value of the secret after a rotation; running ones
keep theirs.
The secrets join those of --readable-secret: the runtime role can read
exactly those and nothing else (ReadableSecretArns), so the stack
template does not change. The plan says so under «Seguridad» (secretEnv)
and, with --json, in secretEnvNames.
On MicroVMs the SDK reads the values with your credentials right before starting the host and passes them as variables of its process: they go to no file and to no image. It also covers fleets and warm slots. For a model key, a gateway is still better: the agent uses the key without being able to read it.
If anything fails, the deployment fails closed and the agent does not start:
on AgentCore the host leaves the code in its failure marker; on MicroVMs, the
SDK raises AgentException(reason="runtime_error"). The detail_code says
what happened, never naming the secret or the value:
detail_code |
What happened |
|---|---|
secret_env_unavailable |
the secrets could not be read (network, credentials, the 15 s deadline) |
secret_env_denied |
the role cannot read a secret, or it does not exist for it |
secret_env_invalid |
a value is not what the reference asks for (a JSON, a string key) or goes over the cap |
secret_env_invalid_key |
a requested key is missing, or a key is not a valid variable name |
secret_env_reserved |
a secret_env_from key would give a reserved name |
Creating the secret from a .env¶
If the values are in a local .env, --create-secrets-from uploads them to a
single JSON secret, rayito/agents/<name>/env (or writes a new version if it
already exists), and wires it as a secret_env_from with those keys:
Before creating anything it shows the secret name and only the names of
the variables, and asks for confirmation (--yes, or an answer in a terminal):
the secret costs $0.40 per month. The .env reader is minimal: NAME=value,
export, quotes and comments, no interpolation. It is the only time Rayito
reads values from a .env, and only because you ask it to.
If the file is inside the agent's code, it is excluded from it: its values go
to the secret, never to the image or the template. A reserved name (for
example AWS_REGION or AWS_PROFILE from a local .env) is an error that
names the variable, never its value: remove it from the file.
Warning about what is missing¶
--check-env (check_env=True / checkEnv: true) compares the names in your
.env.example (or .env.sample, .env.template) with what the agent will
receive ([env], secret_env, the listed keys of secret_env_from, the
gateway variables and the AWS ones) and warns about each one left without a
value (env_missing). It only reads names, never a value. With secret_env
the warning always shows.
The security trade-off¶
A value in the environment is readable by all the agent's code:
- the agent process and everything it starts can read its environment
(
/proc/self/environ, a debug dump); - on AgentCore, also, any code in the session can ask the microVM metadata service for the role credentials and read those secrets;
- on MicroVMs, whoever holds the sandbox access token sees the process
environment with
commands.list(), and a pause keeps the values in the memory snapshot.
Give the agent only the secrets it needs. More in Security.
What is left out¶
- Secrets encrypted with your own KMS key: the role has no
kms:Decryptand startup fails withsecret_env_denied. Use the AWS managed key (aws/secretsmanager). - Secrets of another account: an ARN of another account is
iam_resource_invalid. export(target="docker")does not include them (the context README says so): set them with the platform where you run the container.- An older SDK does not know
[secret_env]and refuses the file (unknown_key).