# Default values for the Flow platform
# This is a YAML-formatted file.

# -- Global configuration shared across all services
global:
  # -- Namespace to install all Flow components
  # This ensures all resources are created in a single namespace for easy management and cleanup
  # If not set, uses the namespace specified during helm install (--namespace flag)
  namespace: ""
  
  # -- Image registry for all Flow services
  imageRegistry: "cr.kn.entit.eu"
  # -- Image pull secrets (list of existing secret names)
  imagePullSecrets: []
  
  # -- Global image settings applied to all services unless overridden
  image:
    # -- Default image tag for all services (overrides Chart.appVersion)
    # Set to a specific version (e.g., "1.0.0") or "latest" for development
    # Individual services can override this with their own image.tag
    tag: ""
    # -- Default image pull policy for all services
    # Options: Always, IfNotPresent, Never
    # When tag is "latest" or empty, this defaults to "Always"
    # Individual services can override this with their own image.pullPolicy
    pullPolicy: "IfNotPresent"
  
  # -- Image registry credentials for private registries (e.g., Harbor)
  # When enabled, creates a dockerconfigjson secret for pulling images
  # This is required for Harbor even for public repositories
  imageCredentials:
    # -- Enable creation of image pull secret from credentials
    enabled: false
    # -- Registry server URL (defaults to imageRegistry if not set)
    registry: ""
    # -- Registry username (can use environment variable via --set global.imageCredentials.username=$HARBOR_USER)
    username: ""
    # -- Registry password (can use environment variable via --set global.imageCredentials.password=$HARBOR_PASSWORD)
    password: ""
    # -- Email for registry authentication (optional)
    email: ""
    # -- Name of the generated secret
    secretName: ""
    # -- Use existing secret instead of creating one (set to secret name to use existing)
    existingSecret: ""
  
  # -- Storage class for persistent volumes
  storageClass: ""

  # -- Azure AD authentication configuration (legacy — kept for dual-auth migration period)
  azureAd:
    enabled: true
    instance: "https://login.microsoftonline.com/"
    domain: ""
    tenantId: ""
    clientId: ""
    clientSecret: ""
    # -- Audience for token validation (defaults to api://<clientId> if not set)
    # This must match the Application ID URI in your App Registration
    audience: ""
    scopes: "access_as_user"
    # -- Azure SDK scope for client credentials flow (defaults to api://<clientId>/.default if not set)
    # Used by activities for service-to-service authentication
    # Must match the audience with /.default suffix
    scope: ""

  # -- Super Admin configuration
  # Users from this Entra tenant are automatically granted SuperUser (global admin) role.
  # This is the "owning" organization — the team operating the Flow platform itself.
  # On a fresh installation, the first user from this tenant to log in becomes SuperUser.
  superAdmin:
    # -- The Entra Directory (tenant) ID whose users should be auto-promoted to SuperUser.
    # This matches the "tid" claim in Entra tokens.
    # If empty, no automatic SuperUser promotion occurs (manual assignment only).
    entraTenantId: ""

  # -- Keycloak authentication configuration
  # When both keycloak.enabled and azureAd.enabled are true, the system runs in
  # dual-auth mode — accepting JWTs from either provider during the migration period.
  #
  # AUTHENTICATION MODES:
  #   1. Entra-only:    azureAd.enabled=true,  keycloak.enabled=false
  #   2. Keycloak-only: azureAd.enabled=false, keycloak.enabled=true
  #   3. Dual-auth:     azureAd.enabled=true,  keycloak.enabled=true
  #   4. Disabled:      azureAd.enabled=false,  keycloak.enabled=false
  #
  # USING ENTRA LOGIN THROUGH KEYCLOAK (recommended for new deployments):
  #   Set azureAd.enabled=false and keycloak.enabled=true, then configure
  #   keycloak.entraIdBroker (see keycloak section below) with your Entra
  #   app registration. Users will see "Sign in with Microsoft Entra ID"
  #   on the Keycloak login page. All tokens are issued by Keycloak.
  #
  # DATABASE:
  #   Internal PostgreSQL (default): The chart automatically creates a "keycloak"
  #   database and user in the shared PostgreSQL instance. No manual setup needed.
  #
  #   External PostgreSQL: You must pre-create the database and user:
  #     CREATE ROLE keycloak WITH LOGIN PASSWORD 'your-password';
  #     CREATE DATABASE keycloak OWNER keycloak;
  #   Then set keycloak.database.host/password below (or use existingSecret).
  keycloak:
    # -- Enable Keycloak as an identity provider
    enabled: false
    # -- Use an external (pre-existing) Keycloak instance instead of deploying one
    external: false
    # -- Keycloak base URL (auto-generated for internal deployment, required for external)
    # For internal: "http://<release>-keycloak:8080" is used automatically
    # For external: set to the full URL, e.g. "https://auth.example.com"
    url: ""
    # -- Keycloak realm name — all Flow users and clients are in this realm
    realm: "flow"
    # -- OIDC client ID for the Flow frontend (public client, PKCE)
    frontendClientId: "flow-frontend"
    # -- OIDC client ID for backend API services (confidential client)
    backendClientId: "flow-backend"
    # -- Backend client secret (ignored if existingSecret is set)
    backendClientSecret: ""
    # -- Use existing secret for backend client secret
    existingSecret: ""
    # -- Key in existing secret containing the backend client secret
    existingSecretKey: "keycloak-backend-client-secret"
    # -- Keycloak admin username (for initial realm setup, only used with internal deployment)
    adminUsername: "admin"
    # -- Keycloak admin password (ignored if existingSecret is set, only used with internal deployment)
    adminPassword: ""
    # -- Existing secret containing admin password
    adminExistingSecret: ""
    # -- Key in the existing secret
    adminExistingSecretKey: "keycloak-admin-password"

  # -- Ingress/Proxy configuration for services behind reverse proxy or ingress controller
  # This is required when using SSL-terminating ingress controllers (e.g., NGINX, Traefik)
  # to ensure OAuth redirect URLs use the correct protocol (https)
  ingress:
    # -- Force a specific scheme for external URLs (http, https, or auto)
    # - "auto": Trust X-Forwarded-Proto header from ingress (default, recommended)
    # - "https": Always use HTTPS for external URLs
    # - "http": Always use HTTP (not recommended for production)
    forceScheme: "auto"
    
    # -- Frontend ingress configuration
    frontend:
      # -- External host/domain for the frontend (e.g., "flow.example.com")
      # If not set, uses the Host header from requests
      externalHost: ""
      # -- Base path if frontend is served under a subpath (e.g., "/app")
      basePath: ""
    
    # -- Backend API ingress configuration (for all backend services)
    backend:
      # -- External host/domain for the backend API (e.g., "api.flow.example.com")
      # If not set, uses the Host header from requests
      externalHost: ""
      # -- Base path if backend API is served under a subpath (e.g., "/api")
      basePath: ""

  # -- Database configuration
  database:
    # -- Database provider: SqlServer or Postgres
    provider: "Postgres"
    # -- SQL Server connection string (when provider is SqlServer)
    sqlServer:
      connectionString: ""
      # -- Use existing secret for connection string
      existingSecret: ""
      existingSecretKey: "connection-string"
    # -- PostgreSQL configuration (when provider is Postgres)
    postgres:
      # -- Use external PostgreSQL instance (set to true for managed services like Azure Database for PostgreSQL, AWS RDS, etc.)
      external: false
      # -- PostgreSQL host (FQDN for managed services, e.g., myserver.postgres.database.azure.com)
      host: "postgresql"
      # -- PostgreSQL port
      port: 5432
      # -- Namespace where PostgreSQL is deployed (for network policy when external=true)
      # - Set to namespace name if PostgreSQL is in a different namespace within the cluster (e.g., "database")
      # - Leave empty if PostgreSQL is completely external (outside the cluster) to allow all egress IPs
      namespace: ""
      # -- Database name
      database: "flow"
      # -- Database username (for Azure, use format: username@servername)
      username: "flow"
      # -- Database password (ignored if existingSecret is set)
      password: ""
      # -- Use existing secret for password
      existingSecret: ""
      # -- Key in existing secret containing the password
      existingSecretKey: "postgres-password"
      # -- SSL mode: Disable, Allow, Prefer, Require, VerifyCA, VerifyFull
      sslMode: "Prefer"
      # -- Additional connection parameters (e.g., "Trust Server Certificate=true&Application Name=flow")
      extraParams: ""
      # -- Connection pool settings
      pooling:
        # -- Minimum pool size
        minSize: 5
        # -- Maximum pool size
        maxSize: 100
        # -- Connection lifetime in seconds (0 = infinite)
        connectionLifetime: 0
        # -- Connection idle timeout in seconds
        connectionIdleLifetime: 300
      # -- Read replica configuration (for read/write splitting)
      # When using HA PostgreSQL (internal or external), read-heavy queries can be
      # routed to replicas to reduce load on the primary
      readReplica:
        # -- Enable read replica connection (requires HA PostgreSQL)
        enabled: false
        # -- Read replica host (auto-resolved for internal HA, set FQDN for external)
        # For internal HA: leave empty to auto-resolve to the read service
        # For external: set to your read replica endpoint (e.g., "myserver-replica.postgres.database.azure.com")
        host: ""
        # -- Read replica port (defaults to same as primary)
        port: 0

  # -- RabbitMQ configuration
  rabbitmq:
    # -- Use external RabbitMQ instance (set to true for managed services like CloudAMQP, Amazon MQ, etc.)
    external: false
    # -- RabbitMQ host (FQDN for managed services)
    host: "rabbitmq"
    # -- Namespace where RabbitMQ is deployed (for network policy when external=true)
    # - Set to namespace name if RabbitMQ is in a different namespace within the cluster (e.g., "messaging")
    # - Leave empty if RabbitMQ is completely external (outside the cluster) to allow all egress IPs
    namespace: ""
    # -- Additional hosts for cluster connection (comma-separated or list)
    additionalHosts: []
    # -- RabbitMQ AMQP port
    port: 5672
    # -- RabbitMQ management port
    managementPort: 15672
    # -- RabbitMQ username
    username: "flow"
    # -- RabbitMQ password (ignored if existingSecret is set)
    password: ""
    # -- Use existing secret for password
    existingSecret: ""
    # -- Key in existing secret containing the password
    existingSecretKey: "rabbitmq-password"
    # -- Virtual host
    vhost: "/"
    # -- Prefetch count (number of unacknowledged messages per consumer)
    # Higher values increase throughput at the cost of less fair distribution.
    # For enterprise workloads handling millions of messages, 20 provides a good
    # balance between throughput and fair load distribution across consumers.
    prefetch: 20
    # -- Activity exchange name
    activityExchange: "workflow.activities"
    # -- Completed exchange name
    completedExchange: "workflow.completed"
    # -- Workflow events exchange name
    workflowEventsExchange: "workflow.logging"
    # -- Use TLS for connection
    tls:
      enabled: false
      # -- Skip TLS certificate verification (not recommended for production)
      insecureSkipVerify: false
      # -- Path to CA certificate (for self-signed certs)
      caCertPath: ""
      # -- Use existing secret containing TLS certificates
      existingSecret: ""
      # -- Key in existing secret containing CA certificate
      caCertKey: "ca.crt"

  # -- Redis configuration
  redis:
    # -- Enable Redis (for caching and distributed locking)
    enabled: false
    # -- Use external Redis instance (set to true for managed services like Azure Cache for Redis, Amazon ElastiCache, etc.)
    external: false
    # -- Namespace where Redis is deployed (for network policy when external=true)
    # - Set to namespace name if Redis is in a different namespace within the cluster (e.g., "cache")
    # - Leave empty if Redis is completely external (outside the cluster) to allow all egress IPs
    namespace: ""
    # -- Redis deployment mode: standalone, sentinel, or cluster
    mode: "standalone"
    
    # -- Standalone/Sentinel configuration
    # -- Redis host (single host for standalone, master name for sentinel, ignored for cluster mode)
    host: "redis"
    # -- Redis port (6379 for standalone, 26379 for sentinel)
    port: 6379
    
    # -- Redis Sentinel configuration (when mode=sentinel)
    sentinel:
      # -- Sentinel master name
      masterName: "mymaster"
      # -- Sentinel nodes (list of host:port)
      nodes: []
      # Example:
      # - "sentinel-0.sentinel:26379"
      # - "sentinel-1.sentinel:26379"
      # - "sentinel-2.sentinel:26379"
      # -- Sentinel password (if different from Redis password)
      password: ""
      # -- Use existing secret for sentinel password
      existingSecret: ""
      existingSecretKey: "sentinel-password"
    
    # -- Redis Cluster configuration (when mode=cluster)
    cluster:
      # -- Cluster nodes (list of host:port for cluster endpoints)
      nodes: []
      # Example for Azure Cache for Redis (clustered):
      # - "myredis.redis.cache.windows.net:6380"
      # Example for Amazon ElastiCache cluster:
      # - "cluster-endpoint.cache.amazonaws.com:6379"
      # Example for self-hosted cluster:
      # - "redis-node-0:6379"
      # - "redis-node-1:6379"
      # - "redis-node-2:6379"
      # -- Maximum number of redirect hops
      maxRedirects: 3
    
    # -- Redis username (Redis 6+ ACL, required for Azure Cache for Redis)
    username: ""
    # -- Redis password (ignored if existingSecret is set)
    password: ""
    # -- Use existing secret for password
    existingSecret: ""
    # -- Key in existing secret containing the password
    existingSecretKey: "redis-password"
    # -- Redis database index (only for standalone/sentinel, not supported in cluster mode)
    database: 0
    
    # -- TLS configuration
    tls:
      # -- Enable TLS (required for most managed Redis services)
      enabled: false
      # -- Skip TLS certificate verification (not recommended for production)
      insecureSkipVerify: false
      # -- Path to CA certificate
      caCertPath: ""
      # -- Use existing secret containing TLS certificates
      existingSecret: ""
      caCertKey: "ca.crt"
    
    # -- Connection settings
    connectTimeout: 5000
    # -- Sync timeout in milliseconds
    syncTimeout: 5000
    # -- Keep alive interval in seconds (0 to disable)
    keepAlive: 60
    # -- Retry settings
    retry:
      # -- Maximum retry attempts
      maxAttempts: 3
      # -- Base delay between retries in milliseconds
      baseDelay: 100
      # -- Maximum delay between retries in milliseconds
      maxDelay: 5000
    
    # -- Connection pool settings
    pool:
      # -- Minimum number of connections to maintain
      minSize: 5
      # -- Maximum number of connections
      maxSize: 50

  # -- Service URLs (internal Kubernetes DNS - uses full FQDN for cross-namespace communication)
  # Format: http://<service-name>.<namespace>.svc.cluster.local:<port>
  # These are templated and will be resolved during Helm rendering
  serviceUrls:
    activityRegistry: "http://{{ .Release.Name }}-activity-registry.{{ .Release.Namespace }}.svc.cluster.local:80"
    definitionStore: "http://{{ .Release.Name }}-definition-store.{{ .Release.Namespace }}.svc.cluster.local:80"
    workflowEngine: "http://{{ .Release.Name }}-workflow-engine.{{ .Release.Namespace }}.svc.cluster.local:80"
    workflowLogging: "http://{{ .Release.Name }}-workflow-logging.{{ .Release.Namespace }}.svc.cluster.local:80"
    connectionStore: "http://{{ .Release.Name }}-connection-store.{{ .Release.Namespace }}.svc.cluster.local:80"
    tenantRegistry: "http://{{ .Release.Name }}-tenant-registry.{{ .Release.Namespace }}.svc.cluster.local:80"
    billingService: "http://{{ .Release.Name }}-billing-service.{{ .Release.Namespace }}.svc.cluster.local:80"
    aiAssistant: "http://{{ .Release.Name }}-ai-assistant.{{ .Release.Namespace }}.svc.cluster.local:80"
    mcpServer: "http://{{ .Release.Name }}-mcp-server.{{ .Release.Namespace }}.svc.cluster.local:80"
    configurationStore: "http://{{ .Release.Name }}-configuration-store.{{ .Release.Namespace }}.svc.cluster.local:80"

