# Default values for the Sure Helm chart.
# These defaults target a small multi-node self-hosted cluster (for example, k3s with 3 nodes)
# with in-cluster Postgres and Redis managed by operators. For true single-node setups or
# external DB/Redis, see the README "Installation profiles" section and provide an override
# values file.

nameOverride: ""
fullnameOverride: ""

image:
  repository: ghcr.io/we-promise/sure
  # Defaults to Chart.appVersion when empty
  tag: ""
  pullPolicy: IfNotPresent
  # Optional: imagePullSecrets to pull from private registries
  imagePullSecrets: []

# Global app configuration
rails:
  env: production
  # Extra environment variables (non-sensitive). Key/values become env vars.
  extraEnv: {}
  # Extra environment variables with full EnvVar objects (supports valueFrom/secretKeyRef)
  extraEnvVars: []
  # Extra envFrom sources applied to all workloads
  extraEnvFrom: []
  # Control whether encryption env vars are injected into workloads
  encryptionEnv:
    enabled: true
  # Use an existing Secret for sensitive values (recommended). If set, the chart will not create a Secret.
  existingSecret: ""
  # If not using existingSecret, define sensitive values here (for testing only; do not commit secrets!).
  # Prefer managing secrets via external tools like Sealed Secrets or External Secrets.
  secret:
    enabled: false
    values:
      SECRET_KEY_BASE: ""
      # Active Record encryption keys
      ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: ""
      ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: ""
      ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: ""
      # Third party optional keys
      OPENAI_ACCESS_TOKEN: ""
      OIDC_CLIENT_ID: ""
      OIDC_CLIENT_SECRET: ""
      OIDC_ISSUER: ""
      LANGFUSE_PUBLIC_KEY: ""
      LANGFUSE_SECRET_KEY: ""
      LANGFUSE_HOST: "https://cloud.langfuse.com"

  # Non-secret env defaults mirrored from .env.local.example
  settings:
    SELF_HOSTED: "true"
    ONBOARDING_STATE: "open"
    AI_DEBUG_MODE: "false"

  # External AI Assistant (optional)
  # Delegates chat to a remote AI agent that calls back via MCP.
  externalAssistant:
    enabled: false
    url: ""           # e.g., https://your-agent-host/v1/chat
    token: ""         # Bearer token for the external AI gateway
    agentId: "main"      # Agent routing identifier
    sessionKey: "agent:main:main"  # Session key for persistent agent sessions
    allowedEmails: ""     # Comma-separated emails allowed to use external assistant (empty = all)
    # For production, use a Secret reference instead of plaintext:
    # tokenSecretRef:
    #   name: external-assistant-secret
    #   key: token

# Database: CloudNativePG (operator chart dependency) and a Cluster CR (optional)
cloudnative-pg:
  config:
    clusterWide: false

