Skip to main content

Configuration Model

Concepts

AIControls configuration has two tiers with exactly one source of truth each: Helm configures the deployment; the app configures itself. This page explains the split, how a fresh install is seeded, and what happens on upgrade.

Self-hosted Kubernetes only

This page describes values.yaml/config.yaml behavior for Self-Hosted Kubernetes deployments. If you're on the managed SaaS offering, every setting on this page is UI/API-managed already — there is no config.yaml to reason about.

Two tiers, not two sources of precedence

Every setting lives in exactly one place. There is no "which one wins" logic to reason about — only which tier a given setting belongs to:

TierLives inExamples
Deployment settingsvalues.yaml / config.yaml only, never the databaseListen/admin ports, storage backend and connection string, TLS/CA file paths, replica count, audit sink wiring
App settingsThe app database only, managed via Settings in the Admin UI (or the admin API)Rate limits, budgets, LLM upstreams, OIDC providers, passthrough mode, allowed domains/emails, PAT policy, policy.failOpen, anomaly and session thresholds, content-safety patterns

The rule of thumb: if changing a setting requires re-binding a socket, re-mounting a file, or re-pointing at a different database, it's a deployment setting. Everything else is an app setting.

Before this split, every app setting saved once through the UI would shadow values.yaml on every subsequent restart or upgrade — with no visible indicator that it was happening. That precedence problem is gone: app settings are no longer read from config.yaml at all after the first boot (see below), so there's nothing left to shadow.

Seeding on first boot

values.yaml/config.yaml still accepts every app-tier field — it just plays a narrower role than it used to. The first time AIControls starts against a fresh database, it copies any app-tier values it finds in config.yaml into the database, one time, for whichever settings the database doesn't already have a value for.

After that first-boot seed, config.yaml is not consulted again for that setting. Managing it going forward means using Settings in the Admin UI (or the equivalent admin API endpoint) — editing values.yaml and running helm upgrade has no further effect on it.

This preserves day-0 automation for GitOps/Helm-driven installs — you can still set an initial rate limit or policy.failOpen: false in values.yaml before the first helm install — without bringing back the old shadowing problem for every install afterward.

What happens on upgrade

Upgrading an existing self-hosted install
  • No behavior change for settings you've already configured. If a setting has a value in the database — whether you set it in the UI or it was seeded on an earlier first boot — that value keeps winning. An in-place upgrade never silently reverts a setting you've configured.
  • values.yaml only fills gaps. On each boot, AIControls seeds a setting from config.yaml only if the database has no value for it yet. Settings that already have a database value are left untouched.
  • Chart-rendered app settings are deprecated. A future release removes app-tier fields from the Helm chart's config.yaml template entirely, replaced by a first-boot-only appSettings.bootstrap block for zero-touch installs. Start managing rate limits, budgets, OIDC, and the other app settings listed above from Settings now, if you aren't already.

If values.yaml sets an app-tier field to a value that differs from what's already in the database, AIControls keeps the database value (per "no behavior change" above) and logs a warning identifying the mismatch — see below for where the corresponding in-app notification appears.

Configuration drift notifications

Every app setting change, and every case where config.yaml disagrees with a value already in the database, raises a notification so admins don't have to go looking for it:

EventFires whenWhere you'll see it
Settings changedAny app setting is saved through the UI or admin APIIn-app (the bell icon)
Policy fail-open changedpolicy.failOpen specifically changes valueIn-app, email, and Slack — deliberately more prominent, since this setting silently determines whether a policy-evaluation error blocks or allows the request
Config value ignoredOn boot, config.yaml sets one or more app-tier settings that already have a different value in the databaseIn-app, listing every setting key affected so you can reconcile values.yaml with what's actually in effect

Configure who receives these and through which channels from Settings → Notifications → Routing, the same as any other notification type.

See also