# -- OpenTelemetry configuration for distributed tracing and metrics
# Requires an OTLP-compatible collector (e.g., Grafana Alloy, Jaeger, OpenTelemetry Collector)
openTelemetry:
  # -- Enable OpenTelemetry tracing and metrics for all services
  enabled: false
  # -- OTLP exporter endpoint (gRPC)
  endpoint: "http://otel-collector:4317"
  # -- OTLP protocol: grpc or http/protobuf
  protocol: "grpc"

# =============================================================================
# Promotion Lifecycle
# =============================================================================

# -- Workflow promotion lifecycle configuration
# Each Flow deployment is a separate environment (Development/Test/Production).
# Configure this deployment's identity and its promotion targets so users can
# promote workflow definitions between environments through the UI.
promotion:
  # -- This deployment's environment name
  # Displayed as a badge in the UI and recorded as source in promotion metadata
  environment: "Development"

  # -- Promotion policy: Manual, Sequential, or AutoDev
  # Manual: any version can be promoted to any configured target
  # Sequential: versions must follow the promotion order (can't skip stages)
  # AutoDev: auto-promote on save, manual for subsequent environments
  policy: "Manual"

  # -- Ordered sequence of environments for the Sequential policy
  promotionOrder:
    - "Development"
    - "Test"
    - "Production"

  # -- Auto-promote on save (for AutoDev policy)
  autoPromoteOnSave: false
  # -- Target environment for auto-promotion
  autoPromoteTarget: ""

  # -- Target environments for promotion
  # Each target requires network connectivity from the frontend-web pod
  # to the target environment's definition-store service.
  # Example:
  #   targets:
  #     - name: "Test"
  #       definitionStoreUrl: "http://flow-definition-store.flow-test.svc.cluster.local:80"
  #     - name: "Production"
  #       definitionStoreUrl: "http://flow-definition-store.flow-prod.svc.cluster.local:80"
  targets: []

