30 KiB
arch-04. Настройки и изменяемые параметры
.env— только несекретная инфраструктурная конфигурация. Production-секреты доставляются отдельно поarch-06-service-hosting-security.md.app_settings(App DB) — единственный источник бизнес-настроек. Имена полей и enum —arch-00-glossary.md. Docker Compose —arch-03-docker-compose-blueprint.md.
Цель
Параметры разделены по слоям:
| Слой | Где | Что |
|---|---|---|
| Инфраструктура | .env |
несекретные host/port/URL, nginx/TLS, режимы и технические параметры |
| Production-секреты | Selectel Secrets Manager → /run/han-chat/secrets; для no-egress VM — root-owned files |
credential-bearing DSN, пароли, private keys, service/webhook tokens, provider credentials |
| Бизнес-логика | таблица app_settings |
лимиты, флаги, телефоны, типы файлов, CORS, consent URLs |
| Настройки SMS runtime | таблица sms.sms_setting |
sender default, provider timeouts, callback flag, worker intervals |
| Контент | text_resources, popular_questions |
тексты UI |
Managed PostgreSQL поднимается до развёртывания приложения. Бизнес-настройки не дублируются в .env: seed в app_settings выполняется миграцией/скриптом модуля database до первого запуска api-backend.
Источники настроек
.env — только несекретная инфраструктура
Корневой backend/.env читается сервисами compose. В репозитории — .env.example, не .env.
Допустимо в .env:
- URL сервисов, публичные endpoint, порты;
- host/port/database/schema без паролей и токенов;
- параметры nginx/TLS и edge rate limits (
NGINX_RATE_LIMIT_*); - идентификация Keycloak: realm, audience, public/internal URL;
- переключатели OTP mock и Yandex SmartCaptcha, а также публичный CAPTCHA client key; mock code и CAPTCHA server key являются секретами;
SECRETS_SOURCE=selectel|file, который выбирает утверждённый механизм доставки, но не содержит secret value;- технические параметры сервисов, пока профильная спецификация не определила service-owned settings; для
sms-serviceruntime-параметры уже вынесены вsms.sms_setting.
Запрещено в .env:
- credential-bearing DSN/URL;
- пароли БД, Keycloak bootstrap password и OTP mock/HMAC secrets;
- S3 access/secret keys;
- Bitrix OAuth secrets и application/webhook tokens;
- service tokens внутренних API;
- SMS API key/callback credentials и OTLP auth header;
- TLS private keys и любые иные credentials.
Они доставляются как Compose/systemd secret files по arch-06. Бизнес-параметры ниже хранятся только в app_settings:
- включение/отключение OTP, OTP-лимиты для UI/продукта;
- телефон оператора, consent URLs/versions;
- лимиты приложения (сообщения, download URL, login);
- типы/размер файлов чата, UX idle timeout;
- CORS origins, feature flags frontend.
app_settings — бизнес-настройки (App DB)
Единственный источник правды для параметров, которые:
- меняет продукт/оператор без redeploy;
- отдаются в
GET /api/v1/public/app-config(публичные ключи); - используются
api-backend(и при необходимости другими сервисами) в runtime.
Позже — редактирование через админку; на MVP — seed-миграция.
text_resources / popular_questions
Контент UI — отдельные таблицы (не app_settings). Обязательная safety-мнемоника MVP:
| mnemonic | Назначение |
|---|---|
safety.chat.blocked |
Generic company-реплика при любом Message Safety deny; текст locale-aware, без раскрытия rule_id |
Миграция/seed обязаны создать активную запись минимум для ru. Изменение text_value не требует redeploy Safety и не меняет API/error code.
Требования к таблице app_settings
Схема: han_app. Детальная DDL — модуль database; arch фиксирует контракт.
Колонки (минимум)
| Колонка | Тип | Назначение |
|---|---|---|
setting_key |
varchar, PK |
Канонический ключ (auth.phone.enabled, см. ниже) |
setting_value |
text, NOT NULL |
Значение (строка; парсинг по типу) |
value_type |
enum |
boolean | integer | string | duration | string_list |
is_public |
boolean |
Разрешён в GET /api/v1/public/app-config |
description |
text, nullable |
Комментарий для админки/ops |
updated_at |
timestamptz |
Последнее изменение |
record_status |
char(1) |
Soft delete: 'A' active |
Правила
- Seed обязателен до первого запуска
api-backendв новой среде (миграция или idempotent seed-скрипт). api-backendзагружает настройки при старте; допускается in-memory cache с инвалидацией поupdated_at(реализация — модуль).- Отсутствие обязательного ключа при старте → сервис не переходит в
ready(fail-fast). - Публичные ключи (
is_public=true) отдаются только через строгий DTOapp-config, не raw dump таблицы. - Секреты и infra не хранятся в
app_settings.
Ключи MVP (seed)
Полный пример значений — раздел «Seed MVP» ниже. Группы:
| Группа | Ключи |
|---|---|
| Auth | auth.phone.enabled, auth.password.enabled |
| OTP (продукт; потребитель — Keycloak SPI через settings bridge api-backend) | otp.phone.max_send_attempts_per_24h, otp.phone.min_seconds_between_attempts, otp.phone.max_verify_attempts, otp.phone.code_length, otp.phone.ttl_seconds, otp.phone.sms_order_timeout_ms |
| Оператор | operator.call.phone |
| Consent | consent.personal_data.*, consent.privacy_policy.document_url, consent.user_agreement.*, consent.marketing.* |
| Файлы чата | chat.attachments.* |
| Rate limits (app) | rate_limit.message_send.*, rate_limit.download_url.*, rate_limit.public_endpoints.*, rate_limit.login.* |
| Notification Center | notification.home.max_items, notification.center.max_items, notification.carousel.*, notification.hidden.default_ttl_days, notification.documents.max_files, notification.expire_job.run_at, notification.upload_draft.ttl_days |
| Rate limits (notifications) | rate_limit.notifications_read.per_user, rate_limit.notifications_action.per_user, rate_limit.notification_upload.per_user, rate_limit.notifications_public.per_ip |
| UX | ux.session.idle_timeout_minutes |
| Security | security.cors.allowed_origins, security.public_cache.max_age_seconds |
Seed MVP
auth.phone.enabled=true
auth.password.enabled=false
otp.phone.max_send_attempts_per_24h=3
otp.phone.min_seconds_between_attempts=30
otp.phone.max_verify_attempts=5
otp.phone.code_length=6
otp.phone.ttl_seconds=60
otp.phone.sms_order_timeout_ms=3000
operator.call.phone=+74999591007
consent.personal_data.required=true
consent.personal_data.document_url=https://www.han0107.ru/privacy/persdata-agree-mobile
consent.personal_data.version=2026-06-10
consent.privacy_policy.document_url=https://www.han0107.ru/privacy
consent.user_agreement.required=true
consent.user_agreement.document_url=https://www.han0107.ru/user-agreement
consent.user_agreement.version=2026-06-10
consent.marketing.required=false
consent.marketing.document_url=https://www.han0107.ru/privacy/ads-agree
consent.marketing.version=2026-06-10
chat.attachments.allowed_extensions=jpg,jpeg,png,webp,heic,heif,pdf
chat.attachments.allowed_mime_types=image/jpeg,image/png,image/webp,image/heic,image/heif,application/pdf
chat.attachments.disallowed_extensions=svg,doc,docx,xls,xlsx,csv
chat.attachments.max_size_mb=5
chat.attachments.storage=selectel_s3
chat.attachments.upload_mode=presigned_put
chat.attachments.safety_scan_required=true
chat.attachments.presigned_upload_ttl_seconds=600
messages.max_text_length=4000
rate_limit.message_send.per_user=30/minute
rate_limit.message_send.per_dialog=20/minute
rate_limit.download_url.per_user=60/hour
rate_limit.public_endpoints.per_ip=60/minute
rate_limit.login.per_ip=10/minute
rate_limit.notifications_read.per_user=120/minute
rate_limit.notifications_action.per_user=60/minute
rate_limit.notification_upload.per_user=20/minute
rate_limit.notifications_public.per_ip=60/minute
notification.home.max_items=7
notification.center.max_items=15
notification.carousel.autoplay_enabled=false
notification.carousel.autoplay_interval_ms=5000
notification.hidden.default_ttl_days=3
notification.documents.max_files=10
notification.expire_job.run_at=00:01
notification.upload_draft.ttl_days=7
ux.session.idle_timeout_minutes=30
security.cors.allowed_origins=https://tohin.ru
security.public_cache.max_age_seconds=3600
Service-owned настройки sms-service
Параметры, изменение которых не меняет Compose, секреты, URL или сетевую топологию, хранятся в sms.sms_setting, а не в .env.
Ключи и seed:
provider.idgtl.default_sender_name=<approved>
provider.idgtl.connect_timeout_ms=3000
provider.idgtl.request_timeout_ms=70000
provider.idgtl.callback_enabled=true
worker.poll_interval_ms=500
worker.lease_seconds=90
В .env остаются только несекретные URL внутренних/внешних сервисов и технические параметры. SMS_DATABASE_URL, service tokens, Direct API key и callback credentials входят в runtime secret catalog. Детальный контракт — module-11-idgtl-sms.md.
<approved> — обязательный deployment placeholder, а не допустимое production-значение. Перед real mode должны существовать active approved template auth_otp с точными placeholders code/ttl_min и согласованный senderName. Отсутствие template/sender делает readiness false.
Service-owned настройки message-safety
Runtime policy хранится в версионированной message_safety.config_versions, а не в .env и не в han_app.app_settings. Сюда входят task lease/deadline/attempts, internal rate/pending limits, retention/cache TTL, URL/DNS pipeline limits, ClamAV policy timeout/signature age и enabled file MIME/size policy. Для ClamAV schema допускает возраст сигнатур не более 720 часов (30 дней), seed max_signature_age_hours равен 240 часам (10 дней). Полный schema/seed/activation contract — module-05 §10.1 и §15.
han_app.app_settings:chat.attachments.* остаётся бизнес-настройкой api-backend. Message Safety не получает cross-schema read к han_app; файл допускается только при пересечении business allow-list, active safety policy и immutable detector manifest. Active policy может сузить manifest, но не добавить parser и не увеличить hard limit.
В env Message Safety остаются только bootstrap/topology/capacity (APP_ENV, worker concurrency, DNS resolver, ClamAV/S3/OTLP endpoints); credentials доставляются secret files. Rules/detector versions вычисляются/проверяются по immutable artifacts. Emergency MOCK остаётся в отдельном root-owned mode file и намеренно не переносится в БД.
Пример .env.example
Только несекретная инфраструктура и выбор secret source. Бизнес-параметры — в seed app_settings, а перечисленные ниже runtime secrets — в конфигурации han-secrets, не в этом файле.
# =============================================================================
# Общие
# =============================================================================
APP_ENV=production-like
SECRETS_SOURCE=selectel
API_PORT=8000
LOG_LEVEL=INFO
# =============================================================================
# Managed PostgreSQL
# =============================================================================
HAN_PG_HOST=<managed-pg-private-host>
HAN_PG_PORT=5433
HAN_PG_DATABASE=han_chat
KC_DB_URL_PROPERTIES=currentSchema=keycloak
# Runtime secrets: DATABASE_URL, BITRIX_DATABASE_URL,
# BITRIX_SYNC_APP_DATABASE_URL, BITRIX_SYNC_DATABASE_URL,
# MESSAGE_SAFETY_DATABASE_URL, SMS_DATABASE_URL, KEYCLOAK_DB_URL.
# Selectel PgBouncer 5433: pool_mode=session; search_path задаётся на уровне ролей.
# Не добавлять options=-csearch_path: pooler отклоняет этот startup parameter.
# =============================================================================
# Публичные URL (HTTPS)
# =============================================================================
PUBLIC_WEB_URL=https://tohin.ru
PUBLIC_API_URL=https://tohin.ru/api
PUBLIC_AUTH_URL=https://tohin.ru/auth
# =============================================================================
# nginx (edge, TLS, rate limits)
# =============================================================================
NGINX_HTTP_PORT=80
NGINX_HTTPS_PORT=443
TLS_CERT_PATH=/etc/nginx/certs/fullchain.pem
TLS_KEY_PATH=/etc/nginx/certs/privkey.pem
NGINX_TLS_PROTOCOLS=TLSv1.2 TLSv1.3
NGINX_HSTS_MAX_AGE=31536000
NGINX_CLIENT_MAX_BODY_SIZE=8m
NGINX_RATE_LIMIT_API=60r/m
NGINX_RATE_LIMIT_AUTH=10r/m
NGINX_RATE_LIMIT_DOWNLOADS=30r/m
NGINX_RATE_LIMIT_PUBLIC=60r/m
NGINX_RATE_LIMIT_POLLING=60r/m
NGINX_RATE_LIMIT_NOTIFICATIONS_READ=120r/m
NGINX_RATE_LIMIT_NOTIFICATIONS_ACTION=60r/m
NGINX_RATE_LIMIT_NOTIFICATION_UPLOAD=20r/m
NGINX_RATE_LIMIT_NOTIFICATIONS_PUBLIC=60r/m
# =============================================================================
# Keycloak (mock остаётся true до controlled SMS cutover)
# =============================================================================
KEYCLOAK_PUBLIC_URL=https://tohin.ru/auth
KEYCLOAK_INTERNAL_URL=http://keycloak:8080
KEYCLOAK_REALM=han-chat
KEYCLOAK_AUDIENCE=han-chat-api
KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_YANDEX_CAPTCHA_ENABLED=false
KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY=
KEYCLOAK_SMS_SERVICE_URL=http://sms-service:8080
# Runtime secrets: KEYCLOAK_OTP_MOCK_CODE,
# KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY and OTP HMAC/bootstrap credentials.
# =============================================================================
# Redis ВМ1 (I4: раздельные DB index)
# =============================================================================
# /0 — api-backend: rate limits, idempotency
# /1 — api-backend realtime/coordination (опционально; можно совместить с /0)
REDIS_URL=redis://redis:6379/0
REDIS_REALTIME_URL=redis://redis:6379/1
# =============================================================================
# Service tokens (internal API) — runtime secret catalog, не .env
# =============================================================================
# MESSAGE_SAFETY_SERVICE_TOKEN, BITRIX_LOCAL_APP_INTERNAL_TOKEN,
# BITRIX_API_INBOX_TOKEN, BITRIX_INTERNAL_API_TOKEN,
# BITRIX_API_FORWARD_TOKEN, BITRIX_SYNC_SERVICE_TOKEN,
# KEYCLOAK_SETTINGS_BRIDGE_TOKEN, SMS_SERVICE_TOKEN,
# KEYCLOAK_SMS_SERVICE_TOKEN, NOTIFICATIONS_TOKEN_PRODUCER_TEST.
# =============================================================================
# SMS provider (секреты — runtime secret catalog; параметры — sms.sms_setting)
# =============================================================================
IDGTL_SMS_BASE_URL=https://direct.i-dgtl.ru
IDGTL_SMS_CALLBACK_PUBLIC_URL=https://tohin.ru/callbacks/idgtl/sms
# Runtime secrets: IDGTL_SMS_API_KEY, IDGTL_SMS_CALLBACK_USERNAME,
# IDGTL_SMS_CALLBACK_PASSWORD.
# =============================================================================
# api-backend (интеграции + resilience I2)
# =============================================================================
BITRIX_LOCAL_APP_BASE_URL=http://bitrix-local-app:8080
BITRIX_API_INBOX_PATH=/internal/openlines/v1/inbox
MESSAGE_SAFETY_URL=https://processing.internal:8443
MESSAGE_SAFETY_CA_FILE=/run/han-chat/secrets/processing-internal-ca.crt
MESSAGE_SAFETY_API_PREFIX=/internal/safety/v2
MESSAGE_SAFETY_CIRCUIT_FAILURE_THRESHOLD=5
MESSAGE_SAFETY_CIRCUIT_OPEN_SEC=30
BITRIX_LOCAL_APP_CIRCUIT_FAILURE_THRESHOLD=5
BITRIX_LOCAL_APP_CIRCUIT_OPEN_SEC=30
BITRIX_LOCAL_APP_HTTP_TIMEOUT_SEC=10
# =============================================================================
# bitrix-sync
# =============================================================================
BITRIX_SYNC_ENABLED=true
BITRIX_SYNC_MODE=full
BITRIX_SYNC_PORTAL_HOST=han0107.bitrix24.ru
BITRIX_SYNC_PORTAL_MEMBER_ID=<approved-member-id>
BITRIX_SYNC_PUBLIC_BASE_URL=https://processing.example.ru
BITRIX_WEBHOOK_ALLOWED_CIDRS=<comma-separated-cidrs>
BITRIX_SYNC_CONTACT_USER_ID_FIELD=UF_CRM_...
BITRIX_SYNC_CONTACT_REGISTERED_FIELD=UF_CRM_1778692456
BITRIX_SYNC_CONTACT_CITIZENSHIP_FIELD=UF_CRM_1768493029
BITRIX_SYNC_HTTP_TIMEOUT_SEC=10
BITRIX_SYNC_DB_POOL_SIZE=5
# Hot worker/rate/retry/reconciliation/alert parameters:
# versioned bitrix_sync.settings, не env.
# Runtime secrets: BITRIX_SYNC_DATABASE_URL,
# BITRIX_SYNC_CRM_REST_WEBHOOK_URL,
# BITRIX_SYNC_CONTACT_RECEIVER_TOKEN,
# BITRIX_SYNC_ALERT_RECEIVER_TOKEN,
# BITRIX_SYNC_SERVICE_TOKEN.
# =============================================================================
# bitrix-local-app
# =============================================================================
BITRIX_CONNECTOR_ID=han_mobile_app
BITRIX_CONNECTOR_NAME=HAN Mobile App
BITRIX_OPEN_LINE_ID=8
BITRIX_PUBLIC_BASE_URL=https://tohin.ru/bitrix
BITRIX_API_FORWARD_URL=http://api-backend:8000/internal/openlines/v1/inbox
# Runtime secrets: BITRIX_CLIENT_ID, BITRIX_CLIENT_SECRET,
# BITRIX_APPLICATION_TOKEN and token encryption key.
# =============================================================================
# api-backend → Message Safety (ВМ1 caller)
# =============================================================================
MESSAGE_SAFETY_POST_TIMEOUT_SEC=5
MESSAGE_SAFETY_TASK_POLL_INTERVAL_SEC=2
MESSAGE_SAFETY_TASK_POLL_MAX_SEC=300
QUARANTINE_ORPHAN_RETENTION_HOURS=48
HAN_APP_SAFETY_CHECKPOINT_RETENTION_DAYS=7
HAN_APP_SAFETY_RECOVERY_MAX_SEC=1200
# =============================================================================
# message-safety (ВМ2 bootstrap/topology/capacity)
# =============================================================================
# Service runtime policy находится в message_safety.config_versions.
# Runtime secrets: MESSAGE_SAFETY_DATABASE_URL,
# MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<secret>@redis:6379/0,
# MESSAGE_SAFETY_SERVICE_TOKEN, S3 quarantine read credentials.
# Emergency mode находится только в root-owned
# /etc/han-chat/message-safety-mode.env и меняется approved helper-ом.
MESSAGE_SAFETY_WORKER_CONCURRENCY=5
MESSAGE_SAFETY_DNS_RESOLVERS=<VPC-resolver-IP>
MESSAGE_SAFETY_CLAMAV_HOST=clamd
MESSAGE_SAFETY_CLAMAV_PORT=3310
# =============================================================================
# Frontend (nginx)
# =============================================================================
FRONTEND_STATIC_PATH=/usr/share/nginx/html
FRONTEND_DEV_PROXY_ENABLED=false
EXPO_DEV_SERVER_URL=http://host.docker.internal:8081
# =============================================================================
# Selectel S3
# =============================================================================
SELECTEL_S3_ENDPOINT_URL=https://s3.storage.selcloud.ru
SELECTEL_S3_BUCKET_DOCUMENTS=han-chat-documents
SELECTEL_S3_BUCKET_ATTACHMENTS=han-chat-attachments
SELECTEL_S3_BUCKET_QUARANTINE=han-chat-quarantine
# Runtime secrets: SELECTEL_S3_ACCESS_KEY, SELECTEL_S3_SECRET_KEY,
# SELECTEL_S3_QUARANTINE_READ_ACCESS_KEY,
# SELECTEL_S3_QUARANTINE_READ_SECRET_KEY.
# =============================================================================
# Observability
# =============================================================================
# На каждой VM это local Docker DNS; ВМ2 не указывает collector ВМ1.
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_SERVICE_NAME_API=api-backend
OTEL_SERVICE_NAME_SMS_API=sms-service
OTEL_SERVICE_NAME_SMS_WORKER=sms-worker
OTEL_TRACES_SAMPLER=always_on
SMS_METRICS_PORT=9464
OTEL_REMOTE_ENDPOINT=192.168.0.5:4317
OTEL_REMOTE_TLS_INSECURE=true
OTEL_QUEUE_SIZE=10000
# Runtime secret when configured: OTEL_REMOTE_AUTH_HEADER.
S3-клиенты используют только virtual-hosted addressing
(https://<bucket>.s3.storage.selcloud.ru/<object-key>). Это часть контракта
presigned URL и CORS Selectel; path-style адресация не поддерживается приложением.
Production использует отдельные allow-listed env manifests ВМ1 и ВМ2; не все переменные примера копируются на оба хоста. На ВМ2 han-secrets под отдельным IAM principal материализует отдельный root-owned файл каждому сервису в /run/han-chat/secrets; общий bundle ВМ1/ВМ2 запрещён.
Для production placeholders и примерные sender/template/credentials отклоняются validate-env и runtime manifest validation. IDGTL_SMS_API_KEY в secret catalog — выданный Direct готовый TOKEN_1 для Basic, без повторного Base64. Реальный статический egress IP хранится в deployment inventory, а не env; если он не обеспечен NAT/сетевой конфигурацией, KEYCLOAK_OTP_MOCK_ENABLED=false запрещён.
Webhook-токены: BITRIX_APPLICATION_TOKEN относится только к bitrix-local-app. CRM sync не использует local app/event handler; штатные HTTP-webhook роботы передают отдельные BITRIX_SYNC_CONTACT_RECEIVER_TOKEN и BITRIX_SYNC_ALERT_RECEIVER_TOKEN в query, поскольку custom Bearer header недоступен. Токены остаются secret-manager values, query исключается из logs/traces, а nginx дополнительно применяет BITRIX_WEBHOOK_ALLOWED_CIDRS. BITRIX_SYNC_CRM_REST_WEBHOOK_URL — отдельный секрет исходящего CRM REST-доступа sync-service.
NOTIFICATIONS_TOKEN_<SOURCE> — индивидуальный секрет продюсера Internal Notifications API. Для seed/smoke используется NOTIFICATIONS_TOKEN_PRODUCER_TEST; secret хранится только в secret store/runtime secret file, а notification_sources.token_hash — только hash. Инструкция не имеет notification.instruction.allowed_hosts: она всегда открывается в новой вкладке, iframe-режима нет.
Namespace переменных Bitrix
bitrix-local-app:BITRIX_CLIENT_*,BITRIX_CONNECTOR_*,BITRIX_PUBLIC_BASE_URL,BITRIX_DATABASE_URL,BITRIX_API_FORWARD_URL,BITRIX_APPLICATION_TOKEN+ service tokens.api-backend:BITRIX_LOCAL_APP_BASE_URL,MESSAGE_SAFETY_URL, circuit/timeout vars, Redis/0//1+ service tokens; бизнес-настройки — изapp_settings. OTP counters не ведёт.bitrix-sync: non-secretBITRIX_SYNC_ENABLED/MODE/PORTAL_HOST/PORTAL_MEMBER_ID,BITRIX_WEBHOOK_ALLOWED_CIDRS, custom field names и capacity bootstrap; runtime secrets — database/inbound CRM REST webhook URL, Contact/alert receiver tokens и service token; hot policy — вbitrix_sync.settings.bitrix-syncне читаетBITRIX_CLIENT_ID/BITRIX_CLIENT_SECRET.
BITRIX_SYNC_ENABLED
| Значение | Поведение |
|---|---|
true (default) |
bitrix-sync обрабатывает sync_queue и принимает CRM webhook |
false |
синхронизация с Bitrix24 CRM не выполняется; сервис стартует в no-op/degraded режиме; чат Open Lines через bitrix-local-app не затрагивается |
При false контейнер остаётся live, /health/ready возвращает 503 sync_disabled, worker/reconciliation не claim-ят работу. Public webhook не должен безусловно подтверждать событие как обработанное: до cutover endpoint закрывается на edge либо возвращает retryable 503. Это не влияет на чат Open Lines.
bitrix_sync.settings
Versioned hot settings содержат batch size/wait, claim size, lease TTL, portal limiter refill/burst, max in-flight, retry base/max/horizon, Contact/alert reconciliation intervals, пороги всплеска Contact, восстановленных без webhook, и IDs/стадии/поля/SLA smart process. Новая версия активируется только после полной type/range/cross-field validation; невалидная версия не заменяет последнюю рабочую.
Secrets, DSN, portal host/member ID, inbound source IP CIDR allow-list, custom Contact field names и cutover watermark не являются hot settings. Полный каталог и defaults — ../modules/module-07-bitrix-sync.md, §12.
Keycloak settings bridge для OTP
OTP settings (otp.phone.*) хранятся в app_settings, но Keycloak не получает прямой доступ к схеме han_app.
MVP-механизм:
api-backendчитает публичные/служебные настройки изapp_settingsи кэширует их.- Для Keycloak SPI доступен internal endpoint
GET /internal/settings/v1/otpв Docker/VPC-сети, защищённый service token. - Keycloak SPI читает limits,
otp.phone.code_length,otp.phone.ttl_secondsиotp.phone.sms_order_timeout_msчерез этот endpoint с локальным cache. - При недоступности settings bridge SPI использует последнее валидное cache-значение; если cache пустой — fail-closed и не выдаёт OTP.
Challenge сохраняет snapshot TTL, длины кода и settings_version; изменение settings влияет только на новые challenges. Счётчики попыток OTP остаются в зоне Keycloak/SPI, не в api-backend.
Разрешённые типы файлов чата (MVP)
Источник значений — ключи app_settings (раздел «Seed MVP»). Остальные arch-* ссылаются сюда.
| Ключ | MVP-значение |
|---|---|
chat.attachments.allowed_extensions |
jpg, jpeg, png, webp, heic, heif, pdf |
chat.attachments.allowed_mime_types |
image/jpeg, image/png, image/webp, image/heic, image/heif, application/pdf |
chat.attachments.max_size_mb |
5 |
messages.max_text_length |
4000 (public/business limit; Safety hard ceiling остаётся 10000) |
Правило: файл принимается только если и расширение, и MIME в allow-list. Детальная проверка — модуль message-safety.
Публичный UI: GET /api/v1/public/app-config (строгий DTO, без секретов).
Публичный config endpoint
GET /api/v1/public/app-config — только ключи с is_public=true из app_settings:
- OTP по телефону (
auth.phone.enabled); - номер оператора;
- типы файлов и max size;
ux.session.idle_timeout_minutes;- feature flags;
- публичные лимиты для подсказок UI.
Секреты, service tokens, внутренние URL не возвращаются. DTO явный, не сериализация всей таблицы. Rate limit: 60/min per IP. Cache-Control: public, max-age из security.public_cache.max_age_seconds.
Публичный content endpoint
GET /api/v1/public/content — text_resources для текущего языка. Те же требования безопасности, что у config.
Nginx и HTTPS
Infra-переменные — .env.example (NGINX_*, TLS_*). Edge rate limits (NGINX_RATE_LIMIT_*) не дублируют rate_limit.* из app_settings: nginx — защита периметра, app — бизнес-лимиты в backend.
Реализация — arch-03-docker-compose-blueprint.md.