cnpg:
  enabled: true  # enable installing the CloudNativePG operator via subchart
  cluster:
    enabled: true  # create a CNPG Cluster custom resource for an in-cluster Postgres
    name: "sure-db"
    instances: 1   # set to 3+ for HA
    storage:
      size: 10Gi
      storageClassName: ""
    # Optional CNPG backup configuration (rendered as `.spec.backup` on the Cluster CR)
    # Example (CNPG volume snapshots):
    # backup:
    #   method: volumeSnapshot
    #   volumeSnapshot:
    #     className: longhorn
    #
    # Example (CNPG barmanObjectStore backups):
    # backup:
    #   method: barmanObjectStore
    #   barmanObjectStore:
    #     destinationPath: s3://my-bucket/cnpg
    #     endpointURL: https://s3.us-east-1.amazonaws.com
    #     s3Credentials:
    #       accessKeyId:
    #         name: my-s3-creds
    #         key: ACCESS_KEY_ID
    #       secretAccessKey:
    #         name: my-s3-creds
    #         key: SECRET_ACCESS_KEY
    #
    # NOTE:
    # - The CNPG Cluster `spec.backup` schema does not support `enabled` or `ttl` keys.
    #   If you add them, this chart will ignore them to avoid CRD warnings.
    # - This chart only hard-validates required fields for supported methods; unknown `method` values
    #   are passed through for CNPG to validate.
    backup: {}

    # NOTE: These are example values for resource limits and requests, adapt them to your cluster and workload needs. CNPG can run on very small resource requests, but you may want to increase limits for production workloads.
    # resources:
    #   requests:
    #     cpu: 100m
    #     memory: 256Mi
    #   limits:
    #     cpu: 200m
    #     memory: 512Mi
    #     
    # Optional CNPG plugin configuration (rendered as `.spec.plugins` on the Cluster CR)
    # Example (barman-cloud plugin as WAL archiver):
    # plugins:
    #   - name: barman-cloud.cloudnative-pg.io
    #     isWALArchiver: true
    #     parameters:
    #       barmanObjectName: minio-backups
    #
    # Example (complete setup: WAL archiving via barman-cloud plugin + snapshot backups):
    # plugins:
    #   - name: barman-cloud.cloudnative-pg.io
    #     isWALArchiver: true
    #     parameters:
    #       barmanObjectName: minio-backups  # references an ObjectStore CR
    # backup:
    #   method: volumeSnapshot
    #   volumeSnapshot:
    #     className: longhorn
    plugins: []
    # auth config for application user
    appUser: sure
    appDatabase: sure
    # Secret name for DB credentials (auto-created if empty and secret.enabled=true)
    existingSecret: ""
    secret:
      enabled: true
      name: ""
      usernameKey: username
      passwordKey: password
    # Optional HA knobs
    minSyncReplicas: 0  # set >0 for synchronous replication
    maxSyncReplicas: 0
    # Optional scheduling for cluster Pods (examples for multi-node k3s; leave empty for single-node)
    nodeSelector: {}
    affinity: {}
    tolerations: []
    topologySpreadConstraints: []
    # Optional additional cluster configuration values
    parameters: {}

# Redis Operator (OT-CONTAINER-KIT) optional dependency and managed Redis CR
redisOperator:
  enabled: true             # install the operator subchart (standalone-ready defaults)
  # Pass-through to the operator subchart (controller) resources if supported by the chart
  # Many users run small k3s nodes; keep defaults empty and document examples in README
  operator:
    resources: {}
  name: ""                 # defaults to <fullname>-redis
  # Mode controls how this chart templates Redis CRs for the OT redis-operator.
  # - replication: only a RedisReplication CR (recommended/production)
  # - sentinel: RedisReplication remains, and an optional RedisSentinel CR can be enabled via sentinel.enabled (advanced)
  # - standalone: reserved for future use
  mode: replication
  sentinel:
    # When true AND mode=sentinel, chart will also render a RedisSentinel CR (advanced).
    # For most self-hosted production clusters, RedisReplication alone is sufficient; enable Sentinel
    # when you specifically want Redis Sentinel-based failover on top of replication.
    enabled: false
    masterGroupName: "mymaster"
    # Dedicated image for RedisSentinel pods. By default this is the OT-CONTAINER-KIT image
    # that understands SERVER_MODE/SETUP_MODE=SENTINEL and runs redis-sentinel on port 26379.
    image:
      repository: quay.io/opstree/redis-sentinel
      tag: v8.4.0
  replicas: 3
  # Image used by the RedisReplication CR (required by operator CRD)
  image:
    # Use OT-CONTAINER-KIT tuned image by default for best compatibility with the operator.
    repository: quay.io/opstree/redis
    tag: v8.4.0
  # Probes for RedisSentinel (sentinel TCP port)
  probes:
    sentinel:
      port: 26379
      initialDelaySeconds: 30
      periodSeconds: 10
    replication:
      port: 6379
      initialDelaySeconds: 30
      periodSeconds: 10
  auth:
    existingSecret: ""     # default to rails.existingSecret when empty
    passwordKey: "redis-password"
  persistence:
    enabled: false
    className: ""          # e.g. longhorn
    size: 8Gi
  # Optional scheduling for RedisReplication Pods (top-level fallback if managed.* not set). Prefer setting under managed.*
  nodeSelector: {}
  tolerations: []
  affinity: {}
  topologySpreadConstraints: []
  workloadResources: {}      # resources for Redis pods created by the CR (data-plane, not operator controller)
  # Sure-managed Redis via Operator CR (works with or without installing the subchart if operator is cluster-wide)
  managed:
    enabled: true           # default to in-cluster HA Redis via operator
    # Optional scheduling knobs for managed RedisReplication (preferred location)
    nodeSelector: {}
    tolerations: []
    affinity: {}
    topologySpreadConstraints: []
    workloadResources: {}
    persistence:
      enabled: false
      className: ""
      size: 8Gi