# =============================================================================
# Core Services
# =============================================================================

# -- Workflow Engine service configuration
workflowEngine:
  enabled: true
  replicaCount: 1
  
  image:
    repository: flow/workflowengine
    tag: ""
    pullPolicy: IfNotPresent
  
  service:
    type: ClusterIP
    port: 80
  
  ingress:
    enabled: false
    className: ""
    annotations: {}
    hosts:
      - host: workflow-engine.local
        paths:
          - path: /
            pathType: Prefix
    tls: []

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

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 30
    targetCPUUtilizationPercentage: 75
    # -- Target memory utilization percentage (optional, enable for memory-intensive workloads)
    # targetMemoryUtilizationPercentage: 80

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

  # -- Runtime configuration
  runtime:
    concurrencyMaxRetries: 5
    concurrencyBaseDelayMs: 100
    concurrencyJitterMs: 50
    # -- Maximum number of activity results to process concurrently per pod
    # Higher values increase throughput but require more DB connections.
    # Set this <= prefetch count and <= primary pool size.
    maxConcurrentActivityResults: 10

  # -- Tenant settings provider configuration
  tenantSettings:
    # -- How long to cache tenant settings (format: HH:MM:SS or minutes as integer)
    cacheDurationMinutes: 5
    # -- Whether to use default settings when TenantRegistry is unavailable
    fallbackToDefaults: true

  # -- Leader election configuration for distributed background services
  # Ensures only one pod runs cleanup/timeout jobs in multi-replica deployments
  leaderElection:
    # -- Default lease duration in minutes
    defaultLeaseDurationMinutes: 1
    # -- Retry interval in seconds when not the leader
    retryIntervalSeconds: 10

  # -- Workflow timeout service configuration
  # Marks long-running workflows as failed based on tenant WorkflowTimeoutMinutes setting
  workflowTimeout:
    # -- How often to check for timed out workflows (in minutes)
    checkIntervalMinutes: 1
    # -- How long to hold leadership lease (in minutes, should be > checkInterval)
    leaseDurationMinutes: 2

  # -- Workflow cleanup service configuration
  # Deletes old completed/failed workflows based on tenant MaxWorkflowHistoryDays setting
  workflowCleanup:
    # -- How often to run cleanup (in minutes, default 60 = 1 hour)
    checkIntervalMinutes: 60
    # -- Max workflows to delete per tenant per run
    # For enterprise workloads with thousands of concurrent flows, a higher batch
    # size prevents cleanup from falling behind workflow creation rate.
    batchSize: 1000
    # -- How long to hold leadership lease (in minutes)
    leaseDurationMinutes: 5

  # -- Rate limiting configuration
  rateLimiting:
    # -- Enable per-tenant rate limiting
    enabled: true
    # -- Maximum requests per tenant per minute
    requestsPerMinute: 1000
    # -- Queue limit (0 = reject immediately when limit reached)
    queueLimit: 0

  # -- Load shedding configuration
  loadShedding:
    # -- Enable load shedding (reject requests when overloaded)
    enabled: false
    # -- Maximum concurrent requests before shedding
    maxConcurrentRequests: 500

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  # -- Additional environment variables
  extraEnv: []

  # -- Additional volume mounts
  extraVolumeMounts: []

  # -- Additional volumes
  extraVolumes: []

# -- Activity Registry service configuration
activityRegistry:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/activityregistry
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Definition Store service configuration
definitionStore:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/definitionstore
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Workflow Logging service configuration
workflowLogging:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/workflowlogging
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Billing Service configuration
billingService:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/billingservice
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

  resources:
    limits:
      cpu: 250m
      memory: 256Mi
    requests:
      cpu: 50m
      memory: 128Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 3
    targetCPUUtilizationPercentage: 75

  # -- Stripe billing integration
  stripe:
    enabled: false
    # successUrl: "https://your-domain.com/billing?session_id={CHECKOUT_SESSION_ID}"
    # cancelUrl: "https://your-domain.com/billing?canceled=true"

  # -- Billing model for this deployment's environment.
  # Subscription: monthly fee with included compute seconds and overage billing.
  #   Used for production environments where tenants have a plan (Free/Pro/Business).
  # BaseAndUsage: flat infrastructure base fee + metered compute-second billing.
  #   Used for dev/test environments — every compute second is pay-as-you-go.
  # Note: Enterprise tier is always billed via custom invoicing regardless of model.
  billingModel: "Subscription"

  # -- Infrastructure unit pricing (SEK per unit per hour).
  # Used by the cost/margin model to calculate the actual infrastructure cost
  # of each compute second, enabling margin analysis and pricing validation.
  # Values sourced from OpenCost / Cleura price list.
  infrastructurePricing:
    # -- Cost per CPU core per hour in SEK
    cpuCoreHour: 0.08333
    # -- Cost per GiB of RAM per hour in SEK
    ramGibHour: 0.06944
    # -- Cost per GiB of persistent storage per hour in SEK
    storageGibHour: 0.00139
    # -- Average hours per month (used for monthly cost projections)
    hoursPerMonth: 730
    # -- Currency code for the pricing values above
    currency: "SEK"

  # -- Resource profile for a single activity worker pod.
  # These values are used by the cost model to calculate the infrastructure
  # cost of one compute second. Should match the actual Kubernetes resource
  # requests for activity workers (see activities.resources below).
  activityWorkerProfile:
    cpuCores: 0.1
    memoryGib: 0.25

  podDisruptionBudget:
    enabled: false
    minAvailable: 1

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

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Connection Store service configuration
connectionStore:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/connectionstore
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Configuration Store service configuration
configurationStore:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/configurationstore
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- AI Assistant service configuration
aiAssistant:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/aiassistant
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

  resources:
    limits:
      cpu: 250m
      memory: 256Mi
    requests:
      cpu: 50m
      memory: 128Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 3
    targetCPUUtilizationPercentage: 80

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- MCP Server configuration (exposes workflows as MCP tools for AI models)
mcpServer:
  enabled: true
  replicaCount: 1

  # -- Public URL is derived from the frontend ingress host + /mcp subpath.
  # Override this only if MCP is exposed on a separate domain.
  publicUrl: ""

  image:
    repository: flow/mcpserver
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 250m
      memory: 256Mi
    requests:
      cpu: 50m
      memory: 128Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 3
    targetCPUUtilizationPercentage: 80

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# -- Tenant Registry service configuration
tenantRegistry:
  enabled: true
  replicaCount: 1

  image:
    repository: flow/tenantregistry
    tag: ""
    pullPolicy: IfNotPresent

  service:
    type: ClusterIP
    port: 80

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

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 10
    targetCPUUtilizationPercentage: 75

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# =============================================================================
# Frontend
# =============================================================================

