# Environment variables

> Runtime env contract: `ATI_PROXY_URL`, `ATI_SESSION_TOKEN`, `ATI_DIR`, JWT keys, SSRF/download allowlists, skill registry, OTel export, and optional `ATI_DB_URL` / `ATI_ADMIN_TOKEN`.

- Repository: Parcha-ai/ati
- GitHub: https://github.com/Parcha-ai/ati
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa/llms-full.txt

## Source Files

- `README.md`
- `AGENTS.md`
- `src/main.rs`
- `src/proxy/server.rs`
- `docs/OTEL.md`
- `docs/PERSISTENCE.md`
- `src/core/otel.rs`

---

---
title: "Environment variables"
description: "Runtime env contract: `ATI_PROXY_URL`, `ATI_SESSION_TOKEN`, `ATI_DIR`, JWT keys, SSRF/download allowlists, skill registry, OTel export, and optional `ATI_DB_URL` / `ATI_ADMIN_TOKEN`."
---

ATI reads process environment variables at startup and on each CLI invocation to choose execution mode (local vs proxy), resolve filesystem paths, authenticate proxy traffic, load operator credentials, gate outbound fetches, and optionally export telemetry or connect to Postgres. Most variables are optional; unset values fall back to documented defaults or disable the feature entirely.

```mermaid
flowchart TB
  subgraph sandbox["Agent sandbox / CLI"]
    RUN["ati run / assist / skill"]
    ENV["ATI_PROXY_URL\nATI_SESSION_TOKEN*\nATI_DIR"]
  end
  subgraph proxy["ati proxy"]
    JWT["ATI_JWT_*"]
    KEYS["ATI_KEY_*"]
    SEC["ATI_SSRF_PROTECTION\nATI_DOWNLOAD_ALLOWLIST"]
    DB["ATI_DB_URL\nATI_ADMIN_TOKEN"]
    SK["ATI_SKILL_REGISTRY"]
    OTEL["OTEL_EXPORTER_OTLP_*"]
  end
  RUN -->|"ATI_PROXY_URL set"| proxy
  RUN -->|"unset"| LOCAL["Local keyring\n~/.ati or ATI_DIR"]
  ENV --> RUN
  JWT --> RUN
  KEYS --> proxy
```

## Mode and filesystem layout

| Variable | Read by | Default | Effect |
|----------|---------|---------|--------|
| `ATI_PROXY_URL` | CLI (`ati run`, `ati assist`, skill fetch in proxy registry mode) | unset | When set and non-empty, tool calls forward to `{url}/call` (and related proxy routes). When unset, ATI uses local mode with manifests and keyring under `ATI_DIR`. |
| `ATI_DIR` | CLI, proxy (`--ati-dir` overrides for proxy only) | `$HOME/.ati`, else `.ati` | Root for `manifests/`, `keyring.enc`, `credentials`, `skills/`, provider cache, and default audit path. |
| `ATI_KEY_FILE` | Local mode session-key read (`security/sealed_file.rs`) | `/run/ati/.key` | Path to the one-shot AES session key file; read once then unlinked. Used in tests and custom orchestrator layouts. |
| `ATI_OUTPUT` | Global CLI flag (`main.rs`) | `json` (clap default) | Default for `--output` / `--format`: `json`, `table`, or `text`. |
| `ATI_CLI_MAX_OUTPUT_BYTES` | CLI tool capture (`core/cli_executor.rs`) | `524288000` (500 MiB) | Per-file cap on subprocess output captured by CLI providers; parse errors or `0` fall back to default. |
| `ATI_AUDIT_FILE` | `ati audit` (`core/audit.rs`) | `{ATI_DIR}/audit.jsonl` | Append-only JSONL audit log path. |

<Note>
`ati proxy` takes `--ati-dir` on the command line; sandboxes normally set `ATI_DIR` so every `ati` subprocess shares the same config root.
</Note>

## Proxy authentication and session tokens

