> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Remote Connection (chatcli connect)

> Connect your terminal to a ChatCLI server over gRPC: addresses, tokens and JWTs, TLS and mTLS, LLM credential modes, one-shot use and troubleshooting.

`chatcli connect` runs your local ChatCLI against a remote [ChatCLI server](/server/server-mode): the model calls go to the server, which holds the provider keys. Everything else (the REPL, `/agent` and `/coder` loops, `@` tools, contexts, memory) runs on your machine, so tools read and change **your** files.

## Connect

```bash theme={"system"}
# Address as a positional argument or with --addr (or CHATCLI_REMOTE_ADDR)
chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN"
chatcli connect --addr chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN"
```

On success:

```text theme={"system"}
Connected to ChatCLI server (version: 1.214.0, provider: CLAUDEAI, model: claude-sonnet-5)
```

When the server runs a [K8s watcher](/kubernetes/k8s-watcher), a second line says so and its context is added to every prompt on the server side.

<Warning>
  The connection is **TLS by default**, with or without `--tls`: a server with a publicly trusted certificate needs no flag, and one signed by a private CA needs `--ca-cert ca.crt` (a CA file implies `--tls`, so the CA is used even when `--tls` is omitted). Plaintext needs an explicit opt-out, `CHATCLI_ALLOW_INSECURE=true`. Against a plaintext server (a local `chatcli server`, a chart with `tls.enabled=false` behind `kubectl port-forward`) you need:

  ```bash theme={"system"}
  CHATCLI_ALLOW_INSECURE=true chatcli connect localhost:50051 --token "$CHATCLI_REMOTE_TOKEN"
  ```

  Without it the connection fails with `tls: first record does not look like a TLS handshake`.
</Warning>

## Flags