# -- Frontend Web service configuration
frontendWeb:
  enabled: true
  replicaCount: 1
  
  image:
    repository: flow/frontend-web
    tag: ""
    pullPolicy: IfNotPresent
  
  service:
    type: ClusterIP
    port: 80

  ingress:
    enabled: true
    className: ""
    # Annotations for Traefik Ingress with Blazor Server support:
    # traefik.ingress.kubernetes.io/affinity: "true"                     # Sticky sessions for SignalR
    # traefik.ingress.kubernetes.io/session-cookie-name: "FLOW_AFFINITY" # Session cookie name
    # traefik.ingress.kubernetes.io/router.entrypoints: websecure        # HTTPS entrypoint
    #
    # Annotations for NGINX Ingress with Blazor Server support:
    # nginx.ingress.kubernetes.io/affinity: "cookie"                    # Sticky sessions for SignalR
    # nginx.ingress.kubernetes.io/session-cookie-name: "FLOW_AFFINITY"  # Session cookie name
    # nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"            # WebSocket timeout
    # nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"            # WebSocket timeout
    # nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"             # Larger buffers for Blazor
    annotations: {}
    hosts:
      - host: flow.local
        paths:
          - path: /
            pathType: Prefix
    tls: []

  resources:
    limits:
      cpu: 250m
      memory: 256Mi
    requests:
      cpu: 50m
      memory: 128Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 3
    targetCPUUtilizationPercentage: 80

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

  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1

  extraEnv: []
  extraVolumeMounts: []
  extraVolumes: []

# =============================================================================
# Activity Services
# =============================================================================

# -- Dedicated activity worker groups for tenant workload isolation
# Each group deploys additional activity workers that consume from group-specific queues.
# Configure tenants to route to a group via TenantSettings.QueueGroup in TenantRegistry.
# Example: A tenant with QueueGroup="enterprise-a" will have messages routed to
# queue "activity.{name}.execute.enterprise-a" instead of the shared queue.
dedicatedActivityGroups: []
# Example configuration:
# dedicatedActivityGroups:
#   - name: "enterprise-a"
#     # Override common resources for this group
#     resources:
#       limits:
#         cpu: 1000m
#         memory: 1Gi
#       requests:
#         cpu: 250m
#         memory: 512Mi
#     # Activity workers to deploy for this group
#     activities:
#       - name: httprequest
#         image:
#           repository: flow/httprequestactivity
#         replicaCount: 3
#       - name: sql
#         image:
#           repository: flow/sqlactivity
#         replicaCount: 2

# -- Activity services common configuration
activities:
  # -- Common image settings for activity services
  image:
    pullPolicy: IfNotPresent

  # -- Common resource settings for activity services
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 256Mi

  # -- Common service settings
  service:
    type: ClusterIP
    port: 80

# -- Individual activity service configurations
httpRequestActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/httprequestactivity
    tag: ""

sqlActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/sqlactivity
    tag: ""

redisActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/redisactivity
    tag: ""

blobStorageActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/blobstorageactivity
    tag: ""

rabbitmqActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/rabbitmqactivity
    tag: ""

kafkaActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/kafkaactivity
    tag: ""

serviceBusActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/servicebusactivity
    tag: ""

storageQueueActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/storagequeueactivity
    tag: ""

cosmosDbActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/cosmosdbactivity
    tag: ""

mongoDbActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/mongodbactivity
    tag: ""

emailActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/emailactivity
    tag: ""

slackActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/slackactivity
    tag: ""

teamsActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/teamsactivity
    tag: ""

twilioActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/twilioactivity
    tag: ""

ftpActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/ftpactivity
    tag: ""

graphqlActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/graphqlactivity
    tag: ""

soapActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/soapactivity
    tag: ""

pdfActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/pdfactivity
    tag: ""

csvActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/csvactivity
    tag: ""

excelActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/excelactivity
    tag: ""

oauthActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/oauthactivity
    tag: ""

keyVaultActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/keyvaultactivity
    tag: ""

eventHubActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/eventhubactivity
    tag: ""

eventGridActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/eventgridactivity
    tag: ""

awsS3Activity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/awss3activity
    tag: ""

awsSqsActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/awssqsactivity
    tag: ""

m365Activity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/m365activity
    tag: ""

pdfTextExtractActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/pdftextextractactivity
    tag: ""

mcpActivity:
  enabled: true
  replicaCount: 1
  image:
    repository: flow/mcpactivity
    tag: ""

# =============================================================================
# Infrastructure Dependencies
# =============================================================================