Proxy-mode clients attach `Authorization: Bearer <token>` on `/call`, `/mcp`, `/help`, and SkillATI routes. Token material is **not** cached across invocations when read from a file—each call re-reads disk so supervisors can rotate JWTs without restarting agents.

### Resolution order (any `*_SESSION_TOKEN` env name)

For env var `<NAME>` (default `ATI_SESSION_TOKEN`):

1. `<NAME>` if set and non-empty (trimmed)
2. `<NAME>_FILE` if set and non-empty → read that path
3. Default file path from `<NAME>` (see below)

File read errors (permissions) return `Err`; missing file returns `Ok(None)`.

| Variable | Role |
|----------|------|
| `ATI_SESSION_TOKEN` | Default bearer JWT for proxy auth; carries space-delimited scopes in the `scope` claim. |
| `ATI_SESSION_TOKEN_FILE` | Override path for token file; when unset, default is `/run/ati/session_token`. |
| Per-provider env (manifest) | `[provider].auth_session_token_env` names a sandbox env var (e.g. `PARCHA_TOOLS_SESSION_TOKEN`) for audience-separated tokens. Same `<NAME>_FILE` and `/run/ati/<slug>` default path rules apply. If the per-provider token is missing, the client falls back to `ATI_SESSION_TOKEN` before sending an unauthenticated request. |

<Warning>
When the proxy has JWT validation configured (`ATI_JWT_PUBLIC_KEY` or `ATI_JWT_SECRET`), missing or invalid tokens are rejected. With no JWT keys configured, the proxy runs in unrestricted dev mode—do not expose that configuration to untrusted networks.
</Warning>

## JWT validation and issuance (proxy)

Configured on the **proxy** process. `jwt::config_from_env()` builds validation settings; absence of both key sources means JWT auth is disabled.

| Variable | Default | Effect |
|----------|---------|--------|
| `ATI_JWT_PUBLIC_KEY` | unset | Path to ES256 public key PEM; enables JWT validation and JWKS endpoint when combined with private key. |
| `ATI_JWT_PRIVATE_KEY` | unset | Path to ES256 private key PEM (token issuance / JWKS). |
| `ATI_JWT_SECRET` | unset | Hex-encoded HS256 shared secret; used if no public key path is set. |
| `ATI_JWT_ISSUER` | unset | If set, JWT `iss` must match. |
| `ATI_JWT_AUDIENCE` | `ati-proxy` | Singular expected `aud` when CSV allowlist is not used. |
| `ATI_JWT_ACCEPTED_AUDIENCES` | unset | Comma-separated allowlist of `aud` values; takes precedence over `ATI_JWT_AUDIENCE` when non-empty after trimming. Used for per-provider audience separation with custom session-token env vars. |

## Proxy credential injection (`ATI_KEY_*`)

When `ati proxy` starts with `--env-keys`, the keyring is built exclusively from environment variables matching `ATI_KEY_*`. The prefix is stripped and the remainder is lowercased for the keyring name.

| Pattern | Example | Keyring key |
|---------|---------|-------------|
| `ATI_KEY_<NAME>` | `ATI_KEY_FINNHUB_API_KEY=...` | `finnhub_api_key` |
| `ATI_KEY_<PROVIDER>_ALLOWED_URLS` | `ATI_KEY_PARCHA_TOOLS_ALLOWED_URLS=*.example.com` | `{provider}_allowed_urls` (glob CSV) |

<ParamField body="ATI_KEY_*" type="string">
Used on the proxy with `--env-keys`, or in tests. Replaces disk keyring for that process. Empty values are skipped. MCP HTTP upstream overrides require a compiled allowlist for providers that declare `mcp_url_env`; without `ATI_KEY_<PROVIDER>_ALLOWED_URLS`, the proxy returns 403.
</ParamField>

Other notable keyring keys (set via `ati key set` or `ATI_KEY_*`):

