spring: application: name: ordinis-app profiles: active: ${SPRING_PROFILES_ACTIVE:dev} server: port: 8080 shutdown: graceful management: endpoints: web: exposure: # CSO finding #3 — defense-in-depth. Default exposure narrowed к трём # безопасным: health (k8s probes), info (UI banner), prometheus # (ServiceMonitor scrape). Sensitive endpoints (env, configprops, # beans, mappings, conditions, loggers) выключены by default — # утечка ORDINIS_KEYCLOAK_ADMIN_CLIENT_SECRET через /actuator/env # потенциальна на любой pod-to-pod атаке. # # Diagnostic четвёрка (conditions/beans/configprops/loggers) + # debug-set (env/mappings/refresh/metrics) включаются через env # override `MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE` на staging # — для autoconfig debug (например: почему # MailSenderAutoConfiguration не fired при наличии SPRING_MAIL_HOST # env var). На prod env override не ставим → defense вместе с # SecurityConfig denyAll на /actuator/{env,beans,...}. include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,prometheus} base-path: /actuator endpoint: health: probes: enabled: true show-details: when-authorized group: liveness: include: livenessState readiness: include: readinessState,db health: # Redis health indicator отключён в writer pod. Writer не использует Redis # напрямую (нет @Cacheable, нет RedisTemplate), но depends на # ordinis-notifications module который включает spring-boot-starter-data-redis # transitive'но (для будущего @EnableCaching). Spring autoconfig пытается # connect к localhost:6379 → fail → /actuator/health = DOWN, забивает # readiness alerts. # Если writer когда-либо понадобится реальный Redis — wire env vars # (см. projection-writer.yaml образец) и убрать этот override. redis: enabled: false prometheus: metrics: export: enabled: true metrics: tags: application: ${spring.application.name} environment: ${ENVIRONMENT:dev} distribution: # Server-side histogram buckets для http.server.requests — нужны # histogram_quantile() в Grafana p50/p95/p99 dashboards. percentiles-histogram: http.server.requests: true slo: http.server.requests: 5ms,10ms,25ms,50ms,100ms,250ms,500ms,1s,2s,5s otel: exporter: otlp: endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://tempo.monitoring:4318} protocol: http/protobuf # Tempo (single-process на staging) принимает только traces. Если включить # экспорт metrics/logs — каждую секунду 404 в логах ("Failed to export logs"). # Когда поднимем полный observability stack (Tempo + Mimir + Loki) — снимем # эти overrides либо переопределим через ENV. metrics: exporter: ${OTEL_METRICS_EXPORTER:none} logs: exporter: ${OTEL_LOGS_EXPORTER:none} resource: attributes: service.name: ${spring.application.name} service.namespace: ordinis deployment.environment: ${ENVIRONMENT:dev} traces: sampler: parentbased_traceidratio sampler.arg: ${ORDINIS_TRACING_SAMPLING:0.1} logging: level: root: INFO cloud.nstart.terravault.ordinis: DEBUG ordinis: outbox: enabled: ${ORDINIS_OUTBOX_ENABLED:true} poll-interval-ms: ${ORDINIS_OUTBOX_POLL_INTERVAL_MS:1000} batch-size: ${ORDINIS_OUTBOX_BATCH_SIZE:100} # Cap на ретраи: после N попыток событие → DLQ (dlq_at), poller его пропускает. # 1000 при poll=1s ≈ ~17 минут активных ошибок (или быстрее на fail-fast топиках). attempt-max: ${ORDINIS_OUTBOX_ATTEMPT_MAX:1000} springdoc: # Per env policy: # prod → swagger UI disabled (real-user environment, не для exploration); # api-docs JSON остаётся для SDK generation / machine consumers # staging/dev → оба enabled (interactive playground для интеграторов) # Controlled via ORDINIS_SWAGGER_UI_ENABLED env var (helm values-prod.yaml: # springdoc.swaggerUi=false). Default true для backward compat. api-docs: path: /v3/api-docs enabled: ${ORDINIS_SWAGGER_API_DOCS_ENABLED:true} swagger-ui: path: /swagger-ui.html enabled: ${ORDINIS_SWAGGER_UI_ENABLED:true} operations-sorter: method tags-sorter: alpha display-request-duration: true # OAuth2 / Keycloak interactive login (PKCE flow). См. OpenApiConfig. # Public client `ordinis-swagger` создаётся в Keycloak realm `nstart` с # redirect URI = https:///swagger-ui/oauth2-redirect.html oauth: client-id: ${ORDINIS_SWAGGER_CLIENT_ID:ordinis-swagger} use-pkce-with-authorization-code-grant: true use-basic-authentication-with-access-code-grant: false scope-separator: " " show-actuator: false packages-to-scan: cloud.nstart.terravault.ordinis.restapi.web --- # DEV profile: подключается к cluster PG+Kafka через kubectl port-forward. # Перед запуском: kubectl port-forward -n ordinis-staging svc/ordinis-postgres-rw 5432:5432 & # kubectl port-forward -n ordinis-staging svc/ordinis-kafka-kafka-bootstrap 9092:9092 & spring: config: activate: on-profile: dev cloud: config: enabled: false vault: enabled: false autoconfigure: exclude: - org.springframework.cloud.vault.config.VaultAutoConfiguration - org.springframework.cloud.vault.config.VaultObservationAutoConfiguration - org.springframework.cloud.vault.config.VaultReactiveAutoConfiguration datasource: url: jdbc:postgresql://${ORDINIS_PG_HOST:localhost}:${ORDINIS_PG_PORT:5432}/${ORDINIS_PG_DB:ordinis} username: ${ORDINIS_PG_USER:ordinis_app} password: ${ORDINIS_PG_PASSWORD:} driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect hibernate.format_sql: true hibernate.jdbc.lob.non_contextual_creation: true open-in-view: false kafka: bootstrap-servers: ${ORDINIS_KAFKA_BOOTSTRAP:localhost:9092} properties: security.protocol: ${ORDINIS_KAFKA_SECURITY:SASL_PLAINTEXT} sasl.mechanism: ${ORDINIS_KAFKA_SASL_MECHANISM:SCRAM-SHA-512} sasl.jaas.config: >- org.apache.kafka.common.security.scram.ScramLoginModule required username="${ORDINIS_KAFKA_USER:ordinis-app}" password="${ORDINIS_KAFKA_PASSWORD:}"; producer: acks: all key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer properties: enable.idempotence: true compression.type: lz4 otel: sdk: disabled: true --- # K8s profile: реальная инфра в кластере. Cloud Config + Vault Kubernetes auth. spring: config: activate: on-profile: k8s import: - "configserver:${SPRING_CLOUD_CONFIG_URI:http://config-server.ordinis-staging:8888}" - "vault://" cloud: config: enabled: true vault: enabled: true uri: ${VAULT_URI:http://vault.config:8200} authentication: KUBERNETES kubernetes: role: ordinis-app service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token kv: enabled: true backend: secret application-name: ordinis/cuod datasource: url: jdbc:postgresql://${ORDINIS_PG_HOST}:${ORDINIS_PG_PORT:5432}/${ORDINIS_PG_DB:ordinis} username: ${ORDINIS_PG_USER} password: ${ORDINIS_PG_PASSWORD} driver-class-name: org.postgresql.Driver kafka: bootstrap-servers: ${ORDINIS_KAFKA_BOOTSTRAP} properties: security.protocol: ${ORDINIS_KAFKA_SECURITY:SASL_PLAINTEXT} sasl.mechanism: ${ORDINIS_KAFKA_SASL_MECHANISM:SCRAM-SHA-512} sasl.jaas.config: >- org.apache.kafka.common.security.scram.ScramLoginModule required username="${ORDINIS_KAFKA_USER}" password="${ORDINIS_KAFKA_PASSWORD}"; producer: acks: all key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer properties: enable.idempotence: true compression.type: lz4 jpa: hibernate: ddl-auto: none open-in-view: false # Auth (см. ordinis-auth: SecurityConfig). public-key из Vault. ordinis: auth: public-key: ${key:} issuer: ${jwt-issuer:} require-authentication: ${ORDINIS_AUTH_REQUIRED:false} allow-query-scope: ${ORDINIS_AUTH_ALLOW_QUERY_SCOPE:true} # AI Schema Assist (Phase 1 step 3-6). Off by default — controllers/services # bean-conditionally registered, frontend hides AI button when 404. # Enable per-env через helm values (см. charts/ordinis/values-staging.yaml). # endpoint: OpenAI-compatible /v1/chat/completions root (без /v1 suffix — # adapter добавит сам). bearer-token optional (Ollama не требует, OpenAI да). ai: enabled: ${ORDINIS_AI_ENABLED:false} endpoint: ${ORDINIS_AI_ENDPOINT:} model: ${ORDINIS_AI_MODEL:qwen2.5:7b} bearer-token: ${ORDINIS_AI_BEARER_TOKEN:} timeout-seconds: ${ORDINIS_AI_TIMEOUT_SECONDS:15} # Keycloak Admin REST integration for UserDisplayService Phase 2 — on-demand # sub→user lookup when JWT capture cache + DB cache both miss. See # KeycloakAdminUserResolver.java + 0023-user-display-cache.xml migration. # Disabled by default; flip on per-environment when the service-account # client + vault secret are provisioned. keycloak: admin: enabled: ${ORDINIS_KEYCLOAK_ADMIN_ENABLED:false} url: ${ORDINIS_KEYCLOAK_ADMIN_URL:} realm: ${ORDINIS_KEYCLOAK_ADMIN_REALM:nstart} client-id: ${ORDINIS_KEYCLOAK_ADMIN_CLIENT_ID:} client-secret: ${ORDINIS_KEYCLOAK_ADMIN_CLIENT_SECRET:} # Notifications module — push email for draft lifecycle events. # Activated via ordinis.notifications.enabled=true (writer pod only). # SMTP creds: spring.mail.* (standard Spring Boot mail properties). # See ordinis-notifications module + migration 0021/0024. notifications: enabled: ${ORDINIS_NOTIFICATIONS_ENABLED:true} poll-interval-ms: ${ORDINIS_NOTIFICATIONS_POLL_INTERVAL_MS:30000} batch-size: ${ORDINIS_NOTIFICATIONS_BATCH_SIZE:50} # Reviewer pool email — receives RecordDraftSubmitted / RecordDraftWithdrawn pings. # Leave blank to disable reviewer-pool notifications (maker-only notifications still work). reviewer-pool-email: ${ORDINIS_NOTIFICATIONS_REVIEWER_POOL_EMAIL:} email: from-address: ${ORDINIS_NOTIFICATIONS_FROM:} base-url: ${ORDINIS_NOTIFICATIONS_BASE_URL:}