# The infro CLI

> Keep your INFRO configuration in an infro.yaml file and apply it from CI with infro plan, infro apply and infro export. Secrets never go in the file.

Source: https://infro.io/docs/manage/cli

> **Note:** INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See [Early access](https://infro.io/early-access).

> **Warning:** **Built, not yet available.** The management API is built and not deployed: `/manage/v1` does not answer requests yet, and nothing on this page can be used today. These pages describe the contract it ships with.

> **Warning:** **`@infro.io/cli` is not published to npm yet.** Until it is, this page describes the CLI as it is being built, and flags and file keys may change before its first release.

The INFRO CLI is for teams that want their organization's configuration in a repository without adopting Terraform. You describe the configuration in `infro.yaml`, `infro plan` shows what would change, and `infro apply` makes the change through the [management API](https://infro.io/docs/manage/overview).

## Commands

| Command | What it does |
| --- | --- |
| `infro plan` | Reads the organization's current configuration and prints what applying the file would create, change or remove. Changes nothing. |
| `infro apply` | Makes those changes, in dependency order, sending the version it read with each one. It stops at the first conflict. |
| `infro export` | Writes the current configuration as YAML, to start a file from what already exists. |

The CLI reads its token from `INFRO_MANAGEMENT_TOKEN`. `infro plan` and `infro export` need read scopes; `infro apply` needs the write scope for each family it changes.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Success, and nothing to change. |
| `2` | Success, and changes are pending. Use it in CI to fail a check when the live configuration has drifted from the file. |
| `1` | An error: invalid file, refused request, or a version conflict. |

## infro.yaml

```yaml
provider_connections:
  openai-prod:
    kind: openai
    environments: [production]
    credential_env: OPENAI_API_KEY   # read from the environment, never stored here

routes:
  chat-default:
    slug: chat-default
    strategy: priority
    targets:
      - connection_id: ${provider_connections.openai-prod.id}
        model: openai/gpt-5.6-sol

notification_channels:
  finops-slack:
    kind: slack
    url_env: FINOPS_SLACK_WEBHOOK

member_budgets:
  lead@example.com:
    spend_limit: { monthly: 300, behavior: alert }
```

- **Secrets never go in the file.** A provider credential is named by `credential_env` and a Slack webhook URL by `url_env`; the CLI reads the value from that environment variable at apply time. The management API never returns either value, so neither can end up in an exported file.
- **Top-level keys are the management API's resource types**, and each entry is keyed by a name you choose. The CLI pairs an entry with the live resource by a natural key: a project's slug (or its name), a connection's name, a gateway key's or channel's label, a route's slug, a member budget's email. There is no state file; that pairing is the only link.
- **One entry refers to another with `${<type>.<name>.id}`**, as the route above does. The CLI creates resources in dependency order, so the id is known by the time it is needed, and the file does not depend on ids generated in one organization.

## Removing resources

By default `infro apply` only creates and changes; a resource missing from the file is left alone. With `--prune`, it also removes resources that are missing from the file, but only those whose `managed_by` is `cli`. Something created in the console, by Terraform or by another script is never removed by the CLI.

## In CI

```bash
export INFRO_MANAGEMENT_TOKEN="$INFRO_TOKEN_FROM_SECRET_STORE"
infro plan
status=$?
if [ "$status" -eq 1 ]; then exit 1; fi
if [ "$status" -eq 2 ]; then infro apply; fi
```