# Redis when using external service (redis-ha.enabled=false) — used only for secret key mapping
redis:
  # Name of the key inside rails.existingSecret (or rails.secret.values) that contains the Redis password
  passwordKey: "redis-password"

# Optional simple Redis (non-HA) that this chart can deploy as a fallback
redisSimple:
  enabled: false
  image:
    repository: docker.io/redis
    tag: "7.2"
    pullPolicy: IfNotPresent
  service:
    port: 6379
  auth:
    enabled: true
    # Use an existing Secret for the Redis password
    existingSecret: ""
    # Key within the Secret that contains the password (used by helpers/REDIS_PASSWORD)
    passwordKey: "redis-password"
  persistence:
    enabled: false
    storageClass: ""
    size: 1Gi
  resources:
    requests:
      memory: "256Mi"
      cpu: "100m"
    limits:
      memory: "512Mi"

# URLs constructed automatically when using in-cluster DB and Redis.
# You can override DATABASE_URL and REDIS_URL explicitly via rails.extraEnv if using external services.

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  className: ""
  annotations: {}
  hosts:
    - host: sure.local
      paths:
        - path: /
          pathType: Prefix
  tls: []

# ServiceMonitor for Prometheus Operator (optional). Set scrape path and port.
serviceMonitor:
  enabled: false
  interval: 30s
  scrapeTimeout: 10s
  path: /metrics
  portName: http
  additionalLabels: {}

# Web (Rails server) Deployment configuration
web:
  enabled: true
  replicas: 1
  revisionHistoryLimit: 3
  # Optional command/args override
  command: []
  args: []
  # Kubernetes rolling update strategy for the web Deployment.
  # Controls how pods are replaced during updates.
  # Default: maxUnavailable=1, maxSurge=0
  # This prevents deployment deadlocks when using topology spread constraints with DoNotSchedule.
  # If you are not using strict topology constraints, you can increase maxSurge for faster rollouts.
  # Example for faster rollouts (when not using DoNotSchedule):
  #   strategy:
  #     rollingUpdate:
  #       maxUnavailable: 0
  #       maxSurge: 1
  strategy:
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 0
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits: {}
  podAnnotations: {}
  nodeSelector: {}
  tolerations: []
  affinity: {}
  topologySpreadConstraints: []
  extraEnv: {}
  extraEnvFrom: []
  extraVolumeMounts: []
  extraVolumes: []
  # Probes
  livenessProbe:
    httpGet:
      path: /up
      port: http
    initialDelaySeconds: 20
    periodSeconds: 10
    timeoutSeconds: 2
    failureThreshold: 6
  readinessProbe:
    httpGet:
      path: /up
      port: http
    initialDelaySeconds: 10
    periodSeconds: 5
    timeoutSeconds: 2
    failureThreshold: 6
  startupProbe:
    httpGet:
      path: /up
      port: http
    failureThreshold: 30
    periodSeconds: 5

# Worker (Sidekiq) Deployment configuration
worker:
  enabled: true
  replicas: 1
  queues: "default"
  # Optional command/args override for Sidekiq
  command: []
  args: []
  # Kubernetes rolling update strategy for the worker Deployment.
  # Controls how pods are replaced during updates.
  # Default: maxUnavailable=1, maxSurge=0
  # This prevents deployment deadlocks when using topology spread constraints with DoNotSchedule.
  # If you are not using strict topology constraints, you can increase maxSurge for faster rollouts.
  # Example for faster rollouts (when not using DoNotSchedule):
  #   strategy:
  #     rollingUpdate:
  #       maxUnavailable: 0
  #       maxSurge: 1
  strategy:
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 0
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits: {}
  podAnnotations: {}
  nodeSelector: {}
  tolerations: []
  affinity: {}
  topologySpreadConstraints: []
  extraEnv: {}
  extraEnvFrom: []
  extraVolumeMounts: []
  extraVolumes: []

