Catalog providers are added through Settings → Add provider in the web UI. A custom provider is declared in $DSH_HOME/settings.yaml with a lowercase provider ID, apiKeyEnv naming an environment variable, an api protocol such as openai-completions, a baseURL, and a models list. The provider ID cannot be renamed later.
The harness is model-agnostic by construction: model adapters are plugin-provided rows in
dsh-base, no different from tools or storage. Adding a model means declaring where it lives and
how to talk to it.
There are two paths, and picking the wrong one accounts for most of the friction.
Path 1 — a provider already in the catalog
If your vendor is in the catalog — DeepSeek, Anthropic, OpenAI and others — do it in the UI:
Settings → Add provider → pick the provider → paste its API key → save.
The key is written to $DSH_HOME/.credentials.yaml. That file is deliberately separate from
$DSH_HOME/settings.yaml so settings can be shared or version-controlled while credentials are not.
That is the whole procedure. If this covers you, stop here.
Path 2 — a custom provider
Use this for an OpenAI-compatible gateway, a self-hosted inference server, or any endpoint not in the catalog. A custom provider needs five things: a lowercase provider ID, a base URL, an API protocol, a credential reference, and a model list.
llm-pi-ai:
providers:
my-gateway:
apiKeyEnv: GATEWAY_API_KEY
api: openai-completions
baseURL: https://gateway.example/v1
models:
- id: vision-preview
input: [text, image]Field by field:
my-gateway — the provider ID. Lowercase, and permanent. There is no rename; changing it
means creating a new provider and deleting the old one, then fixing every model reference that
pointed at the old ID. Choose it as if you will live with it, because you will.
apiKeyEnv — the name of an environment variable, not the key. This is the single most common
mistake in custom provider configuration. Pasting a live secret here both fails to authenticate and
writes a credential into a file designed to be shareable.
api — the wire protocol. openai-completions is the right choice for the large family of
gateways and servers that expose an OpenAI-compatible surface.
baseURL — where requests go. Whether to include the /v1 suffix depends on what your endpoint
expects; getting this wrong produces a 404, not an auth error, which is a useful signal when
debugging.
models — what the provider serves. Each entry needs an id and its input modalities.
input: [text, image] declares a vision-capable model; text-only models declare [text].
Export the credential
export GATEWAY_API_KEY="sk-..."
dsh --profile webTwo things bite people here. A running dsh process holds the environment it started with — adding
the export to ~/.zshrc does nothing until you restart it. And a GUI-launched terminal may never
have sourced your login shell at all. Check in the shell you actually launch from:
printenv GATEWAY_API_KEYVerify before you trust it
Confirm the endpoint and key work independently of the harness:
curl -sS https://gateway.example/v1/models \
-H "Authorization: Bearer $GATEWAY_API_KEY" | head -20401 means the key is wrong. 404 usually means the baseURL is wrong. A connection error is network,
not configuration. Then confirm the provider survived composition:
dsh --profile web --dump-configRemember the patch order — bundles, then the profile's cordis.patch.yml, then the home-level
$DSH_HOME/cordis.patch.yml, then --patch. A home-level layer overriding your edit is a routine
cause of a provider that "is definitely in the file" but is not in the running config.
Multiple providers, one harness
Nothing restricts you to one. Registering several — a cheap model for mechanical work, a stronger one for reasoning, a vision model for screenshots — is normal, and routing between them is exactly the kind of thing sub-agents in Standard mode exist for.
The practical guidance is to keep provider IDs descriptive of role rather than vendor
(fast-bulk, deep-review) where you can. Since the ID is permanent, a name describing what you
use it for survives a vendor swap; a name describing the vendor does not.
Frequently asked
Can I rename a provider ID?
No. The provider ID is permanent. Create a new provider with the ID you want and delete the old one, then update any model references.
Do I put my API key in settings.yaml?
No. apiKeyEnv is the name of an environment variable, not the key. Catalog-provider keys live in $DSH_HOME/.credentials.yaml; custom-provider keys live only in your environment.
Does the endpoint have to be DeepSeek's?
No. Any endpoint speaking a supported protocol works — a commercial gateway, a self-hosted inference server, or another vendor's API.