# Production — the `medical-chatbot-app` release serving realvuxbaro.me. # # This file is the Git source of truth for every stable, non-secret setting of # this cluster. It exists because the same values used to # live only inside the ArgoCD Application's inline `spec.source.helm.values`, # where they were invisible to review, diff and rollback: the cluster could # drift from the repository without a single commit recording it. That is how # practice ended up on DeepSeek with reranking off while production ran Qwen # with reranking on. # # Two things deliberately stay OUT of this file and remain inline on the # Application: # # - `aiService.image` / `web.image` — rewritten on every push by # `.github/scripts/sync_practice_argocd.py`, which regex-replaces the tag # in the inline values. A tag committed here would be stale by design. # - anything secret. Nothing in the practice release currently needs one; # Bedrock authenticates through the node's instance role. # # ArgoCD applies `valueFiles` in order and then the inline values last, so the # image block inline still wins over anything the chart defaults set here. # # The behavioural settings below mirror the production runtime contract audited # on 2026-08-17 (see coordination/ARGOCD_PRODUCTION_MIGRATION_STATE_2026-08-17.md). # Practice is only useful as a migration rehearsal while they stay in sync. global: environment: production # The GHCR packages are private; without this the Pods fail ImagePullBackOff. imagePullSecrets: - name: ghcr-pull-secret # PostgreSQL and Qdrant are owned by the separate `medical-chatbot-data` # release so that redeploying the app never touches persistent state. This # release therefore runs neither, and points at the data release's Services. postgres: enabled: false qdrant: enabled: false url: http://medical-chatbot-data-medical-chatbot-qdrant.medical-chatbot-data.svc.cluster.local:6333 secret: postgresHost: medical-chatbot-data-medical-chatbot-postgres.medical-chatbot-data.svc.cluster.local aiService: config: embeddingProvider: cohere-v4 embeddingDimensions: 1024 evidenceMinimumScore: 0.12 awsRegion: us-east-1 answerProvider: bedrock-converse answerModelId: qwen.qwen3-next-80b-a3b rerankEnabled: true # Second trace destination alongside Tempo, for the LLM-shaped view # (per-trace answers + eval scores). Self-hosted on this same cluster by # the separate `langfuse` ArgoCD Application. The keys it needs are # secrets and stay inline on the Application, like jwtSecret does — # setting this URL alone changes nothing until they exist. # # The in-cluster Service, NOT https://langfuse.realvuxbaro.me. That public # name resolves to the Elastic IP of the very node these Pods run on, so a # Pod reaching it has to hairpin out and back through the node's own # public address — which silently never completes here. Spans were created # and dropped with no error in ai-service's log and nothing arriving on # Langfuse's otel-ingestion-queue; an identical span sent from a laptop # over the public URL ingested fine, which is what isolated it to the # cluster-internal hop. Cross-namespace is fine: Services resolve # cluster-wide, unlike Secrets. langfuseBaseUrl: http://langfuse-web.medical-chatbot-data.svc.cluster.local:3000 # Auth is live in production (2026-08-19). These stayed false here for a day # while the live Application carried `enabled: true` inline, so Git and the # cluster disagreed and the cluster silently won — recreating the Application # from Git would have turned auth off without a word. They now match. # # secret.jwtSecret is deliberately still absent: it is a real credential and # lives inline on the Application, the same way secret.grafanaAdminPassword # does. The chart fails closed via `required` if it is ever missing while # either service below is on — which is exactly what took the whole render # down on the first attempt (2026-08-18). Set the secret first, always. authService: enabled: true apiGateway: enabled: true observability: grafana: # Dashboards stay open so a demo needs no credentials, but read-only: this # Grafana answers on the public production hostname, and it previously ran # anonymous *Admin* with the login form disabled — which handed the # datasource and dashboard APIs to anyone who found the URL, and left no # way to sign in as a real admin either. anonymous: enabled: true role: Viewer ingress: enabled: true className: traefik # Primary, not merely first in a list: `ingress.host` is what Grafana's # GF_SERVER_ROOT_URL is built from, so it must be the name users arrive on. # `realvuxbaro.me` moved here from the Compose host on 2026-08-17 — routed # first and certificated only once its A record pointed at this cluster, # because cert-manager solves HTTP-01 and the challenge could not reach here # before that. host: realvuxbaro.me # The rehearsal name is kept so existing links and bookmarks still resolve. extraHosts: - readytochat.realvuxbaro.me annotations: cert-manager.io/cluster-issuer: letsencrypt-prod traefik.ingress.kubernetes.io/router.entrypoints: websecure # Separate secrets per hostname: a renewal failure on one name must not take # the other offline. tls: - hosts: - readytochat.realvuxbaro.me secretName: readytochat-tls - hosts: - realvuxbaro.me secretName: realvuxbaro-tls