prerelease Prerelease stable Latest
Kumoss
prerelease Prerelease stable Latest

Configuration

Every `config.yaml` field: type, default, validation rule, and effect.

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.

How the file is loaded

  • The core Dockerfile copies the repository’s config.yaml to /etc/kumoss/config.yaml at image build time, so every core image, including the published one, carries the repository’s default. The checked-in docker-compose.yml bind-mounts the repository’s config.yaml over that path, so with Compose an edit on the host takes effect after docker compose restart core.

  • The KUMOSS_CONFIG environment 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 no enabled flag and is always called: its own code defaults are endpoint: "http://iac:8082" and token_env: "KUMOSS_IAC_TOKEN", independent of whether a config.yaml is loaded at all.

  • Validation runs once at startup. A validation error aborts the boot with a message containing the ConfigError text quoted in the tables below. Section errors are aggregated: a file with a bad oidc, llm, and storage section reports all three at once, and inside llm every missing credential variable is listed together. Two checks are the exception — the bearer-token and enabled-endpoint checks on services.* run on the assembled model, only after every section has validated, so they can surface on the next boot attempt after a section error is fixed.

  • Secrets never belong in config.yaml. Fields whose name ends in env hold 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 and restart the core. "Requires restart" in the tables below always refers to that sequence. The file is read once at startup, so a running core never picks up an edit. If you run an image without a mounted file, the baked-in copy is the one read, and a change means rebuilding the core image from your edited checkout.

Requirement summary

Category Fields

Mandatory (the core refuses to boot otherwise)

The environment variable named by database.kumoss_database_url_env must be set; the variable named by services.iac.token_env must be set (the IaC sidecar is always called and has no enabled flag); the credentials that LiteLLM requires for llm.model and llm.small_model must be set for providers LiteLLM can validate.

Conditionally mandatory

oidc.client_id when oidc.issuer_url is set; the token_env variable of every other enabled sidecar; the variable named by storage.account_key_env when storage.provider is STORAGE_ACCOUNT; a derivable account name in storage.endpoint_url for STORAGE_ACCOUNT; a remote backend declared by every target repository while storage.terraform_state_bucket is blank — which only happens if a deployment explicitly sets it to "", since the shipped value enables Kumoss-managed state by default; web_search.endpoint when web_search.provider is searxng.

Optional with defaults

Everything else.

environment

YAML path Type Default Requirement Meaning and effect

environment

one of development, staging, production

development

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

oidc.issuer_url

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 (dev@kumoss.local) that holds the top role of both role groups (devops and panel admin). When set, the core validates bearer JWTs against the issuer’s JWKS, discovered lazily on the first request.

oidc.client_id

string

""

Required when issuer_url is set

Public client id of the SPA. Validation error otherwise: oidc.issuer_url is set but oidc.client_id is empty; set oidc.client_id (and optionally oidc.audience) in config.yaml.

oidc.audience

string

""

Optional

Expected aud claim. Blank accepts client_id and api://<client_id>. Needed for identity providers that issue tokens for a separately named API (Auth0, Okta).

oidc.scope

string

"openid profile email"

Optional

Scopes the SPA requests. A {client_id} placeholder is expanded at load time (and fails validation if client_id is blank). When left at the default and the issuer host is login.microsoftonline.com, api://{client_id}/.default is appended automatically.

oidc.clock_skew_seconds

integer

60

Optional

Leeway applied to exp, iat, and nbf validation.

Related environment variables: none. The SPA is a public PKCE client and the core needs only the issuer’s public keys. Requires restart: 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

admin.default_root_email

string

""

Optional

Bootstrap administrator. A user whose access token carries an email claim equal to this address (case-insensitive) and email_verified: true is elevated to devops plus panel admin on login, and re-checked on every request. Elevation is one-way: clearing the field never demotes anyone. Identity providers that never emit email_verified (Entra ID, Auth0, Okta) cannot use this; grant the first admin in the database instead, as described in Bootstrap admin. Ignored while authentication is disabled.

Requires restart: yes.

services

Wiring for the four sidecars. Each sidecar implements an OpenAPI contract in contracts/openapi/. Kumoss bundles services for IaC and notifications, and any implementation of their contract can replace the bundled one by changing endpoint; mapping and authorization are optional extension points you implement yourself and point endpoint at.

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

