config.yaml at the repository root is Kumoss’s single configuration
file for the core service. This page documents every supported section
and field. The authoritative schema is the SystemConfig Pydantic
model in core/src/shared/config/system_config.py; when this page and
the code disagree, the code wins and this page has a bug.
Which fields you must touch depends on the deployment model: see Quickstart and Deploy to production.
Related guides: Environment
variables and secrets, LiteLLM
providers and models, OIDC
setup, Monitoring with
Phoenix, Phoenix prompt
templates, Guides, and
Administer users and locks — and the
sidecar READMEs under services/.
How the file is loaded
-
The core Dockerfile copies the repository’s
config.yamlto/etc/kumoss/config.yamlat image build time. The file is baked into the image. Editing it on the host does nothing until you rundocker compose build coreand restart the container.docker compose watchsyncs onlycore/, not the configuration. -
The
KUMOSS_CONFIGenvironment variable overrides the path. The path is interpreted inside the container, and the file must actually be present there (for example through a bind mount or a mounted secret). If the path is missing, or is a directory (which is what Docker creates when you bind-mount a non-existent host file), the core silently falls back to the built-in defaults. -
Every field has a default in code, so the core can boot with no configuration file at all. The built-in default disables every optional sidecar (
notifications,mapping,authz). The IaC sidecar has noenabledflag and is always called: its own code defaults areendpoint: "http://iac:8082"andtoken_env: "KUMOSS_IAC_TOKEN", independent of whether aconfig.yamlis loaded at all. -
Validation runs once at startup. A validation error aborts the boot with a message containing the
ConfigErrortext quoted in the tables below. Section errors are aggregated: a file with a badoidc,llm, andstoragesection reports all three at once, and insidellmevery missing credential variable is listed together. One check is the exception — the bearer-token check onservices.*runs on the assembled model, only after every section has validated, so it can surface on the next boot attempt after a section error is fixed. -
Secrets never belong in
config.yaml. Fields whose name ends inenvhold the _name of an environment variable; the value is read from the environment at boot or at use time.
Changing any field therefore means: edit config.yaml, rebuild the
core image, restart. "Requires rebuild" in the tables below always
refers to that sequence. If you instead mount the file at the path in
KUMOSS_CONFIG, the equivalent is: update the mounted file and restart
the core — no image rebuild needed, because the container reads the
file from disk at startup rather than from the baked-in copy.
Requirement summary
| Category | Fields |
|---|---|
Mandatory (the core refuses to boot otherwise) |
The environment variable named by |
Conditionally mandatory |
|
Optional with defaults |
Everything else. |
environment
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
one of |
|
Optional |
A label for the deployment tier. Any other value fails validation. |
Operational effect: it selects the Phoenix tracing project prefix
(dev-, pre-, pro-; see Monitoring
with Phoenix) and it is the tag used when fetching prompts from
Phoenix at runtime (see
Customize prompts). The
seeder tags every prompt it creates with all three values, so switching
between them finds the seeded prompts without re-tagging. It
does not enable any security control: setting production does not
turn on authentication, authorization, or stricter defaults. A prompt
created or re-versioned by hand in Phoenix is found only if its version
carries the tag for the current value.
Requires rebuild: yes.
oidc
Authentication settings for the single-page application and the API. Full per-provider instructions are in Enable authentication.
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
string |
|
Optional |
OIDC issuer URL. Blank disables authentication entirely: every
request, from anyone who can reach the API, resolves to a fixed local
development identity ( |
|
string |
|
Required when |
Public client id of the SPA. Validation error otherwise: |
|
string |
|
Optional |
Expected |
|
string |
|
Optional |
Scopes the SPA requests. A |
|
integer |
|
Optional |
Leeway applied to |
Related environment variables: none. The SPA is a public PKCE client
and the core needs only the issuer’s public keys. Requires rebuild:
yes. The public route GET /api/v1/auth/config returns the effective
values.
Related: admin.default_root_email in config.yaml bootstraps the
first administrator from this same login flow — see
Bootstrap
admin (one-way; blank to skip).
admin
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
string |
|
Optional |
Bootstrap administrator. A user whose access token carries an |
Requires rebuild: yes. This is the core’s mechanism and is unrelated to
the authorization sidecar’s KUMOSS_AUTHZ_ROOT_ADMIN_EMAIL.
services
Wiring for the four sidecars. Each sidecar implements an OpenAPI
contract in contracts/openapi/, and any implementation of the
contract can replace the bundled one by changing endpoint.
Common fields, available under services.notifications,
services.mapping, services.authz, and services.iac. The enabled
flag exists only for the three optional sidecars; services.iac has
none because the core cannot run without it.
| YAML path | Type | Code default | Shipped config.yaml |
Requirement | Meaning and effect |
|---|---|---|---|---|---|
|
boolean |
|
|
Optional |
Whether the core calls the sidecar. A disabled sidecar is never
contacted: mapping is done locally, notifications are dropped, and
authorization answers "authorized". Compose still starts the
container. An |
|
string (base URL) |
|
|
Conditional (needed when enabled; always for |
Base URL the core calls, resolvable from inside the core container.
An empty |
|
string (variable name) |
|
|
Conditional (always for |
Name of the environment variable holding the bearer token sent on
every call. A sidecar the core will call whose variable resolves to
an empty value aborts the boot with |
|
float (seconds) |
|
|
Optional |
Per-request HTTP budget. Every sidecar call returns promptly (long work runs as jobs the core polls), so this covers one round trip only. Honoured by the IaC and notifications clients; the mapping and authorization clients currently use fixed budgets of 10 and 15 seconds and ignore this field. |
Fields specific to the IaC sidecar:
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
float (seconds) |
|
Optional |
How often the core polls |
|
float (seconds) |
|
Optional |
Maximum total wait for one job. It must cover both the time the job
spends queued (jobs on the same workspace run one at a time, in order)
and the command itself. The bundled sidecar imposes no timeout of its
own on the engine process, so this value is the only bound. A
validation run submits several jobs in sequence (init, validate, plan,
and show when drift is requested), each with its own |
The IaC sidecar is mandatory. Every operating mode runs engine
commands through it, so its configuration has no enabled flag:
endpoint and token_env are always required and the service is
always contacted. Each has its own boot check with its own
ConfigError: the empty-endpoint text is quoted in the endpoint row
above, the empty-token text in the token_env row.
Related environment variables: the ones named by each token_env, in
core/.env, with the same value in the sidecar’s .env. See
Environment variables.
Requires rebuild: yes.
orchestration
Iteration limits and behaviour switches for the core’s agent loops. Raise the limits carefully: they bound the cost of a runaway session.
| YAML path | Type | Code default | Shipped config.yaml |
Requirement | Meaning and effect |
|---|---|---|---|---|---|
|
boolean |
|
|
Optional |
Runs the compliance auditor once a generate round has been validated
and its report written: a second, independent small-model agent reads
the session’s first request and the raw plan, and checks them against
the rules in the |
|
boolean |
|
|
Optional |
Locks the session when the generate report’s impact banner comes
back |
|
integer |
|
|
Optional |
Maximum detect-and-remediate iterations in a drift session. |
|
integer |
|
|
Optional |
Maximum generate-then-validate attempts per generation task before the
round fails with |
|
integer |
|
|
Optional |
Maximum generate-then-import attempts in an import round. A failed plan or a rejected import is fed back to the generator and counts as one attempt. When the attempts run out after a passing plan, the round still reports what did import and lists what did not. |
|
integer |
|
|
Optional |
Maximum model turns inside one agent loop. On the last turn only the
agent’s sentinel tool is offered, so the agent can still return what it
has; a loop that reaches the limit anyway aborts with
|
|
integer |
|
|
Optional |
Number of polls a server-sent-events subscription performs before it
closes. The loop sleeps 4 or 5 seconds depending on the branch taken,
so 2160 iterations run for roughly three hours rather than exactly
|
|
integer |
|
|
Optional |
How many drift operations are grouped into one remediation task. |
|
integer |
|
|
Optional |
How many one-second polls the GitHub provider performs waiting for a
pull request to become mergeable before failing with |
What the two gates lock, and what they do not. Both switches write
the same session flag, so their effect is identical: a locked session
answers 409 to POST /v1/iac/apply and to PUT
/v1/repository/pr/merge. Creating a pull request is not
lock-checked, and the audited code has already been pushed to the
working branch by then, so the gate guards the apply boundary rather
than the commit. The lock clears when a later generate round passes
with a non-high banner, or when a panel editor toggles it in the
admin panel; drift
rounds never set or clear it. Because both default to false in code
and true in the shipped file, an orchestration block that omits
them silently turns both gates off — spell them out in any file you
write yourself. The full round-by-round flow is in
Compliance gate.
Requires rebuild: yes. See Guides for where each limit applies.
llm
Model selection through LiteLLM. Details, provider tables, and router examples are in LiteLLM providers and models.
| YAML path | Type | Code default | Shipped config.yaml |
Requirement | Meaning and effect |
|---|---|---|---|---|---|
|
string (LiteLLM model string, or a |
|
|
Optional (credentials mandatory) |
High-quality model used by the IaC generator, target generator, and report generator chains. |
|
string |
|
|
Optional (credentials mandatory) |
Cheaper model used by every other chain and by tool-internal LLM calls. |
|
float |
|
|
Optional |
Applied to both roles (forced to |
|
integer |
|
|
Optional |
Completion cap sent on every call. |
|
float (seconds, must be |
|
|
Optional |
Per-request budget LiteLLM enforces on a single inference call, chat completions and the web-search Responses path alike. Each of the three internal retries gets a fresh budget. |
|
list of LiteLLM Router entries |
|
not set |
Optional |
Advanced routing: load balancing across entries that share a
|
Validation: at boot the core asks LiteLLM which environment variables
each configured model needs and fails with LLM credentials missing
from environment: … if any is unset. Some providers have no
validation mapping and fail on the first call instead; see the
LiteLLM
guide.
Related environment variables: the provider’s credential variables in
core/.env. Requires rebuild: yes for the YAML; a credential change
only needs a container restart.
paths
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
path |
|
Optional |
Directory where the core clones repositories, one session directory
each. Must be writable by the core and shared with the IaC sidecar at
the same path: in the compose stack both containers mount the
|
|
string matching |
|
Optional |
Name of the plan artifact written by |
|
string matching |
|
Optional |
Name of the state backend override the core writes into every
workspace before |
Requires rebuild: yes.
database
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
string (variable name) |
|
Optional (the variable it names is mandatory) |
Name of the environment variable that holds the actual PostgreSQL
connection URL. URLs embed credentials, which is why the URL itself is
not in this file. Boot fails with |
Schema note: there are no migrations; tables are created with
create_all. A database volume created by an older schema must be
migrated by hand or dropped.
Requires rebuild: yes for the field name; the URL value lives in
core/.env.
redis
Redis is a cache in front of the database. Timeouts are deliberately short so a slow Redis fails fast and reads fall through to PostgreSQL.
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
string (variable name) |
|
Optional |
Name of the variable holding the Redis URL (which may embed a password). |
|
string |
|
Optional |
Fallback used when the variable is unset or empty. The default points at the compose service, so the stack needs no Redis variable. |
|
integer |
|
Optional |
Connection pool size. |
|
float (seconds) |
|
Optional |
Connect timeout. |
|
float (seconds) |
|
Optional |
Read and write timeout. |
|
float (seconds) |
|
Optional |
How long a request may wait for a free pooled connection. |
Boot fails if Redis cannot be reached during initialisation. Requires rebuild: yes.
telemetry
OpenTelemetry export and, in the current implementation, also the address of the Phoenix prompt registry. See Monitor with Phoenix and Customize prompts.
| YAML path | Type | Code default | Shipped config.yaml |
Requirement | Meaning and effect |
|---|---|---|---|---|---|
|
string (base URL ending in |
|
|
Optional |
Base URL of the trace collector. The core appends |
|
integer |
|
|
Optional |
Maximum number of attributes per span (attribute count, not length). When exceeded the SDK drops the oldest attributes first, which on LLM spans are the session and user identifiers and the span kind. |
|
boolean |
|
|
Optional |
Also print every span to the core’s standard output. Spans contain prompts and plans; do not enable this where logs are shared. |
The code default only works when the core runs outside Docker on the same host as Phoenix. Requires rebuild: yes.
http
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
list of strings |
|
Optional |
Browser origins allowed by CORS. The compose stack serves the SPA from
nginx on port 80, so |
Requires rebuild: yes.
storage
Object storage for generated artifacts (reports, plans, code changes)
and, by default, for Terraform/OpenTofu state. The browser downloads
artifacts through presigned URLs. storage.terraform_state_bucket
ships set to kumoss-terraform-state, so by default Kumoss manages
state in a second bucket in the same store, sharing the provider and
the credentials with artifacts; set it to "" to opt out and let each
repository keep its own backend instead. See
Configure state
backends for the state side in full.
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
one of |
|
Optional |
Backend. |
|
string |
|
Optional |
Artifacts bucket name, or blob container name for |
|
string |
|
Optional |
Bucket (or blob container) holding Terraform/OpenTofu state, separate
from |
|
string matching |
|
Optional |
Object name of each project’s state inside
|
|
string |
|
Conditional |
Endpoint the core’s SDK calls from inside the compose network. For
|
|
string |
|
Conditional |
Host the browser reaches. Presigned S3 URLs bind the host header, so this must be the externally visible address of the store (in the compose stack, nginx forwards port 9000 to RustFS). |
|
string |
|
Optional |
Region for signing ( |
|
string (variable name) |
|
Optional |
Variable holding the access key. Falls back to |
|
string (variable name) |
|
Optional |
Variable holding the secret key, same fallback rules. |
|
string (variable name) |
|
Optional (the variable is mandatory for |
Variable holding the Azure shared key. Boot fails with
|
|
float (seconds) |
|
Optional |
Per-request connect budget. |
|
float (seconds) |
|
Optional |
Per-request read budget. |
|
integer |
|
Optional |
Retry count (botocore standard mode). |
|
integer in |
|
Optional |
Lifetime of presigned download URLs. Must stay between 108000 (30
hours) and 604800 (7 days). The floor keeps URLs valid for longer than
the 24-hour cached session detail that embeds them; the ceiling is the
SigV4 limit. Boot fails with |
While Kumoss-managed state is on, these fields have a second consumer:
endpoint_url, region, and the credential variables are rendered
into the backend block that the IaC sidecar executes. Two
consequences follow. The sidecar container must be able to reach
storage.endpoint_url (in the Compose stack both containers sit on
bridge-network, so http://object-storage:9000 resolves). And
unless both access_key_env and secret_key_env resolve to
non-empty values, the backend block omits the credential lines entirely
— a single one being set is not enough — and the engine falls back to
the sidecar’s ambient credentials; an instance profile on the core
alone is not enough.
Requires rebuild: yes. Related environment variables in core/.env:
RUSTFS_ACCESS_KEY, RUSTFS_SECRET_KEY, STORAGE_ACCOUNT_KEY.
git
Credentials and identity for the branches Kumoss pushes and the pull requests it opens. Single-tenant: one token serves every session.
| YAML path | Type | Default | Requirement | Meaning and effect |
|---|---|---|---|---|
|
one of |
|
Optional |
Git hosting provider. Selects the pull-request API implementation and
the host written into |
|
string (variable name) |
|
Optional |
Variable holding the account username. |
|
string (variable name) |
|
Optional |
Variable holding the personal access token. |
|
string |
|
Optional |
Commit author name. |
|
string |
|
Optional |
Commit author e-mail. |
At boot the core always sets the global git author identity. If both
credential variables are non-empty it also writes
https://<user>:<token>@<provider-host> to ~/.git-credentials and
enables the store credential helper. If either is empty it logs a
warning and continues; pushes then need a pre-populated
~/.git-credentials, and pull-request creation and merge — which call
the provider’s REST API with GIT_TOKEN — fail. Repository URLs must
be https://: SSH, git://, http://, file:// and local paths are
rejected by the API, so SSH keys are never an alternative. URLs that
embed a password or token are rejected too; a bare userinfo username
is accepted.
Requires rebuild: yes for the YAML; token values live in core/.env.
Example: minimal local development
Safe on an isolated workstation. Authentication is disabled, so everyone reaching port 80 is the local developer with full roles.
environment: "development"
oidc:
issuer_url: ""
client_id: ""
services:
iac:
endpoint: "http://iac:8082"
token_env: "KUMOSS_IAC_TOKEN"
orchestration:
enable_compliance_checker: true
block_on_high_impact: true
llm:
model: "anthropic/claude-sonnet-5"
small_model: "anthropic/claude-haiku-4-5"
telemetry:
collector_url: "http://phoenix:6006/"
storage:
provider: "RUSTFS"
endpoint_url: "http://object-storage:9000"
public_endpoint_url: "http://localhost:9000"
# Shipped default: Kumoss keeps Terraform state in the bundled
# RustFS, in its own bucket, so a scratch repository with no backend
# of its own works out of the box. Set it to "" to use the backend
# each repository declares instead.
terraform_state_bucket: "kumoss-terraform-state"
git:
provider: "GITHUB"
Everything omitted keeps its default. Credentials go in core/.env.
Example: production-shaped deployment (fictitious)
Illustrates the shape of a hardened configuration. Values are
fictitious; environment: production only changes tagging, so every
control below must be set explicitly.
environment: "production"
oidc:
issuer_url: "https://login.example.invalid/realms/platform"
client_id: "kumoss-web"
clock_skew_seconds: 60
admin:
default_root_email: "platform-admin@example.invalid"
services:
notifications:
enabled: true
endpoint: "https://notifications.kumoss.example.invalid"
token_env: "KUMOSS_NOTIFICATIONS_TOKEN"
mapping:
enabled: true
endpoint: "https://mapping.kumoss.example.invalid"
token_env: "KUMOSS_MAPPING_TOKEN"
authz:
enabled: true
endpoint: "https://authz.kumoss.example.invalid"
token_env: "KUMOSS_AUTHZ_TOKEN"
iac:
endpoint: "http://iac:8082"
token_env: "KUMOSS_IAC_TOKEN"
job_timeout: 3600.0
orchestration:
enable_compliance_checker: true
block_on_high_impact: true
# Credentials stay in core/.env under the provider's default variable
# names (here AZURE_API_KEY, AZURE_API_BASE, AZURE_API_VERSION); no
# model_list is needed for that. Use llm.model_list for load balancing
# across entries that share a model_name, custom credential env var
# names, or per-entry endpoints (see LLM providers and models) — there
# is no router-level fallback support.
llm:
model: "azure/main-deployment"
small_model: "azure/small-deployment"
temperature: 0.1
max_output_tokens: 32000
telemetry:
collector_url: "https://phoenix.kumoss.example.invalid/"
http:
cors_origins:
- "https://kumoss.example.invalid"
storage:
provider: "STORAGE_ACCOUNT"
bucket: "kumoss-artifacts"
# Shipped default: Kumoss owns state instead of the target
# repositories. Blob container separate from the artifacts container
# so state escapes any artifact lifecycle policy — enable versioning
# and soft delete on it. Set it explicitly to "" — a bare key with no
# value fails at boot — when the repositories already declare their
# own backends.
terraform_state_bucket: "kumoss-terraform-state"
endpoint_url: "https://demoplatformartifacts.blob.core.windows.net"
public_endpoint_url: "https://demoplatformartifacts.blob.core.windows.net"
presign_expiry_seconds: 172800
git:
provider: "GITLAB"
author_name: "Kumoss"
author_email: "kumoss@noreply.invalid"
Points that this example relies on but that the file cannot express:
the mapping and authz endpoints must be real implementations of their
contracts (the bundled ones are a passthrough and a permissive stub);
Phoenix must be reachable at collector_url for prompt seeding; the
Phoenix UI must be protected separately because it receives prompts and
plans; and every TOKEN, STORAGE_ACCOUNT_KEY,
KUMOSS_SQL_DATABASE_URL, GIT_TOKEN, and the AZURE_API
credentials must come from a secret store. See
Environment variables and
secrets.