# Signing in

> How the LangAlpha CLI picks a server and a credential, signs in through the browser, including on a remote machine with no browser, and where it keeps your sign-in.

Source: https://docs.langalpha.ai/cli/signing-in/

Every CLI command talks to one LangAlpha server with one credential. This page explains how the CLI picks both, and how `langalpha login` signs you in.

## Which credential a command uses

The CLI takes the first credential it finds, in this order:

1. **`--token T`** on the command line.
2. **`LANGALPHA_TOKEN_FILE`**, the path of a file holding a token. Inside a [computer](https://docs.langalpha.ai/cli/inside-a-computer/) the agent's commands have it set. The file is read again on every request, and once more after a refused one, because the server rewrites it while commands run. Where it is set, nothing below is tried: a file with no token in it is an error, never a reason to fall back to your own sign-in.
3. **Your saved sign-in** for the server, from `langalpha login`.
4. **None**, which a self-hosted server that asks for no sign-in accepts.

## Which server

The server is `--server URL` if you pass it, then `LANGALPHA_URL` (an empty value counts as unset), then the server you last signed in to, and finally `http://localhost:8000`, the default address of a self-hosted install.

Signing in to a server makes it the default for later commands, so you usually pass `--server` once, to `login`.

When `LANGALPHA_TOKEN_FILE` is set, the file names the server its token belongs to, and the CLI refuses to send that token anywhere else: a `--server` or `LANGALPHA_URL` naming another server is an error.

## Signing in with langalpha login

`langalpha login` first asks the server how it takes a sign-in (`GET /api/v1/auth/cli`, described in [Authentication](https://docs.langalpha.ai/api/authentication/#how-a-client-learns-to-sign-in)). Then it goes one of two ways.

**No sign-in needed.** A server that asks for no credential answers so, and `login` only records it as your default server: "http://localhost:8000 needs no sign-in."

**In the browser.** When the server names a sign-in provider, `login` prints a sign-in link and opens it in your browser. Sign in there, and the browser hands the result back to the CLI on a port of `127.0.0.1` on your machine; the tab then says you can close it. The CLI waits up to 5 minutes. Pass `--no-browser` to print the link without opening it. To sign in on a machine with no browser, see the next section.

```sh
langalpha login --server https://<your-server>
```

A browser sign-in renews itself. The CLI refreshes the access token shortly before it runs out, and again if the server refuses it. When the refresh itself is refused, the sign-in has ended, and the command says to run `langalpha login` again (exit code 3).

A server that asks for sign-in but offers the CLI no way to do it answers that it "takes no CLI sign-in", and `login` exits with code 3.

## Signing in on a remote machine

On a machine you reach over SSH and that has no browser, run `langalpha login --no-browser` there. It prints the sign-in page instead of opening it and waits up to 5 minutes. The page sends the browser back to `127.0.0.1` on the machine running `langalpha`, so a browser on your laptop can only finish the sign-in if you forward that port to the remote machine over SSH.

The port is not fixed. The CLI asks the operating system for a free one each time it runs, so it differs on every `login`. You read it from the printed page: the `redirect_uri` part of the link holds the callback address, with the port after `127.0.0.1%3A`. In `redirect_uri=http%3A%2F%2F127.0.0.1%3A44387%2Fcallback` the port is `44387`.

1. On the remote machine, run `langalpha login --no-browser --server https://<your-server>` and leave it running.
2. Find the port in the printed link, here `44387`.
3. On your laptop, in a second terminal, forward that port to the remote machine, and keep the connection open:

   ```sh
   ssh -L 44387:127.0.0.1:44387 <user>@<remote-host>
   ```

4. Open the printed page in the browser on your laptop and sign in. The browser is sent to `http://127.0.0.1:44387/callback` on your laptop, SSH carries that to the remote machine, and the CLI there finishes the sign-in.

Do steps 3 and 4 within the 5 minutes. The port is free to use on your laptop only if nothing else holds it there; if `ssh` reports that it cannot listen on the port, stop `login` and start it again to get a new one. The sign-in is saved on the remote machine, where you ran `login`.

:::note[Hosted and self-hosted]
**Hosted:** `langalpha login` opens the browser to sign in with your LangAlpha account. What the CLI may then do follows your account and plan, and the CLI is listed under Connected apps in your account settings, where Disconnect signs it out.

**Self-hosted:** With the default `HOST_MODE=oss`, the server asks for no sign-in and every request acts as the local user, so `login` only records the server. With sign-in turned on, the browser sign-in works once the operator sets `OAUTH_RESOURCE_ISSUER`, `OAUTH_RESOURCE_JWKS_URL`, `OAUTH_RESOURCE_AUDIENCE` and `OAUTH_CLI_CLIENT_ID` in `.env`, and the CLI then asks that provider for the scopes the server lists.
:::

## Where the sign-in is kept

`login` saves one sign-in per server in `~/.config/langalpha/credentials.json` (or under `$XDG_CONFIG_HOME/langalpha/` when that is set), along with which server is the default. The folder is readable only by you and the file is written owner-only.

Commands run side by side share the file safely. A refresh holds a lock beside it, so parallel commands spend the rotating refresh token once and reuse what it bought, and a refresh never writes back a sign-in that `logout` or a new `login` replaced in the meantime. If the file cannot be read, the CLI leaves it as it is and asks you to fix or remove it before signing in again.

## Signing out

`langalpha logout` forgets your saved sign-in for the server (the default one, or `--server`), and clears the default if it was that server. It does not revoke anything on the server. To end a browser sign-in for good on the hosted platform, disconnect the CLI under Connected apps.

## Checking who you are

`langalpha whoami` asks the server who it takes you for:

```text
server   http://localhost:8000
user     local-dev-user
via      session
scopes   all
```

`via` is how you are signed in: `session` (the web app's sign-in, or a server with none), `oauth` (a browser sign-in), `computer` (a computer's own token, which adds a `computer` line), or `service`. `scopes` lists what a token may do; a session holds every permission, and a token holding none prints `none`.

## Scopes

A token reaches only the routes whose scope it holds. The CLI's commands need these:

| Scope | Commands |
| --- | --- |
| `agent:read` | Every `list` and `show`, `threads follow`, `models list`, and the workspace lookup behind `-w` |
| `agent:run` | `threads new`, `send`, `retry`, `approve`, `reject`, `cancel`, and `automations trigger` |
| `workspace:write` | `workspaces create`, `archive`, `delete`; `threads archive`, `delete`; `automations create`, `edit`, `pause`, `resume`, `delete` |
| `computer:write` | `computers add`, `start`, `stop`, `remove` |

A command missing a scope fails with exit code 3 and names the scope. `data:read` covers market data routes, which the CLI does not call. Some actions take no scope at all and stay with the web app: approving or rejecting a brokerage order is one.

## Related

- [The LangAlpha CLI](https://docs.langalpha.ai/cli/overview/)
- [Sign-in commands](https://docs.langalpha.ai/cli/auth/)
- [Using the CLI inside a computer](https://docs.langalpha.ai/cli/inside-a-computer/)
- [Authentication](https://docs.langalpha.ai/api/authentication/)