# Migrations: how to run database migrations
migrations:
  # strategy: job (default) runs a Helm hook pre-install/pre-upgrade Job
  # strategy: initContainer runs migrations on web pod start via initContainer instead
  strategy: job
  job:
    backoffLimit: 3
    ttlSecondsAfterFinished: 600
    nodeSelector: {}
    tolerations: []
    affinity: {}
    resources: {}
    # Optional overrides for the migrate job
    command: ["bash", "-lc"]
    args: |
      DB_HOST=$(echo "$DATABASE_URL" | sed 's/.*@//; s/:.*//')
      until pg_isready -h "$DB_HOST" -p 5432; do echo "Waiting for DB..."; sleep 5; done
      echo "Preparing database (db:prepare)" && \
      DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:prepare
  initContainer:
    # Optional additional safety net: when enabled, adds a db-migrate initContainer
    # to the web Deployment that only runs migrations if there are pending ones.
    # This can be used together with strategy: job for extra protection on pod restarts.
    enabled: false
    command: ["bash", "-lc"]
    args: |
      DB_HOST=$(echo "$DATABASE_URL" | sed 's/.*@//; s/:.*//')
      until pg_isready -h "$DB_HOST" -p 5432; do echo "Waiting for DB..."; sleep 5; done

      if bundle exec rake db:pending_migrations | grep -q "pending"; then
        echo "Running db:migrate" && \
        DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:migrate
      else
        echo "No pending migrations"
      fi
    resources: {}

# SimpleFin encryption backfill job (post-install/upgrade)
simplefin:
  encryption:
    enabled: false
    # If enabled, Active Record Encryption keys must be provided via rails.existingSecret or rails.secret.values
    backfill:
      enabled: true
      dryRun: true  # default to dry-run for safety; set false to perform writes
      ttlSecondsAfterFinished: 600
      nodeSelector: {}
      tolerations: []
      affinity: {}
      # Optional overrides for the backfill job
      command: ["bash", "-lc"]
      args: |
        # Inline DB_PASSWORD into DATABASE_URL for this job only (DATABASE_URL comes from the chart)
        if [ -n "$DB_PASSWORD" ]; then
          export DATABASE_URL="${DATABASE_URL//\$(DB_PASSWORD)/$DB_PASSWORD}"
        fi

        DB_HOST=$(echo "$DATABASE_URL" | sed 's/.*@//; s/:.*//')
        until pg_isready -h "$DB_HOST" -p 5432; do echo "Waiting for DB..."; sleep 5; done

        echo "Running SimpleFin encrypt_access_urls backfill (dry_run=$DRY_RUN)" && \
        bundle exec rake "sure:simplefin:encrypt_access_urls[dry_run=$DRY_RUN]"
      resources: {}

# Optional CronJobs
cronjobs:
  enabled: false
  items: []
  # - name: nightly-backfill
  #   schedule: "15 2 * * *"
  #   command: ["bash", "-lc", "bundle exec rake some:task"]
  #   concurrencyPolicy: Forbid
  #   successfulJobsHistoryLimit: 1
  #   failedJobsHistoryLimit: 3
  #   resources: {}

# Security context defaults
podSecurityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000
  fsGroup: 1000
  fsGroupChangePolicy: OnRootMismatch

securityContext:
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: false
  capabilities:
    drop:
      - ALL

# Optional writable /tmp for Rails/Sidekiq when enforcing read-only root FS
writableTmp:
  enabled: false

# HorizontalPodAutoscaler templates (disabled by default)
hpa:
  web:
    enabled: false
    minReplicas: 2
    maxReplicas: 5
    targetCPUUtilizationPercentage: 70
  worker:
    enabled: false
    minReplicas: 2
    maxReplicas: 10
    targetCPUUtilizationPercentage: 70

