> For the complete documentation index, see [llms.txt](https://docs.kyvvu.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kyvvu.com/cli-reference/doctor.md).

# kyvvu doctor

**What you'll learn:** How to find out, in one command, whether your setup works — and if it doesn't, which part.

***

## `kyvvu doctor`

Runs nine checks and prints one line each. It never stops at the first failure, because the check that explains a failure is usually the one after it.

```
Usage: kyvvu doctor [OPTIONS]

Options:
  --api-url TEXT    Kyvvu API URL every check uses (overrides KV_API_URL)
  --agent-key TEXT  Agent to diagnose (defaults to KV_AGENT_KEY)
  --json            Emit JSON instead of a report
```

Exit code is `1` when any check **failed**, `0` when everything passed or only warned — so it works in a CI step or a support reply.

### A healthy setup

```bash
$ kyvvu doctor --agent-key my-agent
✓ Python              3.13.15 (CPython), minimum 3.10
✓ Versions            kyvvu 0.23.0, kyvvu-engine 0.13.0 (satisfies <1.0.0,>=0.13.0)
✓ .env                /app/.env defines 3 KV_* key(s): KV_AGENT_KEY, KV_API_KEY, KV_API_URL
✓ API                 https://platform.kyvvu.com reachable (200)
✓ Credential          API key valid
✓ Registration cache  'my-agent' cached, registered at 2026-09-11T11:27:53+00:00 (agent_id=agent_6442dabc)
✓ Policies            15 policy(ies) in effect from the api source at https://platform.kyvvu.com (7 at step_execution, enforcing)
✓ Trace sink          https://platform.kyvvu.com
• Local trial         no local trial record (kyvvu installed 2026-09-10)

All checks passed.
```

The **Versions** check compares the installed `kyvvu-engine` with the range the SDK declares, read from the installed package metadata. It needs nothing beyond the SDK's own dependencies: for a `>=X,<Y` range on release numbers, which is what the SDK declares, it decides on its own. Only a range or version outside that (a pre-release engine, say) needs `packaging`, and without it the line reads `• Versions … not verified (packaging not installed)` rather than guess.

The **Credential** check verifies the CLI session (`/auth/me`, which reports the account email) and the agent's API key (`KV_API_KEY`, or the stored default agent key) — **both**, when both exist, on one line. The API key is checked against `/policies/rules`, which names no account, so it reports only `API key valid`, and it is checked against the platform an agent started here would use, which is where its validity matters. A valid session never hides a rejected key: the line fails and names the key.

### Pointing it at another environment

`--api-url` decides which platform **every** check talks to: the API and Credential probes, the policy fetch and the trace sink. You do not also need `KV_API_URL` exported. Against a dev environment with a key minted somewhere else, every line names the URL you passed:

```bash
$ kyvvu doctor --api-url https://platform-dev.kyvvu.com --agent-key my-agent
✓ API                 https://platform-dev.kyvvu.com reachable (200)
✗ Credential          API key rejected by https://platform-dev.kyvvu.com
                      → Mint a new API key in the dashboard of https://platform-dev.kyvvu.com, or point the agent at the platform that issued this one (--api-url / KV_API_URL).
• Registration cache  no cached registration for 'my-agent' — the next run will register
✗ Policies            0 policies in effect: the configured api source at https://platform-dev.kyvvu.com could not be read and there was no cached set to fall back on — the platform rejected the API key (401)
                      → The agent's API key (KV_API_KEY) is not accepted by https://platform-dev.kyvvu.com: mint one in that platform's dashboard, or point the agent at the platform that issued it with --api-url / KV_API_URL.
✓ Trace sink          https://platform-dev.kyvvu.com
```

With a `kyvvu auth` session as well, the Credential line reports both, and a valid session does not turn a rejected key into a tick:

```bash
✗ Credential          CLI session valid — dev@mail-test.kyvvu.com; API key rejected by https://platform-dev.kyvvu.com
```

Without `--api-url`, the Policies and Trace sink checks resolve the platform exactly as an agent started in the same shell would: `KV_API_URL`, otherwise `https://platform.kyvvu.com`. The API check and the session half of the Credential check additionally fall back to the URL of a stored `kyvvu auth` session, which the CLI reads and the SDK does not; the API key is always checked where the agent would use it. When the two differ, the report says so on every line it affects and names the flag that aligns them. After `kyvvu auth --api-url https://platform-dev.kyvvu.com`, with a dev key in `KV_API_KEY` and no `KV_API_URL`:

```bash
$ kyvvu doctor --agent-key my-agent
✓ API                 https://platform-dev.kyvvu.com reachable (200)
✗ Credential          CLI session valid — dev@mail-test.kyvvu.com; API key rejected by https://platform.kyvvu.com
                      → If this key was minted on https://platform-dev.kyvvu.com, pass --api-url https://platform-dev.kyvvu.com or export KV_API_URL=https://platform-dev.kyvvu.com; otherwise mint a new one in the dashboard of https://platform.kyvvu.com.
• Registration cache  no cached registration for 'my-agent' — the next run will register
✗ Policies            0 policies in effect: the configured api source at https://platform.kyvvu.com could not be read and there was no cached set to fall back on — the platform rejected the API key (401)
                      https://platform.kyvvu.com is not the platform your CLI session uses (https://platform-dev.kyvvu.com); to diagnose that one, pass --api-url https://platform-dev.kyvvu.com or export KV_API_URL=https://platform-dev.kyvvu.com
                      → The agent's API key (KV_API_KEY) is not accepted by https://platform.kyvvu.com: mint one in that platform's dashboard, or point the agent at the platform that issued it with --api-url / KV_API_URL.
! Trace sink          https://platform.kyvvu.com — not the platform your CLI session uses (https://platform-dev.kyvvu.com), so its dashboard will not show these traces
                      → To send them there, pass --api-url https://platform-dev.kyvvu.com or export KV_API_URL=https://platform-dev.kyvvu.com.
```

The report is all `kyvvu doctor` prints. The policy check runs a real engine runner, but that runner's own stderr notices (the `Policy fetch failed …` warning and the `KYVVU: 0 policies in effect` banner an agent would print) are silenced for the probe, because the Policies line already says the same thing with the reason attached. An agent started in that shell still prints them.

### The line that matters

**Policies** is the reason this command exists. A policy *count* cannot tell apart situations that need completely different fixes — several of them report zero and allow every step:

| What you see                                                                                                              | What it means                                                                                                                                                                                                                                    | What to do                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `15 policy(ies) in effect … (7 at step_execution, enforcing)`                                                             | Working.                                                                                                                                                                                                                                         | Nothing.                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `0 policies in effect: the api source answered with no policies`                                                          | Nothing is assigned to this agent.                                                                                                                                                                                                               | Restore the bundled baseline: in the dashboard open **Manifests**, pick the Kyvvu baseline and **Assign** it, or `POST /api/v1/assignments` with the bundled `manifest_path` (listed by `GET /api/v1/baseline`) and no `repo_id`. Re-registering will **not** bring an unassigned baseline back — that is deliberate. See [Removing and restoring the baseline](/policy-authoring/bundled-baseline.md#removing-and-restoring-the-baseline). |
| `the api source offered a policy set that cannot be loaded, so nothing was loaded` (a failing check)                      | A policy in the set is unloadable: an unknown `rule_type`, params that do not match the rule, a duplicate name, a bad regex or the retired `scope:` key. A set is loaded whole or refused whole, so an agent started here would refuse to start. | Fix the policies listed under the check — each one names its reason.                                                                                                                                                                                                                                                                                                                                                                        |
| `0 policies in effect: the configured api source at <url> could not be read`                                              | The fetch failed and there was no cache to fall back on. The reason follows the dash: a `401` means that platform rejected the agent's API key.                                                                                                  | For a 401 or 403, fix `KV_API_KEY` for that URL, or point the agent at the platform that issued the key (`--api-url` / `KV_API_URL`). Otherwise fix the API check above.                                                                                                                                                                                                                                                                    |
| `N policy(ies) in effect, but the set is stale`                                                                           | Enforcing an older set; the last refresh failed.                                                                                                                                                                                                 | Check connectivity — the agent is not enforcing what the platform currently says.                                                                                                                                                                                                                                                                                                                                                           |
| `N policy(ies) in effect, but this client is too old for the platform (state=stale_cache, cause=client_upgrade_required)` | The platform refused this client (`426 client_upgrade_required`). The agent keeps enforcing the last set it fetched, but receives no policy changes.                                                                                             | Run the `pip install` command shown as the remedy, then restart the agent.                                                                                                                                                                                                                                                                                                                                                                  |
| `the platform refused this client as too old and no policies are cached` (a failing check)                                | The same refusal with nothing to fall back on: an agent started here would refuse to start.                                                                                                                                                      | Run the `pip install` command shown as the remedy.                                                                                                                                                                                                                                                                                                                                                                                          |
| `N policy(ies) in effect … but none at step_execution`                                                                    | The agent passed one check at registration; **every step it takes afterwards is allowed**. A fail-open blind spot with a tick on it.                                                                                                             | Assign a manifest carrying `step_execution` policies — the bundled baseline has them.                                                                                                                                                                                                                                                                                                                                                       |

An unloadable set prints every refusal with its reason, and `kyvvu doctor` exits non-zero:

```bash
$ kyvvu doctor
✗ Policies            the local source offered a policy set that cannot be loaded, so nothing was loaded and an agent started here would refuse to start
                      refused: policies[0] (Never loads): unknown rule_type 'tainted_path_blokc'. It is not in the engine's rule registry. Did you mean 'tainted_path_block'?
                      → Fix the policies listed under this check, then re-run.
```

### A client too old for the platform

When the platform no longer serves your installed engine, it answers the policy fetch with `426 client_upgrade_required` (see [Client and platform compatibility](/reference/compatibility.md)). The **Policies** check names that state and gives the upgrade as the remedy. With a policy cache (`KV_POLICY_CACHE_PATH`) holding the last set fetched, it is a warning: the agent is still governed, by a set that will not be refreshed.

```bash
$ kyvvu doctor --agent-key my-agent
! Policies            15 policy(ies) in effect, but this client is too old for the platform (state=stale_cache, cause=client_upgrade_required): it needs kyvvu-engine>=0.14.0 — the agent keeps enforcing the last set it fetched and will not receive policy changes until it is upgraded
                      → Upgrade: pip install -U kyvvu 'kyvvu-engine>=0.14.0'
```

An agent started in the same shell also prints its one-line notice on stderr (`kyvvu doctor` silences it for its own probe):

```
KYVVU: kyvvu 0.23.0 (kyvvu-engine 0.13.0) is too old for this platform (needs kyvvu-engine>=0.14.0); still enforcing the policies fetched at 2026-10-16T14:10:52+00:00; pip install -U kyvvu 'kyvvu-engine>=0.14.0' (state=stale_cache, cause=client_upgrade_required for agent_key='my-agent')
```

With nothing cached, the check fails, and `kyvvu doctor` exits non-zero. The line under it is the exact `KyvvuClientUpgradeRequired` message an agent started here would raise:

```bash
$ kyvvu doctor --agent-key my-agent
✗ Policies            the platform refused this client as too old and no policies are cached, so an agent started here would refuse to start
                      kyvvu 0.23.0 (kyvvu-engine 0.13.0) is too old for this platform (needs kyvvu-engine>=0.14.0); no policies are cached, so nothing can be enforced; pip install -U kyvvu 'kyvvu-engine>=0.14.0'
                      → Upgrade: pip install -U kyvvu 'kyvvu-engine>=0.14.0'
```

With `--json`, the check carries `"stale_cause": "client_upgrade_required"` and the platform's refusal under `client_upgrade`.

### Traces going nowhere

The **Trace sink** check is the tell for the commonest silent failure: an agent that registers, appears in the dashboard, and has an empty Logs page.

```bash
$ kyvvu doctor
! Trace sink          stdout (auto, because no policy source is configured here) — steps are printed, not sent to the platform
                      → Set KV_LOG_LOCATION to your API URL, or pass log_location=<api_url>; or set KV_API_KEY and an agent key (KV_AGENT_KEY or --agent-key), so the platform becomes the policy source and 'auto' resolves to it.
```

The hint names what is actually missing. With `KV_POLICY_FILE` set, the local file is the policy source and `auto` follows it, so the hint says that instead of promising the platform:

```bash
$ KV_POLICY_FILE=policies.yaml kyvvu doctor
! Trace sink          stdout (auto, because policies come from a local source, not the platform) — steps are printed, not sent to the platform
                      → 'auto' follows the policy source, here the local policy file (KV_POLICY_FILE), so steps are printed. To send them to the platform, set KV_LOG_LOCATION to your API URL; or set an agent key (KV_AGENT_KEY or --agent-key), so the platform becomes the policy source and 'auto' resolves to it.
```

(That run had `KV_API_KEY` set; without it the hint asks for both.)

### Working offline

`kyvvu doctor` needs no credentials at all — being logged out is one of the things it diagnoses. With `KV_POLICY_FILE` set it reports on a local manifest instead of the platform, so it works the same for a [`kyvvu serve --policy-file`](/cli-reference/serve.md) setup.

### The local trial record

If you ran [`kyvvu try --record-trial`](/cli-reference/try.md), the **Local trial** check reads that opt-in marker and reports how long it took you to get from `pip install` to a blocked step:

```bash
$ kyvvu doctor
• Local trial         3 offline run(s); first 2026-09-11; install to first block: 4m12s
                      → Delete it with `kyvvu try --forget-trial` (~/.kyvvu/try-marker.json).
```

That marker is **opt-in and never transmitted**. Nothing sends it anywhere, and declining costs nothing: with no marker, the check is informational and every other command behaves identically.

## `doctor` vs `whoami`

They sit side by side and answer different questions.

|                 | `kyvvu whoami`                          | `kyvvu doctor`                  |
| --------------- | --------------------------------------- | ------------------------------- |
| Question        | Who am I, and what would this shell do? | Is anything broken, and what?   |
| Needs a session | Yes — exits 1 without one               | No                              |
| On a failure    | Stops                                   | Keeps going, reports everything |
| Exit code       | 1 when logged out                       | 1 only when a check failed      |
| Output          | Six stable lines a script can grep      | A report                        |

`doctor` can also resolve something `whoami` deliberately refuses to guess: `whoami` only reads this shell's `KV_*`, so with no `KV_AGENT_KEY` it qualifies its answer about where traces go. `doctor` takes `--agent-key`, so given one it resolves the trace sink for real — and, with credentials, asks the platform what that agent's policy set actually is.

***

## Next steps

* [kyvvu auth](/cli-reference/auth.md) — log in, and what `whoami` reports
* [kyvvu try](/cli-reference/try.md) — enforcement with no account and no network
* [kyvvu serve](/cli-reference/serve.md) — a local policy evaluation server