# -- Keycloak internal deployment configuration
# Only used when global.keycloak.enabled=true and global.keycloak.external=false
# ==========================================================================
# Keycloak Internal Deployment
# ==========================================================================
# Controls the Keycloak pod deployed by this chart.
# Set keycloak.enabled=false when using an external Keycloak (global.keycloak.external=true).
#
# DATABASE CONNECTION:
#   Keycloak needs its own PostgreSQL database. How this works depends on your setup:
#
#   1. Internal PostgreSQL (postgresql.enabled=true, default):
#      The chart automatically creates the "keycloak" database, user, and password
#      inside the shared PostgreSQL instance. Nothing to configure — it just works.
#
#   2. External PostgreSQL (global.database.postgres.external=true):
#      Keycloak connects to your external PostgreSQL using the host/port from
#      global.database.postgres (unless overridden with keycloak.database.host/port).
#      You MUST pre-create the database and user manually:
#        CREATE ROLE keycloak WITH LOGIN PASSWORD 'your-password';
#        CREATE DATABASE keycloak OWNER keycloak;
#      Then set keycloak.database.password (or existingSecret) to match.
#
#   3. Dedicated database host for Keycloak:
#      Set keycloak.database.host to point Keycloak at a different PostgreSQL
#      instance than the one used by the rest of Flow.
keycloak:
  # -- Enable internal Keycloak deployment
  enabled: true

  image:
    repository: quay.io/keycloak/keycloak
    tag: "26.1"
    pullPolicy: IfNotPresent

  # -- Deployment mode: "standalone" for dev, "ha" for production (Infinispan clustering)
  mode: "standalone"

  # -- Number of replicas (only used when mode=ha)
  replicaCount: 1

  # -- Keycloak runs in production mode by default (optimised, requires TLS or proxy headers)
  # Set to "start-dev" for local development without TLS
  command: "start"

  # -- Keycloak PostgreSQL database connection
  # When internal PostgreSQL is used, these are auto-provisioned by the init script.
  # When external PostgreSQL is used, you must set host and password.
  database:
    # -- Use a completely separate database instance for Keycloak
    # When true, the init script does NOT create the keycloak DB in shared PostgreSQL.
    # You must provide keycloak.database.host pointing to the dedicated instance.
    dedicated: false
    # -- Database vendor (postgres or dev-mem for ephemeral dev mode)
    vendor: "postgres"
    # -- Database name
    database: "keycloak"
    # -- Database username
    username: "keycloak"
    # -- Database password
    # For internal PostgreSQL: leave empty to auto-generate. The same password is used
    # to create the DB role and to connect from Keycloak (stored in the keycloak secret).
    # For external PostgreSQL: set this to the password of the pre-created role.
    password: ""
    # -- Use an existing Kubernetes secret for the database password
    existingSecret: ""
    existingSecretKey: "keycloak-db-password"
    # -- Database host override
    # Leave empty to auto-resolve:
    #   - Internal PG: uses the chart's PostgreSQL service (e.g. <release>-postgresql-primary)
    #   - External PG: uses global.database.postgres.host
    # Set explicitly to point Keycloak at a different host.
    host: ""
    # -- Database port override (0 = use the same port as global.database.postgres.port)
    port: 0

  # -- Extra environment variables for Keycloak
  extraEnv: []

  # ==========================================================================
  # Realm configuration
  # ==========================================================================
  # These values are injected into the realm import JSON at deploy time.
  # The realm is auto-imported on first Keycloak boot. To re-import after
  # changes, delete the Keycloak pod (the realm is re-imported on startup).
  realm:
    # -- Additional redirect URIs for the frontend client (production URLs)
    # The realm always includes localhost URIs for development.
    # Add your production domain(s) here, e.g.:
    #   frontendRedirectUris:
    #     - "https://flow.your-domain.com/*"
    frontendRedirectUris: []

    # -- Additional web origins for CORS (production domains)
    # Must match the redirect URI domains without path/wildcard, e.g.:
    #   frontendWebOrigins:
    #     - "https://flow.your-domain.com"
    frontendWebOrigins: []

  # ==========================================================================
  # Entra ID (Azure AD) Identity Broker
  # ==========================================================================
  # When using Keycloak as the sole identity provider (global.azureAd.enabled=false,
  # global.keycloak.enabled=true), users can still sign in with their existing
  # Microsoft Entra ID / Azure AD accounts through Keycloak's identity brokering.
  #
  # HOW IT WORKS:
  #   Keycloak is configured as a "broker" — it delegates authentication to Entra ID
  #   via OIDC, then creates/links a local Keycloak user automatically. From the
  #   backend services' perspective, all tokens come from Keycloak.
  #
  # ENTRA APP REGISTRATION SETUP:
  #   1. Create (or reuse) an App Registration in Azure Entra ID
  #   2. Under "Authentication", add a redirect URI:
  #        https://<keycloak-host>/realms/flow/broker/microsoft/endpoint
  #      (e.g. https://auth.flow.your-domain.com/realms/flow/broker/microsoft/endpoint)
  #   3. Under "Certificates & secrets", create a client secret
  #   4. Copy the Application (client) ID → keycloak.entraIdBroker.clientId
  #   5. Copy the Directory (tenant) ID → keycloak.entraIdBroker.tenantId
  #   6. Copy the client secret value → keycloak.entraIdBroker.clientSecret
  #
  # SCENARIOS:
  #   ┌──────────────────────────────┬───────────────────┬──────────────────────┐
  #   │ Scenario                     │ global.azureAd    │ entraIdBroker        │
  #   ├──────────────────────────────┼───────────────────┼──────────────────────┤
  #   │ Keycloak-only, local users   │ enabled: false    │ enabled: false       │
  #   │ Keycloak + Entra login       │ enabled: false    │ enabled: true        │
  #   │ Dual-auth (migration)        │ enabled: true     │ enabled: false       │
  #   │ Full Keycloak with Entra IdP │ enabled: false    │ enabled: true        │
  #   └──────────────────────────────┴───────────────────┴──────────────────────┘
  #
  #   - "Keycloak + Entra login": Keycloak is the only token issuer. Users click
  #     "Sign in with Microsoft" on the Keycloak login page. Backend services only
  #     need to validate Keycloak tokens.
  #   - "Dual-auth (migration)": Both Entra and Keycloak tokens are accepted
  #     directly by backend services. Use this during migration only.
  entraIdBroker:
    # -- Enable Microsoft Entra ID as an identity provider in Keycloak
    enabled: false

    # -- Display name shown on the Keycloak login page button
    displayName: "Microsoft Entra ID"

    # -- Azure AD Tenant ID (Directory ID from Azure portal)
    tenantId: ""

    # -- Azure AD Application (client) ID from the App Registration
    clientId: ""

    # -- Azure AD client secret from the App Registration
    # For production, use --set or an external secret instead of storing in values
    clientSecret: ""

    # -- Trust email addresses from Entra ID without requiring verification
    # Safe to enable since Entra verifies emails itself
    trustEmail: true

    # -- First login flow — what happens when an Entra user logs in for the first time
    # "first broker login": Keycloak's default flow (link or create account)
    firstLoginFlow: "first broker login"

    # -- Attribute sync mode: INHERIT, IMPORT, LEGACY, or FORCE
    # IMPORT: Sync attributes on first login only
    # FORCE: Sync on every login (keeps Keycloak in sync with Entra changes)
    syncMode: "FORCE"

  # -- Resource requests and limits
  resources:
    limits:
      cpu: "1000m"
      memory: "1Gi"
    requests:
      cpu: "250m"
      memory: "512Mi"

  # -- Keycloak HTTP port (internal, before proxy)
  httpPort: 8080

  # -- Keycloak management port (health/metrics endpoints)
  # Since Keycloak 24+, health and metrics are served on a separate management
  # interface. Default: 9000. Probes must target this port, not httpPort.
  managementPort: 9000

  # -- Enable health and metrics endpoints
  health:
    enabled: true
  metrics:
    enabled: true

  # -- Proxy mode — "edge" when behind an SSL-terminating ingress/proxy
  proxy: "edge"

  # -- Ingress for Keycloak
  ingress:
    enabled: false
    className: ""
    annotations: {}
    host: ""
    tls: []

# -- PostgreSQL internal deployment configuration
# Only used when global.database.postgres.external=false
postgresql:
  # -- Enable internal PostgreSQL deployment
  enabled: true
  
  # -- Deployment mode: standalone or ha (high-availability with streaming replication)
  mode: "standalone"
  
  image:
    repository: postgres
    tag: "16-alpine"
    pullPolicy: IfNotPresent
  
  # -- Number of replicas (only used when mode=ha, must be >= 2)
  replicaCount: 1
  
  # -- PostgreSQL superuser password
  auth:
    # -- PostgreSQL admin username
    postgresUsername: "postgres"
    # -- PostgreSQL admin password
    postgresPassword: ""
    # -- Application database name
    database: "flow"
    # -- Application username
    username: "flow"
    # -- Application password
    password: ""
    # -- Use existing secret for passwords
    existingSecret: ""
    # -- Key for postgres password in existing secret
    secretKeys:
      adminPasswordKey: "postgres-password"
      userPasswordKey: "password"
      replicationPasswordKey: "replication-password"
  
  # -- Replication configuration (used when mode=ha)
  replication:
    # -- Enable synchronous replication for zero data loss (impacts performance)
    synchronous: false
    # -- Number of synchronous replicas (when synchronous=true)
    numSynchronousReplicas: 1
    # -- Replication username
    username: "replicator"
    # -- Replication password
    password: ""
  
  # -- Primary node configuration
  primary:
    # -- PostgreSQL configuration parameters
    # These defaults are tuned for standalone mode with 1Gi RAM.
    # For HA/enterprise workloads, override with values appropriate for your resource limits.
    # See values-enterprise.yaml for enterprise-tuned configuration.
    configuration: |
      max_connections = 200
      shared_buffers = 256MB
      effective_cache_size = 768MB
      maintenance_work_mem = 64MB
      checkpoint_completion_target = 0.9
      wal_buffers = 16MB
      default_statistics_target = 100
      random_page_cost = 1.1
      effective_io_concurrency = 200
      min_wal_size = 1GB
      max_wal_size = 4GB
      max_worker_processes = 4
      max_parallel_workers_per_gather = 2
      max_parallel_workers = 4
      max_parallel_maintenance_workers = 2
    
    # -- pg_hba.conf entries
    pgHbaConfiguration: |
      local all all trust
      host all all 127.0.0.1/32 trust
      host all all ::1/128 trust
      host all all 0.0.0.0/0 scram-sha-256
      host replication all 0.0.0.0/0 scram-sha-256
    
    # -- Initialization scripts (run on first startup)
    initScripts: {}
    
    resources:
      limits:
        cpu: 1000m
        memory: 1Gi
      requests:
        cpu: 250m
        memory: 512Mi
    
    # -- Persistent storage for primary
    persistence:
      enabled: true
      storageClass: ""
      size: 10Gi
      accessModes:
        - ReadWriteOnce
    
    nodeSelector: {}
    tolerations: []
    affinity: {}
  
  # -- Replica/standby configuration (used when mode=ha)
  replica:
    # -- Number of read replicas (recommend >= 2 for enterprise workloads)
    replicaCount: 2

    # -- Hot standby settings
    configuration: |
      hot_standby = on
      max_standby_streaming_delay = 30s
      wal_receiver_status_interval = 10s
      hot_standby_feedback = on

    resources:
      limits:
        cpu: 1000m
        memory: 1Gi
      requests:
        cpu: 250m
        memory: 512Mi
    
    persistence:
      enabled: true
      storageClass: ""
      size: 10Gi
      accessModes:
        - ReadWriteOnce
    
    nodeSelector: {}
    tolerations: []
    affinity: {}
  
  service:
    type: ClusterIP
    port: 5432
    # -- Create headless service for StatefulSet
    headless:
      annotations: {}
  
  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1
  
  # -- Metrics/monitoring
  metrics:
    enabled: false
    image:
      repository: prometheuscommunity/postgres-exporter
      tag: "v0.15.0"
    port: 9187
    resources:
      limits:
        cpu: 100m
        memory: 128Mi
      requests:
        cpu: 50m
        memory: 64Mi
  
  nodeSelector: {}
  tolerations: []
  affinity: {}