| Keyring name | Purpose |
|--------------|---------|
| `gcp_credentials` | GCP service account JSON for `ATI_SKILL_REGISTRY=gcs://...` |
| `sandbox_signing_shared_secret` | HMAC request signing when passthrough sig-verify is enabled (`ATI_KEY_SANDBOX_SIGNING_SHARED_SECRET` under `--env-keys`) |

## Outbound safety: SSRF and downloads

| Variable | Values | Scope |
|----------|--------|-------|
| `ATI_SSRF_PROTECTION` | unset (off), `warn`, `1` / `true` | HTTP executor and file-manager downloads via `validate_url_not_private`. Blocks loopback, RFC1918, link-local, `.internal`, `.local`, and DNS-resolved private IPs. |
| `ATI_DOWNLOAD_ALLOWLIST` | Comma-separated host patterns | `file_manager:download` only. Unset = no host restriction (still subject to SSRF when enabled). Patterns: exact host, `*.suffix`, or `*` (not recommended). |

<Warning>
Production proxies should set `ATI_DOWNLOAD_ALLOWLIST`. When unset, any non-private host is allowed for downloads—acceptable for local dev only.
</Warning>

## Skill registry (SkillATI)

| Variable | Values | Effect |
|----------|--------|--------|
| `ATI_SKILL_REGISTRY` | unset, `gcs://<bucket>`, `proxy` | Enables remote SkillATI. `gcs://` reads from GCS using keyring key `gcp_credentials`. `proxy` fetches `/skillati/*` through `ATI_PROXY_URL` with session token auth. |
| `ATI_SKILL_REGISTRY_INDEX_OBJECT` | Comma-separated object paths | Overrides GCS catalog index candidates (default tries `_skillati/catalog.v1.json` and fallbacks). |
| `ATI_PROXY_URL` | URL | Required when `ATI_SKILL_REGISTRY=proxy`. |
| `ATI_SESSION_TOKEN` | JWT | Auth for proxy-backed SkillATI client. |

Local skills under `{ATI_DIR}/skills/` load regardless of registry settings.

## Optional persistence and admin API

Requires a binary built with `--features db`.

| Variable | Default | Effect |
|----------|---------|--------|
| `ATI_DB_URL` | unset | Postgres connection string. Unset or empty → persistence disabled, `/health` reports `db: "disabled"`. Set with unreachable DB → proxy **fails startup**. Set without `db` feature → startup error instructing rebuild. |
| `ATI_ADMIN_TOKEN` | unset | Plain-text bearer for `/admin/keys/*`. Required alongside a connected DB; if DB is connected but token unset, admin routes return 503. |
| `ATI_MASTER_KEY` | unset | Base64 32-byte KEK for envelope encryption of secrets at rest (persistence layer). |
| `ATI_MASTER_KEY_<id>` | unset | Versioned KEKs for rotation. |
| `ATI_MASTER_KEY_ACTIVE` | `m1` when multi-version | Selects which KEK version encrypts new writes. |

Use `ati proxy --migrate` to apply embedded SQL migrations when `ATI_DB_URL` is set.

## Observability

### Logging

| Variable | Default | Effect |
|----------|---------|--------|
| `RUST_LOG` | `info` (or `debug` with `--verbose`) | `tracing` filter via `EnvFilter::from_default_env()`. |

### OpenTelemetry (`--features otel`)

Runtime export is active only when `OTEL_EXPORTER_OTLP_ENDPOINT` is set (non-empty). See `docs/OTEL.md` for full detail.

| Variable | Default | Notes |
|----------|---------|-------|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | unset | Base OTLP HTTP endpoint; unset disables OTel layer at runtime. |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `{base}/v1/traces` | Signal-specific override. |
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | `{base}/v1/metrics` | Signal-specific override. |
| `OTEL_EXPORTER_OTLP_HEADERS` | unset | Comma-separated `k=v` (e.g. Grafana `Authorization=Basic ...`). |
| `OTEL_SERVICE_NAME` | `ati-proxy` | Resource attribute; wins over `service.name` in `OTEL_RESOURCE_ATTRIBUTES`. |
| `OTEL_RESOURCE_ATTRIBUTES` | unset | Lower-priority resource defaults. |
| `ENVIRONMENT_TIER` | unset | Maps to `deployment.environment` on OTel resource and Sentry environment. |
| `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG` | SDK defaults | Standard OTel sampling env vars. |
| `ATI_OTEL_DEBUG` | `false` | Reserved; no-op today. |

