image:
  repository: forgejo.coulomb.social/coulomb/state-hub
  tag: ""
  pullPolicy: IfNotPresent

imagePullSecrets: []

replicaCount: 1

namespace:
  create: true
  labels:
    railiance.io/postgres-client: state-hub-db
    railiance.io/layer: s5-app

service:
  type: ClusterIP
  port: 8000
  targetPort: 8000

config:
  enabled: true
  name: state-hub-config
  corsOrigins: "http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001"
  sbomNexusUrl: ""
  sbomNexusReadMode: legacy
  sbomNexusWriteMode: legacy
  # CUST-WP-0067-T03. "unknown" is the honest default for a chart that could be
  # installed anywhere; the production values set this to primary.
  instanceRole: unknown
  instanceLabel: ""

# STATE-WP-0084-T02. The OpenBao Kubernetes auth role binds to this name and
# not to `default`, so this is load-bearing, not cosmetic.
serviceAccount:
  create: true
  name: state-hub

secret:
  name: state-hub-env

# Forge read credential for deriving private repositories (STATE-WP-0084,
# MASON-WP-0003). Disabled by default: a hub without it still derives every
# public repository, so this is added capability, not a prerequisite.
#
# There is no agent injector and no secrets-store CSI driver on this cluster, so
# the pod authenticates to OpenBao itself with its projected ServiceAccount
# token and reads the KV path. Nothing here is the credential; these are only
# coordinates. Rotating the token in OpenBao needs no chart change and no
# redeploy.
forgeRead:
  enabled: false
  openbao:
    # Matches every existing ClusterSecretStore on this cluster.
    addr: http://openbao.openbao.svc:8200
    authMount: kubernetes
    role: state-hub-forge-derivation
    # KV v2: the read path carries the `data/` infix, mount `platform`.
    secretPath: platform/data/workloads/state-hub/forge-derivation
    secretKey: FORGE_READ_TOKEN
    # Audience for the projected ServiceAccount token.
    #
    # Empty renders no audience, giving the token the API server's audience —
    # what the four existing external-secrets roles use, and what an OpenBao
    # Kubernetes auth role with no bound audience will accept. A token with an
    # audience the role does not bind is rejected at TokenReview, so this must
    # not be set to "openbao" until the role binds that audience.
    #
    # Worth revisiting: with no audience, a copy of this token is a credential
    # for the cluster API. Binding `openbao` on the role and setting it here
    # narrows it to OpenBao alone. Either way the token is short-lived and
    # kubelet-rotated, which the legacy auto-mounted token is not.
    audience: ""
    expirationSeconds: 3600

resources:
  requests:
    cpu: 250m
    memory: 512Mi
  limits:
    cpu: 1000m
    memory: 2Gi

ingress:
  enabled: false
  className: traefik
  host: state-hub.coulomb.social
  tls: true
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod

# Database migrations (STATE-WP-0083-T07). Runs as a Helm pre-install/pre-upgrade
# hook rather than an init container: an init container runs per pod, so more
# than one replica means concurrent `alembic upgrade` with no locking. A hook
# runs once per release and fails the upgrade if the migration fails.
migrations:
  enabled: true
  backoffLimit: 1
  resources:
    requests:
      cpu: 50m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 1Gi

# Classification allowed-values (CUST-WP-0067-T09). The API validates repo
# classification against the-custodian canon; a container has no such checkout,
# so the file travels with the release as a ConfigMap. Without it every
# classification write fails with a 500.
classificationAllowed:
  enabled: true
  name: state-hub-classification-allowed
  mountPath: /etc/state-hub/classification

# MCP layer (CUST-WP-0067-T08). Runs from the same image with a different
# command, as a stateless HTTP client over the API service. ClusterIP only —
# it proxies an unauthenticated API, so it must never gain an Ingress.
mcp:
  enabled: false
  replicaCount: 1
  transport: sse
  # Defaults to the in-cluster API Service; override only to point elsewhere.
  apiBase: ""
  service:
    port: 8001
    targetPort: 8001
  probes:
    enabled: true
    liveness:
      initialDelaySeconds: 30
      periodSeconds: 30
      timeoutSeconds: 5
      failureThreshold: 3
    readiness:
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
  resources:
    requests:
      cpu: 50m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 512Mi

probes:
  enabled: true
  path: /state/health
  port: 8000
  liveness:
    initialDelaySeconds: 30
    periodSeconds: 30
    timeoutSeconds: 5
    failureThreshold: 3
  readiness:
    initialDelaySeconds: 10
    periodSeconds: 10
    timeoutSeconds: 5
    failureThreshold: 3

podSecurityContext: {}
securityContext: {}

nodeSelector: {}
tolerations: []
affinity: {}

# Consistency sweep: mount railiance01 clone tree and match host_paths hostname.
sweep:
  enabled: false
  hostname: ""
  hostPath: /home/tegwick
  sshHostPath: /home/tegwick/.ssh
