> 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/serve.md).

# kyvvu serve

**What you'll learn:** How to run a local policy evaluation server for non-Python agents.

***

## Usage

```
Usage: kyvvu serve [OPTIONS]

Options:
  --host TEXT         Bind address (default: 127.0.0.1)
  --port INTEGER      Bind port (default: 8080)
  --policy-file TEXT  Manifest YAML to enforce locally (repeatable). No credentials needed.
  --baseline          Enforce the bundled frozen baseline set. No credentials needed.
  --api-url TEXT      Kyvvu platform API URL
  --api-key TEXT      Bearer API key
  --agent-key TEXT    Agent key for policy fetch
```

## What it does

Starts a local HTTP server that wraps the kyvvu-engine. Non-Python agents (JavaScript, Go, Rust, etc.) can call this server to evaluate steps, record history, and end tasks — same engine, same policies, same sub-millisecond evaluation.

## Two policy sources; only one needs an account

Credentials are needed only when policies are actually **fetched**.

```bash
# Offline: no account, no API key, no network.
kyvvu serve --baseline
kyvvu serve --policy-file manifests/security/owasp-agentic-default.yaml
kyvvu serve --policy-file owasp.yaml --policy-file my-extras.yaml
```

```
kyvvu serve — policy evaluation server
  Policies:  bundled frozen baseline (local, no credentials)
  Endpoints: http://127.0.0.1:8080/evaluate, /record, /end_task, /health
```

`GET /health` then reports `"source": "local"` with the policy count and `"load_state": "enforcing"`, so an airgapped harness can assert at startup that something is actually being enforced.

A local source **clears the credentials for that process**, so `--baseline` in a shell that exports `KV_API_KEY` runs the bundled set rather than quietly fetching a different one. An invalid or missing manifest refuses to start, with every problem listed: a server that boots ungoverned on a typo is worse than one that does not boot.

Precedence is flags > environment > `~/.kyvvu/config.toml`, in both directions:

* `--policy-file` / `--baseline` win over an exported `KV_API_KEY`.
* `--api-key` / `--agent-key` win over an exported `KV_POLICY_FILE`. (They used not to: the env var set the local source, which then blanked the credentials typed on the command line.)
* Passing local *and* API flags together is refused rather than resolved, because silently picking a winner is how a server ends up enforcing something other than what was asked for.
* With nothing explicit, an exported `KV_POLICY_FILE` is honoured — unless the API is also fully configured, in which case the API wins, matching what an embedded `KyvvuRunner` with the same environment would do.

`kyvvu-engine`'s own `kyvvu-serve` entry point takes `--policy-file` too, but not `--baseline` — the frozen set is policy *content* and ships in the `kyvvu` SDK, not in the BSL-licensed engine.

## Prerequisites

Requires `uvicorn` and `fastapi`. These are included when you install the engine with serve extras:

```bash
pip install "kyvvu-engine[serve]"
```

Or install the full SDK (which includes everything):

```bash
pip install kyvvu
```

## Starting the server against the platform

```bash
$ export KV_API_URL=https://platform.kyvvu.com
$ export KV_API_KEY=KvKey-...
$ export KV_AGENT_KEY=my-agent
$ kyvvu serve
kyvvu serve — policy evaluation server
  API:       https://platform.kyvvu.com
  Agent:     my-agent
  Endpoints: http://127.0.0.1:8080/evaluate, /record, /end_task, /health
```

Or with explicit flags:

```bash
kyvvu serve --host 0.0.0.0 --port 9090 \
  --api-url https://platform.kyvvu.com \
  --api-key KvKey-... \
  --agent-key my-agent
```

## Endpoints

| Method | Path              | Purpose                                                                                                                                               |
| ------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/health`         | Liveness probe — returns the full policy status: `source`, `load_state`, policy count, `policies_offered`, `dropped`, last fetch time, TTL remaining. |
| `POST` | `/evaluate`       | Preflight evaluation of an intended behaviour. Returns `{action, risk_score, policies, blocked}`.                                                     |
| `POST` | `/record`         | Record a completed step. Returns `{step, task_id}`.                                                                                                   |
| `POST` | `/end_task`       | Close a task — evict history and flush logs. Returns `{status, task_id}`.                                                                             |
| `POST` | `/register_agent` | Evaluate agent-registration policies.                                                                                                                 |

See [REST API (Non-Python)](/integrations/rest-api.md) for full request/response examples.

## Configuration

All `KV_*` environment variables work identically to `KyvvuRunner`:

* `KV_POLICY_FILE` — manifest YAML path(s), comma-separated: the credential-free policy source
* `KV_API_URL` — platform API URL
* `KV_API_KEY` — bearer API key
* `KV_AGENT_KEY` — agent key for policy fetch
* `KV_LOG_LOCATION` — trace flush destination (URL, `stdout`, file path, `none`, or `auto` — the default, which follows the policy source)

{% hint style="warning" %}
**`auto` changes where a credentialed `kyvvu serve` writes its trace.** With `KV_API_URL`, `KV_API_KEY` and `KV_AGENT_KEY` set and no `--policy-file`, the policy source is the platform, so `auto` sends the trace there too — a sidecar that was piping this container's stdout goes quiet. Set `KV_LOG_LOCATION=stdout` to keep the old behaviour. Offline mode (`--policy-file`) is unaffected: it resolves to `stdout`, even in a shell exporting `KV_API_KEY`.
{% endhint %}

\- \`KV\_LOG\_FORMAT\` — trace format (\`kv\`, \`json\`, or \`otlp\`) - \`KV\_INCIDENT\_LOCATION\` — incident destination; unset = inherit the trace sink (\`…/api/v1/incidents\` for \`kv\`) - \`KV\_INCIDENT\_FORMAT\` — incident format; unset = inherit \`KV\_LOG\_FORMAT\` - \`KV\_POLICY\_TTL\_SECONDS\` — policy cache TTL

The server also reads `~/.kyvvu/config.toml` (populated by `kyvvu auth`).

***

## Next steps

* [kyvvu try](/cli-reference/try.md) — the same offline enforcement, without the HTTP server
* [REST API (Non-Python)](/integrations/rest-api.md) — integration guide with curl examples
* [Configuration Reference](/deployment/configuration.md) — all environment variables
* [Architecture](/core-concepts/architecture.md) — why in-process evaluation is preferred over remote
