31 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.
Seed, JSON Schema и referenced artifacts входят в immutable Message Safety image. Их изменение требует одновременно нового pinned image digest и новой монотонной config version через config-admin job. Нельзя активировать config, который старый runtime image не валидирует, оставлять старый image как автоматический rollback после активации несовместимой schema или редактировать active row на месте. Rollback выполняется новой config version, совместимой с выбранным image; retired version повторно не активируется.
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 остаётся в отдельном read-only bind file root:han-message-safety 0640 с dedicated GID контейнера и намеренно не переносится в БД.
Пример .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.
Feature flag, edge allow-list и readiness образуют единый fail-closed
инвариант: при false активный nginx allow-list содержит только deny all;;
при true требуется хотя бы один reviewed source CIDR и готовый receiver.
Непустой allow-list при disabled, пустой allow-list при enabled или
расхождение route/readiness являются preflight error.
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.