gemma-4-31b is registered, users can pass it anywhere they would
pass a hosted model:
~/.rkat/config.toml, so the workspace can inherit them.
Replace the generic server/alias examples with the chosen recipe rather than
combining every example, and run only commands whose aliases you registered.
Model
Every self-hosted setup has three pieces:
The alias is the name users type. The
remote_model is the model name returned
or expected by the serving stack.
Server entries carry connection facts only. The legacy
bearer_token /
bearer_token_env server fields are rejected at config parse; credentials are
owned by the realm auth profile selected through the binding. A self-hosted
server with no realm binding fails closed at run time.Server Shape
For Ollama, LM Studio, vLLM, and most private gateways, use the OpenAI-compatible transport:config.toml
self_hosted.default_model must name one configured alias when more than one
self_hosted.models entry exists. With exactly one alias it may be omitted and
that alias becomes the default. Meerkat rejects an ambiguous multi-model
configuration instead of choosing by table order.
api_style = "chat_completions" is the conservative default for Gemma 4 and
other self-hosted tool-calling models. Use another API style only after the
specific serving stack has been validated with Meerkat tools, structured output,
and multimodal input.
Realm Binding
Self-hosted credentials are realm-owned. Declare a backend, auth profile, and binding forprovider = "self_hosted" in the same realm config, and make it
the realm default (or select it per run with --auth-binding):
config.toml
config.toml
Which Binding Authenticates Which Server
self_hosted is a provider class, not an endpoint identity: a private vLLM
box and a hosted OpenAI-compatible gateway are both self_hosted and hold
different secrets. Selecting a model therefore constrains credential resolution
to the server that serves it - [self_hosted.models.<alias>].server - and not
to whichever realm on the chain happens to declare a self-hosted default.
A backend identifies its endpoint in one of two ways:
A binding that declares neither is unconstrained: it is used only when it is
the single unconstrained self-hosted binding reachable from the active realm, so
one-server setups keep working with no annotation.
A binding whose auth profile runs a credential command
(
source = { kind = "command" }) must declare server: the command’s token is
the host’s, and only the declaration says where it goes. A realm with such a
binding that declares none does not compose. The binding is never inferred from
its default_model and never unconstrained, and naming it explicitly for a model
it does not declare fails closed.
When the server cannot be identified honestly - no binding names it, or several
unconstrained bindings are reachable - the run fails closed with an error naming
the server and every binding considered, rather than sending some other server’s
key and surfacing that endpoint’s Unauthorized. Fix it by adding
server = "<server_id>" to the backend profile that authenticates it, or by
selecting the binding explicitly with --auth-binding <realm>:<binding>. An
explicit selection wins, except when the named binding declares a different
server, or is a credential command binding that declares none: those fail
closed too.
Alias Shape
config.toml
call_timeout_secs bounds one model call. stream_inactivity_timeout_secs
sets how long this model’s stream may stay silent, for example during a long
prefill before the first token, before Meerkat aborts the call as stalled and
retries it. It must be at least 60 seconds: the client waits up to 30 seconds
after the model’s final event for the usage trailer, and a shorter window
would race that wait, so config validation refuses it. Omit it to use the
[retry] stream_inactivity_timeout window (300 seconds by default); an
explicit [retry] stream_inactivity_timeout applies to every model, this one
included.
vision = true advertises image input. It is separate from image tool-result
blocks: the registry forces image_tool_results = false for
api_style = "chat_completions"; only a validated responses endpoint may
advertise that result shape.
supports_thinking and supports_reasoning describe the behavior Meerkat
should expose through the configured transport. Gemma 4 is reasoning-capable,
but normalized reasoning controls and trace streaming still vary by serving
stack, so validate the behavior you plan to rely on.
Keep supports_web_search = false. The current self-hosted factory path has no
provider-native web-search lowering. Setting it to true would suppress the
fallback without creating a native executor, so search requests would have no
working path. An explicit Meerkat web-search enable fails closed when no
fallback executor can be provisioned.
Gemma 4 Aliases
Recommended aliases:Ollama
Use Ollama when the model runs on the same machine as Meerkat and you want the lightest local setup.1
Serve the model
2
Register Ollama
In the common
[realm.global.backend.local] binding backend, change
server = "local" to server = "ollama". Keep the binding named local
and its local_auth profile in global.config.toml
3
Add aliases
config.toml
LM Studio
Use LM Studio when you want a desktop-managed OpenAI-compatible server.1
Start the local server
Load the Gemma 4 model in LM Studio, then start the local server.
2
Register LM Studio
In the common
[realm.global.backend.local] binding backend, change
server = "local" to server = "lmstudio". Keep the binding named local
and its local_auth profile in global.config.toml
3
Alias the served model
Use the model name LM Studio exposes in its
/v1/models output.config.toml
vLLM
Use vLLM when you want a private server with more deployment control.1
Launch vLLM
Start vLLM with the Gemma 4 model you want to expose.
2
Register the server
In the common If the endpoint requires a token, replace the common
[realm.global.backend.local] binding backend, change
server = "local" to server = "vllm". Keep the binding named local.config.toml
global auth profile
with this one and set VLLM_API_TOKEN before running Meerkat:config.toml
3
Add remote aliases
Point aliases at the exact model names your vLLM endpoint exposes.
config.toml
Refusals and Content Filters
Since 0.9.4 a refusal from an OpenAI-compatible server ends the run the way an OpenAI refusal does: a streameddelta.refusal ends the response as a
refusal, and a finish_reason of content_filter ends it as a content
filter. The run fails as content_filtered with that stop and an
open_ai-tagged detail, and the refusal text is never committed as an
answer. See
Refusals and content filters.
Validation
Run these after adding or editing self-hosted model config:local is the binding ID, not necessarily the server ID. Its backend’s
server must match the selected recipe (ollama, lmstudio, or vllm) and
the alias’s server.
The expected result:
rkat modelsshows aself_hostedprovider group and the aliases you added.rkat doctorresolves the realm binding and reports the server as reachable.rkat auth bindingsshows which backend and auth profile thelocalbinding joins, andrkat auth test localresolves it without running a model turn.rkat run -m ...works without an explicit--provider.- The alias points at the exact upstream
remote_modelexposed by the server.
rkat run fails with “no credential binding for self-hosted server”, add the
realm binding shown above, and give its backend profile the
server = "<server_id>" line so it names the endpoint it authenticates.
See Also
Providers
Hosted and self-hosted provider model.
CLI configuration
Config file locations and model settings.
CLI commands
Commands for running, diagnosing, and inspecting models.
