# CLI

`tglow` is the Tailglow command line. Every endpoint in the [API reference](/api) is a command, because both are generated from the same source: the API's own routes and validations. A new endpoint becomes a new command with no separate release, and the CLI cannot describe an API that does not exist.

It is built for two readers at once. In a terminal it prints tables and asks before deleting. Piped, redirected, or run by an agent, it emits JSON and never prompts.

## Installation

```bash
npm install -g @tailglow/cli
```

Node 18.3 or newer. `bun add -g @tailglow/cli` and `pnpm add -g @tailglow/cli` work the same way.

Verify it:

```bash
tglow --version
```

Prefer a shorter name? Alias it rather than installing over one, since `tg` belongs to other tools on many systems:

```bash
alias tg=tglow
```

## Authenticating

In a terminal, running `tglow` with no command offers sign-in when no credential is configured for the selected API. Choose your account, an API key, or Cancel. `tglow help` and `tglow --help` always show help. When a credential is configured, or without an interactive terminal, `tglow` shows help.

You can also start sign-in explicitly:

```bash
tglow login
```

It asks how you want to sign in, opens the approval page in your browser, and counts down to the code's expiry, ten minutes after creation. Check that the page shows the code your terminal printed, then approve.

At expiry, the CLI makes one final poll to collect an approval given just before the deadline. If the code expired, the interactive wait ends cleanly; run `tglow login` to try again. If the server still reports pending, it keeps the sign-in and shows `tglow login --wait` to resume. These normal interactive endings exit `0`. A poll in progress can finish after expiry so an approved credential is not lost.

An approved request can be collected for up to fifteen minutes after approval. `tglow login --wait` waits up to one minute at a time and exits `75` while still pending; it can be run again.

- **Sign in with your Tailglow account** gives the CLI a session that acts as you, with what your role allows in each of your teams. It starts in the team you choose; `--team` or `tglow config set team` switches it. It is listed as **CLI** under **Profile > Security**, ends when you sign out of every device, and ends on its own after six months unused.
- **Sign in with an API key** creates a key for one team, named after this machine, with the permissions you choose. The team needs a project first.

`--method account` or `--method api-key` skips the question.

Without a terminal, for example when an agent runs it, `tglow login` prints the link and the code and exits. Approve the sign-in, then run `tglow login --wait` to finish. It exits `0` once signed in, `75` while approval is still pending (run it again), and `1` if the sign-in was denied, expired or withdrawn, or the API could not be reached (the sign-in is kept, so run it again once the connection is back).

```bash
tglow whoami    # who the CLI acts as, in which team, with which credential
tglow logout    # end the session, or delete the key, that tglow login created
```

The credential is stored in `~/.tailglow/config.json`, readable only by you, and is only ever sent to the API that issued it.

### API keys

An API key you already have works too. Create one in the dashboard under **Team settings**, then store it once:

```bash
tglow config set api_key tg_api_...
```

A stored key is never printed back. Credentials are tried in this order:

| Source                     | Use it for                                   |
| -------------------------- | -------------------------------------------- |
| `--api-key <key>`          | A single call. Visible in your shell history |
| `TAILGLOW_API_KEY`         | CI, containers, and anything scripted        |
| `tglow login`              | Your own machine                             |
| `tglow config set api_key` | A key you created in the dashboard           |

In CI, prefer the environment variable. A key passed as a flag appears in the process list.

## Getting around

The shape is always the same:

```bash
tglow <resource> <command> [flags]
```

Three levels of help, and between them they are the whole manual:

```bash
tglow help                         # every resource, and the flags that apply everywhere
tglow drains                       # every command on drains
tglow drains create --help         # every flag, its type, and its accepted values
```

Command help is generated from the same validations the API enforces, so a required field is required here for the same reason it is required there.

## Reading the docs

Every guide and API reference page ships inside the CLI as markdown:

```bash
tglow docs                  # the index of every page
tglow docs guides/sdk       # one page
tglow docs /api/drains.md   # any link from the index works as it is
```

The pages match the version of the CLI you have installed, so they describe the commands it can run, and they work without a network connection. Each page ends with a link to its latest version online.

Unlike other commands, `tglow docs` prints markdown even when its output is piped, because the reader is usually an agent. Add `--json` to get `{ path, url, content }` for a page, or `{ index, pages, online }` for the index.

## Working with a project

Most commands act on a project. Set it once instead of repeating it:

```bash
tglow config set project prj_...
tglow metrics list
```

`--project` overrides the stored default for a single call. Every other id has to be named explicitly: only `project` and `team` fall back to configuration, so nothing in your environment can quietly become the target of a delete.

## Flags

Field names are snake_case in the API and kebab-case on the command line: `time_window_minutes` becomes `--time-window-minutes`. Path parameters are flags too, never positional.

```bash
tglow sources create --project prj_x --name "Web events"        # a string
tglow pages create --project prj_x --name Status --is-public    # true
tglow pages create --project prj_x --name Status --is-public=false
tglow metrics create --project prj_x --group-by user_id,country # a list
tglow views create-join --project prj_x --joins '[{"lookup_view_id":"view_b","alias":"b","base_field":"user_id","lookup_field":"id"}]'
```

A boolean is passed alone for true, or written attached for false. Anything typed `object`, `object[]`, or `Scopes` takes JSON as a single argument, and the command's `--help` lists the keys it takes.

## Output

A terminal gets a table. Everything else gets JSON, so `| jq` needs no flag:

```bash
tglow projects list | jq -r '.data[].name'
```

JSON is the API's response as it was sent, envelope included: read `.data` for results and `.pagination.next_cursor` to continue. `--json` forces it when you want JSON in a terminal.

Failures go to stderr and follow the same mode, so a pipeline reading stdout never has to tell a result apart from an explanation of why there is none.

## Long lists

List commands paginate. `--all` follows the cursor until the results run out or `--max` is reached:

```bash
tglow metrics list --project prj_x --all --max 500
```

The cap defaults to 10,000 and exists because records and collection documents are unbounded. A run that stops early prints the exact command to continue with. Under `--json`, `--all` returns `{ data, truncated, next_cursor }` instead, so a caller can resume on its own with `--after`.

`--all` owns paging, so it does not combine with `--limit` or `--before`. Use `--limit` on its own to size a single page.

## Deleting

In a terminal, a delete asks first. `--yes` skips the question.

**Without a terminal, a delete runs immediately and is never confirmed.** That is deliberate, so scripts and agents are not blocked on a question they cannot answer, but it means a delete in CI happens the moment it is called.

Most resources delete softly: the response tells you when permanent removal happens.

## Scripting it

The CLI is designed to be driven by other programs, including AI agents:

```bash
export TAILGLOW_API_KEY=tg_api_...
tglow metrics create --help --json     # the full contract for one command, as data
```

`--help --json` returns the command's flags, types, requiredness, accepted values, and the syntax for each one. An agent can read that and construct a valid call without any other documentation.
