For the complete documentation index, see llms.txt. This page is also available as Markdown.

Provider connections

Store provider credentials once on the server and let importer, analyzer, and evaluator tasks pick them up automatically, instead of setting them in every worker's environment.

Every import guide so far sets provider credentials in the worker's environment: LANGFUSE_SECRET_KEY, LANGSMITH_API_KEY, and so on. That works, but it ties every credential to one worker's process, and rotating a key means touching every worker that might claim an import task.

A connection is the alternative: a server-side resource that holds a provider's credentials and non-secret values, so an importer, analyzer, or evaluator task carries them to whichever worker claims it. Like a secret, the sensitive values are encrypted at rest. Unlike a secret, a connection is scoped to one provider and can be marked the provider's default, so most imports, analyzers, and evaluators don't need to name one at all.

Create one from a plugin schema

Importers, analyzers, and evaluators can declare a connection_schema, the set of environment variables their provider SDK reads. Naming any plugin drives the create form:

kitaru connection create langfuse-prod --importer kitaru/langfuse

Name the plugin, not a version. A connection holds credentials for the plugin's provider, and every version of that plugin uses the same connection, so --importer, --analyzer, and --evaluator take a plugin name or UUID here rather than a NAME@VERSION reference.

For an analyzer or evaluator, use --analyzer or --evaluator instead:

kitaru connection create model-judge-prod --analyzer model-judge
kitaru connection create tone-judge-prod --evaluator tone-judge

This prompts for each property in the schema, in order, hiding input for anything the schema marks as secret (LANGFUSE_SECRET_KEY, in Langfuse's case). Skip the prompts with --set KEY=VALUE for a non-secret property or --set-secret KEY=VALUE for a secret one, repeated for as many keys as you already know:

kitaru connection create langfuse-prod \
  --importer kitaru/langfuse \
  --set-secret LANGFUSE_PUBLIC_KEY=pk-... \
  --set-secret LANGFUSE_SECRET_KEY=sk-... \
  --set LANGFUSE_BASE_URL=https://cloud.langfuse.com

Non-interactively (--non-interactive, or scripted from CI), every required property must arrive through --set or --set-secret, since there is no terminal to prompt on.

Create one for a provider directly

Skip the schema and address the provider by name, useful for a provider without a built-in importer or a custom importer that never declared a schema:

Defaults

Add --default on create, or promote an existing connection later:

A provider has at most one default connection. Setting a new one clears the previous default for that provider in the same request, there's nothing to unset by hand. kitaru connection update CONNECTION --no-default clears a connection's default status without setting another.

kitaru connection list, get CONNECTION, and update CONNECTION [--set ...] [--set-secret ...] round out management. An update replaces the whole env and secret maps, so --set sends the stored env with the keys you name applied on top and keeps the other values, while --set-secret sends exactly the secret values you name and replaces every stored one. Responses never carry secret values, only a secret_keys list naming which keys are set. Deleting a connection also deletes the secret holding its values, so delete CONNECTION requires --force.

Use one on an import, analyzer, or evaluator

An import that fetches from a provider's API can name a connection explicitly:

--connection only applies to an API import (one with no FILE argument), since a file upload is parsed without talking to the provider at all. On the REST API and the Python client, name it as connection_id on the API source of the import create request.

An analyzer selected for any import can name its own connection:

Repeat --analyzer-connection ANALYZER@VERSION=CONNECTION when different analyzers need different credentials. The analyzer token must exactly match one of the --analyzer values. On the REST API and the Python client, set connection_id on that analyzer's AnalyzerConfig.

An evaluator named on an import, experiment, replay, or evaluation can name its own connection the same way, with --evaluator-connection EVALUATOR@VERSION=CONNECTION, or connection_id on its EvaluatorConfig.

Resolution

A plugin connection resolves when its job is created, including evaluator jobs, in this order:

  1. The connection explicitly named for that importer, analyzer, or evaluator.

  2. Otherwise, the default connection for that plugin's provider.

  3. Otherwise, nothing is injected, and the package reads the worker's own environment, exactly as it did before connections existed. Only a worker that declares the provider claims such a task, see Worker credentials.

Self-hosted, single-tenant deployments can keep doing that. A connection overrides the worker's environment, it is never required. The importer for a file upload resolves no connection, since it does not call a provider API. An analyzer or evaluator attached to that import can still need its own connection.

The resolved importer connection is recorded on the import as connection_id. Each resolved analyzer connection is recorded in its analyzer config and copied to the analysis task. An evaluator connection is likewise recorded on its task at job creation. A default connection created afterward only applies to new jobs; it does not change the credentials label or connection of a pending task. To recover such a task, use a worker with the required credentials and selector, or submit a new job after creating the default connection. If a resolved connection is deleted before a worker claims the task, that task runs with nothing injected.

Merge order

A connection's values land in the task process environment alongside everything else the worker already merges, lowest precedence first:

  1. The worker's own environment.

  2. The connection's env values.

  3. The task's own env (set on the import request).

  4. The connection's secret values.

  5. KITARU_* contract variables (the API URL, the per-task token, and the like).

Only step 2 is new. Creating or updating a connection rejects a KITARU_* key outright, and rejects a key set as both an env value and a secret, since the merge order can't express "this key wins" for a collision that shouldn't exist in the first place.

Worker credentials

Some customers won't hand a Kitaru server their provider credentials at all, and a connection doesn't change that: it still means putting a secret on the server. For that case, the credentials stay in the worker's environment. An API import, analysis, or evaluation task whose plugin declares a connection_schema but resolved no connection carries the label kitaru/requires-credentials=<provider> alongside its usual task labels, and a worker selects the providers it holds credentials for:

That worker claims Langfuse API imports and reads LANGFUSE_SECRET_KEY and friends from its own environment, and it skips tasks that need any other provider's credentials, as well as task kinds it does not claim. List several providers as kitaru/requires-credentials=langfuse,openai. No connection is created, and none is needed.

A worker that sets no such selector is read as if it had set an empty one, kitaru/requires-credentials=, so it skips every task that needs provider credentials it would have to bring itself. Tasks whose credentials arrive through a connection, and tasks that need none, such as file imports and offline evaluations, carry no label and are claimed by any worker as before. A plugin that declares no schema stamps no label either. Tasks for importers, analyzers, and evaluators that declare a provider also carry kitaru/provider=<provider> whether or not a connection resolved, so --selector kitaru/provider=langfuse still pins a worker to one provider's tasks.

For a judge evaluator whose key stays on the worker, use the evaluator task kind and its provider:

If the evaluator instead uses a server connection, ensure a worker is running with --claim evaluator; it does not need the credential selector. See Judge evaluations.

Ephemeral workers register under the same rule. Set KITARU_SERVER_EPHEMERAL_WORKER__SELECTORS, or server.ephemeralWorker.selectors in the Helm chart, to a list of selectors, such as a kitaru/requires-credentials selector naming the providers whose credentials KITARU_SERVER_EPHEMERAL_WORKER__ENV carries.

SDK and MCP

client.connections exposes create, get, list, iter, update, and delete, mirroring the CLI. On the MCP server, kitaru_connection_read reads connections in read-only mode, without secret values, kitaru_connections_manage creates, updates, and sets defaults in standard mode, and kitaru_delete deletes a connection (kind: "connection") in destructive mode.

Next

Set up the connection your provider's importer needs, then pick up where its own guide left off: Langfuse, LangSmith, Braintrust, Logfire, Arize Phoenix, or MLflow.

Last updated

Was this helpful?