klava
README file written by hands primarily
Klava is a CLI tool for using code agents with any provider. Use Claude Code with your OpenAI-compatible provider. Make code agents more secure by filtering out leaked secret keys and cryptographic keys from your filesystem.
Features
- Fast & Lightweight: Written in Rust with async I/O
- Universal: Works with any OpenAI-compatible API (OpenRouter, Qwen, Cloud.ru, local LLMs)
- Security Guardrails: Filters out leaked secret keys and cryptographic keys from your filesystem before sending requests to LLM APIs
Installation
You can install klava from pip, npm, cargo or mise!
# Python (uvx and uv tool run it without a permanent install)
pip install klava
uv tool install klava
uvx klava --help
# Node (npx runs it without a permanent install)
npm install --global @zatsepin/klava
npx @zatsepin/klava --help
# Rust
cargo install klava --locked
mise use -g cargo:klava
Prebuilt binaries ship for linux x86_64/arm64 (glibc ≥ 2.17), macOS
(universal2: Intel + Apple Silicon) and Windows x86_64, so neither pip nor npm
needs a Rust toolchain. Alpine/musl and other platforms fall back to
cargo install klava.
Or you can build klava from source. You need to install rustup, clone the repository and then
cargo install --path . --locked
Releasing (maintainers)
Version and tag come from Cargo.toml [package] — bump it, plus
pyproject.toml and npm/package.json (the test pipeline runs
scripts/check-version.sh and fails on drift), then tag v<version>. The
Woodpecker pipelines build and publish PyPI wheels/sdist and the npm platform
packages per agent OS; both registries skip files that already exist, so a
re-run of the same tag is safe.
Launch AI Code Agent
The launch command starts the proxy server and launches an AI code agent with appropriate environment variables:
klava launch
klava launch claude
klava launch claude --provider ~deepseek/deepseek-flash-latest
klava launch opencode
klava launch pi
The first run has no models yet: klava launch (or klava providers add)
opens the onboarding wizard, which logs in to a provider, lists its models,
checks the connection and writes the ones you pick.
--provider <model> uses that configured model for this launch.
Harnesses see the real model names (~deepseek/deepseek-flash-latest,
glm-5.1, …) — the proxy advertises them on GET /v1/models and each agent's
config lists them, so the model picker and the model label in the UI show what
actually answers. A request picks the model named in its body; a name that is
not configured falls back to the configured defaults.
When several [[models]] entries share a name they form a pool. Pick how it is
used with routing:
routing |
Behaviour |
|---|---|
"shuffle" (default) |
random entry per request — spreads load/cost across endpoints |
"round_robin" |
cycles through the pool in declaration order |
"first" |
always the first entry (declaration order = failover priority) |
Managed background service (Linux/systemd, macOS/launchd)
klava launch (and klava up --daemon) manage the proxy as a per-port
background service whenever a supported service manager is available:
- Linux: systemd user unit
klava-<port>.service— auto-started at login,Restart=alwayson crash; - macOS: launchd LaunchAgent
~/Library/LaunchAgents/klava-<port>.plist—RunAtLoadat login,KeepAliveon crash.
In both cases the unit/plist is created on first use and kept up to date
(binary path, port, env keys), the service is started idempotently and
health-checked via /health, and each port is its own instance — so several
proxies can run side by side. Since the service takes over the port, agents
started later just reuse it — no per-session proxy spawn, no
"proxy is dead" surprises.
klava status # list managed instances
klava restart # restart the proxy on the configured port
klava stop # stop the proxy on the configured port
systemctl --user status klava-48017 # Linux: unit status
journalctl --user -u klava-48017 -f # Linux: live proxy logs
launchctl list | grep klava # macOS: instance state
tail -f ~/Library/Logs/klava-48017.log # macOS: live proxy logs
Pass --no-service to klava launch (or use plain klava up) to run a
short-lived child/foreground proxy instead — e.g. for CI or custom ports.
On Windows the managed service is not available yet; klava up keeps
working there.
Supported code agents:
| Agent | Description | Link |
|---|---|---|
claude |
Claude Code | https://code.claude.com |
opencode |
OpenCode Agent | https://opencode.ai/ |
pi |
Pi Agent | https://pi.dev |
Configuration File Location
To find the config file path on your system, run:
klava config
Platform-specific config file locations:
- Linux and macOS:
~/.config/klava/config.toml - Windows:
%APPDATA%\klava\config\config.toml
Set KLAVA_CONFIG_FILE=/path/to/config.toml to use another file (a second
profile, or a throwaway config for tests and CI).
Config file
klava providers add # onboarding wizard: Klava, OpenRouter, Cloudflare, Qwen, custom
klava providers list # what is configured, which model is the default
klava providers check # one tiny completion per model
klava providers set # choose the default model
klava providers remove m # drop a model (all entries with that name)
The wizard writes self-contained [[models]] entries — each carries its own
connection (type, base URL, key), so the same name on two endpoints is a pool
and no global "active provider" switch exists:
port = 48017
verbose = false
completion_model = "~deepseek/deepseek-flash-latest"
reasoning_model = "~deepseek/deepseek-flash-latest"
routing = "shuffle" # shuffle | round_robin | first
decide_model = "typesafe/jev-1.13" # one entry; backend auto-detected
[[models]]
name = "~deepseek/deepseek-flash-latest"
type = "klava" # klava login, no key in the file
[[models]]
name = "~deepseek/deepseek-flash-latest" # same name = pool with the entry above
type = "openai-compatible"
base_url = "https://openrouter.ai/api"
api_key_name = "OPENROUTER_API_KEY"
[[models]]
name = "glm-5.1"
type = "openai-compatible"
base_url = "https://foundation-models.api.cloud.ru"
api_key_name = "CLOUD_RU_KEY"
[[models]]
name = "deepseek-v4-flash"
type = "openai-compatible"
base_url = "https://chat.immers.cloud"
api_key_name = "IMMERSE_TOKEN"
chat_completions_path = "/v1/endpoints/generate/chat/completions"
[[models]]
name = "qwen3-coder-plus"
type = "qwen-code"
Notice: base_url should not include /v1 at the end!
Notice: If your provider exposes the chat completions endpoint at a nested
path (e.g. …/v1/endpoints/generate/chat/completions), set
chat_completions_path to that absolute path. It defaults to
/v1/chat/completions otherwise.
Notice: You can use api_key_name if you have the api_key in your environment,
if you want you can set api_key in the toml file with the api_key variable.
For type = "klava" no key is needed: the wizard uses klava login and klava
reads the saved session.
Notice: an entry with neither api_key nor api_key_name sends no
Authorization header — that is how a local server (Ollama, llama.cpp) is
configured. Naming an api_key_name whose variable is empty is an error.
Notice: upstream_model overrides the id sent upstream when the public name
differs (e.g. a friendly alias). name is what agents send and see.
Notice: klava model names may start with ~, which the shell expands. Quote
them on the command line: klava providers set '~deepseek/deepseek-flash-latest'.
Notice: The type can be openai-compatible, klava or qwen-code.
qwen-code uses an authentication flow involving reverse engineering of the Qwen Code application. You can use 1000 free requests per day of the great large model, but with risks due to this being an unofficial implementation of qwen-code.
Cloudflare Workers AI
Cloudflare has no OpenAI-style /v1/models listing, so the wizard asks for the
account id and stores the account-scoped endpoint:
[[models]]
name = "@cf/meta/llama-4-scout-17b-16e-instruct"
type = "openai-compatible"
base_url = "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1"
api_key_name = "CLOUDFLARE_AUTH_TOKEN"
Decide model (System One / jigor)
decide_model names exactly one [[models]] entry. Every classifier call
klava makes — the web-search tuning and any extension that asks for a decide —
uses it. The jigor backend is detected from that entry, so there is no
backend switch to set:
| Entry type / URL | Backend |
|---|---|
type = "klava" (the default) |
klava gateway |
host openrouter.ai |
OpenRouter |
host api.cloudflare.com (…/accounts/<id>/ai/…) |
Cloudflare Workers AI |
| any other OpenAI-compatible gateway | klava wire (default) |
Credentials come from the entry (api_key / api_key_name), falling back to
the saved klava login. Without a configured entry the built-in klava gateway
is used, so decide works out of the box after klava login. Set jigor =
on the entry only for a self-hosted gateway that cannot be recognised.
Because a classifier must be deterministic, a decide_model name shared by
several entries is rejected with an error — unlike regular models, which pool.
Manual Claude Execution
If you run klava up, and you want to run claude manually use this command
ANTHROPIC_BASE_URL=http://localhost:48017 claude
Don't forget to change the port to your klava proxy server port.
View Proxy Logs
View or follow proxy server logs:
klava logs # View recent logs
klava logs -f # Follow logs (like tail -f)
klava logs --follow # Follow logs (long form)
Note: The -v flag works only with the klava up command to enable verbose logging. When used with klava logs, it doesn't change the output. To see verbose logs, start the server with klava up -v first, then view the logs.
Known Limitations
The following Anthropic API features are not supported currently (Claude Code and similar tools work without these parameters): Many features like images are not ready yet... It's a work-in-progress tool.
Troubleshooting & Known Pitfalls
- ❌ Wrong:
https://openrouter.ai/api/v1 - ✅ Correct:
https://openrouter.ai/api - The proxy automatically adds
/v1/chat/completions
Roadmap & Contributing
Contributions welcome!
Current vision is:
- Refactoring -> split codebase into many crates like proxy, cli, providers ...)
- Add more Guardrails
- Add Token optimization algorithms. Reduce cost is a crucial goal
- Extensions: installable at runtime, in any language. See EXTENSIONS.md.
- Add more providers and agents
- Add benchmark wars between agents
- Add remote hand control using Telegram and Mobile
If you like it you can support me:
- Subscribe -> https://zatsepin.dev/subscribe
Metadata
Release files for klava 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| klava-0.3.0.tar.gz | 343.1 kB | Without provenance | Details for klava-0.3.0.tar.gz |
Built distributions (wheels)
| File | Actions Reset | |||
|---|---|---|---|---|
| klava-0.3.0-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details for klava-0.3.0-py3-none-win_amd64.whl |
| klava-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details for klava-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
| klava-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details for klava-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
| klava-0.3.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl | Python 3 | none | macOS 11.0+ universal2 (ARM64, x86-64), macOS 11.0+ x86-64, macOS 11.0+ ARM64 | Details for klava-0.3.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl |
Total release size: 16.6 MB
Release files / klava-0.3.0.tar.gz
| Download URL | klava-0.3.0.tar.gz |
|---|---|
| Size | 343.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d59b548f4a6e8a15565fbf54dd75164c0c1ba9cdea6002e5f0791cd3223ef6b8
|
|
BLAKE2b-256 checksum How to use checksums |
10c1012c649bf2b74dc67d6b7177586404e05c91b3af79e4f96f2a8245ebe615
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CentOS Linux","version":"7","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / klava-0.3.0-py3-none-win_amd64.whl
| Download URL | klava-0.3.0-py3-none-win_amd64.whl |
|---|---|
| Size | 3.0 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
d316a6d16ad4f4d4da1a1a4be1ccf5cf4c467707c263943093e88d0388ccec2b
|
|
BLAKE2b-256 checksum How to use checksums |
6676fdc3710d57fda58bab3c898ea9b342dca6bc089a36a951cf87336a7bc057
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / klava-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | klava-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 3.6 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
e80888fbf8d44599772b575040469514e9a375a313914c2fdc0ca16b78f3de36
|
|
BLAKE2b-256 checksum How to use checksums |
c9792459bc78608ceedc1b57eab0d649b826a6d456669998300440ddbd2fb078
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CentOS Linux","version":"7","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / klava-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | klava-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
b2f31257bbbeb535fe496993ebb473a02755d2927bca2a83b3f81bf01bb4e011
|
|
BLAKE2b-256 checksum How to use checksums |
b50b605a959928937a27f62ef03925eebaa5879818b39c25170f961f8b834469
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CentOS Linux","version":"7","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / klava-0.3.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl
| Download URL | klava-0.3.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl |
|---|---|
| Size | 6.3 MB |
| Tags | Python 3 macOS 11.0+ ARM64 macOS 11.0+ universal2 (ARM64, x86-64) macOS 11.0+ x86-64 |
|
SHA-256 checksum How to use checksums |
fc102667d090db52189c632e6a3083f0760efa7f01ea3c718d3032720e00cf2a
|
|
BLAKE2b-256 checksum How to use checksums |
39b6db83116c5fea72381a7c355a774d813a5c274a46161662d55454c254489e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for klava 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.