# Default values for the bloom chart. Environment values files override these (values-dev.yaml
# for the k3s dev cluster, values-staging.yaml / values-prod.yaml for the M25-provisioned
# clusters). Comments here are the reference documentation for every key.

nameOverride: ""
fullnameOverride: ""

image:
  # The legacy bloom-ai image, still referenced by the (off-by-default) swarm simulator; its
  # tag is also the release-wide default that server.image.tag follows when empty.
  repository: ghcr.io/hidden-claw/bloom-ai
  # Deploys MUST pin the commit SHA (`--set image.tag=<sha>`): immutable tags are what make
  # `helm rollback` return to the previously-running code (M15 semantics). "latest" exists only
  # as a local fallback, mirroring docker-compose's default.
  tag: "latest"
  # IfNotPresent lets a node restart reuse the already-pulled image without a live GHCR
  # credential - the CI deploy refreshes `ghcr-pull` only at deploy time (deploy/k8s/README.md).
  pullPolicy: IfNotPresent

# Registry credential for the private GHCR image. `ghcr-pull` is created/refreshed in the bloom
# namespace by deploy/k8s/bootstrap-k3s.sh (M24-1) and by the CI deploy (M24-5).
imagePullSecrets:
  - name: ghcr-pull

# Pod/container security hardening shared by both workloads (server, swarm). The
# images run as dedicated non-root users declared by *name* (`USER bloom`), and Kubernetes
# refuses to start a runAsNonRoot container with a non-numeric image user - it cannot verify
# the uid isn't 0 at admission (the k3s CreateContainerConfigError, #425). So each workload
# pins its numeric uid/gid in its own podSecurityContext block below, merged over this baseline.
podSecurityContext:
  runAsNonRoot: true
  seccompProfile:
    type: RuntimeDefault
containerSecurityContext:
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL

server:
  # The TypeScript backend (apps/server, M29): the second service the strangler-fig migration
  # grows behind the gateway. OFF by default - the dev deploy runs `helm upgrade --wait`, and
  # until M29-3 routes a gateway path here there is nothing for it to serve; M29-3 flips it on
  # once the image (pushed by CI on main) and the route both exist.
  enabled: false
  replicaCount: 1
  # Stateless (no in-memory store like the retired Python api), so a surged replica is safe.
  strategy:
    type: RollingUpdate
  # Numeric uid/gid pins merged over the shared podSecurityContext: the image creates
  # `adduser -u 10001 bloom` but declares `USER bloom` by name, so the chart pins the number
  # for runAsNonRoot admission.
  podSecurityContext:
    runAsUser: 10001
    runAsGroup: 10001
    fsGroup: 10001
  image:
    repository: ghcr.io/hidden-claw/bloom-ai-server
    # Empty = follow image.tag, so one `--set image.tag=<sha>` pins every artifact of a release.
    tag: ""
  containerPort: 8090
  service:
    type: ClusterIP
    port: 8090
  # Non-secret BLOOM_* config only, rendered into the <release>-server-config ConfigMap.
  # Secret material arrives via `existingSecret` below.
  config: {}
  # The server reads the same secret keys the Python API used (BLOOM_SUPABASE_DB_URL et al.),
  # so it keeps the Secret name the M24-4 sync populates rather than duplicating the sync.
  existingSecret: bloom-api-secrets
  # GitHub App PEM (#747 follow-up): the engineer engine mints per-run installation tokens from
  # a private key file at BLOOM_GITHUB_APP_PRIVATE_KEY_PATH. When set, the named Secret is mounted
  # (subPath github-app.pem) at that path so /health's engine.runner probe + github_app check work.
  # EMPTY by default -> no volume renders, so staging/prod are untouched and the server degrades
  # gracefully (engine.runner null, no crash). Dev sets the existing bloom-github-app-key Secret.
  githubAppKeySecret: ""
  # Tailnet peers by MagicDNS name (M17-7, #747): pods route to tailscale IPs through the
  # node's tailscaled, but cluster DNS does not consult the tailscale resolver, so the
  # *.ts.net names the app needs (openclaw gateway / engine-runner) are pinned per environment.
  # Standard k8s hostAliases entries: [{ip: "100.x.y.z", hostnames: ["name.ts.net"]}, ...].
  hostAliases: []
  # Liveness = the process serves HTTP; readiness additionally proves DB connectivity
  # (apps/server/src/app.ts), so a dead DB pulls the pod from rotation without restarting it.
  livenessProbe:
    httpGet:
      path: /healthz
      port: http
    initialDelaySeconds: 3
    periodSeconds: 15
    timeoutSeconds: 5
    failureThreshold: 3
  readinessProbe:
    httpGet:
      path: /readyz
      port: http
    initialDelaySeconds: 3
    periodSeconds: 10
    timeoutSeconds: 5
    failureThreshold: 3
  resources:
    requests:
      cpu: 50m
      memory: 64Mi
    limits:
      memory: 256Mi
  # node runs as PID 1 (exec-form CMD) and drains in-flight requests on SIGTERM.
  terminationGracePeriodSeconds: 30