# -- RabbitMQ internal deployment configuration
# Only used when global.rabbitmq.external=false
rabbitmq:
  # -- Enable internal RabbitMQ deployment
  enabled: true
  
  # -- Deployment mode: standalone or ha (clustered with quorum queues)
  mode: "standalone"
  
  image:
    repository: rabbitmq
    tag: "3.13-management-alpine"
    pullPolicy: IfNotPresent
  
  # -- Number of replicas (for HA mode, should be odd number >= 3)
  replicaCount: 1
  
  auth:
    # -- Default username
    username: "flow"
    # -- Default password
    password: ""
    # -- Erlang cookie for clustering (required for HA)
    erlangCookie: ""
    # -- Use existing secret
    existingSecret: ""
    secretKeys:
      passwordKey: "rabbitmq-password"
      erlangCookieKey: "erlang-cookie"
  
  # -- RabbitMQ configuration
  configuration: |
    ## Cluster formation
    cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s
    cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
    cluster_formation.k8s.address_type = hostname
    cluster_formation.node_cleanup.interval = 30
    cluster_formation.node_cleanup.only_log_warning = true
    cluster_partition_handling = pause_minority
    ## Queue settings
    queue_master_locator = min-masters
    ## Memory and disk
    vm_memory_high_watermark.relative = 0.6
    disk_free_limit.relative = 1.5
    ## Networking
    tcp_listen_options.backlog = 128
    tcp_listen_options.nodelay = true
    tcp_listen_options.linger.on = true
    tcp_listen_options.linger.timeout = 0
    tcp_listen_options.sndbuf = 32768
    tcp_listen_options.recbuf = 32768
  
  # -- Additional plugins to enable
  plugins: "rabbitmq_management rabbitmq_peer_discovery_k8s rabbitmq_prometheus"
  
  # -- HA-specific configuration
  ha:
    # -- Enable mirroring policies (classic HA, use quorum queues instead for new deployments)
    mirroringEnabled: false
    # -- Default queue type for new queues (classic or quorum)
    defaultQueueType: "quorum"
  
  resources:
    limits:
      cpu: 1000m
      memory: 1Gi
    requests:
      cpu: 250m
      memory: 512Mi
  
  persistence:
    enabled: true
    storageClass: ""
    size: 8Gi
    accessModes:
      - ReadWriteOnce
  
  service:
    type: ClusterIP
    amqpPort: 5672
    managementPort: 15672
    # -- Create headless service for StatefulSet
    headless:
      annotations: {}
  
  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1
  
  # -- Metrics/monitoring
  metrics:
    enabled: false
    port: 15692
  
  nodeSelector: {}
  tolerations: []
  affinity: {}

# -- Redis internal deployment configuration
# Only used when global.redis.external=false and global.redis.enabled=true
redis:
  # -- Enable internal Redis deployment
  enabled: false
  
  # -- Deployment mode: standalone or ha (with Redis Sentinel)
  mode: "standalone"
  
  image:
    repository: redis
    tag: "7-alpine"
    pullPolicy: IfNotPresent
  
  auth:
    # -- Enable password authentication
    enabled: true
    # -- Redis password
    password: ""
    # -- Use existing secret
    existingSecret: ""
    existingSecretKey: "redis-password"
  
  # -- Master/primary configuration
  master:
    # -- Redis configuration
    configuration: |
      maxmemory 256mb
      maxmemory-policy allkeys-lru
      appendonly yes
      appendfsync everysec
      save 900 1
      save 300 10
      save 60 10000
    
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 100m
        memory: 256Mi
    
    persistence:
      enabled: true
      storageClass: ""
      size: 5Gi
      accessModes:
        - ReadWriteOnce
    
    nodeSelector: {}
    tolerations: []
    affinity: {}
  
  # -- Replica configuration (used when mode=ha)
  replica:
    # -- Number of replicas
    replicaCount: 2
    
    resources:
      limits:
        cpu: 250m
        memory: 256Mi
      requests:
        cpu: 50m
        memory: 128Mi
    
    persistence:
      enabled: true
      storageClass: ""
      size: 5Gi
      accessModes:
        - ReadWriteOnce
    
    nodeSelector: {}
    tolerations: []
    affinity: {}
  
  # -- Sentinel configuration (used when mode=ha)
  sentinel:
    # -- Enable Sentinel for automatic failover
    enabled: true
    
    image:
      repository: redis
      tag: "7-alpine"
    
    # -- Number of Sentinel instances (should be odd, >= 3)
    replicaCount: 3
    
    # -- Quorum for failover decision
    quorum: 2
    
    # -- Sentinel port
    port: 26379
    
    # -- Time in milliseconds before considering master down
    downAfterMilliseconds: 30000
    
    # -- Failover timeout in milliseconds
    failoverTimeout: 180000
    
    # -- Number of replicas to reconfigure in parallel during failover
    parallelSyncs: 1
    
    resources:
      limits:
        cpu: 100m
        memory: 128Mi
      requests:
        cpu: 50m
        memory: 64Mi
  
  service:
    type: ClusterIP
    port: 6379
    sentinelPort: 26379
    # -- Create headless service for StatefulSet
    headless:
      annotations: {}
  
  # -- Pod disruption budget
  podDisruptionBudget:
    enabled: false
    minAvailable: 1
  
  # -- Metrics/monitoring
  metrics:
    enabled: false
    image:
      repository: oliver006/redis_exporter
      tag: "v1.55.0"
    port: 9121
    resources:
      limits:
        cpu: 100m
        memory: 128Mi
      requests:
        cpu: 50m
        memory: 64Mi
  
  nodeSelector: {}
  tolerations: []
  affinity: {}

# =============================================================================
# Common Configuration
# =============================================================================

# -- Pod security context
podSecurityContext:
  fsGroup: 1000

# -- Container security context
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL
  readOnlyRootFilesystem: true

# -- Service account configuration
serviceAccount:
  create: true
  annotations: {}
  name: ""

# -- Pod annotations
podAnnotations: {}

