config.yaml¶
config.yaml defines models, providers, projects, entrypoints and skills. Nearly every value is a
Jinja template reading an environment variable, so most deployments only edit .env and never
touch the YAML.
The authoritative copy of the defaults ships in the package at src/core/defaults/config.yaml.
Resolution¶
BaseConfig (src/core/config.py) loads three layers:
- Bundled defaults —
core/defaults/config.yaml, always present. <cwd>/config/config.yaml— if present, replaces the bundled defaults entirely.<cwd>/config/config.custom.yaml— if present, deep-merged on top.
The recommended pattern is to leave the bundled defaults alone and write only the keys you change
into config/config.custom.yaml, so upgrades keep bringing you new defaults.
projects is an entity section: when your overlay defines it, projects that exist only in the
bundled defaults (i.e. default) are pruned rather than merged, so placeholders stay out of your
project list. All other sections merge normally.
The same layering applies to prompts.yaml.
Value syntax¶
models:
large: "@jinja {{env.LLM_MODEL_LARGE or 'openai:gpt-4.1'}}"
providers:
github:
token: "@jinja {{ env.get('GITHUB_TOKEN', '') }}"
projects:
default:
pullrequests:
github:
token: "@jinja {{ this.providers.github.token }}" # cross-reference
@jinjavalues are rendered by Dynaconf at load time; bothenv.NAMEandenv.get('NAME', '')forms are used.this.<dotted.path>references another value in the same file — this is how a provider is defined once and reused per project.- Rendering produces strings, so booleans are read through
BaseConfig.get_bool()(true,1,yes,on). Write plaintrue/falsein your own YAML. - Dynaconf environments are disabled — there is no
[development]/[production]layering, andDYNACONF_ENVhas no effect. Use separate.envfiles or overlays instead.
Sections¶
core¶
| Key | Environment variable | Default |
|---|---|---|
core.log.dir |
CORE_LOG_DIR |
/data/logs |
core.storage.file.dir |
CORE_STORAGE_FILE_DIR |
/data/storage |
core.version_check_url |
VERSION_CHECK_URL |
(empty — disabled) |
models¶
Two named slots that agents reference via this.models.*. Values are
Pydantic AI model strings.
| Key | Environment variable | Default | Used for |
|---|---|---|---|
models.large |
LLM_MODEL_LARGE |
openai:gpt-4.1 |
Main chat agent |
models.small |
LLM_MODEL_SMALL |
openai:gpt-4.1-mini |
Code research subagent |
openai: targets the Responses API; use openai-chat: for Chat Completions semantics or a
gateway that only speaks that API. Temperature and token limits are per-agent, see
agents.
providers¶
Credentials defined once and referenced from projects. Every provider has a mock flag that
returns canned data instead of calling the API.
| Provider | Keys |
|---|---|
devops |
url, organization, project, pat, repoId, mock, allowInsecureCloneUrl |
gitlab |
api_url, project_id, token, mock, allowInsecureCloneUrl |
github |
api_url, owner, repo, token, mock, allowInsecureCloneUrl |
bitbucket |
api_url, workspace, repo_slug, username, token, mock, allowInsecureCloneUrl |
jira |
domain, email, token, mock, imageModel |
The matching environment variables are listed in
environment variables. bitbucket.api_url defaults to
https://api.bitbucket.org/2.0; the other URLs default to empty.
When a pull request provider clones its repository, the clone URL is validated before the
token is used: it must be https:// and its host (including any port) must match the
provider's configured host (devops.url, gitlab.api_url, or the web host derived from
github.api_url / bitbucket.api_url). URLs that embed credentials are always rejected.
allowInsecureCloneUrl: true is a development-only override that additionally permits
http://; the host check still applies.
projects¶
A project ties one git checkout to the providers that serve its pull requests, issues and
pipelines. The bundled defaults ship a single project named default.
projects.<name>.git
| Key | Environment variable | Default | Description |
|---|---|---|---|
path |
GIT_REPO_PATH |
/code |
Local checkout to analyze |
defaultBranch |
— | main |
Comparison base branch |
autoPull |
GIT_AUTOPULL |
false |
Pull periodically |
pullIntervalSeconds |
GIT_PULL_INTERVAL_SECONDS |
120 |
Interval when autoPull is on |
Provider slots — each maps a provider name to that provider's settings:
| Slot | Providers |
|---|---|
pullrequests |
devops, gitlab, github, bitbucket |
issues |
devops, gitlab, github, jira, linear |
pipelines |
gitlab |
Delete the slots you do not use — an unconfigured provider is simply not offered to the agent.
From code, access goes through ProjectConfig (src/core/project_config.py):
from core.config import get_default_config
project = get_default_config().get_default_project_config()
project.get_git_config()
project.get_provider_config("pullrequests", "github")
slack¶
| Key | Environment variable | Default |
|---|---|---|
slack.bot.botToken |
SLACK_BOT_TOKEN |
(empty) |
slack.bot.appToken |
SLACK_APP_TOKEN |
(empty) |
slack.bot.processingTimeout |
SLACK_PROCESSING_TIMEOUT |
6000 |
slack.bot.alwaysRespond |
ALWAYS_RESPOND |
false |
slack.attachments.enabled |
SLACK_ATTACHMENTS_ENABLED |
false |
slack.attachments.maxFileSizeMb |
SLACK_ATTACHMENT_MAX_SIZE_MB |
25 |
slack.attachments.maxInlineTextKb |
SLACK_ATTACHMENT_MAX_INLINE_TEXT_KB |
50 |
slack.assistant.welcomeMessage |
— | greeting for new Assistant threads |
slack.assistant.includeFeedbackButtons |
SLACK_INCLUDE_FEEDBACK_BUTTONS |
false |
slack.assistant.suggestedPrompts |
— | four {title, message} examples |
Attachment forwarding is opt-in on purpose: it sends private Slack files into the LLM context.
Overriding suggestedPrompts replaces the whole list.
http, agui, openai, nats, cli¶
These mirror their environment variables one-to-one — see
environment variables. http.server.* configures the
shared server used by the AG-UI and OpenAI-compatible entrypoints; agui.server.enabled and
openai.server.enabled decide whether it starts at all.
Both entrypoints run the agent, so authentication is fail-closed. An enabled entrypoint needs
agui.server.apiKeys / openai.server.apiKeys (AGUI_API_KEYS / OPENAI_API_KEYS, a
comma-separated string or a YAML list of Bearer tokens). If the value is empty, missing or has any
other type, startup aborts with a configuration error instead of serving the agent openly. To run
without authentication on purpose, set agui.server.allowUnauthenticated /
openai.server.allowUnauthenticated (AGUI_ALLOW_UNAUTHENTICATED / OPENAI_ALLOW_UNAUTHENTICATED)
to true. This is unsafe outside a trusted network: anyone who reaches the port gets repository
access, the registered tools and your LLM spend.
skills¶
Skills are Python modules that register extra agent tools through the hook
system. load_skills() imports them at startup.
skills:
enable:
- skills.scheduler
- mycompany_skills.deploy
search_paths:
- /opt/dev-agents/custom-skills
| Key | Default | Description |
|---|---|---|
skills.enable |
[] |
Module paths to import, in order |
skills.search_paths |
[] |
Extra import roots |
agents¶
| Key | Default | Description |
|---|---|---|
agents.gitchatbot.model |
this.models.large |
Model for the main chat agent |
agents.gitchatbot.maxTokens |
2000 |
Response token limit |
agents.gitchatbot.temperature |
0.7 |
Sampling temperature |
agents.gitchatbot.timeoutSeconds |
60 |
Per-run timeout |
subagents¶
| Key | Environment variable | Default | Description |
|---|---|---|---|
subagents.coderesearch.model |
— | this.models.small |
Code research subagent model |
subagents.claude_code.cli_path |
CLAUDE_CODE_PATH |
(empty) | Claude Code CLI binary |
subagents.claude_code.model |
CLAUDE_CODE_MODEL |
claude-sonnet-5 |
Model for that subagent |
When the claude extra is installed and subagents.claude_code.cli_path is set, the chat agent
uses the Claude Code research tool; otherwise it falls back to the built-in code research
subagent.
A minimal overlay¶
# config/config.custom.yaml
models:
large: "anthropic:claude-sonnet-5"
small: "anthropic:claude-haiku-4-5"
projects:
backend:
git:
path: "/code/backend"
defaultBranch: "develop"
autoPull: true
pullrequests:
github:
api_url: "@jinja {{ this.providers.github.api_url }}"
owner: "@jinja {{ this.providers.github.owner }}"
repo: "@jinja {{ this.providers.github.repo }}"
token: "@jinja {{ this.providers.github.token }}"
mock: false
agents:
gitchatbot:
timeoutSeconds: 120
Because projects is an entity section, this leaves exactly one project — backend.
Check what loaded¶
python -c "
from core.config import get_default_config
c = get_default_config()
print('models.large:', c.get_value('models.large'))
print('projects:', c.get_available_projects())
print('git path:', c.get_default_project_config().get_git_config().get('path'))
"
An empty value means the environment variable behind it is unset.