Skip to content

38 — KMS Config & Model References (air-gap findings)

This page records the model references and required config/secrets for the Knowledge Management Service, found while wiring it to the air-gapped Foundry. It is the KMS analogue of the assistant-service Model References page. For the deploy runbook see 37 — KMS Deployment; for Foundry itself see Foundry / LLM Endpoint.

Summary

KMS is single-layer for models (TOML only — no baked user catalog like assistant-service's models.yaml). But the upstream config/default.toml defaults to models the air-gap Foundry doesn't serve, and several required @format {env[...]} keys were missing from the Vault secret (KMS crashes at config load without them). Both are fixed in helm/environments/values-mod-auh1-dev-ask.yaml; the secret keys must be seeded in Vault.


Model references (single layer — TOML)

KMS references models only via config/default.toml, overridden per-env by the chart's envConf.configToml. There is no baked model catalog.

Field Section Upstream default (not served) Air-gap override
embedding_model_id [organization] BAAI/bge-m3 qwen/qwen3-embedding-0-6b (1024-dim)
vlm_model_name [organization] core42-oicm-auh1-shd/qwen-3-5-9b qwen/qwen3-5-122b-a10b-kms (KMS-dedicated, vision)
foundry_endpoint [document_understanding] (env-specific) http://foundry-litellm.foundry.svc.cluster.local:4000/v1
model_provider [document_understanding] foundry unchanged ✅
reranker [retriever] @none unchanged (disabled) ✅

embedding_model_id is load-bearing

BAAI/bge-m3 is not served by the air-gap Foundry. If left unoverridden, every document ingestion fails to vectorize → Weaviate stays empty → KMS is functionally dead. It must be a Foundry-served embedding id. qwen/qwen3-embedding-0-6b produces 1024-dim vectors (verified) and that is the dimensionality Weaviate stores.

VLM model decision

vlm_model_name drives visual document understanding (image/PDF extraction). The upstream 9B VLM isn't served. We point it at qwen/qwen3-5-122b-a10b-kms — the KMS-dedicated, vision-capable Foundry instance (keeps ingestion load off the interactive qwen3-5-122b-a10b). If visual extraction isn't needed yet, this path can be disabled instead.


Required @format {env[...]} keys — KMS crashes without them

default.toml uses Dynaconf @format {env[...]} for these. Any one missing → DynaconfFormatError at config load → CrashLoopBackOff before the app runs. This is independent of the model fixes.

Env var Source Status before fix Resolution
APP__ENV chart extraEnv ❌ not set → KMS ignored the mounted override and used default.toml set APP__ENV=mod-auh1-dev-ask in extraEnv
HATCHET__CLIENT_TOKEN Vault → secret ✅ seeded
DATABASE__PASSWORD extraEnv secretKeyRef (CNPG)
DOCUMENT_UNDERSTANDING__FOUNDRY_KEY Vault → secret ❌ missing seed = Foundry PROXY_MASTER_KEY
DOCUMENT_UNDERSTANDING__DI_SUBSCRIPTION_KEY Vault → secret ❌ missing Azure DI is unreachable air-gapped → seed a placeholder to satisfy @format; rely on the Foundry VLM path
VECTOR_DB__API_KEY Vault → secret ❌ missing generate; must equal Weaviate's AUTHENTICATION_APIKEY_ALLOWED_KEYS
CORE_SERVICE__JWT_SECRET Vault → secret ❌ missing must equal core-service's value (copy from core-service-secret)
APP__MASTER_ENCRYPTION_KEY Vault → secret ❌ missing generate once (openssl rand -hex 32); never rotate (encrypts stored data)

We initially seeded the wrong key names

The first KMS secret seed used SECRET_KEY and API_KEY — neither is referenced by default.toml. They were no-ops. The authoritative list is the @format table above.


Weaviate authentication — apikey is mandatory

The KMS Weaviate client always applies Auth.api_key(config.vector_db.api_key) (src/knowledge_management/config/vector_database.py), with no conditional. An anonymous-only Weaviate therefore returns 401. The subchart must enable apikey auth with a key matching VECTOR_DB__API_KEY.

Wired in values-mod-auh1-dev-ask.yaml (weaviate: block):

weaviate:
  authentication:
    anonymous_access:
      enabled: false
  authorization:
    rbac:
      enabled: false      # apikey-authenticated user gets full access (bring-up)
  env:
    AUTHENTICATION_APIKEY_ENABLED: "true"
    AUTHENTICATION_APIKEY_USERS: "kms-admin"
  envSecrets:
    # subchart mounts env AUTHENTICATION_APIKEY_ALLOWED_KEYS from this secret, same key name
    AUTHENTICATION_APIKEY_ALLOWED_KEYS: knowledge-management-service-secret

Single source of truth: seed both VECTOR_DB__API_KEY and AUTHENTICATION_APIKEY_ALLOWED_KEYS in the Vault path to the same value, so the KMS client and the Weaviate server agree.


Seed the Vault secret (complete)

export VAULT_ADDR=https://vault.cl1.sq4.aegis.internal
export VAULT_SKIP_VERIFY=true

# Foundry key — reuse the gateway master key (least-privilege: mint a KMS virtual key later)
FOUNDRY_KEY=$(kubectl get secret -n foundry foundry-secret -o jsonpath='{.data.PROXY_MASTER_KEY}' | base64 -d)

# Shared JWT secret — the source of truth is the ASSISTANT-SERVICE Vault path (NOT core-service-secret,
# which uses Zitadel/PAT auth and has no shared HS secret). assistant-service validates core-service
# tokens with this value, so it is the correct shared secret for KMS too.
JWT_SECRET=$(vault kv get -field=CORE_SERVICE__JWT_SECRET secret/ask/assistant-service/auh1-dev)

# One key value used by BOTH the KMS client and the Weaviate server
WEAVIATE_KEY=$(openssl rand -hex 32)

vault kv patch secret/ask/knowledge-management-service/auh1-dev \
  DOCUMENT_UNDERSTANDING__FOUNDRY_KEY="$FOUNDRY_KEY" \
  DOCUMENT_UNDERSTANDING__DI_SUBSCRIPTION_KEY="airgap-disabled-placeholder" \
  VECTOR_DB__API_KEY="$WEAVIATE_KEY" \
  AUTHENTICATION_APIKEY_ALLOWED_KEYS="$WEAVIATE_KEY" \
  CORE_SERVICE__JWT_SECRET="$JWT_SECRET" \
  APP__MASTER_ENCRYPTION_KEY="$(openssl rand -hex 32)"

# force ESO to resync the k8s secret
kubectl annotate externalsecret -n ask knowledge-management-service-external-secret \
  force-sync=$(date +%s) --overwrite

The JWT secret is NOT in core-service-secret

core-service-secret holds Zitadel/IAM creds (IAM_SERVICE__PAT, APP__CORE_SERVICES_MACHINE_TOKEN, SECURITY__*) — no shared HS JWT secret. The CORE_SERVICE__JWT_SECRET (HS, 64-char) lives in the assistant-service Vault path and is the value all token validators must share. KMS, the assistant-service, and core-service's signer must all use the same value or cross-service tokens fail validation.

APP__MASTER_ENCRYPTION_KEY is permanent

It encrypts data KMS stores. Generate it once and keep it stable — rotating it makes existing encrypted data unreadable. Treat it like a database master key.


Verify after sync

# every @format key present in the rendered secret
for k in DOCUMENT_UNDERSTANDING__FOUNDRY_KEY DOCUMENT_UNDERSTANDING__DI_SUBSCRIPTION_KEY \
         VECTOR_DB__API_KEY AUTHENTICATION_APIKEY_ALLOWED_KEYS CORE_SERVICE__JWT_SECRET \
         APP__MASTER_ENCRYPTION_KEY HATCHET__CLIENT_TOKEN; do
  v=$(kubectl get secret -n ask knowledge-management-service-secret -o jsonpath="{.data.$k}" 2>/dev/null)
  echo "$k: $([ -n "$v" ] && echo present || echo MISSING)"
done

# pods should leave CrashLoop once config loads
kubectl get pods -n ask | grep -iE "knowledge|weaviate"

# the model ids KMS will actually call
kubectl exec -n ask deploy/knowledge-management-service -- \
  cat /app/config/mod-auh1-dev-ask.toml | grep -E "embedding_model_id|vlm_model_name|foundry_endpoint"

Each model id printed must appear in Foundry /v1/models (see Foundry → Validate).