| Flag | Env var | Description |
| - | - | - |
| `--addr <host:port>` (or positional) | `CHATCLI_REMOTE_ADDR` | Server address. `dns:///` resolution: every address a name resolves to is used round-robin |
| `--token <string>` | `CHATCLI_REMOTE_TOKEN` | Sent as `authorization: Bearer <token>`: the shared server token **or** a JWT |
| `--tls` | — | TLS 1.3 with the system CAs, or `--ca-cert`; without it the client still dials TLS with the system CAs unless `CHATCLI_ALLOW_INSECURE=true` |
| `--ca-cert <path>` | — | CA bundle that signed the server certificate; implies `--tls` |
| `--provider <name>` | — | Override the server's provider: `OPENAI`, `OPENAI_ASSISTANT`, `CLAUDEAI`, `BEDROCK`, `GOOGLEAI`, `XAI`, `ZAI`, `MINIMAX`, `MOONSHOT`, `STACKSPOT`, `OLLAMA`, `COPILOT`, `OPENROUTER`. `DEVIN` is not offered: the server image has no Devin CLI |
| `--model <name>` | — | Override the server's model |
| `--llm-key <string>` | `CHATCLI_CLIENT_API_KEY` | Your own provider API key or OAuth token, forwarded to the server |
| `--use-local-auth` | — | Forward the OAuth credential from `~/.chatcli/auth-profiles.json` |
| `--client-id`, `--client-key`, `--realm`, `--agent-id` | — | StackSpot credentials |
| `--ollama-url <url>` | — | Ollama base URL for this request (see [Ollama](#llm-credential-modes)) |
| `-p <prompt>` | — | One-shot: send one prompt, print the reply, exit |
| `--raw` | — | With `-p`: no Markdown/ANSI formatting |
| `--max-tokens <n>` | — | Response token limit; unset, the server uses the provider's `*_MAX_TOKENS`, else the model's catalog ceiling |

Client-side environment only (no flags):

| Variable | Meaning |
| - | - |
| `CHATCLI_ALLOW_INSECURE=true` | Dial plaintext when `--tls` is not given |
| `CHATCLI_TLS_CLIENT_CERT`, `CHATCLI_TLS_CLIENT_KEY` | Client certificate for mTLS; used **only with `--tls`** |

There is no `--server` flag and no TLS server-name override: the name you dial must be in the server certificate.

## TLS and mTLS

```bash theme={"system"}
# TLS, private CA
chatcli connect chatcli.example.com:50051 --tls --ca-cert ca.crt --token "$CHATCLI_REMOTE_TOKEN"

# TLS, publicly trusted certificate (for example behind an ingress on 443)
chatcli connect chatcli.example.com:443 --tls --token "$CHATCLI_REMOTE_TOKEN"

# Mutual TLS: the certificate is the credential (role from the server's CHATCLI_MTLS_ROLE)
CHATCLI_TLS_CLIENT_CERT=alice.crt CHATCLI_TLS_CLIENT_KEY=alice.key \
  chatcli connect chatcli.example.com:50051 --tls --ca-cert ca.crt

# Mutual TLS plus a JWT: the JWT's identity and role win over the certificate's
CHATCLI_TLS_CLIENT_CERT=alice.crt CHATCLI_TLS_CLIENT_KEY=alice.key \
  chatcli connect chatcli.example.com:50051 --tls --ca-cert ca.crt --token "$JWT"
```

Through `kubectl port-forward` you dial `localhost`, so the server certificate needs `localhost` (and `127.0.0.1`) in its SANs.

## LLM credential modes

<Tabs>
  <Tab title="Server credentials">
    No credential flags: the server uses its own provider and keys.

    ```bash theme={"system"}
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN"
    ```

    Requests that name no provider or model go through the server's [fallback chain](/server/server-mode#fallback-chain) when it has one.
  </Tab>

  <Tab title="Your API key">
    ```bash theme={"system"}
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" \
      --provider OPENAI --llm-key sk-xxx
    ```

    The key travels with each request and is used for that request only; the server never refreshes it.
  </Tab>

  <Tab title="Local OAuth">
    ```bash theme={"system"}
    # Once, inside chatcli: /auth login anthropic   (or openai-codex, github-copilot)
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" --use-local-auth
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" --use-local-auth --provider COPILOT
    ```

    Without `--provider` it tries Anthropic, then OpenAI, then GitHub Copilot. Only `CLAUDEAI`, `OPENAI` and `COPILOT` are OAuth providers; others need `--llm-key`.
  </Tab>

  <Tab title="StackSpot">
    ```bash theme={"system"}
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" \
      --provider STACKSPOT --client-id <id> --client-key <key> --realm <realm> --agent-id <agent>
    ```
  </Tab>

  <Tab title="Ollama">
    A URL sent by the client must be HTTPS on a public address, or the server refuses it ([SSRF protection](/server/server-mode#ssrf-protection)):

    ```bash theme={"system"}
    chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" \
      --provider OLLAMA --ollama-url https://ollama.example.com
    ```

    For an Ollama on a private network, configure it on the **server** (`OLLAMA_ENABLED=true`, `OLLAMA_BASE_URL=http://ollama:11434`) and connect with `--provider OLLAMA` only.
  </Tab>
</Tabs>

## One-shot mode

```bash theme={"system"}
chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" \
  -p "Explain Kubernetes pod disruption budgets"

# Script-friendly output
chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN" \
  --provider GOOGLEAI --llm-key "$GOOGLEAI_API_KEY" --model gemini-3.8-flash \
  -p "Summarize this diff: $(git diff HEAD~1)" --raw
```

`-p` sends one `SendPrompt` and exits with a non-zero status on error. Each run opens a new connection and authenticates again; that is fine for a tight loop, because the server's [failed-authentication limiter](/server/server-mode#failed-authentication-limiter) only counts authentications that fail.

## Interactive mode

Without `-p` you get the full REPL with the remote model:

```bash theme={"system"}
chatcli connect chatcli.example.com:50051 --tls --token "$CHATCLI_REMOTE_TOKEN"
```

* `/agent`, `/coder`, `@file`, `@git`, `@command` and the other tools run **locally**; only the model calls go to the server.
* `/switch` changes provider or model; `/cost` prices the real token usage the server reports for each reply.
* Replies stream as the provider produces them (`StreamPrompt`); the final message carries the usage, stop reason, and the provider and model that answered.

### Remote plugins, sessions and watcher status

`chatcli connect` binds the session to the server exactly like the **in-REPL** `/connect` command, which switches an already running local session:

```text theme={"system"}
/connect chatcli.example.com:50051 --tls --ca-cert ca.crt --token <token>
```

| Feature | `/connect` inside the REPL | `chatcli connect` |
| - | - | - |
| Server's plugins in `/plugin list`, executed on the server | yes | yes |
| `/agent list` and `/agent skills` add the server's agents and skills (`Agents on the server (<address>)`, `Skills on the server (<address>)`) after the local ones | yes | yes |
| `/session save` / `/session load` offer local, remote or both | yes | yes |
| `/watch status` queries the server's watcher | yes | yes |
| `/disconnect` back to the local provider | yes | yes |
| `Server has N plugins, N agents, N skills available` line | yes | no |

The server lists and executes plugins for `user` and `admin` callers only: a `readonly` credential sees no remote plugins and cannot download one (`PermissionDenied`).

`/connect` accepts `--token`, `--tls`, `--ca-cert` (which implies `--tls` here too), `--provider`, `--model`, `--llm-key`, `--use-local-auth` and the StackSpot/Ollama flags. It does not read `CHATCLI_REMOTE_TOKEN`; pass `--token`. `CHATCLI_ALLOW_INSECURE` and the mTLS variables apply as for `chatcli connect`.

`/watch status` on a remote server prints:

```text theme={"system"}
 K8s Watcher (remote): Watching 3 targets: 2 healthy, 1 warning, 0 critical
   Target: multi/3 targets | Pods: 9 | Alerts: 1 | Snapshots: 42
```

## Environment defaults

```bash theme={"system"}
export CHATCLI_REMOTE_ADDR=chatcli.example.com:50051
export CHATCLI_REMOTE_TOKEN=<token or JWT>
chatcli connect --tls --ca-cert ca.crt
```

## Multiple replicas

The client resolves the address with `dns:///` and balances round-robin across every address, pinging each connection every 30 seconds (5 seconds timeout) to drop dead pods. Against a Kubernetes server with more than one replica, point it at a headless Service (`service.headless: true` in the chart; automatic in the operator when `spec.replicas > 1`).

## Troubleshooting

| Message | Cause | Fix |
| - | - | - |
| `server address is required (use --addr or positional argument)` | No address | Pass one, or set `CHATCLI_REMOTE_ADDR` |
| `transport: authentication handshake failed: tls: first record does not look like a TLS handshake` | Plaintext server, client dialed TLS | `CHATCLI_ALLOW_INSECURE=true`, or enable TLS on the server |
| `x509: certificate signed by unknown authority` | Private CA not trusted | `--tls --ca-cert ca.crt` |
| `x509: certificate is valid for …, not …` | Dialed name not in the certificate SANs | Dial a SAN name, or reissue the certificate |
| `error reading server preface: remote error: tls: certificate required` | Server requires mTLS | `CHATCLI_TLS_CLIENT_CERT`/`CHATCLI_TLS_CLIENT_KEY` with `--tls` |
| `CHATCLI_TLS_CLIENT_CERT set but CHATCLI_TLS_CLIENT_KEY is missing` | Half of the key pair | Set both |
| `failed to read CA certificate: …` / `failed to parse CA certificate` | Wrong `--ca-cert` path or not PEM | Point it at the PEM CA file |
| `connect: connection refused` | Nothing listening at that address | Check the address, port, port-forward, and the server's bind |
| `Connected … (… provider: remote, model: remote)` followed by `authentication failed` | The server rejected the token (health checks need none, `GetServerInfo` does) | Use the right token; check its `exp` if it is a JWT |
| `authentication failed` on every call from one host with a valid token; the server log shows `auth failure rate limit exceeded` | Another caller on the same host exhausted the host's failure budget (5 failed authentications, then one per 12 s) | Fix that caller's credential; valid credentials are never throttled and the budget refills on its own |
| `token expired` | JWT past its `exp` | Mint a new one |
| `rate limit exceeded, retry after N seconds` | Over the server's per-caller rate limit | Wait N seconds (at least 1; the same value is in the `retry-after` header), or ask for higher `CHATCLI_RATE_LIMIT_*` |
| `invalid provider configuration: … non-HTTPS provider URLs are blocked …` | `--ollama-url http://…` | HTTPS URL, or configure Ollama on the server |
| `--use-local-auth only supports OAuth providers (CLAUDEAI, OPENAI, COPILOT) …` | Another provider with `--use-local-auth` | Use `--llm-key` |
| `no local OAuth credentials found. Run 'chatcli' then '/auth login anthropic' …` | No stored OAuth login | Run `/auth login …` first |

## Next steps

<CardGroup cols={2}>
  <Card title="Server Mode" icon="server" href="/server/server-mode">
    Configure and operate the server
  </Card>

  <Card title="Docker & Kubernetes" icon="docker" href="/start/docker-deployment">
    Deploy it
  </Card>

  <Card title="K8s Watcher" icon="binoculars" href="/kubernetes/k8s-watcher">
    Kubernetes context in every prompt
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.