Build with the AI
Use with opencode, Aider, Continue & nanocoder
Point any OpenAI-compatible coding agent at Krallo. opencode can even auto-load the models from a link.
Krallo exposes an OpenAI-compatible chat completions endpoint at
https://exe.krallo.com/api/v1. Any tool that speaks the OpenAI Chat Completions wire format
— opencode, Aider, Continue, nanocoder, the official OpenAI SDKs, or a raw curl — can drive it with
no adapter. You authenticate with a krallo_live_ bearer key, calls are token-metered on your own
wallet, and tools (function calling) and streaming both work.
Before you begin
You need one thing: an API key with the ai:invoke scope.
- Open the console at /ops/dev and create an app.
- Mint a key and grant it
ai:invoke(see Scopes). Copy thekrallo_live_…secret — it is shown once. - Export it as an environment variable so your editor never hard-codes it:
export KRALLO_API_KEY=krallo_live_...
powershell
$env:KRALLO_API_KEY = "krallo_live_..."
The key is a secret. Keep it in an environment variable or your OS keychain — never commit it to a repo or paste it into client-side code. If it leaks, rotate it in the console. See Authentication.
opencode: automatic setup
Recommended
opencode reads a remote provider configuration from a .well-known/opencode
document on the provider’s host, and this document sits at the top of opencode’s config hierarchy.
Krallo serves one at https://exe.krallo.com/.well-known/opencode. Because Krallo publishes the
full provider block — including every curated model — you do not write an
opencode.json by hand: you just supply the key and opencode loads the krallo
provider and all four models for you.
/.well-known/opencodeThis endpoint is public and unauthenticated — it carries no secrets, only model ids and display names — so you can open it in a browser to see exactly what opencode will load.
Set your API key
opencode reads the key from the KRALLO_API_KEY environment variable referenced by the served
config ({env:KRALLO_API_KEY}). Export it as shown above, then start
opencode and select any krallo-* model. That is the entire setup.
What the link serves
The document is a standard opencode config fragment. It declares the krallo provider (backed by the
@ai-sdk/openai-compatible npm package that opencode fetches automatically), the API base URL, the
key reference, and each model with a friendly name:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"krallo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Krallo",
"options": {
"baseURL": "https://exe.krallo.com/api/v1",
"apiKey": "{env:KRALLO_API_KEY}"
},
"models": {
"krallo-chat": { "name": "Krallo Chat" },
"krallo-code": { "name": "Krallo Code" },
"krallo-fast": { "name": "Krallo Fast" },
"krallo-reasoning": { "name": "Krallo Reasoning" }
}
}
}
}
If your opencode version does not pick up the remote document, fall back to the manual configuration below. It is the same provider block — identical base URL, key reference, and models — written into your local opencode.json.
opencode: manual configuration
To pin the configuration locally (or if remote config is not applied), add the provider block to your
opencode.json. Place the file in your project root or at
~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"krallo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Krallo",
"options": {
"baseURL": "https://exe.krallo.com/api/v1",
"apiKey": "{env:KRALLO_API_KEY}"
},
"models": {
"krallo-code": { "name": "Krallo Code" },
"krallo-chat": { "name": "Krallo Chat" },
"krallo-reasoning": { "name": "Krallo Reasoning" },
"krallo-fast": { "name": "Krallo Fast" }
}
}
}
}
Keep KRALLO_API_KEY exported in your shell so the {env:KRALLO_API_KEY} reference
resolves. Restart opencode and pick a krallo-* model from the model selector.
Available models
All four models accept the same request shape. Reference them by these public ids — Krallo maps each to an
internal route on its side, and the underlying provider names are intentionally not exposed. Omit the
model field and requests default to krallo-code.
| Model id | Display name | Best for |
|---|---|---|
krallo-code default | Krallo Code | Editing, refactoring, and agentic coding tasks. |
krallo-chat | Krallo Chat | General conversation and explanation. |
krallo-reasoning | Krallo Reasoning | Deep, multi-step problem solving. |
krallo-fast | Krallo Fast | Low-latency, low-cost quick turns. |
An unknown model returns a 400 with error type model_not_found and the
list of valid ids. You can also enumerate them programmatically via
GET /api/v1/models. Full request/response detail lives on
Chat completions.
Aider
Aider talks to any OpenAI-compatible endpoint through its OpenAI provider. Set the base URL and key, then name
the model with the openai/ prefix so Aider routes it through that provider:
export OPENAI_API_BASE=https://exe.krallo.com/api/v1
export OPENAI_API_KEY=$KRALLO_API_KEY
aider --model openai/krallo-code
On Windows PowerShell, set the variables with $env: first:
$env:OPENAI_API_BASE = "https://exe.krallo.com/api/v1"
$env:OPENAI_API_KEY = $env:KRALLO_API_KEY
aider --model openai/krallo-code
Continue
In Continue, add Krallo as an OpenAI-compatible model. Using config.yaml
(~/.continue/config.yaml):
models:
- name: Krallo Code
provider: openai
model: krallo-code
apiBase: https://exe.krallo.com/api/v1
apiKey: ${{ secrets.KRALLO_API_KEY }}
The equivalent in the legacy config.json form:
{
"models": [
{
"title": "Krallo Code",
"provider": "openai",
"model": "krallo-code",
"apiBase": "https://exe.krallo.com/api/v1",
"apiKey": "krallo_live_..."
}
]
}
nanocoder
nanocoder is a community,
local-first CLI coding agent whose default provider type is openai-compatible — exactly
what Krallo serves. Add Krallo as a provider in agents.config.json, either in your project root
or at the user level (Windows %APPDATA%\nanocoder\agents.config.json, Linux
~/.config/nanocoder/agents.config.json, macOS
~/Library/Preferences/nanocoder/agents.config.json):
{
"nanocoder": {
"providers": [
{
"name": "Krallo",
"baseUrl": "https://exe.krallo.com/api/v1",
"apiKey": "${KRALLO_API_KEY}",
"models": ["krallo-code", "krallo-chat", "krallo-reasoning", "krallo-fast"]
}
]
}
}
nanocoder substitutes ${KRALLO_API_KEY} from your environment, so the variable you exported in
Before you begin works here too. Then launch with Krallo selected — the
--provider value matches the name field in your config — or switch
provider/model from inside the session:
nanocoder --provider Krallo --model krallo-code
Any OpenAI-compatible SDK
Because the endpoint is byte-for-byte OpenAI Chat Completions, the official SDKs work by overriding the base URL.
Point them at https://exe.krallo.com/api/v1 — the SDK appends /chat/completions
itself, so do not include that suffix in the base URL.
Node.js
Node.jsimport OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://exe.krallo.com/api/v1",
apiKey: process.env.KRALLO_API_KEY,
});
const res = await client.chat.completions.create({
model: "krallo-code",
messages: [{ role: "user", content: "Write a bubble sort in Rust." }],
});
console.log(res.choices[0].message.content);
Or with plain environment variables that the SDK reads automatically:
shellexport OPENAI_BASE_URL=https://exe.krallo.com/api/v1
export OPENAI_API_KEY=$KRALLO_API_KEY
Python
pythonfrom openai import OpenAI
client = OpenAI(
base_url="https://exe.krallo.com/api/v1",
api_key="krallo_live_...",
)
res = client.chat.completions.create(
model="krallo-code",
messages=[{"role": "user", "content": "Explain a mutex in two sentences."}],
)
print(res.choices[0].message.content)
curl
The lowest-common-denominator check — a direct request with no SDK:
shellcurl https://exe.krallo.com/api/v1/chat/completions \
-H "Authorization: Bearer $KRALLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "krallo-code",
"messages": [{"role": "user", "content": "Say hello in one sentence."}]
}'
The response is a standard OpenAI chat.completion object:
{
"id": "chatcmpl-4f1c9a...",
"object": "chat.completion",
"created": 1752566400,
"model": "krallo-code",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": { "role": "assistant", "content": "Hello there!" }
}
],
"usage": { "prompt_tokens": 11, "completion_tokens": 3, "total_tokens": 14 }
}
Tools and streaming
Coding agents need two things beyond plain text, and both are supported.
-
Streaming. Send
"stream": trueand Krallo returns Server-Sent Events in the OpenAIchat.completion.chunkformat, terminated bydata: [DONE]. Text turns stream live token by token. See Streaming. -
Tools / function calling. Pass a
toolsarray and the model returnstool_calls. Krallo relays tool-call requests back to your client and never executes tools server-side — your agent drives the loop: it runs the tool, appends the result as atoolmessage, and calls again. This is exactly how opencode and Aider expect to operate. See Tools & function calling.
When a request asks for streaming and carries tools, Krallo computes the turn and then replays it as valid SSE frames (including any tool_calls). You still receive a well-formed stream that ends in [DONE] — it just is not emitted incrementally for tool turns.
Attach your own persona
You can steer any coding agent’s behavior by attaching one of your own personas. Set a per-request
persona id (or pin one to the app in the console) and its system prompt is prepended to your
messages. The persona must be one you can use — your own, or a shared system/global one; an unknown or
foreign explicit persona id returns a 400. See Personas.
Troubleshooting
| Symptom | Cause & fix |
|---|---|
.well-known/opencode returns 404 not_found |
The Developer API is not enabled on that host, so the auto-config document is not served. Use the manual configuration instead. |
401 unauthorized |
Missing or invalid bearer key. Confirm KRALLO_API_KEY is exported and begins with krallo_live_. See Authentication. |
403 / insufficient_scope |
The key lacks ai:invoke. Grant it in the console. See Scopes. |
400 model_not_found |
The model id is not one of the four public ids. Drop any provider prefix your tool added (Aider’s openai/ prefix is stripped before it reaches Krallo). |
402 insufficient_credits |
Your wallet balance cannot cover the request; the body reports required_units and balance. Top up. See Billing & quotas. |
429 too many requests |
Per-key rate limit hit. Back off and retry. See Rate limits. |
502 upstream_error |
The model provider was momentarily unavailable; the call is not charged. Retry. |
Windows PowerShell: curl is an alias for Invoke-WebRequest and will reject -H flags and bash \ line-continuations. Use the real curl.exe on one line, or Invoke-RestMethod with a real header table.
Errors raised by the AI endpoints themselves (400, 402, 502, …) follow the OpenAI shape
{ "error": { "message", "type" } }; authentication failures (401/403) use the native
{ "error": { "code", "message" }, "meta" } envelope, and the 429 is a flat
{ "error": "rate_limited", … } body. The full catalog is on
Errors.
Related pages
- Chat completions — the endpoint every tool on this page calls, in depth.
- Streaming — the SSE chunk format.
- Tools & function calling — the client-driven agent loop.
- Personas — attach your own system prompt.
- Billing & quotas — how token metering works.