Configuration Model
How deployment inputs are organized in the bsqai-platform CLI.
This page explains how deployment configuration is structured in the bsqai-platform CLI.
Configuration is split into two layers: a global platform.toml file that defines platform-wide settings, and per-component variables.toml files that define each component's identity, resources, secrets, and SSO enrollment. Environment variables can override any value at any layer.
Configuration layers
1. Global platform defaults
platform.toml is located at src/manifests/source/platform.toml. It defines the shared foundation used across all platform applications.
| Section | Purpose |
|---|---|
[common] | Base domain, registry, storage classes, image pull secret, realm name, affinity enforcement |
[git] | Manifests repository URL, branch, and path for ArgoCD reconciliation |
[custom_tls] | Optional TLS material (CA cert, certificate, key) when DNS-based issuance is not used |
[proxy] | HTTP/HTTPS proxy and no-proxy settings |
[azure] | Azure-specific: subscription, DNS, tenant, client credentials |
[gcp] | GCP-specific: service account namespaces |
[apps] | Sync wave ordering for parent ArgoCD Applications (common=1, coreai=2, proai=3) |
[default] | Default sizing values (CPU, memory, replicas) applied when a component does not specify its own |
2. Environment files
Environment variables are loaded in three layers, from lowest to highest priority:
platform.env— shared settings committed alongside the deployment (lowest priority)local.env— personal overrides, gitignored (overridesplatform.env)- OS environment — always wins (highest priority, used mainly in CI/CD)
Both .env files live at the repository root. They are loaded automatically when the CLI starts.
3. Per-component configuration
Each component has a variables.toml file inside its source directory under src/manifests/source/<group>/<component>/. The file is organized into TOML tables:
[component]
name = "keycloak"
namespace = "keycloak"
version = "26.5.2"
format = "helm"
chart_name = "keycloak"
chart_version = "18.10.0"
release_name = "keycloak"
url_prefix = "iam"
parent_app = "common"
sync_wave = "7"
sync_options = "- SkipDryRunOnMissingResource=true"
sync_annotations = ""
[database]
postgresql_name = "keycloak"
postgresql_owner = "keycloak"
postgresql_password = {value = "", secret = true}The [component] table is required and defines identity, chart coordinates, namespace, and ArgoCD sync behavior. Additional tables ([database], [sso], [auth], [server], etc.) are component-specific and become template variables prefixed with the table name.
4. Variable resolution
When the CLI loads a component, it resolves each variable through this chain:
- Environment variable override — if an env var matching
COMPONENTNAME_TABLENAME_KEY(uppercased, hyphens replaced with underscores) exists, it wins - Explicit TOML value — a non-empty value in
variables.tomlis used as-is - Generator — if the variable has a
generatekey (e.g.,generate = "s3_access_key_id"), a value is generated automatically when the TOML value is empty - Global default fallback — for flagged values (
secret = true,token = true,cert = true), the CLI falls back toGLOBAL_SECRET_DEFAULT,GLOBAL_TOKEN_DEFAULT, orGLOBAL_CERT_DEFAULTenvironment variables - Empty string — if none of the above produce a value
Type coercion is automatic. The CLI matches the type of the TOML reference value: booleans ("true"/"false"), integers, and floats are coerced from string env vars. Complex types (dicts and lists) must be provided as JSON-encoded strings when overriding from an environment variable or .env file.
Secrets management
Secrets in variables.toml use a metadata wrapper instead of a plain value:
postgresql_password = {value = "", secret = true}The secret = true flag tells the CLI that the value is sensitive. When the rendered template produces a Kubernetes Secret manifest, the CLI automatically seals it with kubeseal before writing the output. The sealed secret can be safely stored in Git.
Three flag types exist:
| Flag | Fallback env var | Typical use |
|---|---|---|
secret = true | GLOBAL_SECRET_DEFAULT | Passwords, client secrets |
token = true | GLOBAL_TOKEN_DEFAULT | API tokens, registry tokens |
cert = true | GLOBAL_CERT_DEFAULT | Certificate material |
Auto-generated values are also supported. S3 access keys use built-in generators:
access_key_id = {value = "", generate = "s3_access_key_id"}
secret_access_key = {value = "", generate = "s3_secret_access_key"}When the value is empty and no environment override exists, the generator produces a cryptographically random credential.
SSO scope
All platform components with a user-facing endpoint are enrolled into Keycloak for single sign-on. This is controlled per component through an [sso] table in variables.toml.
How it works
When a component has [sso] enabled = true, the CLI generates:
- an SSO configuration secret containing the OIDC client definition, groups, and roles
- a setup job that registers the component with Keycloak before deployment (uses a negative sync wave to run before the component)
- a PostDelete cleanup job that removes the Keycloak client when the component is deleted
When enabled is false or the [sso] table is absent, none of these resources are created.
SSO configuration structure
The [sso] table defines the full OIDC client registration:
[sso]
enabled = true
client_id = "grafana"
client_secret = {value = "", secret = true}
description = "Grafana OAuth client"
default_scope = ["openid", "profile", "email"]
standard_flow_enabled = true
direct_access_grants_enabled = true
service_accounts_enabled = false
redirect_uris = ["*"]
groups = [{name = "Grafana-Viewer"}]
roles = []Client secrets left empty are auto-generated. Keycloak-side client secrets are stored centrally in the Keycloak component's [clients] table and distributed via sealed secrets.
Components with SSO support
| Layer | Components |
|---|---|
| Runtime | Grafana, PgAdmin, Rook Ceph RGW, ArgoCD |
| CoreAI | Portal, LLM Backend (CoreAI API), MLflow, Temporal, Model Installer, LiteLLM |
| Data | Superset, Strimzi (Kafka), Airbyte (via OAuth2 Proxy), Attu (via OAuth2 Proxy) |
Keycloak enroll credentials
The setup and cleanup jobs authenticate against Keycloak using a dedicated enroll user. The password for this user is controlled by [auth] enroll_password in the Keycloak component's variables.toml. When left empty, a random password is generated.
For a deeper explanation of how the enrollment mechanism works, see the Keycloak component page.
Environment-specific settings
Some settings only apply in specific deployment environments.
Azure
[azure]
enabled = true
subscription_id = ""
dns_resource_group = ""
hosted_zone_name = ""
tenant_id = ""
client_id = ""
client_secret_b64 = ""
dns_client_json = ""These control external-dns zone ownership and cert-manager DNS-01 challenge solving in Azure.
GCP
[gcp]
enabled = false
gcloud_svc_acct_namespaces = ""Proxy
[proxy]
no_proxy_url = ""
http_url = ""
https_url = ""Set these when the cluster requires an outbound proxy for internet access.
Custom TLS
[custom_tls]
enabled = false
ca_crt = ""
crt = ""
key = ""Used when certificates are provided directly instead of issued through DNS automation.
Safe review checklist
Before applying a deployment, validate:
- Does the target environment have the required storage classes (
rwo_storageclass,rwx_storageclass)? - Does
base_domainmatch a DNS zone the platform can manage? - Is the container registry reachable from the cluster?
- Is
[git]pointing to the correct manifests repository and branch? - Is the certificate model clear: custom TLS material or DNS-driven issuance?
- Are only the needed platform tiers enabled?
Minimal input shape
[common]
base_domain = "<platform-domain>"
rwo_storageclass = "<rwo-storage-class>"
rwx_storageclass = "<rwx-storage-class>"
registry_name = "<registry-host>"
registry_platform_repository = "coreai/platform"
registry_backend_repository = "coreai/backend"
registry_portal_repository = "coreai/portal"
registry_helm_repository = "coreai/helm"
realm_name = "dataplatform"
[git]
repo_url = "<manifests-repository-url>"
branch = "<branch>"
path = "platform"
[custom_tls]
enabled = falseUse that structure as the baseline, then add only the environment-specific and component-specific inputs required by the deployment target.