# Chat model providers

The desktop app carries a chat, and a model answers it. This page is about
where that model comes from: what a provider is here, how its key is
configured, and what each of the two providers Owl drives needs from you.

## What `owl providers` is for

Manage the model providers the desktop chat speaks to.

The chat lives in the desktop app and is answered by the daemon, which holds
the keys: they go to the OS keychain, and the database keeps only a reference
to them (ADR-0019, ADR-0022). The key is typed here rather than in the app, so
the app never holds one.

Owl speaks to anthropic and openrouter. Which models a provider offers is
Owl's to know for Anthropic and yours to say for OpenRouter, which carries
whatever your account has.

What the chat may do with a model is bounded: it reads what Owl knows -
Projects, Jobs, Runs, logs and diffs - and it never acts on its own. A command
it proposes runs only once you have agreed to it, from a fixed allowlist of
read-only programs, executed without a shell
([ADR-0022](/docs/decisions/0022-desktop-chat)).

## A provider is not an Account, and not a Driver

The three are easy to run together and mean different things
([CONTEXT.md](https://github.com/vojtechmares/coding-owl/blob/main/CONTEXT.md) defines the other two):

| | What it is | Configured with |
| --- | --- | --- |
| Model provider | Where the desktop chat's model comes from | `owl providers add` |
| Account | The subscription Owl runs an Agent as, with its own tool configuration and its own rate limits | `owl account add` |
| Driver | The plugin that knows how to operate one coding tool, such as Claude Code | `owl account add --driver` |

An Account and a Driver are about work Owl does for you while you are away. A
model provider is only about the conversation you have with Owl afterwards:
nothing a Job runs on goes through one, and configuring one does not change
what an Agent runs as.

## Configuring a key

Configure a way to reach models.

The key is read from standard input rather than taken as a flag, so it is not
in the shell history or in what every process on the machine can see:

```
owl providers add <provider> --key-stdin < key.txt
```

It is put in the credential store; the database records only that it is there.
Configuring a provider that is already configured replaces it, which is how a
key is rotated.

A file is only one way to get the key onto standard input, and the least safe:
keep it outside every Project so it cannot be committed, and delete it once the
provider is configured. Anything that prints the key will do instead, which is
what a password manager is for:

```
pass show anthropic/api-key | owl providers add anthropic --key-stdin
```

The credential store is the OS keychain where there is one, and a file under
the data home readable only by its owner where there is not - a continuous
integration runner, say. The daemon's own configuration chooses, and says which
it used in its log at startup ([ADR-0019](/docs/decisions/0019-accounts)).

There is no other way in. No flag takes a key as an argument, and the desktop
app never asks for one, because it is not the process that would hold it.

## Anthropic

Owl knows Anthropic's model list itself, so there is no `--model` to name when
you configure anthropic:

```
owl providers add anthropic --key-stdin < key.txt
```

That offers the chat these models:

- `claude-opus-5`
- `claude-sonnet-5`
- `claude-haiku-4-5-20251001`

Requests go to `https://api.anthropic.com` unless `--base-url` says otherwise.

## OpenRouter

OpenRouter carries models from many vendors and speaks the OpenAI wire shape
for all of them, which is why there is no separate `openai` provider. Which of
them your key can reach is your own account's business rather than Owl's, so
`--model` is required for openrouter, once per model:

```
owl providers add openrouter --key-stdin \
  --model anthropic/claude-sonnet-4.5 \
  --model openai/gpt-5 \
  < key.txt
```

Requests go to `https://openrouter.ai/api/v1` unless `--base-url` says
otherwise. A base url has to be somewhere a request can be sent: http or https,
with a host, and without a query, a fragment, or a name and password - every
request puts a path after it, and a credential in a url would end up in the
database and in a listing, which is not where one lives. Owl accepts http as
well as https, but the key goes on every request, so http belongs only to
something on the same machine, such as a proxy you are running yourself.

## Listing and removing

```
owl providers list
```

reports each configured provider with the models it offers and, where
`--base-url` named somewhere, that url - `(none)` for a provider on its own
default. It prints nothing else: a key is never printed. With none configured
it says so, and how to configure one.

```
owl providers remove openrouter
```

stops the chat speaking to a provider and deletes its key from the credential
store along with it. Removing one that is not configured exits non-zero and
says so.

## See also

- [ADR-0019](/docs/decisions/0019-accounts) - Accounts, credentials, and where a
  secret goes when there is no keychain.
- [ADR-0022](/docs/decisions/0022-desktop-chat) - the desktop chat as a daemon
  service, and the rules its commands run under.
- [CONTEXT.md](https://github.com/vojtechmares/coding-owl/blob/main/CONTEXT.md) - the words Owl uses for the rest of it.

---

Source: [docs/guide/chat-providers.md](https://github.com/vojtechmares/coding-owl/blob/main/docs/guide/chat-providers.md)