services.<name>.enabled (not for iac)

boolean

false

false for notifications, mapping, authz

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 bundled notifications container; mapping and authorization have no bundled container. An enabled key under services.iac is ignored.

services.<name>.endpoint

string (base URL)

"" for notifications, mapping, authz; http://iac:8082 for iac (its own code default, not just the shipped value)

http://notifications:8080, http://iac:8082; "" for mapping and authz (no bundled implementation)

Conditional (needed when enabled; always for iac)

Base URL the core calls, resolvable from inside the core container. An empty services.iac.endpoint aborts the boot with services.iac.endpoint is empty; the IaC service is mandatory. Point it at an implementation of the iac contract. An enabled optional sidecar with an empty endpoint also aborts the boot, with Enabled services have no endpoint: services.<name>.endpoint. Point each at an implementation of its contract, or flip it to enabled: false.

services.<name>.token_env

string (variable name)

"" for notifications, mapping, authz; KUMOSS_IAC_TOKEN for iac (its own code default)

KUMOSS_NOTIFICATIONS_TOKEN, KUMOSS_MAPPING_TOKEN, KUMOSS_AUTHZ_TOKEN, KUMOSS_IAC_TOKEN

Conditional (always for iac)

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 Services the core calls have no bearer token in the environment: services.<name> → $<VAR>. Set the listed env vars; the optional sidecars can also be flipped to enabled: false (services.iac cannot — it is mandatory).

services.<name>.timeout

float (seconds)

30.0

30.0

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

services.iac.job_poll_interval

float (seconds)

5.0

Optional

How often the core polls GET /v1/jobs/{job_id} for a submitted engine command.

services.iac.job_timeout

float (seconds)

3600.0

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 job_timeout.

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

orchestration.enable_compliance_checker

boolean

false

true

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 general-compliance-report prompt. The session is locked if any finding comes back with severity error or critical; warning findings are recorded in the report but do not lock. The report is stored as a round artifact and shown in the portal. A lock also sends an iac.compliance.failed notification. When false the audit is skipped entirely and reported as an empty passing result, so it can never lock and nothing is stored. Generate rounds only — drift rounds do not audit, and apply does not re-audit.

orchestration.block_on_high_impact

boolean

false

true

Optional

Locks the session when the generate report’s impact banner comes back high, and sends an iac.impact.high notification. The banner itself — low, medium, or high, judged against the general-compliance-impact prompt criteria — is part of every report either way; this flag decides only whether it gates. It is evaluated independently of the auditor above: the round ends locked if either condition fires.

orchestration.max_drift_reports

integer

3

3

Optional

Maximum detect-and-remediate iterations in a drift session.

orchestration.max_validation_iteration

integer

8

5

Optional

Maximum generate-then-validate attempts per generation task before the round fails with Validation loop exceeded.

orchestration.max_import_iteration

integer

5

5

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.

orchestration.max_tool_agent_executions

integer

40

40

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 ToolExecutionsExceeded and the round fails. Renamed from max_tool_chain_executions: the old key is ignored, so a file that still sets it silently gets the built-in 40. See Agent tool loop.

orchestration.max_session_events_iteration

integer

2160

2160

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 2160 × 5s.

orchestration.drift_group_operations

integer

8

8

Optional

How many drift operations are grouped into one remediation task.

orchestration.pull_request_readiness_seconds

integer

10

10

Optional

How many one-second polls the GitHub provider performs waiting for a pull request to become mergeable before failing with readiness polling exhausted.

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

llm.model

string (LiteLLM model string, or a model_name alias when model_list is set)

anthropic/claude-sonnet-5

main (an llm.model_list alias)

Optional (credentials mandatory)

High-quality model used by the IaC generator, target generator, and report generator chains.

llm.small_model

string

anthropic/claude-haiku-4-5

small (an llm.model_list alias)

Optional (credentials mandatory)

Cheaper model used by every other chain and by tool-internal LLM calls, including the web_search summarisation.

llm.temperature

float

0.1

0.1

Optional

Applied to both roles (forced to 1.0 when a chain requests extended thinking).

llm.max_output_tokens

integer

32000

32000

Optional

Completion cap sent on every call.