# HTTP exposure for the server. Off by default (the chart installs with no Ingress, like the
# pre-M24-3 chart); every environment overlay turns it on with its own class + hostname. The
# dashboard SPA is not served here - it ships to Cloudflare Pages, which reverse-proxies /api
# back to the server.
ingress:
  enabled: false
  # IngressClass per environment: "traefik" on k3s dev (kept enabled - decision in
  # deploy/k8s/README.md); staging/prod pick their cluster's controller in their overlay.
  className: traefik
  # Extra annotations applied to the server Ingress (controller-specific tuning).
  annotations: {}
  # The TS `-server` exposure (the strangler-fig cutover seam, docs/dev-cutover-runbook.md,
  # now the sole backend). INERT by default - server-ingress.yaml renders nothing until
  # server.enabled is true AND an overlay sets host + paths.
  server:
    host: ""
    paths: []
  tls:
    # In-cluster TLS on the Ingress. false in dev - the exe.dev edge terminates TLS in front
    # of Traefik:80 - true in staging/prod where cert-manager (below) fills the secrets.
    enabled: false

# ACME (Let's Encrypt) issuance: a namespaced Issuer for the ingress TLS secrets. Off in dev
# (exe.dev edge fronts TLS); on in staging/prod. Requires cert-manager in the cluster
# (installed by the M25 cluster provisioning) - rendering needs nothing.
certManager:
  enabled: false
  issuer:
    # Defaults to <release>-bloom-letsencrypt; override to reuse a pre-existing Issuer name.
    name: ""
    # ACME directory. The staging overlay points at the Let's Encrypt *staging* directory
    # (untrusted but rate-limit-free) so cert churn while M25 stands up DNS costs nothing.
    server: https://acme-v02.api.letsencrypt.org/directory
    # Expiry/problem notifications. Optional for ACME; set it before first real issuance (M25).
    email: ""

swarm:
  # Engineer-swarm SIMULATOR (test harness) - guarded and OFF by default, mirroring compose's
  # opt-in `--profile swarm`. Enable per environment/run only when exercising the coordination
  # loop against a sandbox repo.
  enabled: false
  replicaCount: 1
  # The legacy bloom-ai image creates its user with uid 10001, pinned here for runAsNonRoot
  # admission (same rationale as server.podSecurityContext).
  podSecurityContext:
    runAsUser: 10001
    runAsGroup: 10001
    fsGroup: 10001
  # Non-secret SWARM_* settings (SWARM_REPO, SWARM_ENGINEERS, SWARM_BLOCK_RATE, ...), rendered
  # into the <release>-swarm-config ConfigMap. Tokens/keys go in `existingSecret`.
  config: {}
  # Secret injected with envFrom (SWARM_GITHUB_TOKEN or the SWARM_GITHUB_APP_* credentials),
  # populated from the SWARM_* Actions secrets by the sealed-secrets sync (M24-4). Optional:
  # no such secret exists while the swarm stays off.
  existingSecret: bloom-swarm-secrets
  resources:
    requests:
      cpu: 50m
      memory: 128Mi
    limits:
      memory: 512Mi
  terminationGracePeriodSeconds: 30
