settings.json
Override any provider's base URL, API key, display name, or default model — merged across a project, org-managed, and user scope hierarchy.
settings.json is Stella's configuration file. It has five top-level sections, each with
its own merge rule across the scope hierarchy:
| Section | Configures | Merge across scopes |
|---|---|---|
providers | Override any built-in provider — or define new ones | per field |
agent_engine_config | Per-agent models, gateways, prompts, effort, params | per field, per agent |
hooks | Shell commands on lifecycle events | concatenate |
tools | Built-in tool switches (today: bash) | last scope wins |
mcp | MCP registry override | last scope wins |
The providers map lets you override the built-in defaults for any provider — without
reaching for a provider-specific environment variable. In user scope
(~/.config/stella/settings.json) it is the recommended way to, for example, route a Z.ai
coding-plan subscription through its dedicated endpoint — see the
coding-plan recipe.
The providers map
Keyed by provider id. Every field is optional — supply only what differs from the built-in default:
{
"providers": {
"zai": {
"id": "zai",
"name": "ZAI Provider",
"base_url": "https://api.z.ai/api/coding/paas/v4",
"api_key": "your_api_key"
}
}
}| Field | Type | Meaning |
|---|---|---|
id | string | Optional restatement of the map key; if present it must equal the key — a mismatch is a hard, named load error (guards against copy-pasting the wrong provider) |
name | string | Display name shown by stella config / stella models |
base_url | string | Base URL requests go to (a coding-plan endpoint, a proxy, a gateway) |
api_key | string | API key for this provider (slots into the credential chain) |
api_key_env | string | Name of an environment variable to read the key from — safer than an inline api_key in a committed project file |
default_model | string | Model used when none is pinned with --model |
dialect | string | Wire adapter for a new provider: openai-compatible (default) | openai-responses | anthropic | gemini (vertex/bedrock are reserved to built-ins) |
The map key is the provider id (zai above). Leave a field out entirely to inherit the
built-in default — note that an empty string for base_url, name, or
default_model overwrites the built-in with an empty value (breaking the provider or its
model selection) rather than being ignored; only api_key treats empty as unset.
providers both overrides built-ins and defines brand-new providers: use an id that
isn't built-in, set the required base_url, and optionally a dialect (defaults to
openai-compatible). That is how you point Stella at any OpenAI-compatible gateway from
config alone.
The scope hierarchy
settings.json is read across three scopes and merged, applied lowest to highest so the
more specific scope wins. The merge is field by field for providers and
agent_engine_config (whose allowed_models list replaces wholesale rather than
merging), last-scope-wins for mcp and tools, and hooks are the exception — they
concatenate across scopes (see Lifecycle hooks).
| Preference | Scope | Path |
|---|---|---|
| Highest | project | <workspace>/.stella/settings.json |
| Middle | org-managed | STELLA_MANAGED_SETTINGS, else the platform default (below) |
| Lowest | user | ~/.config/stella/settings.json |
The org-managed path is resolved as:
- The
STELLA_MANAGED_SETTINGSenvironment variable (an explicit path — for MDM/CI), else /Library/Application Support/stella/settings.jsonon macOS, or/etc/stella/settings.jsonon other Unix.
A project entry overrides an org-managed one, which overrides a user one — per field. So a project can change just a base URL while inheriting the API key from a higher scope. This lets an organization ship shared defaults (a gateway base URL, a team key) that a project can still override.
A missing file at any scope is fine — it is simply skipped. A file that is present but malformed is reported as a named error rather than silently dropping your configuration.
Worked example
Suppose three files are present:
{
"providers": {
"zai": {
"api_key": "sk-personal",
"name": "User Name",
"base_url": "https://api.z.ai/api/paas/v4"
}
}
}{
"providers": {
"zai": {
"name": "Org Gateway",
"base_url": "https://org-gateway.example/v4"
}
}
}{
"providers": {
"zai": {
"base_url": "https://api.z.ai/api/coding/paas/v4"
}
}
}The merged result for zai — with the project trusted (STELLA_TRUST_PROJECT=1):
base_url→https://api.z.ai/api/coding/paas/v4— project winsname→Org Gateway— org-managed wins over user (project didn't set it)api_key→sk-personal— from user (only the user scope set it)
Without STELLA_TRUST_PROJECT=1 — the default for any repo you didn't explicitly
trust — the project's base_url is a credential-routing field and is dropped by the
trust boundary: the merged base_url above would be the
org gateway's, with a stderr notice naming the skipped field. Project-scope
base_url/api_key/api_key_env examples anywhere in these docs assume a trusted
project; for personal routing, user scope needs no flag.
Precedence within a field
settings.json values are one link in a larger chain. Command-line flags still win:
Base URL — --base-url flag > the ZAI_GLM_CODING_PLAN=1 toggle (Z.ai only) >
settings.json base_url > the built-in default. So if both the env toggle and a
settings.json base_url are set for Z.ai, the env toggle wins.
API key — --api-key flag > the provider's env var > settings.json
api_key > ~/.config/stella/credentials.toml > interactive prompt. See the
credential chain.
An API key configured only in settings.json is enough for Stella to auto-detect and
run that provider — no provider-specific environment variable required.
The Z.ai coding-plan example
If you subscribe to a Z.ai coding plan, point providers.zai.base_url at the coding
endpoint instead of setting the bespoke ZAI_GLM_CODING_PLAN=1 variable. A subscription
is personal, so this belongs in user scope — project scope would silently drop the
base_url unless the repo is trusted with STELLA_TRUST_PROJECT=1:
{
"providers": {
"zai": {
"base_url": "https://api.z.ai/api/coding/paas/v4"
}
}
}The full walkthrough — plan tiers, verification, and routing the whole pipeline at the subscription — is the coding-plan recipe.
Your ZAI_API_KEY env var (or a settings.json api_key) supplies the key; the base URL
now comes from the file. Verify:
stella config # Base URL should read https://api.z.ai/api/coding/paas/v4The ZAI_GLM_CODING_PLAN=1 environment toggle still works and actually takes precedence
over a settings.json base_url for Z.ai — so if you switch to the provider-agnostic
setting, unset the env toggle to avoid a surprising override. It may be removed in a future
release.
The mcp section
settings.json also has a top-level mcp section. Its registry_url sets the base URL of
the MCP Server Registry that stella mcp search and the deck's MCP tab query; when unset it
falls back to the official registry. Like providers, it merges last-scope-wins per field,
so a project can point at a different registry than the user default.
{
"mcp": {
"registry_url": "https://registry.example.internal/mcp"
}
}The tools section
The top-level tools section switches built-in tools that are off by default. There
are two such switches — raw bash and the web family:
{
"tools": {
"bash": "on",
"web": "on"
}
}Absent or "off", the bash tool is not registered in any session — including
interactive ones; the agent works through the structured tools (run_tests,
build_project, run_script, the process tools) instead.
"web": "on" registers the web family — web_fetch,
web_extract_assets, web_download, and (with a BRAVE_API_KEY or TAVILY_API_KEY)
web_search. It is off by default for the same reason as bash: a fetched page is both
untrusted input and an outbound network channel. Logged-in fetches are configured
separately in ~/.config/stella/web_auth.toml.
The values are the strings "on"/"off" — a bare boolean or a typo is a hard, named
parse error rather than a silent ignore. Each field merges last-scope-wins independently,
so a project can switch web on for a repo whose workflow needs it while your user scope
keeps it off. See Built-in tools for what registers when.
The project trust boundary
A repository you just cloned can carry a <workspace>/.stella/settings.json — and an
untrusted repo must not be able to run commands on your machine or route your API keys to
a server it controls. Stella therefore holds back the dangerous parts of a project-scope
file until you opt in with STELLA_TRUST_PROJECT=1:
- Hooks. Project-scope
hooksload only when the project is trusted. (The legacySTELLA_PROJECT_HOOKS=1flag still unlocks hooks — and only hooks.) - Credential routing. Project-scope values for
providers.<id>.base_url,providers.<id>.api_key,providers.<id>.api_key_env, andmcp.registry_urlare silently dropped (the trusted-scope values win) — otherwise a malicious repo could point a provider at its own base URL and exfiltrate the key you attach.
A stderr notice names anything skipped, so a trusted-but-forgotten flag is diagnosable.
Cosmetic project fields (name, default_model, dialect) apply regardless of trust.
Lifecycle hooks
settings.json also carries hooks — shell commands that fire on agent lifecycle events.
That schema is documented on the Hooks page.
Unlike providers, hooks concatenate across scopes — every scope can add matchers and
none replaces another's. Hooks from the user and org-managed scopes always load, but hooks
declared in a repository's project-scope file load only behind the
project trust boundary (STELLA_TRUST_PROJECT=1, or the
legacy hooks-only STELLA_PROJECT_HOOKS=1); a stderr notice names any skipped project
hooks.
Security
settings.json may contain an API key in plaintext. If you store keys there, treat the
file like any other secret — restrict its permissions and keep the project-scope file out
of version control (add .stella/settings.json to .gitignore if it holds credentials).
For keys specifically, credentials.toml is written
with owner-only permissions and is the safer home.
Configuration
How Stella is configured — settings.json, credentials, workspace files, and the scope hierarchy.
Agent engine config
Configure each engine agent — default, worker, judge, triage — with its own model, gateway, prompt, reasoning effort, and sampling parameters via agent_engine_config in settings.json, or interactively in the AGENT ENGINE tab's engine panel.