llm.timeout

float (seconds, must be > 0)

600.0

600

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.

llm.model_list

list of LiteLLM Router entries

[]

2 entries

Optional

Advanced routing: load balancing across entries that share a model_name, per-entry endpoints, and os.environ/VAR credential references. Router-level fallbacks between different models are not configurable today; the core passes only model_list to the Router. When non-empty, model and small_model must match a model_name and boot validation runs against the listed entries. As shipped, the aliases are main (a self-hosted OpenAI-compatible endpoint with a per-entry api_base and os.environ/-referenced key) and small (vertex_ai/claude-sonnet-5-5).

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 restart: yes, for the YAML and for a credential change.

The search backend behind the agents' web_search tool. Requests go through the LiteLLM unified search API (LiteLLM search), and the shipped config.yaml routes every search through the bundled SearXNG sidecar. Pointing it at another provider is a config change, but the backend has to exist: the stack only deploys SearXNG.

YAML path Type Code default Shipped config.yaml Requirement Meaning and effect

web_search.provider

string — a LiteLLM search provider name

searxng

searxng

Optional

Selects the search engine called by the web_search tool. The shipped stack deploys a SearXNG sidecar and leaves this at searxng. Any name in litellm.types.utils.SearchProviders is accepted — tavily, brave, perplexity, exa_ai, google_pse and others — but a hosted provider reads credentials from its own env vars, ignores endpoint, and needs a search service the stack does not ship: you have to provide that backend yourself. Empty disables web search altogether: there is no LLM-native fallback, so the tool answers "unavailable" instead of asking the model to search for itself. That is what lets self-hosted OpenAI-compatible endpoints (vLLM and the like) get live results.

web_search.endpoint

string (URL)

http://localhost:8888

http://searxng:8888

Conditional

Base URL of the search service, passed to LiteLLM as api_base. Required for searxng; hosted providers take their credentials from environment variables instead and usually need no endpoint.

web_search.max_results

integer (1–20)

5

8

Optional

Upper bound on results returned per query.

web_search.max_page_chars

integer (500–20000)

6000

10000

Optional

Truncates the readable text extracted from each result page. That product — max_results × max_page_chars — is the summarising inferences' input; the agent sees only the summaries.

web_search.timeout

float (seconds, must be > 0)

30.0

30.0

Optional

Per-request HTTP budget, covering the engine call and each page read.

web_search.summarize_top

integer (1–10)

1

commented out

Optional

How many of the ranked summaries reach the agent, most relevant first.

web_search.summarize_max_chars

integer (500–20000)

4000

commented out

Optional

Caps each summary, and each raw excerpt kept when a page’s summary inference fails. A summary stands in for its page, so too low a value loses detail the agent cannot get back.

Each search starts the same way: the engine returns ranked hits, and every hit’s page is rendered in headless Chromium through Playwright and reduced to markdown with trafilatura, so the agent works from the pages themselves — including JavaScript-rendered ones — rather than the engine’s snippets. The pages are read concurrently from one shared browser, and Chromium presents the fingerprint bot protection expects, so blogs and forums answer 200 where a bare HTTP client would get 403. A page that will not render falls back to its snippet, so a partial failure costs detail rather than the whole search.

Two small-model steps stand between the pages and the agent: one summary per page, all concurrent — an extract rather than a gist, since the summary replaces the page — then a single cross-page inference that ranks the pages answering the query. The top summarize_top summaries are what the agent reads, most relevant first, bounding a search’s context cost at summarize_top × summarize_max_chars instead of the max_results × max_page_chars product.

The reduction degrades in layers and never fails a search: a page whose summary inference fails keeps a truncated raw excerpt, a ranking that cannot be parsed falls back to every summary in engine order, and any other summariser error falls back to the raw, unfiltered results. Selecting nothing is a verdict rather than a failure — every page irrelevant, or a ranking that explicitly picks none — and the tool answers with the reason and a nudge to rephrase the query. What caps a stuck agent is the agent loop’s own orchestration.max_tool_agent_executions budget.

Validation: an unknown provider name aborts the boot; searxng without an endpoint does too; an endpoint without a provider is rejected as a half-configured state.