# Pipelock: AI agent security proxy (optional)
# Provides forward proxy (outbound HTTPS scanning) and MCP reverse proxy
# (inbound MCP traffic scanning for prompt injection, DLP, tool poisoning).
# More info: https://github.com/luckyPipewrench/pipelock
pipelock:
  enabled: false
  image:
    repository: ghcr.io/luckypipewrench/pipelock
    tag: "2.2.0"
    pullPolicy: IfNotPresent
    imagePullSecrets: []
  replicas: 1
  # Pipelock run mode: strict, balanced, audit
  mode: balanced
  # Trusted domains: allow internal services whose public DNS resolves to private IPs.
  # Without this, SSRF scanning blocks legitimate internal-to-internal traffic.
  # Example: ["api.internal.example.com", "*.corp.example.com"]
  trustedDomains: []
  forwardProxy:
    enabled: true
    port: 8888
    maxTunnelSeconds: 300
    idleTimeoutSeconds: 60
  mcpProxy:
    port: 8889
    # Auto-computed when empty: http://<fullname>:<service.port>/mcp
    upstream: ""
  # WebSocket proxy: bidirectional frame scanning for ws/wss connections.
  # Runs on the same listener as the forward proxy at /ws?url=<ws-url>.
  websocketProxy:
    enabled: false
    maxMessageBytes: 1048576       # 1MB per message
    maxConcurrentConnections: 128
    scanTextFrames: true           # DLP + injection scanning on text frames
    allowBinaryFrames: false       # block binary frames by default
    forwardCookies: false
    maxConnectionSeconds: 3600     # 1 hour max connection lifetime
    idleTimeoutSeconds: 300        # 5 min idle timeout
    originPolicy: rewrite          # rewrite, forward, or strip
  # MCP tool policy: pre-execution rules for tool calls (shell obfuscation, etc.)
  mcpToolPolicy:
    enabled: true
    action: warn
    # Redirect profiles: route matched tool calls to audited handler programs instead
    # of blocking. The handler returns a synthetic MCP response. Fail-closed on error.
    # Example:
    #   redirectProfiles:
    #     safe-fetch:
    #       exec: ["/pipelock", "internal-redirect", "fetch-proxy"]
    #       reason: "Route fetch calls through audited proxy"
    redirectProfiles: {}
  # MCP session binding: pins tool inventory on first tools/list, detects injection
  mcpSessionBinding:
    enabled: true
    unknownToolAction: warn
  # Tool call chain detection: detects multi-step attack patterns (recon, exfil, etc.)
  toolChainDetection:
    enabled: true
    action: warn
    windowSize: 20
    maxGap: 3
  service:
    type: ClusterIP
  resources:
    requests:
      cpu: 50m
      memory: 64Mi
    limits:
      memory: 128Mi
  podAnnotations: {}
  nodeSelector: {}
  tolerations: []
  affinity: {}
  topologySpreadConstraints: []

  # Prometheus Operator ServiceMonitor for /metrics on the proxy port
  serviceMonitor:
    enabled: false
    interval: 30s
    scrapeTimeout: 10s
    path: /metrics
    portName: proxy      # matches Service port name "proxy" (8888)
    additionalLabels: {}

  # Ingress for MCP reverse proxy (port 8889) — external AI assistants need this in k8s
  ingress:
    enabled: false
    className: ""
    annotations: {}
    hosts:
      - host: pipelock.local
        paths:
          - path: /
            pathType: Prefix
    tls: []

  # PodDisruptionBudget — protects pipelock during node drains.
  # WARNING: minAvailable with replicas=1 blocks eviction entirely.
  # Use maxUnavailable: 1 for single-replica deployments, or increase replicas.
  pdb:
    enabled: false
    minAvailable: ""       # set to 1 when replicas > 1
    maxUnavailable: 1      # safe default: allows 1 pod to be evicted

  # Structured logging for k8s log aggregation
  logging:
    format: json
    output: stdout
    includeAllowed: false
    includeBlocked: true

  # Escape hatch: ADDITIONAL config sections appended to pipelock.yaml.
  # Use for sections not covered by structured values above (session_profiling,
  # data_budget, adaptive_enforcement, kill_switch, sandbox, reverse_proxy, etc.)
  # Do NOT duplicate keys already rendered above - behavior is parser-dependent.
  extraConfig: {}

  # Hard-fail helm template when externalAssistant is enabled without pipelock.
  # NOTE: This only guards the rails.externalAssistant path. Direct MCP access
  # (/mcp endpoint with MCP_API_TOKEN) is not detectable from Helm values.
  # For full coverage, also ensure pipelock is enabled whenever MCP_API_TOKEN is set.
  requireForExternalAssistant: false
