Stelladocs
Configuration & Settings

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:

SectionConfiguresMerge across scopes
providersOverride any built-in provider — or define new onesper field
agent_engine_configPer-agent models, gateways, prompts, effort, paramsper field, per agent
hooksShell commands on lifecycle eventsconcatenate
toolsBuilt-in tool switches (today: bash)last scope wins
mcpMCP registry overridelast 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"
    }
  }
}
FieldTypeMeaning
idstringOptional 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)
namestringDisplay name shown by stella config / stella models
base_urlstringBase URL requests go to (a coding-plan endpoint, a proxy, a gateway)
api_keystringAPI key for this provider (slots into the credential chain)
api_key_envstringName of an environment variable to read the key from — safer than an inline api_key in a committed project file
default_modelstringModel used when none is pinned with --model
dialectstringWire 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).

PreferenceScopePath
Highestproject<workspace>/.stella/settings.json
Middleorg-managedSTELLA_MANAGED_SETTINGS, else the platform default (below)
Lowestuser~/.config/stella/settings.json

The org-managed path is resolved as:

  1. The STELLA_MANAGED_SETTINGS environment variable (an explicit path — for MDM/CI), else
  2. /Library/Application Support/stella/settings.json on macOS, or
  3. /etc/stella/settings.json on 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:

~/.config/stella/settings.json (user)
{
  "providers": {
    "zai": {
      "api_key": "sk-personal",
      "name": "User Name",
      "base_url": "https://api.z.ai/api/paas/v4"
    }
  }
}
org-managed (STELLA_MANAGED_SETTINGS)
{
  "providers": {
    "zai": {
      "name": "Org Gateway",
      "base_url": "https://org-gateway.example/v4"
    }
  }
}
<workspace>/.stella/settings.json (project)
{
  "providers": {
    "zai": {
      "base_url": "https://api.z.ai/api/coding/paas/v4"
    }
  }
}

The merged result for zaiwith the project trusted (STELLA_TRUST_PROJECT=1):

  • base_urlhttps://api.z.ai/api/coding/paas/v4project wins
  • nameOrg Gatewayorg-managed wins over user (project didn't set it)
  • api_keysk-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:

~/.config/stella/settings.json
{
  "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/v4

The 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:

settings.json
{
  "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 familyweb_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 hooks load only when the project is trusted. (The legacy STELLA_PROJECT_HOOKS=1 flag 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, and mcp.registry_url are 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.

Stella is totally free and open source, dual-licensed MIT or Apache 2.0— your choice: MIT's three-paragraph simplicity, or Apache 2.0's explicit patent grant that enterprises prefer. Use it, fork it, embed it, ship it — no account, no phone-home, no strings. What that means →

On this page