# =============================================================================
# KEDA Autoscaling (Optional)
# =============================================================================
# Requires KEDA to be installed in the cluster (https://keda.sh)
# When enabled, KEDA ScaledObjects replace standard HPAs for message-driven scaling
# based on RabbitMQ queue depth rather than CPU/memory metrics.

keda:
  # -- Enable KEDA-based autoscaling
  enabled: false
  # -- WorkflowEngine scaling based on results queue depth
  workflowEngine:
    # -- Queue to monitor for scaling decisions
    queueName: "workflowengine.results"
    # -- Target queue length per replica (scale up when exceeded)
    queueLength: 50
    # -- Seconds to wait before scaling down after queue drains
    cooldownPeriod: 60
    # -- How often KEDA checks queue depth
    pollingInterval: 15
  # -- Activity worker scaling based on execute queue depth
  activities:
    # -- Target queue length per replica
    queueLength: 20
    # -- Seconds to wait before scaling down
    cooldownPeriod: 60
    # -- How often KEDA checks queue depth
    pollingInterval: 15

# =============================================================================
# Network Policies (SOC2/NIS2 Compliance)
# =============================================================================
# Network policies enforce zero-trust network security by restricting
# pod-to-pod communication to only what is explicitly allowed.

networkPolicy:
  # -- Enable network policies for all components
  enabled: false
  
  # -- Default policy for pods without specific rules
  defaultDenyIngress: true
  defaultDenyEgress: false
  
  # -- Allow DNS egress for all pods (required for service discovery)
  allowDnsEgress: true
  
  # -- Allow egress to all namespaces (permissive mode)
  # Enable this if cross-namespace egress to infrastructure services is being blocked.
  # This adds an additional egress rule allowing traffic to any pod in any namespace.
  # WARNING: This is less secure and should only be used for debugging or when 
  # namespace-based selectors don't work in your CNI.
  allowAllNamespacesEgress: false
  
  # -- Allow all egress (fully permissive egress)
  # When true, adds a rule that allows all egress traffic (equivalent to "- {}")
  # Use this if specific egress rules are not working with your CNI.
  # This still provides ingress control while allowing unrestricted outbound traffic.
  allowAllEgress: false
  
  # -- Control egress in network policies
  # When false, network policies only control ingress (egress is unrestricted)
  # When true, network policies also control egress with explicit allow rules
  # Set to false if egress rules are blocking infrastructure connections
  controlEgress: true
  
  # -- Additional ingress rules for all application pods
  additionalIngressRules: []
  # - from:
  #   - namespaceSelector:
  #       matchLabels:
  #         name: monitoring
  #   ports:
  #   - protocol: TCP
  #     port: 9090
  
  # -- Additional egress rules for all application pods
  additionalEgressRules: []
  # - to:
  #   - ipBlock:
  #       cidr: 10.0.0.0/8
  #   ports:
  #   - protocol: TCP
  #     port: 443
  
  # -- Infrastructure-specific network policies
  infrastructure:
    # -- PostgreSQL network policy
    postgresql:
      enabled: true
      # -- Allow ingress only from application pods and replicas
      allowedNamespaces: []
      # -- Additional allowed pod selectors
      additionalAllowedPods: []
    
    # -- RabbitMQ network policy
    rabbitmq:
      enabled: true
      allowedNamespaces: []
      additionalAllowedPods: []
    
    # -- Redis network policy
    redis:
      enabled: true
      allowedNamespaces: []
      additionalAllowedPods: []

    # -- Keycloak network policy
    keycloak:
      enabled: true

# =============================================================================
# Backup Configuration (SOC2/NIS2 Compliance)
# =============================================================================
# Automated backups for disaster recovery and compliance requirements.
# Supports local PVC storage, S3-compatible storage, and Azure Blob Storage.

backup:
  # -- Enable backup functionality
  enabled: false
  
  # -- Global backup settings
  global:
    # -- Backup storage type: pvc, s3, minio, azure, gcs
    # Note: For clusters without RWX volume support, use 's3' or 'minio'
    storageType: "minio"
    
    # -- PVC storage configuration (requires RWX volumes for concurrent backup jobs)
    # Not recommended for clusters without RWX support
    pvc:
      storageClass: ""
      size: 50Gi
      accessModes:
        - ReadWriteOnce
    
    # -- S3-compatible storage configuration (AWS S3, MinIO, etc.)
    # Recommended for clusters without RWX volume support
    s3:
      # -- S3 endpoint URL (for MinIO, use internal service URL)
      # Example for MinIO in cluster: http://minio.minio-system.svc.cluster.local:9000
      # Example for AWS S3: https://s3.amazonaws.com
      endpoint: "http://minio.minio-system.svc.cluster.local:9000"
      # -- Bucket name for backups
      bucket: "flow-backups"
      # -- AWS region (only needed for AWS S3)
      region: "us-east-1"
      # -- Access key ID (ignored if existingSecret is set)
      accessKeyId: ""
      # -- Secret access key (ignored if existingSecret is set)
      secretAccessKey: ""
      # -- Use existing secret containing S3 credentials
      existingSecret: ""
      # -- Keys in the existing secret
      secretKeys:
        accessKeyId: "access-key-id"
        secretAccessKey: "secret-access-key"
    
    # -- Azure Blob Storage configuration
    azure:
      storageAccount: ""
      container: ""
      sasToken: ""
      existingSecret: ""
      secretKeys:
        sasToken: "sas-token"
    
    # -- Google Cloud Storage configuration
    gcs:
      bucket: ""
      projectId: ""
      serviceAccountKey: ""
      existingSecret: ""
      secretKeys:
        serviceAccountKey: "service-account-key"
    
    # -- Retention policy
    retention:
      # -- Number of daily backups to keep
      daily: 7
      # -- Number of weekly backups to keep
      weekly: 4
      # -- Number of monthly backups to keep
      monthly: 12
    
    # -- Encryption settings
    encryption:
      # -- Enable backup encryption
      enabled: false
      # -- Encryption key (base64 encoded, 32 bytes for AES-256)
      key: ""
      # -- Use existing secret for encryption key
      existingSecret: ""
      existingSecretKey: "encryption-key"
  
  # -- PostgreSQL backup configuration
  postgresql:
    enabled: true
    
    # -- Backup schedule (cron format)
    schedule: "0 2 * * *"  # Daily at 2 AM
    
    # -- Backup type: full, incremental (requires pgBackRest or similar)
    type: "full"
    
    # -- Number of backups to retain
    retentionCount: 7
    
    # -- Backup image
    image:
      repository: postgres
      tag: "16-alpine"
    
    # -- Resources for backup job
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 100m
        memory: 256Mi
    
    # -- Additional pg_dump options
    pgDumpOptions: "--format=custom --compress=9"
    
    # -- Pre-backup script (runs before backup)
    preBackupScript: ""
    
    # -- Post-backup script (runs after successful backup)
    postBackupScript: ""
    
    # -- Backup verification
    verification:
      enabled: true
      # -- Restore to temporary database and run integrity checks
      restoreTest: false
    
    # -- Point-in-time recovery (PITR) configuration
    pitr:
      enabled: false
      # -- WAL archiving destination
      walArchiveCommand: ""
      # -- WAL archive storage (same as backup storage if not specified)
      walStorageType: ""
  
  # -- RabbitMQ backup configuration
  rabbitmq:
    enabled: true
    
    # -- Backup schedule (cron format)
    schedule: "0 3 * * *"  # Daily at 3 AM
    
    # -- What to backup: definitions, messages, both
    backupType: "definitions"
    
    # -- Number of backups to retain
    retentionCount: 7
    
    # -- Backup image
    image:
      repository: curlimages/curl
      tag: "8.5.0"
    
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 50m
        memory: 128Mi
  
  # -- Redis backup configuration
  redis:
    enabled: true
    
    # -- Backup schedule (cron format)
    schedule: "0 4 * * *"  # Daily at 4 AM
    
    # -- Number of backups to retain
    retentionCount: 7
    
    # -- Backup image
    image:
      repository: redis
      tag: "7-alpine"
    
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 50m
        memory: 128Mi
    
    # -- Use BGSAVE before backup (recommended for data consistency)
    useBgsave: true

