---
slug: cli
title: Aetherfy CLI — install, authentication, and configuration
kind: reference
surface: cli
summary: The Aetherfy CLI is a Go binary named afy, built from source with make install, authenticated with an afy_live_ or afy_test_ API key stored in credentials.yaml, and configured through config.yaml, environment variables, and four global flags.
sources:
  - aetherfy-cli:cmd/root.go
  - aetherfy-cli:cmd/login.go
  - aetherfy-cli:cmd/logout.go
  - aetherfy-cli:cmd/whoami.go
  - aetherfy-cli:cmd/version.go
  - aetherfy-cli:cmd/completion.go
  - aetherfy-cli:internal/config/config.go
  - aetherfy-cli:internal/config/credentials.go
  - aetherfy-cli:internal/config/paths.go
---

# Aetherfy CLI

## Installing the Aetherfy CLI

The Aetherfy CLI is a single Go binary named `afy`. It is distributed as source
from the module `github.com/aetherfy/cli` and licensed Apache 2.0. Building it
requires Go 1.21 or newer.

```bash
git clone https://github.com/aetherfy/cli.git
cd cli
make install
```

`make install` compiles the binary and installs it as `afy` on your `PATH`.
Confirm the install:

```bash
afy version
```

Prebuilt archives are also attached to the GitHub releases of the Aetherfy CLI
at [https://github.com/aetherfy/cli/releases](https://github.com/aetherfy/cli/releases)
if you prefer a binary to a source build.

## Authenticating the Aetherfy CLI

`afy login` authenticates the Aetherfy CLI with an API key. Its short
description is "Authenticate with your Aetherfy API key".

| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--api-key` | | string | empty | API key to use for authentication |

When `--api-key` is absent the command prompts for the key on stdin. The key
format is validated locally, then the Aetherfy API is called to confirm the key
is real. The Aetherfy CLI accepts `afy_live_...` or `afy_test_...` — the prefix
followed by at least 32 alphanumeric characters. That is a deliberately
permissive client-side check, not the issued format: Aetherfy generates the
prefix plus exactly 32 hexadecimal characters, documented at
[/platform/api-keys](/platform/api-keys). Create and revoke keys at
[https://app.aetherfy.com/dashboard/settings/api-keys](https://app.aetherfy.com/dashboard/settings/api-keys).

```bash
# Interactive — prompts for the key
afy login

# Non-interactive
afy login --api-key afy_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

On success the credentials are written to `credentials.yaml` inside the Aetherfy
config directory with file permissions `0600`. Logging in with a `afy_test_` key
prints the warning `Using test API key - requests will be in test mode`.

| Command | Behaviour |
|---|---|
| `afy logout` | Removes the stored credentials. No flags. Prints `Not currently logged in.` when there are none. |
| `afy whoami` | Shows the current authentication status. No flags and no API call — it reads the cached credentials and prints the masked API key, the key type (test or live), the API URL, the email, the tier, and the path to the credentials file. |

## The Aetherfy CLI configuration directory

The Aetherfy CLI resolves its configuration directory in this order:

1. `$AETHERFY_CONFIG_DIR`, if set.
2. On Windows, `%APPDATA%\aetherfy`.
3. On Linux, `$XDG_CONFIG_HOME/aetherfy`, if `XDG_CONFIG_HOME` is set.
4. Otherwise, `~/.aetherfy`.

The directory is created with mode `0700`. It holds two files: `config.yaml`
(settings) and `credentials.yaml` (the stored API key, mode `0600`).

## The Aetherfy CLI config file

Settings live in `<config dir>/config.yaml`. Every key has a default, so the file
is optional.

| Key | Default |
|---|---|
| `api_url` | `https://agents.aetherfy.com/api/v1` |
| `default_region` | `us-east-1` |
| `output_format` | `text` |
| `no_color` | `false` |
| `verbose` | `false` |

A complete Aetherfy `config.yaml`:

```yaml
api_url: https://agents.aetherfy.com/api/v1
default_region: us-east-1
output_format: text
no_color: false
verbose: false
```

## Environment variables read by the Aetherfy CLI

| Variable | Effect |
|---|---|
| `AETHERFY_API_KEY` | API key. Highest priority — it is checked before the credentials file, so it overrides `afy login`. |
| `AETHERFY_CONFIG_DIR` | Overrides the config directory. |
| `NO_COLOR` | Any non-empty value disables coloured output. |
| `XDG_CONFIG_HOME` | Config directory base on Linux. |
| `APPDATA` | Config directory base on Windows. |
| `AETHERFY_API_URL` | Overrides the `api_url` config key. |
| `AETHERFY_DEFAULT_REGION` | Overrides the `default_region` config key. |
| `AETHERFY_OUTPUT_FORMAT` | Overrides the `output_format` config key. |
| `AETHERFY_NO_COLOR` | Overrides the `no_color` config key. |
| `AETHERFY_VERBOSE` | Overrides the `verbose` config key. |

Setting `AETHERFY_API_KEY` is the usual way to run the Aetherfy CLI in CI, where
there is no interactive login and no writable home directory:

```bash
export AETHERFY_API_KEY=afy_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
afy deploy --yes
```

## Global flags of the Aetherfy CLI

These persistent flags are accepted by every Aetherfy CLI command.

| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--api-url` | | string | empty | API base URL (overrides config) |
| `--output` | `-o` | string | empty | Output format: text, json, table |
| `--verbose` | `-v` | bool | false | Verbose output |
| `--no-color` | | bool | false | Disable coloured output |

The root command also accepts `--version`, which prints the same information as
`afy version`.

## Output formats in the Aetherfy CLI

`-o text` is the default. `-o table` is accepted, but list output is already
rendered as a table, so it changes nothing.

`-o json` is honoured by exactly these Aetherfy commands:

| Command |
|---|
| `afy agents list` |
| `afy agents status` |
| `afy agents run` |
| `afy agents runs` |
| `afy agents schedule pause` |
| `afy agents schedule resume` |
| `afy deployments` |
| `afy logs` (non-follow only) |
| `afy secrets list` |
| `afy spawn` |
| `afy workspaces list` |
| `afy workspaces info` |
| `afy workspaces agents` |

Every other Aetherfy command prints text regardless of `-o`.

Three quirks are worth knowing before you parse Aetherfy CLI output in a script:

| Quirk | Detail |
|---|---|
| Empty-result JSON | `afy workspaces list` and `afy workspaces agents` print a text message on an empty result even with `-o json`. Of the list commands, only `afy secrets list` emits `[]`. |
| `-o` on `agents pull` | `afy agents pull` re-uses `-o` to mean an **output file path**, shadowing the global format flag for that one subcommand. |
| `--follow` | `afy logs --follow` ignores `-o json` and always streams text. |

## Exit codes of the Aetherfy CLI

| Code | When |
|---|---|
| 0 | Success |
| 1 | Command failure — `afy deploy`, every `afy github` subcommand, `afy agents run --wait` when the run fails, and `afy agents diff` when there are differences |
| 3 | Not logged in |

Exit codes are not uniform across the Aetherfy CLI, and scripts must not assume
they are. Several commands print an `Error:` line and still exit 0: `afy logs`,
`afy secrets`, `afy rollback`, `afy spawn`, `afy deployments`, `afy agents runs`,
and `afy login`. Only the commands listed under code 1, plus the authentication
gate that returns 3, guarantee a non-zero status on failure.

`afy agents diff` exiting 1 on any difference is deliberate and makes it usable
as a CI drift gate:

```bash
afy agents diff --path ./my-agent || echo "aetherfy.yaml differs from deployed state"
```

## Shell completion for the Aetherfy CLI

`afy completion [bash|zsh|fish|powershell]` writes a completion script to stdout.
It takes exactly one argument, restricted to those four shells.

```bash
# Bash
source <(afy completion bash)

# Zsh
source <(afy completion zsh)

# Fish
afy completion fish | source
```

```powershell
# PowerShell
afy completion powershell | Out-String | Invoke-Expression
```

To make completion permanent, add the matching line to your shell's startup file
or write the Aetherfy script into your shell's completion directory.

## Version information from the Aetherfy CLI

`afy version` prints the Aetherfy CLI version, the commit, the build date, the Go
version it was built with, and the platform. It takes no flags.

```bash
afy version
```

## Where to go next with the Aetherfy CLI

| Page | What it covers |
|---|---|
| [/cli/agents](/cli/agents) | The whole `afy agents` group — lifecycle, status, schedules, runs, diff, pull |
| [/cli/deploy](/cli/deploy) | `afy init`, `afy deploy`, `afy deployments`, `afy rollback` |
| [/cli/logs](/cli/logs) | `afy logs` and its filters |
| [/cli/secrets](/cli/secrets) | `afy secrets` — agent-scoped and workspace-scoped |
| [/cli/workspaces](/cli/workspaces) | `afy workspaces` |
| [/cli/github](/cli/github) | `afy github` — connect, link, auto-deploy |
| [/cli/spawn](/cli/spawn) | `afy spawn` — parent agents starting child agents |
| [/agents/aetherfy-yaml](/agents/aetherfy-yaml) | The `aetherfy.yaml` manifest the CLI reads and writes |
| [/platform/limits](/platform/limits) | Plan quotas and per-request caps |
