Husk
GitHub

Models

Providers and API keys

Eleven providers, the exact environment variable each one reads, where to get a key, and which four have a free tier you can actually use.

Eleven providers ship in @husk-ai/models. Three have their own wire format and their own file; the rest speak OpenAI's /v1/chat/completions dialect and are configuration rather than code.

ProviderPriorityEnvironment variableFree tier
anthropic95ANTHROPIC_API_KEYno
openai85OPENAI_API_KEYno
google80GOOGLE_API_KEY or GEMINI_API_KEYyes
groq60GROQ_API_KEYyes
deepseek58DEEPSEEK_API_KEYno
cerebras56CEREBRAS_API_KEYyes
openrouter55OPENROUTER_API_KEYyes, on :free models
mistral52MISTRAL_API_KEYno
together50TOGETHER_API_KEYno
lmstudio42none — LMSTUDIO_HOST moves the endpointlocal, so free
ollama40none — OLLAMA_HOST moves the endpointlocal, so free

Priority is the quality ordering auto walks, highest first. It is not a statement about which provider is better at your task.

A provider with no key set is not an error. It is reported, with the fix:

husk doctor, models section

Setting a key

Husk reads keys from the process environment. Nothing is stored in ~/.husk, and no command writes a key to disk.

export ANTHROPIC_API_KEY=sk-ant-...
husk doctor            # confirms it took

For a persistent, non-shell-history setup, ~/.husk/.env is the file Husk reserves for local overrides. On Windows, setx ANTHROPIC_API_KEY "sk-ant-..." and open a new terminal.

The three first-party providers

anthropic

export ANTHROPIC_API_KEY=sk-ant-...       # console.anthropic.com
export ANTHROPIC_BASE_URL=...             # optional, for a proxy

Catalogued models: claude-opus-5, claude-sonnet-5, claude-haiku-4-5-20251001. All three support tools, vision, streaming and extended thinking, with a 200k context window.

Three things here are not shared with the OpenAI dialect, and they are why this provider has its own file: tool results ride inside a user message rather than a tool role, extended thinking is a content block whose signature must be echoed back verbatim, and prompt caching is opt-in per block.

Caching is placed on exactly two blocks — the system prompt, and the last stable user turn. Every cache_control marker costs a cache write, and marking the newest turn caches a prefix that will never be seen again.

Anthropic is also the one provider here that charges to write a cache entry. The catalog carries cacheWritePerMTok as an explicit price rather than a multiplier, because the write price is not a fixed fraction of input anywhere else.

When you enable thinking, Husk raises max_tokens to at least the thinking budget plus 1024 and drops temperature and top_p, because the API rejects a sampling override while thinking is on.

openai

export OPENAI_API_KEY=sk-...              # platform.openai.com
export OPENAI_BASE_URL=...                # optional, for a compatible gateway

Catalogued: gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, gpt-5, gpt-5-mini, gpt-4o, gpt-4o-mini, o3, o4-mini.

This is the base dialect plus the quirks OpenAI's own models have. Anything matching o1–o9 or gpt-5 is treated as a reasoning model: max_tokens becomes max_completion_tokens, temperature and top_p are deleted, and a thinking budget is mapped onto reasoning_effort.

Thinking budgetreasoning_effort
unsetmedium
≤ 2048low
2049–16383medium
≥ 16384high

Sending the wrong parameter name to a reasoning model is a 400, and a 400 is the one error class the router will not retry — so getting this right here is the difference between a working request and a dead one.

google

export GOOGLE_API_KEY=AIza...             # aistudio.google.com; GEMINI_API_KEY also read
export GOOGLE_BASE_URL=...                # optional

Catalogued: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite. Flash and Flash-Lite are marked free — reachable at zero cost on the Gemini API free tier, at a low rate limit. Pro is not.

Gemini is genuinely a different wire format, not a dialect: contents instead of messages, model instead of assistant, function results keyed by name rather than by call id, and a JSON Schema subset that rejects half of draft-07. Each of those is a translation @husk-ai/models owns.

The OpenAI-compatible providers

Each of these is a base URL, an environment variable and at most one quirk. If you find yourself needing a method for one, the quirk belongs behind a config flag instead.

groq — free tier

export GROQ_API_KEY=gsk_...               # console.groq.com/keys

Catalogued: llama-3.3-70b-versatile, llama-3.1-8b-instant. Both are marked free and both support tools. Groq's selling point is latency; the 70B is the most capable genuinely-free hosted model in the catalog.

cerebras — free tier

export CEREBRAS_API_KEY=...               # cloud.cerebras.ai

Catalogued: llama-3.3-70b, llama3.1-8b, both marked free. Cerebras rejects stream_options.include_usage, so Husk does not send it — which means token counts on a streamed Cerebras call come from Husk's own estimate rather than from the provider.

openrouter — free on :free models

export OPENROUTER_API_KEY=sk-or-...       # openrouter.ai

OpenRouter is one of three providers Husk asks for a live catalogue instead of trusting the static one. The :free suffix is the whole pricing signal: deepseek/deepseek-chat bills and deepseek/deepseek-chat:free does not, so a discovered model ending in :free is priced at zero and is eligible for --model free.

Husk sends OpenRouter's two attribution headers, http-referer: https://github.com/Hotragn/husk and x-title: Husk. Without them your calls are filed as anonymous on OpenRouter's activity page.

deepseek

export DEEPSEEK_API_KEY=...               # platform.deepseek.com

Catalogued: deepseek-chat, deepseek-reasoner. Both prices are conservative estimates, not published figures — see Cost.

mistral

export MISTRAL_API_KEY=...                # console.mistral.ai

Catalogued: mistral-large-latest, mistral-small-latest. Mistral's endpoint rejects unknown top-level stream options, so stream_options is not sent.

together

export TOGETHER_API_KEY=...               # api.together.ai/settings/api-keys

Catalogued: meta-llama/Llama-3.3-70B-Instruct-Turbo, Qwen/Qwen2.5-Coder-32B-Instruct. Together is asked for a live catalogue. Note that a Together model id contains a slash of its own; Husk splits provider/model at the first slash only, so together/meta-llama/Llama-3.3-70B-Instruct-Turbo resolves correctly.

lmstudio — local

No key. Husk probes http://127.0.0.1:1234/v1/models with a 2-second timeout and treats everything LM Studio serves as free.

export LMSTUDIO_HOST=http://192.168.1.20:1234/v1   # optional, to point elsewhere

Unavailable reads as http://127.0.0.1:1234/v1 is not listening, with the fix Start the LM Studio local server (Developer tab) and load a model.

ollama — local

No key. Husk lists what is actually pulled on the machine — never a catalogue of models you would have to download first — and reads the real context window and tool support from /api/show.

export OLLAMA_HOST=http://127.0.0.1:11434          # the default

Local models is the end-to-end setup for both of these.

Self-hosting a compatible endpoint

Anything that speaks /v1/chat/completions can be pointed at through the base-URL override of a provider you are not otherwise using:

export OPENAI_BASE_URL=http://10.0.0.5:8000/v1
export OPENAI_API_KEY=whatever-your-gateway-wants
husk models

Husk will apply OpenAI's reasoning-model quirks to any model id matching o1–o9 or gpt-5, so avoid those names on a gateway that does not want them.

What Husk sends where

Model requests go to the provider you configure. Other tasks may contact public sources, search providers, remote computers, and image registries. The installed runtime has no product telemetry or crash reporter; the hosted website and docs use Vercel Web Analytics.