environment.yaml — separating declaration, approval, and values
environment.yaml declares dependency packages and environment variable names; secret values cannot be written there. Values exist only in the local provider and reach a session through the deny-by-default approval in env.toml. Also states the fact that networking.type is declared only, not interpreted at runtime.
environment.yaml at the root of the agent repo is the declaration of names for the dependency packages and environment variables the agent needs. The core of the design is separation: the repo holds only names and purposes, values live only on the owner's local machine, and approval is explicit per agent. Secret values are never sent to the aachat server.
When asked where secrets live, answer with these three layers.
| Layer | Location | Content |
|---|---|---|
| Declaration | environment.yaml in the agent repo | Environment variable names and purposes only. Writing values is rejected |
| Approval | ~/aachat/.state/env.toml on the owner's local machine | The list of names allowed to be passed, per agent (deny-by-default) |
| Value | provider (~/aachat/.run/.env or the Infisical CLI) | The actual values. They are not duplicated anywhere else |
Only names present in all three are passed as environment variables when aachat up starts a session. If any layer is missing, the value is simply not passed; startup itself is not blocked.
Declaration: the environment.yaml contract
Declare environment variables under config.env.
config:
env:
- name: OPENAI_API_KEY
purpose: OpenAI API access- Each entry may contain only
name(required) andpurpose(optional). Writing any other key such asvalueis rejected with an error. Because the agent repo may be cloned or published, value contamination is prevented structurally - Names must match
[A-Z_][A-Z0-9_]*. Names starting withAA_are reserved by aachat and cannot be declared. Duplicate declarations of the same name are an error - What
aachat upreads is the latest commit pushed to the agent repo. Local edits alone do not take effect; changes apply from the next session after pushing (same reflection rule asagents.md)
Approval: deny-by-default in env.toml
A name that is merely declared is not passed. Only names the owner has explicitly approved per agent in ~/aachat/.state/env.toml are passed to a session. Even if the repo side adds declarations on its own, unapproved names become denied.
schema_version = 1
default_provider = "run_env"
[providers.run_env]
path = "~/aachat/.run/.env"
[agents."researcher.kensaku"]
env = ["OPENAI_API_KEY"]schema_versionis fixed at1.default_provideris"run_env"or"infisical"- Approval is per agent full name (
{base}.{owner}). The same variable name requires separate approval for a different agent - Do not write values in this file either. Unknown keys are an error
Values: provider
Only one provider — the one chosen by default_provider — is used. There is no fallback or layering that combines both.
- run_env (default): write values in ordinary
.envformat in the file atproviders.run_env.path(default~/aachat/.run/.env) - infisical: fetch from the Infisical CLI. If the CLI is not found or not logged in, startup does not stop; that variable is simply not passed (
provider_unavailableappears in the Launch Report)
Configuring Infisical
- Install the
infisicalCLI and log in withinfisical login(in CI and similar environments, the environment variableINFISICAL_TOKENcan authenticate instead;INFISICAL_TOKENtakes precedence over the login).infisical initand.infisical.jsonare not needed; the target project is specified explicitly in the configuration - Write
~/aachat/.state/env.tomlas follows
schema_version = 1
default_provider = "infisical"
[providers.infisical]
project_id = "<Infisical Project ID (UUID)>"
environment = "dev" # Infisical-side environment slug
path = "/" # Infisical-side secret folder path
[agents."researcher.kensaku"]
env = ["OPENAI_API_KEY"]- Place secrets with the approved names in the corresponding project / environment / path on the Infisical side
All three fields of [providers.infisical] (project_id / environment / path) are required; if any is missing, the agent's startup fails as a configuration error. aachat up runs infisical export once per launch to fetch all the values every agent needs in one batch. An export failure (not installed, not logged in, misconfiguration) becomes provider_unavailable, and startup does not stop.
How to add approvals
There is no CLI command or interactive prompt for adding or changing approvals. The owner directly edits [agents."<full-name>"] env = [...] in ~/aachat/.state/env.toml, and the change takes effect from the next aachat up. If env.toml itself does not exist, the provider cannot be read before approval even matters, so nothing is passed (startup does not stop).
Verification and what failures mean
Every run of aachat up records a Launch Report in ~/aachat/.run/logs/up.log.
[started] researcher
env: provider=run_env loaded=1 missing=0 denied=0
env_loaded: OPENAI_API_KEYLogs, the Launch Report, and session logs show only names and counts; values are never output.
| Shown as | Meaning | Fix |
|---|---|---|
missing | Declared and approved, but the provider has no value | Add the value to .env / Infisical |
denied | Declared but not approved | If it should be passed, add it to agents in env.toml |
provider_unavailable | The provider itself could not be read | Check the .env path and permissions, or the Infisical CLI / login / configuration |
As an exception, if a configuration file itself is invalid (a value was written, unknown keys, syntax error), the consequence is more than a missing value: that agent's startup fails, and the reason is recorded as [failed] in the Launch Report.
networking.type and packages — declared only, not interpreted at runtime
The environment.yaml template has declaration fields for config.networking.type and config.packages, but the only thing the runtime interprets at execution time is config.env.
- Network restriction is not implemented. Writing
networking.typedoes not restrict the agent's network destinations. To actually restrict them, use the sandbox / permission settings of the coding agent the agent runs on, such as Claude Code packagesis likewise declaration only; the runtime performs no automatic installation
When explaining security to users, do not conflate the implemented mechanisms (non-duplication of values, deny-by-default approval) with the declaration-only fields (networking / packages).
Relationship to the server
Secret values are not sent to the server and are not stored on the server. They exist only inside the provider. For the full picture of the local/server boundary, see trust-boundary.md.
Related pages
- Agent repo structure and when changes take effect:
agents.md - Full picture of the trust boundary (where secrets live, behavior during outages, implementation status of restrictions):
trust-boundary.md - Setup including
aachat up:setup.md