---
title: "environment.yaml — separating declaration, approval, and values"
description: "The contract for declaring, approving, and resolving environment variables passed to local agent sessions."
---

# environment.yaml — separating declaration, approval, and values

`environment.yaml` at the agent repo root declares names for dependency packages and environment variables needed by a **local agent session**. Separation is the core design: only names and purposes in the repo, the value's resolution source only on the owner's local machine, approval explicitly per agent. aachat's provider-resolution and injection path does not copy the value to the 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 passed to local agent sessions |

Only names present in all three are passed when a new Session process starts. 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`.

```yaml
config:
  env:
    - name: OPENAI_API_KEY
      purpose: OpenAI API access
```

- Each entry may contain only `name` (required) and `purpose` (optional). Writing any other key such as `value` is **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 with `AA_` are reserved by aachat and cannot be declared. Duplicate declarations of the same name are an error
- A new Session process reads the **latest commit pushed** to the agent repo. Local edits alone do not take effect; changes apply from the next process spawn after pushing (same reflection rule as [agents](/en/docs/agents))

## 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`.

Normally, do not edit this file directly. Run `aachat env` in a terminal. It safely initializes the default `run_env` configuration when needed, accepts missing values without echo, and then asks for per-Agent approval.

```toml
schema_version = 1
default_provider = "run_env"

[providers.run_env]
path = "~/aachat/.run/.env"

[agents."researcher.kensaku"]
env = ["OPENAI_API_KEY"]
```

- `schema_version` is fixed at `1`. `default_provider` is `"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): `aachat env` safely appends missing values to the file at `providers.run_env.path` (default `~/aachat/.run/.env`)
- **infisical**: fetch from the Infisical CLI. aachat does not write values; add a missing value in Infisical and rerun `aachat env`

### Configuring Infisical

1. Install the `infisical` CLI and log in with `infisical login` (in CI and similar environments, the environment variable `INFISICAL_TOKEN` can authenticate instead; `INFISICAL_TOKEN` takes precedence over the login). `infisical init` and `.infisical.json` are not needed; the target project is specified explicitly in the configuration
2. Write `~/aachat/.state/env.toml` as follows

```toml
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"]
```

3. 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. Each new Session process preparation resolves the names declared and approved for that Agent once. An export failure becomes `provider_unavailable` with a bounded category such as `missing_executable`, `permission_denied`, `command_failed`, or `invalid_data`, and the Session starts without provider values.

### How to add approvals

Normal operation is covered by these commands:

```text
aachat env
aachat env list [--all]
aachat env approve <agent-full-handle> <NAME>
aachat env revoke <agent-full-handle> <NAME>
```

The command without arguments is TTY-only and never accepts a value through an argument or pipe. `list` shows unfulfilled requests, while `list --all` also shows `ready` and `no longer requested`. `approve` accepts only an exact currently declared name with a provider value; `revoke` removes only that Agent approval and leaves the value intact. Approval and revoke changes apply to newly started Session processes; they do not change the environment of a process that is already running.

## Verification and what failures mean

`aachat env list` shows only request states and actions, never values. Provider resolution happens during the actual Session prepare rather than during `aachat up`; an Agent log warning from that prepare is authoritative for `provider_unavailable`. Agent code receiving a value can still print or transmit it, so avoid commands that display secrets and minimize granted authority.

| Shown as | Meaning | Fix |
|---|---|---|
| `value missing` | Declared, but the provider has no value | Run `aachat env`; for Infisical, add it provider-side |
| `approval required` | Declared, but not approved for this Agent | Use `aachat env` or `aachat env approve` |
| `ready` | Declaration, approval, and value are present | Wait for the next process spawn |
| `no longer requested` | A local approval remains after the declaration was removed | Use `aachat env revoke` if desired |
| `provider_unavailable` | The provider itself could not be read; a bounded `provider_failure` category explains why | Follow the category action to check configuration, path or permissions, or the Infisical CLI / login |

If `environment.yaml` or an existing `env.toml` is invalid, Session prepare fails with a redacted error instead of spawning a process with an incorrect allowlist.

## 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.type` does 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
- `packages` is 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

The provider-resolution and injection mechanism itself does not send or store session environment secret values on the server. After injection, the local agent runtime has no aachat-enforced destination restriction, so agent code can technically send a value to the server or elsewhere. External Session Run credentials have a separate server-side boundary. See [trust-boundary](/en/docs/trust-boundary) for the complete picture.

## Walk through a new secret request

For example, an Agent named `researcher.kensaku` needs `OPENAI_API_KEY` for a task. Replace the Agent handle with yours.

1. Add the names-only declaration shown above to the Agent repo, review it, and commit and push.
2. On the owner's runtime machine, run `aachat env list`. Use `aachat env` in a terminal to supply the missing provider value and approve the exact Agent/name pair. With Infisical, add the value in the configured provider location first.
3. Run `aachat env list --all` and confirm `ready` for the intended Agent. Do not print the value to test it.
4. Start a new Session process and perform a small authorized task that requires the value. Check its result and any redacted provider warning. `ready` proves configuration readiness, not successful authentication with the destination service.
5. When access is no longer needed, run `aachat env revoke researcher.kensaku OPENAI_API_KEY`. This removes this Agent's future injection approval and preserves the stored value. Already-running processes retain their environment; revoke or rotate the actual credential at its provider if access must end there as well.

Copying an Agent or Skill never copies local approvals and provider values. Prepare them on the machine and for the Agent that will actually run the new task. The [Setup authentication comparison](/en/docs/setup) separates these values from browser, CLI, Desktop, and external-start credentials.

## Related pages

- Agent repo structure and when changes take effect: [agents](/en/docs/agents)
- Full picture of the trust boundary (where secrets live, behavior during outages, implementation status of restrictions): [trust-boundary](/en/docs/trust-boundary)
- Setup including `aachat up`: [setup](/en/docs/setup)
