6.2 KiB
bitrix-sync
Изолированный Python 3.12 сервис durable-синхронизации Contact между han_app и
Битрикс24. Сервис не участвует в Open Lines и не имеет HTTP-зависимости от
api-backend.
Entrypoints
han-bitrix-sync-api— health, internal status и два bounded robot receiver;han-bitrix-sync-worker— queue/webhook/rebind workflows с lease fencing;han-bitrix-sync-reconciliation— один advisory-lock incremental run;alembic upgrade head— отдельная контролируемая миграция, не startup DDL.
Disabled mode требует только BITRIX_SYNC_ENABLED=false и
BITRIX_SYNC_MODE=disabled, не читает БД и не принимает webhook. Full mode
валидирует весь каталог secret files, portal identity, custom fields, HTTPS host
lock и непустой CIDR allow-list до startup.
Наблюдаемость
JSON stdout включён всегда. Если задан стандартный
OTEL_EXPORTER_OTLP_ENDPOINT, traces, metrics и логи дополнительно отправляются
напрямую по OTLP/gRPC через bounded batch queues. Ошибка инициализации, экспорта
или остановки телеметрии не меняет readiness и не останавливает API, worker или
reconciliation. Процессы различаются как bitrix-sync-api,
bitrix-sync-worker и bitrix-sync-reconciliation; namespace остаётся
han-chat.
FastAPI (кроме /health/live), SQLAlchemy и HTTPX инструментированы. Перед
stdout и OTLP выполняется application redaction: query/token/credential URL,
headers, payload, PII, object keys и SQL не экспортируются. Бизнес-метрики
используют только закрытые множества labels; UUID и внешние идентификаторы в
labels/spans не записываются.
Границы безопасности
- CRM credential URL используется как единый секрет; redirect выключен, TLS проверяется, REST method выбирается только из закрытого allow-list.
- Receiver принимает только
application/x-www-form-urlencodedс bounded content length, числом и длиной полей. Query token сравнивается constant-time. - nginx должен перезаписывать
X-Real-IPиз TCP peer, проверять CIDR до proxy и не логировать$request_uri, args или body. Контейнер receiver недоступен напрямую. - DB хранит только hash CRM-master значений в snapshot; safe command projection не содержит PII. URL credential, form body и token не логируются.
- Запись CRM-master полей выполняется в одной транзакции после
SET LOCAL han.sync_suppress='true'.
Локальные проверки
Лёгкие проверки, не требующие Docker, сервиса или реального PostgreSQL:
python -m pytest
python -m ruff check app tests
PostgreSQL integration и Bitrix contract suites намеренно являются внешними gates: локальный managed PostgreSQL не поднимается Compose-файлом.
External gates до BITRIX_SYNC_ENABLED=true
- Применить migrations migration-role и проверить grants runtime-role.
- На disposable managed PostgreSQL проверить concurrent
SKIP LOCKED, lease expiry/fencing, active mapping uniqueness, rebind partial failure, transaction-local GUC без утечки и crash после CRM success. - Подтвердить на целевом портале wire-контракты
duplicate.findbycomm, Contact add/get/update, mixedbatch, custom fields, enum dictionary иcrm.item.listсopened=1, registration REST field=1. - Заполнить и активировать валидную
business_alertssettings version: entity/category/stage/field IDs и responsible party. Placeholdernullзапрещает alert receiver.
value_json настройки business_alerts использует ключи entity_type_id,
category_id, stage_new, опциональный responsible_id и объект field_ids.
В field_ids REST-имена пользовательских полей сопоставляются ключам
alert_number, fingerprint, alert_type, severity, app_user_id,
selected_external_id, occurrence_count,
first_occurred_at, last_occurred_at, workflow_id.
Конфликтующие Contact передаются в стандартное поле contactIds, а значение
BITRIX_SYNC_CONTACT_SOURCE — в стандартное поле sourceId.
5. Проверить least-privilege credential negative tests; credential администратора
запрещён.
6. Валидировать nginx exact routes, no-redirect HTTP policy, body/rate limits,
version-controlled CIDR и отсутствие query/body в access/error/traces.
7. Запустить synthetic webhook с реальным robot form contract, затем убедиться,
что durable inbox commit предшествует 202.
8. Выполнить 10k incremental reconciliation/load gate, webhook-loss recovery,
429/5xx/TLS/DNS/timeout/uncertain-create и restart-at-each-step tests.
9. Проверить container image digest, dependency/SBOM/vulnerability scan и
compose hardening; root Compose ВМ2 подключает этот fragment отдельно.
10. Зафиксировать cutover watermark, отменить только pre-cutover active tasks,
выполнить disabled preflight и затем controlled enablement.
compose.fragment.yaml — сервисный фрагмент, не root Compose и не команда
развёртывания. Reconciliation entrypoint выполняет один run; расписание задаёт
root-owned scheduler/deployment layer.