# =============================================================================
# Audit Logging (SOC2/NIS2 Compliance)
# =============================================================================

auditLogging:
  # -- Enable enhanced audit logging
  enabled: false
  
  # -- Log all API requests
  logApiRequests: true
  
  # -- Log authentication events
  logAuthEvents: true
  
  # -- Log data access events
  logDataAccess: true
  
  # -- Log administrative actions
  logAdminActions: true
  
  # -- Audit log retention days
  retentionDays: 365
  
  # -- Export audit logs to external system
  export:
    enabled: false
    # -- Export type: syslog, splunk, elasticsearch, azure-sentinel
    type: "syslog"
    # -- Export endpoint
    endpoint: ""
    # -- Use TLS for export
    tls: true

# =============================================================================
# TLS Configuration (SOC2/NIS2 Compliance)
# =============================================================================
# Internal mTLS between microservices using cert-manager with self-signed CA.
# Certificates are valid for services within the configured namespace(s).

tls:
  # -- Enable TLS for internal service communication
  enabled: false
  
  # -- Namespaces for which certificates should be valid
  # By default uses the installation namespace (from global.namespace or --namespace flag)
  # Add additional namespaces if services need to communicate across namespaces
  namespaces: []
  # Example:
  # - "flow"
  # - "flow-activities"
  # - "flow-infrastructure"
  
  # -- Use cert-manager for automatic certificate management
  # Requires cert-manager to be installed in the cluster
  # See: https://cert-manager.io/docs/installation/
  certManager:
    # -- Enable cert-manager integration
    enabled: true
    
    # -- Create a self-signed ClusterIssuer for internal certificates
    # If false, you must provide an existing issuer via issuerRef
    createSelfSignedIssuer: true
    
    # -- Name of the self-signed CA issuer (created by this chart)
    selfSignedIssuerName: "{{ .Release.Name }}-selfsigned-issuer"
    
    # -- Name of the CA certificate (created by this chart)
    caCertificateName: "{{ .Release.Name }}-internal-ca"
    
    # -- Name of the CA issuer that signs service certificates
    caIssuerName: "{{ .Release.Name }}-ca-issuer"
    
    # -- Reference to an existing issuer (used when createSelfSignedIssuer=false)
    issuerRef:
      # -- Name of the existing issuer
      name: ""
      # -- Kind: Issuer or ClusterIssuer
      kind: "ClusterIssuer"
      # -- Group (usually cert-manager.io)
      group: "cert-manager.io"
    
    # -- Certificate duration (default: 90 days)
    duration: "2160h"  # 90 days
    
    # -- Certificate renewal before expiry (default: 30 days before)
    renewBefore: "720h"  # 30 days
    
    # -- Private key algorithm: RSA, ECDSA, Ed25519
    privateKey:
      algorithm: "ECDSA"
      size: 256  # For ECDSA: 256, 384, or 521. For RSA: 2048, 4096
    
    # -- CA certificate settings
    ca:
      # -- CA certificate duration (default: 10 years)
      duration: "87600h"  # 10 years
      # -- CA renewal before expiry
      renewBefore: "8760h"  # 1 year
      # -- CA common name
      commonName: "Flow Internal CA"
      # -- CA organization
      organization: "Entit AB"
  
  # -- Mutual TLS (mTLS) settings
  # When enabled, services require client certificates for inter-service communication.
  # This works alongside existing OAuth 2.0 bearer token auth (defense in depth).
  mtls:
    # -- Require client certificates for inter-service communication
    enabled: false
    # -- Client certificate validation mode
    # RequireCertificate: connections without valid client certs are rejected
    # AllowCertificate: client certs are validated if present, but not required
    clientCertificateMode: "RequireCertificate"
    # -- Allow health check endpoints (/health, /health/ready) without client certificates
    # When true, Kestrel listens on port 8080 (HTTP) for health probes in addition to 8443 (HTTPS/mTLS)
    allowAnonymousHealthChecks: true

  # -- External client certificate settings
  # Allows services outside the cluster (Windows desktop app, Azure-hosted frontend)
  # to authenticate via mTLS when connecting to backend services
  externalClients:
    # -- Enable external client certificate management
    enabled: false
    # -- Additional trusted CA certificates for validating external client certificates
    # If external clients use certificates signed by a different CA, add those CAs here
    additionalCaCerts: []
    # -- Secret name containing additional CA certificates (alternative to inline certs)
    additionalCaSecret: ""
    # -- External client certificate definitions
    # Each entry creates a cert-manager Certificate resource signed by the internal CA
    clients: []
    # Example:
    # - name: "desktop-client"
    #   commonName: "flow-desktop-client"
    #   duration: "8760h"     # 1 year
    #   renewBefore: "720h"   # 30 days
    #   organization: "Entit AB"
    # - name: "azure-frontend"
    #   commonName: "flow-azure-frontend"
    #   duration: "8760h"
    #   organization: "Entit AB"

  # -- Use existing TLS secret (alternative to cert-manager)
  # This secret must contain tls.crt, tls.key, and ca.crt
  existingSecret: ""

  # -- Minimum TLS version (1.2 or 1.3)
  minVersion: "1.2"

  # -- Cipher suites (leave empty for secure defaults)
  # Recommended for TLS 1.2: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GLM_SHA256
  cipherSuites: []

  # -- Additional DNS names to include in all certificates
  additionalDnsNames: []
  
  # -- Service-specific certificate overrides
  # By default, each service gets a certificate with DNS names for all configured namespaces:
  #   - <service-name>
  #   - <release>-<service-name>.<namespace>.svc.cluster.local (for each namespace)
  services:
    workflowEngine:
      # -- Additional DNS names for this service's certificate
      additionalDnsNames: []
    aiAssistant:
      additionalDnsNames: []
    activityRegistry:
      additionalDnsNames: []
    definitionStore:
      additionalDnsNames: []
    workflowLogging:
      additionalDnsNames: []
    connectionStore:
      additionalDnsNames: []
    tenantRegistry:
      additionalDnsNames: []
    frontendWeb:
      additionalDnsNames: []
    billingService:
      additionalDnsNames: []
    mcpServer:
      additionalDnsNames: []
    configurationStore:
      additionalDnsNames: []

# =============================================================================
# Monitoring (Prometheus Operator + Grafana)
# =============================================================================

# -- Prometheus monitoring integration
# Requires Prometheus Operator (kube-prometheus-stack) installed in the cluster
monitoring:
  # -- Create PrometheusRule for alerting
  prometheusRules:
    enabled: false
    # -- Additional labels for PrometheusRule (for rule selection in Prometheus)
    additionalLabels: {}
  # -- Create ServiceMonitor for auto-discovery
  serviceMonitor:
    enabled: false
    # -- Scrape interval
    interval: "30s"
    # -- Additional labels for ServiceMonitor
    additionalLabels: {}
  # -- Deploy Grafana dashboards as ConfigMaps
  grafanaDashboards:
    enabled: false
    # -- Additional labels for dashboard ConfigMaps
    additionalLabels: {}

# =============================================================================
# External Secrets Operator
# =============================================================================

# -- External Secrets Operator integration for secrets management
# Requires ESO (external-secrets.io) installed in the cluster
externalSecrets:
  # -- Enable ExternalSecret resources
  enabled: false
  # -- SecretStore or ClusterSecretStore name
  secretStore: ""
  # -- Kind of secret store: SecretStore or ClusterSecretStore
  secretStoreKind: "ClusterSecretStore"
  # -- How often to sync secrets
  refreshInterval: "1h"
  # -- PostgreSQL password reference
  postgres:
    key: "flow/database"
    property: "password"
  # -- RabbitMQ password reference
  rabbitmq:
    key: "flow/rabbitmq"
    property: "password"
  # -- Redis password reference
  redis:
    key: "flow/redis"
    property: "password"
  # -- Azure AD client secret reference
  azureAd:
    key: "flow/azure-ad"
    property: "client-secret"
