Providers

A provider is a source of inference. You declare which ones you have; a cognet declares the roles it needs; the runtime matches them at boot.

That is the whole configuration step. You never write down which model runs which part of an agent — that is decided from what you actually have, every time it starts.

// ~/.axon/profiles/<you>/profile.config.ts
export default defineProfile({
    providers: [Anthropic(), Ollama(), Axon()],
})

Declare once, on your profile, and every agent you run inherits it. If you are signed in and have declared nothing at all, Axon() is already there — the rest of this tab is for when you want your own keys, your own hardware, or a specific vendor.

A provider is a catalogue, not a model

This is the one idea everything else follows from.

A provider answers "what can I supply" — a list of models with their context windows and modalities. It does not answer "which model should this agent use", because that question cannot be settled when you write the config: it depends on which cognet is running and what roles it declares.

So a cognet asks for main: { type: "generate", in: "text", out: "text", context: 100_000 } and the runtime finds something you have that fits. A cognet asking for a vision model on a machine with none says so at axon prepare, before anything runs — not at the first call that needs it.

One consequence worth internalising: an agent someone else published runs on your providers, not theirs. They never named a vendor, so there is nothing in their agent to be wrong about yours.

The three postures

Choosing a provider is really choosing about cost, keys, and where your tokens go.

Managed — Axon(). Inference through Axon Cloud, billed to your account. No keys, works the moment you are signed in, and its catalogue covers everything reachable through OpenRouter. The default, and what a fresh axon init ships with.

Bring your own key. Your credential, your vendor relationship, their rates — no markup, and Axon is not in the request path. Either an aggregator that fronts many vendors (OpenRouter(), Codex()) or a vendor directly:

Local — Ollama(). Inference on your own hardware. No API costs, and nothing leaves your machine — the strongest privacy posture available. The terminal manages the server and model downloads for you.

And one for the test suite: Mock() replaces inference with a deterministic local responder. The full loop still runs — tool calls execute, context accumulates — which is what makes agent tests fast, free, and repeatable.

Declaring more than one

Most setups have several. A local model for the cheap roles, a frontier route for the hard ones, managed inference to cover whatever is left:

export default defineProfile({
    providers: [Ollama(), Anthropic(), Axon()],
})

Order is preference. When one model is reachable through several of your providers — Claude Sonnet is available on Anthropic(), OpenRouter() and Axon() — the one you listed first wins. That is what makes [Anthropic(), Axon()] mean "my key first, managed as the fallback".

Order only ever breaks a tie between candidates that all satisfy the role. It can never make an unusable model usable, and it never overrides a local model, which always ranks first. Behaviour is the full account.

Credentials

Every factory takes the same options: key for your own credential, url for a self-hosted or regional endpoint, slots to cap concurrent calls.

type ProviderOptions = {
    /** Your own credential, instead of the environment or your account vault. */
    key?: string
    /** Endpoint override — self-hosted daemons, regional endpoints, proxies. */
    url?: string
    /** Ceiling on concurrent calls a fanned-out role may spend here. */
    slots?: number
}

Where a key comes from when you do not pass one depends on the provider:

Credential
Axon()Your signed-in session. Nothing to set
Codex(), OpenRouter()Your account vault — connect once, works on every machine you sign in from
Direct vendorsAn environment variable, named on each provider's page
Ollama(), Mock()None

A provider you declared but never configured is not an error. Declaring one is a statement about what you have, not a promise that it is set up — the failure comes when something tries to use it, naming the variable you need to set.

Picking a model

You do not have to. If you want to, model: on the agent is a preference applied to the cognet's primary role:

export default defineAgent({ model: "anthropic:claude-sonnet-4-6" })

The provider:model form pins the route as well as the model; a bare claude-sonnet-4-6 names the model and lets your provider order choose how to reach it. Either way it is a preference: a pin naming something you cannot supply falls back to ranking and tells you it did, rather than refusing to boot.

Press * in the terminal to override the model for the current session. See Models.

engine: is removed. It named one model for a whole agent, which could not survive a cognet declaring several roles. Sources move to your profile's providers:; a model preference becomes model:. See defineAgent.