### Sentry (`--features sentry`)

| Variable | Default | Effect |
|----------|---------|--------|
| `SENTRY_DSN` or `GREP_SENTRY_DSN` | unset | Enables Sentry when `ENVIRONMENT_TIER` is `production`, `staging`, or `demo`. |
| `SERVICE_NAME` | `ati-proxy` | Sentry server name. |
| `ATI_SENTRY_DEBUG` | `false` | `1` / `true` enables Sentry client debug mode. |

<Info>
If `OTEL_EXPORTER_OTLP_ENDPOINT` or `SENTRY_DSN` is set but the matching Cargo feature was not compiled in, ATI logs a startup warning and ignores export.
</Info>

## Typical deployment profiles

<Steps>
<Step title="Local developer laptop">
Unset `ATI_PROXY_URL`. Optionally set `ATI_DIR` to a project-local tree. Leave `ATI_SSRF_PROTECTION` and `ATI_DOWNLOAD_ALLOWLIST` unset for permissive HTTP. Store API keys with `ati key set` or `~/.ati/credentials`.
</Step>
<Step title="Sandbox with proxy (zero credentials in agent)">
Set `ATI_PROXY_URL`, `ATI_SESSION_TOKEN` (or rely on `/run/ati/session_token` rotation). Do **not** ship `keyring.enc` into the sandbox. Scope tools via JWT `scope` claim.
</Step>
<Step title="Production proxy host">
Run `ati proxy --env-keys` or disk keyring under `--ati-dir`. Configure `ATI_JWT_PUBLIC_KEY` + `ATI_JWT_ISSUER`, `ATI_DOWNLOAD_ALLOWLIST`, `ATI_SSRF_PROTECTION=1`, MCP allowlists via `ATI_KEY_*_ALLOWED_URLS`. Optionally `ATI_DB_URL`, `ATI_ADMIN_TOKEN`, `OTEL_*`, and `ATI_SKILL_REGISTRY=gcs://...`.
</Step>
</Steps>

## Build-time feature gates

| Cargo feature | Env vars requiring rebuild |
|---------------|----------------------------|
| `db` | `ATI_DB_URL`, `ATI_ADMIN_TOKEN`, `ATI_MASTER_KEY*` |
| `otel` | `OTEL_EXPORTER_OTLP_*` |
| `sentry` | `SENTRY_DSN`, `GREP_SENTRY_DSN` |

## Test-only variables

| Variable | Purpose |
|----------|---------|
| `ATI_DB_URL_TEST` | Integration tests against a live Postgres instance; skipped when unset. |

## Related pages

<CardGroup>
<Card title="Execution modes" href="/execution-modes">
How `ATI_PROXY_URL` switches local vs proxy execution and where credentials live.
</Card>
<Card title="Configure JWT and keys" href="/configure-jwt-and-keys">
Issuing tokens, JWT env setup, and `ati key` workflows.
</Card>
<Card title="Deploy proxy server" href="/deploy-proxy-server">
`--env-keys`, `--migrate`, passthrough, and production proxy flags.
</Card>
<Card title="File manager operations" href="/file-manager-operations">
Download allowlist semantics and SSRF interaction for `file_manager:download`.
</Card>
<Card title="Skills registry and fetch" href="/skills-registry-and-fetch">
GCS layout, `ATI_SKILL_REGISTRY`, and proxy SkillATI routes.
</Card>
<Card title="Security and production" href="/security-and-production">
Threat model, hardening checklist, and OTel/Sentry in prod builds.
</Card>
</CardGroup>