On a network that inspects HTTPS, page reads fail with CERTIFICATE_VERIFY_FAILED while the engine call itself keeps working, and every hit silently degrades to its snippet. See Behind a TLS-inspecting proxy.

Related environment variables: the chosen provider’s credential variables in core/.env (SearXNG needs none; see the Environment variables and secrets page). The bundled SearXNG sidecar reads its instance secret from services/searxng/.env (SEARXNG_SECRET) and takes no credential from the core. Requires rebuild: yes for the YAML.

paths

YAML path Type Default Requirement Meaning and effect

paths.upload_folder

path

/workspaces

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 workspaces volume at /workspaces, and both images run as the same unprivileged user (kumoss, uid and gid 10001).

paths.session_plan_filename

string matching ^[A-Za-z0-9._-]{1,128}$

session.plan

Optional

Name of the plan artifact written by plan into the session workspace, carried by the pinned workspace, and executed by apply. It must be a single safe filename: no path separators, no spaces, at most 128 characters. Keep the .plan suffix so the gitignore rules the core injects continue to cover it.

paths.backend_override_filename

string matching ^[A-Za-z0-9._-]{1,128}_override\.tf$

backend_override.tf

Optional

Name of the state backend override the core writes into every workspace before init when storage.terraform_state_bucket is set. It must be a single safe filename ending in _override.tf: Terraform only merges files with that suffix over the rest of the configuration, and the gitignore rules the core injects only cover that suffix. The file holds the state store credentials, so the agent’s file tools can neither see nor touch it.

Requires restart: yes.

database

YAML path Type Default Requirement Meaning and effect

database.kumoss_database_url_env

string (variable name)

KUMOSS_SQL_DATABASE_URL

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 Missing env variable for kumoss database when the variable is empty, and database initialisation fails when it is unreachable. A postgresql:// scheme is rewritten to postgresql+asyncpg://, and a sslmode= query parameter to asyncpg’s ssl=, so URLs copied from a managed PostgreSQL service work as-is.

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

redis.redis_url_env

string (variable name)

KUMOSS_REDIS_URL

Optional

Name of the variable holding the Redis URL (which may embed a password).

redis.default_url

string

redis://redis:6379/0

Optional

Fallback used when the variable is unset or empty. The default points at the compose service, so the stack needs no Redis variable.

redis.max_connections

integer

20

Optional

Connection pool size.

redis.socket_connect_timeout

float (seconds)

2.0

Optional

Connect timeout.

redis.socket_timeout

float (seconds)

2.0

Optional

Read and write timeout.

redis.pool_timeout

float (seconds)

2.0

Optional

How long a request may wait for a free pooled connection.

Boot fails if Redis cannot be reached during initialisation. Requires restart: 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

telemetry.collector_url

string (base URL ending in /)

http://phoenix:6006/

http://phoenix:6006/

Optional

Base URL of the trace collector. The core appends v1/traces by plain string concatenation, so the value must end with a slash: http://phoenix:6006/ becomes http://phoenix:6006/v1/traces. The same URL is used as the Phoenix client base URL for prompt seeding and fetching, so today it must point at a Phoenix server even if you export traces elsewhere. The default is the Phoenix service of the bundled stack; running the core outside docker needs an explicit http://localhost:6006/.

telemetry.otel_attribute_count_limit

integer

1024

1024

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.

telemetry.otel_console_exporter

boolean

false

false

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 restart: yes.

http

YAML path Type Default Requirement Meaning and effect

http.cors_origins

list of strings

["http://localhost"]

Optional

Browser origins allowed by CORS. The compose stack serves the SPA from nginx on port 80, so http://localhost is enough. Add http://localhost:5173 when running the Vite dev server against the API container. Set your real origin for a deployed instance.

Requires restart: 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

storage.provider

one of RUSTFS, S3, STORAGE_ACCOUNT (enum names) or rustfs, s3, storage_account (enum values)

RUSTFS

Optional

Backend. RUSTFS: the bundled RustFS or any S3-compatible server with a custom endpoint. S3: real AWS S3 on its regional endpoint (both URLs below are ignored; the region builds the endpoint). STORAGE_ACCOUNT: an Azure storage account using a shared key.

storage.bucket

string

kumoss-artifacts

Optional

Artifacts bucket name, or blob container name for STORAGE_ACCOUNT. Created at boot if missing.

