# Zero CLI

`zero` is one command line for every Zero product, published as `@zeroapps/cli`
on npm:

- `zero vault …` — [projects, environments and secrets](/vault/cli/)
- `zero errors …` — [error reports and issues](/errors/cli/)
- `zero keys …` — the organization-scoped API key both products accept

## Run it

The current published version is `0.7.0`. Run it without installing:

```bash
pnpm dlx @zeroapps/cli@0.7.0 --help
```

Install it globally to drop the `pnpm dlx` prefix and run `zero …` directly:

```bash
npm i -g @zeroapps/cli@0.7.0
```

## Sign in

On your own machine, sign in with your browser instead of handling a key:

```bash
zero login
zero whoami
```

`zero login` opens your browser, asks you to approve and to pick an
organization, then stores the sign-in for this machine. Every `zero` command
works from then on with nothing exported.

```
User ID: user_…
Org ID: org_…
Credential: signed in as you@example.com
```

Pick the organization carefully: the sign-in reaches exactly that one. Run
`zero login` again to switch the machine default, or give one project directory
its own organization (see [A directory with its own
organization](#a-directory-with-its-own-organization)).

The sign-in belongs to you and to this machine, so ending it disturbs no one
else:

```bash
zero logout
```

`zero logout` ends the sign-in the current directory uses: the machine default,
or the directory's own one when it has one.

If your browser cannot open — an SSH session, a container, a CI runner — forward
the callback port and pin it:

```bash
ssh -L 8976:127.0.0.1:8976 you@host
zero login --port 8976
```

Unattended machines have no browser at all. Use an API key there.

## In GitHub Actions, use OIDC

Record the repository with `zero ci trust add --repo owner/repo`, give the job
`permissions: id-token: write`, and delete the `ZERO_API_KEY` secret. The CLI
then exchanges the job's OIDC token for API access that expires in 15 minutes.
See [GitHub Actions with OIDC](/vault/github-actions/).

## Authenticate with an API key

Servers, containers and other CI providers use a key: they have no browser and
no config file. On your own machine, prefer `zero login` — the environment
variable is a fallback, not the normal path.

Create one in the dashboard under **API keys**
(see [API keys](/account/api-keys/)), or from an already signed-in terminal with
`zero keys create`.

```bash
export ZERO_API_KEY=zv_your_key_here
zero whoami
```

Credentials are resolved in this order, first match wins:

1. `--api-key` flag
2. a directory-bound context — an API key or a sign-in (see below)
3. the `ZERO_API_KEY` environment variable
4. a GitHub Actions OIDC token (see above)
5. a `zero login` sign-in for this machine

A context beats the environment variable because you chose it for that
directory, while the variable is ambient: a stale `ZERO_API_KEY` in your shell
must not redirect a project's commands to another organization. OIDC comes after
the variable, so you can pin a key during a migration, and before a sign-in,
because a runner has no browser. A sign-in is last, because it applies to every
directory on the machine. `zero whoami` prints a `Credential:` line naming which
one answered.

The API base URL defaults to `https://api.zeroapps.dev`. It is a bare origin: the
CLI appends `/vault/v1` or `/errors/v1` itself. Override it with `--base-url` or
`ZERO_API_URL` only if you target a different instance, and leave off any product
path.

### Contexts (optional)

If you juggle several organizations, save them as named contexts instead of
exporting the variable. A context holds one credential — an API key or a
browser sign-in — and binds to directories. Contexts live in
`~/.config/zero/config.json` (mode 0600, since it holds plaintext keys and every
sign-in this machine has).

```bash
zero context add work --api-key zv_your_key_here
zero context use work        # binds the current directory to this context
zero context current         # prints the bound context name
zero context list            # every context, and what it carries
```

`zero context use` binds the directory you run it in (and its subdirectories) to
that context, so `zero` there uses that credential without the environment
variable.

### A directory with its own organization

To keep one project on a different organization than the rest of the machine,
sign in again and store it as a context:

```bash
cd ~/work/cragstronauts
zero login --context cragstronauts   # pick the project's organization
zero context use cragstronauts
```

```
Signed in as you@example.com (org org_…).
Added context: cragstronauts
Run `zero context use cragstronauts` in the project directory.
```

From then on, that directory and its subdirectories use that organization, and
every other directory keeps the machine sign-in:

```bash
zero whoami          # inside the project: the project's org
cd ~ && zero whoami  # everywhere else: the machine's org
```

Both sign-ins are yours and live side by side. Notes:

- The consent screen offers the organization your Clerk session is currently in.
  If the second sign-in comes back with the same organization as your default,
  the CLI says so; switch the active organization at
  [dash.zeroapps.dev](https://dash.zeroapps.dev) and run it again.
- If that sign-in is revoked or removed, commands in the directory stop with
  `context "…" has no sign-in on this machine`. They never fall back to another
  organization.
- `zero context remove <name>` drops the binding and signs out of that
  organization. `zero context unset` removes only the binding for the current
  directory.

## Manage API keys

Once authenticated, the CLI creates and revokes the same organization-scoped keys
as the dashboard. After `zero login` that includes your first key, so a new
machine never needs the dashboard for one.

```bash
zero keys create -l ci            # prints the new key once; save it
zero keys list                    # id, prefix, label, created date
zero keys revoke <id>             # revoke by id from the list
```

A created key is shown once, same as in the dashboard. Save it immediately; if you
lose it, revoke it and create another.

## Exit codes

| Code | Meaning |
| --- | --- |
| 0 | The command did what you asked |
| 1 | The API refused: bad credential, missing project, conflict |
| 75 | The API was unreachable or failing — a retry may work |

Failures print one line on stderr, so stdout stays clean for pipes. Set
`ZERO_DEBUG=1` to add the stack trace when you are chasing a bug.

## Upgrading from `zv`

`zero` replaces the old `zv` CLI outright. There is no alias and nothing carries
over:

- Vault commands moved under `zero vault` (`zv secrets list` is now
  `zero vault secrets list`). Flags are unchanged.
- `ZEROVAULT_API_KEY` and `ZEROVAULT_API_URL` are not read. Export `ZERO_API_KEY`
  and `ZERO_API_URL` instead.
- `ZERO_API_URL` takes a bare origin. A `zv`-era value ending in `/vault` will
  produce 404s.
- Contexts saved in `~/.config/zerovault/config.json` are not read. Re-add them
  with `zero context add`.

## Next

- [Vault commands](/vault/cli/) for projects, environments and secrets.
- [Errors commands](/errors/cli/) for reports and issues.