Files

124 lines
5.4 KiB
YAML

# 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