storage.terraform_state_bucket

string

kumoss-terraform-state, both as shipped in config.yaml and as the field default if the key is absent

Optional

Bucket (or blob container) holding Terraform/OpenTofu state, separate from storage.bucket. Non-blank — the shipped value — means Kumoss manages state: the bucket is created at boot if missing and the core writes paths.backend_override_filename into every workspace before init, addressing it with the key <project_id>/<storage.terraform_state_filename>. Backend type follows storage.provider (s3 for RUSTFS/S3, azurerm for STORAGE_ACCOUNT). Set it explicitly to "" to opt out — no value, "", and whitespace-only are all blank, and blank means no bucket is created, no override is written, and each repository must declare its own remote backend, whose credentials belong to the IaC sidecar. A bare terraform_state_bucket: with nothing after it is not the same as "": it parses as YAML null and fails boot with a Pydantic validation error. See Configure state backends.

storage.terraform_state_filename

string matching ^[A-Za-z0-9._-]{1,128}$

terraform.tfstate

Optional

Object name of each project’s state inside storage.terraform_state_bucket, stored under the <project_id>/ prefix. It must be a single safe filename: no path separators, no spaces, at most 128 characters. Changing it orphans the existing state: every project then starts from an empty state under the new key.

storage.endpoint_url

string

http://object-storage:9000

Conditional

Endpoint the core’s SDK calls from inside the compose network. For STORAGE_ACCOUNT it must be the account blob endpoint (https://<account>.blob.core.windows.net, or the emulator form http://<host>:<port>/<account>); the account name is derived from it. Boot fails with storage.endpoint_url must be an account blob endpoint …​ when it cannot be derived.

storage.public_endpoint_url

string

http://localhost:9000

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).

storage.region

string

us-east-1

Optional

Region for signing (RUSTFS) or for building the endpoint (S3). Ignored for STORAGE_ACCOUNT.

storage.access_key_env

string (variable name)

RUSTFS_ACCESS_KEY

Optional

Variable holding the access key. Falls back to rustfsadmin for RUSTFS when unset; empty for S3, which then uses the AWS SDK default credential chain. A non-empty value always wins, so blank RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY in core/.env when switching to S3 (the sample ships them set to rustfsadmin).

storage.secret_key_env

string (variable name)

RUSTFS_SECRET_KEY

Optional

Variable holding the secret key, same fallback rules.

storage.account_key_env

string (variable name)

STORAGE_ACCOUNT_KEY

Optional (the variable is mandatory for STORAGE_ACCOUNT)

Variable holding the Azure shared key. Boot fails with storage.provider STORAGE_ACCOUNT requires env var STORAGE_ACCOUNT_KEY. when empty.

storage.connect_timeout

float (seconds)

3.0

Optional

Per-request connect budget.

storage.read_timeout

float (seconds)

10.0

Optional

Per-request read budget.

storage.max_attempts

integer

3

Optional

Retry count (botocore standard mode).

storage.presign_expiry_seconds

integer in [108000, 604800]

172800 (48 hours)

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 storage.presign_expiry_seconds must be between 108000 …​ and 604800 …​ otherwise.

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

git.provider

one of GITHUB, AZURE_DEVOPS, GITLAB (enum names) or github.com, dev.azure.com, gitlab.com (enum values)

GITHUB

Optional

Git hosting provider. Selects the pull-request API implementation and the host written into ~/.git-credentials. Any other value, including an empty string, fails validation. The pull-request adapters support only the public SaaS hosts: github.com, gitlab.com (top-level namespace and project, no subgroups), and dev.azure.com. GitHub Enterprise Server, self-managed GitLab, and *.visualstudio.com URLs are rejected as malformed at pull-request time.

git.pat_user_env

string (variable name)

GIT_USER

Optional

Variable holding the account username.

git.pat_token_env

string (variable name)

GIT_TOKEN

Optional

Variable holding the personal access token.

git.author_name

string

Kumoss

Optional

Commit author name.

git.author_email

string

kumoss@noreply.invalid

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 restart: 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"

web_search:
  provider: "searxng"
  endpoint: "http://searxng:8888"

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 your own implementations of their contracts (Kumoss bundles neither); 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.