Поставка софта Ограничения: юрисдикции, экспорт, персональные данные
0%

Ограничения: юрисдикции, экспорт, персональные данные

Ограничения: юрисдикции, экспорт, персональные данные

Команда из шести человек, продукт работает, первые платящие клиенты есть. За одну неделю приходят четыре письма, и ни одно из них не про баги.

Первое — от отдела закупок крупного клиента: «подтвердите, что данные наших сотрудников не покидают территорию страны». Второе — автоматическое, от магазина приложений: сборка не принята, не заполнена декларация об использовании шифрования. Третье — от человека, который полгода назад удалил аккаунт: он требует подтвердить, что его данные стёрты, и упоминает срок в календарных днях. Четвёртое — от платёжного провайдера: выплаты по одному направлению приостановлены до выяснения.

Каждое письмо в отдельности выглядит как бумажная формальность. Вместе они означают одно: продукт технически готов и всё равно не может быть поставлен четырём разным покупателям по четырём разным причинам, и ни одну из этих причин нельзя починить хотфиксом в пятницу.

Ограничение — это условие, при котором работающий продукт нельзя поставить конкретному покупателю. Оно приходит не из вашего кода, но живёт в вашем коде. И в отличие от бага, его нельзя обнаружить в проде и исправить в следующем релизе: к моменту обнаружения обязательство уже возникло, а сделка уже сорвана.

Это последняя глава трека, и она собирает то, что в предыдущих главах всплывало на полях: региональные стеки в многотенантности, налоги и валюты в биллинге, анкеты площадок в магазинах приложений, возрастные рейтинги в дистрибуции игр, обязательства лицензий в главе про лицензии. Здесь мы разбираем не сами нормы — это работа юриста, — а механизм: как внешнее ограничение превращается в структуру данных, ветку кода, артефакт и срок.

Чем ограничение не является

Три подмены, из-за которых тема годами лежит в тумбочке. Это не безопасность. Безопасность отвечает на вопрос «что сделает злоумышленник». Соответствие требованиям отвечает на вопрос «что вы покажете третьей стороне через два года». Пересечение большое, но не совпадение: система может быть отлично защищена и полностью непригодна для продажи в регулируемый сектор, потому что у неё нет неизменяемого журнала доступа. И наоборот — можно иметь стопку сертификатов при дырявой авторизации. Инженерная часть безопасности разобрана в треке security, приватность с технической стороны — в отдельной главе.

Это не право. Юрист говорит, какая норма применима. Инженер говорит, каким механизмом она исполняется и чем это доказывается. Ни один из двоих не может сделать работу другого. Ошибка в обе стороны одинаково дорога: инженер, который решил «мы, наверное, подпадаем под исключение», и юрист, который написал «система должна обеспечить удаление данных» без понимания, что бэкапы восстанавливаются целиком.

Это не «документы». Политика обработки данных — это не решение, а описание того, что уже решено. Если политика написана раньше, чем сделан механизм, вы просто пообещали то, что не делаете. Живой пример на этом портале: раздел /legal — оферта, политика обработки персональных данных, согласие, правила возврата. Это не декоративные страницы, а формулировки обязательств, каждое из которых обязано иметь исполнителя в коде.

Четыре формы, в которые превращается любое ограничение

Практическая находка, которая экономит месяцы: какое бы ограничение вам ни назвали, в системе оно принимает одну из четырёх форм — или комбинацию.

  1. Атрибут. У какой-то сущности появляется поле: страна субъекта, класс данных, режим тенанта, возрастная категория, признак организации-покупателя. Атрибут можно отфильтровать, посчитать и проверить тестом.
  2. Ветка. Перед действием появляется проверка, которая может сказать «нет»: не выдать право, не принять регистрацию, не отправить письмо, не показать функцию.
  3. Доказательство. Действие оставляет запись, пригодную для показа третьей стороне: журнал, отчёт, подписанный артефакт, сохранённый ответ внешнего сервиса.
  4. Срок. Обязательство привязано ко времени: хранить столько-то лет, ответить за столько-то дней, пересмотреть раз в год, уведомить в течение стольких-то часов.

Отсюда рабочий приём: если требование не удаётся выразить ни в одной из четырёх форм, вы его ещё не поняли — и это повод вернуться к юристу с конкретным вопросом, а не с просьбой «объяснить закон». Вопрос звучит так: «какое поле мы должны хранить, какое действие обязаны запретить, что предъявить проверяющему и в какой срок».

Откуда ограничения берутся: шесть независимых источников

Ошибка новичка — думать, что ограничения приходят «от государства». Государство — только один из шести источников, и обычно не самый быстрый.

Три наблюдения по картинке.

  • Быстрее всех бьёт площадка. Регулятор появится через годы, если появится. Правила магазина остановят релиз завтра, а решение принимается автоматом и обжалуется неделями (магазины приложений).
  • Дороже всех обходится отрасль клиента. Требования банка или больницы приезжают к вам через договор, и в этом договоре вы принимаете обязательства, которых в вашей собственной юрисдикции никто не предъявлял: добровольные до подписи, обязательные после.
  • Тише всех работает содержимое артефакта. Копилефтная зависимость в проприетарной сборке не мешает ничему ровно до дня, когда кто-то попросит исходники. Живой случай портала — инстанс it-tools под GPL-3.0 на поддомене: сам факт публичной установки создал обязанность отдавать исходный код вместе с изменениями (лицензии, открытый код).

Юрисдикция — это четыре страны, а не одна

В любой сделке участвует минимум четыре страны, и путать их — самая частая архитектурная ошибка в этой теме.

Страна Что она определяет Где живёт в системе
Поставщика форма отчётности, валютные ограничения, экспортный режим конфигурация организации, одна на инсталляцию
Покупателя косвенный налог, права потребителя, требования к договору поле в записи покупателя, доказательства места
Размещения данных резидентность, трансграничная передача атрибут тенанта и атрибут каждого хранилища
Канала и платежей правила площадки, эквайера, банка-получателя конфигурация канала, не покупателя

Пятая появляется в B2B: страна пользователя не равна стране покупателя. Договор подписан юридическим лицом в одной стране, а работают в системе сотрудники из пяти. Резидентность требуется по данным сотрудников, налог считается по покупателю, а экспортные ограничения — по фактическому месту доступа.

Отсюда конкретное требование к схеме: не одно поле country. Минимальный набор, который позже не придётся мигрировать:

-- Юрисдикционные атрибуты разнесены по смыслу: каждый отвечает на свой вопрос и меняется независимо.
CREATE TABLE tenant (
    id                   uuid PRIMARY KEY,
    billing_country      char(2)  NOT NULL,                 -- налог и форма договора
    data_region          text     NOT NULL,                 -- 'eu-central', 'ru-central': выбор стека
    residency_level      smallint NOT NULL DEFAULT 1 CHECK (residency_level BETWEEN 1 AND 5),
    industry_regime      text[]   NOT NULL DEFAULT '{}',    -- 'finance', 'health', 'public': журналы и сроки
    channel              text     NOT NULL,                 -- у канала свои правила, не путать со страной
    screening_checked_at timestamptz,                       -- когда сверяли стороны со списками
    screening_result     text     NOT NULL DEFAULT 'pending'
);

-- Доказательства места покупателя лежат отдельно и не перезаписываются: через два года спросят,
-- на каком основании применена именно эта ставка и именно этот регион.
CREATE TABLE buyer_location_evidence (
    tenant_id   uuid        NOT NULL REFERENCES tenant(id),
    kind        text        NOT NULL,   -- 'card_bin' | 'billing_address' | 'ip' | 'phone_cc' | 'declared'
    value       text        NOT NULL,
    observed_at timestamptz NOT NULL,
    PRIMARY KEY (tenant_id, kind, observed_at)   -- история, а не последнее значение
);

Механика налогов и валют разобрана в биллинге и здесь не повторяется; важно лишь, что колонка billing_country — это тот же атрибут, из которого растут и налог, и право потребителя на возврат, и язык договора.

Резидентность данных: пять уровней одного слова

«Данные должны находиться в стране» — фраза, за которой прячутся пять принципиально разных требований с разницей в стоимости в десятки раз. Выяснять, какой именно уровень имеется в виду, надо до проектирования, а не после подписи.

  1. Копия в стране. Достаточно, чтобы в стране лежала актуальная копия; обработка и первичное хранение могут быть где угодно. Дёшево: репликация плюс мониторинг отставания.
  2. Первичное хранение только в стране. Основная база и все резервные копии — внутри. Уже требует отдельного контура хранения и пересмотра всей схемы бэкапов (объектное хранилище).
  3. Обработка только в стране. Вычисления тоже внутри: значит, отдельный кластер приложения, отдельный конвейер выкладки, отдельные очереди и отдельная аналитика.
  4. Доступ только изнутри. Персонал поддержки и дежурные инженеры не имеют доступа из-за границы. Это ломает единое дежурство: либо нанимаете людей в стране, либо строите механику доступа через посредника с журналированием (дежурства).
  5. Суверенный контур. Ключи шифрования и административный контроль не должны находиться под юрисдикцией иностранного лица. Практически это отдельное юридическое лицо, отдельный оператор и почти отдельный продукт.

$$ C_{\text{регионы}} \approx C_{\text{база}} \cdot \left( 1 + k \cdot (n - 1) \right), \qquad k \in \lbrack 0{,}3;\ 0{,}9 \rbrack $$

Здесь $n$ — число регионов, а $k$ — доля, которая не переиспользуется. На первом уровне $k$ близко к 0,3: платите за железо и за трафик. На четвёртом-пятом $k$ подбирается к 0,9, потому что дублируется не инфраструктура, а организация: люди, процессы, дежурства, аудит. Именно поэтому второй регион почти никогда не стоит «плюс сервер», а стоит «плюс команда» — тот же эффект, что и в стоимости платформы.

Где данные пересекают границу, пока все смотрят на базу

Классический разговор: «база у нас в нужной стране, всё в порядке». База — самый заметный и обычно единственный корректно настроенный элемент. Утекает всё остальное.

Двенадцать штатных потоков, которыми данные покидают юрисдикцию: бэкапы, реплики, журналы, метрики, трекер ошибок, аналитика, почта и пуш, платёжный провайдер, поддержка, CRM, сеть доставки, сборка и ИИ-сервисы

Практический порядок работы с этим списком — ровно тот, что на схеме внизу, и он контринтуитивен:

  1. Выкинуть поток целиком, если он не нужен. Самая дешёвая резидентность — та, которой не потребовалось.
  2. Убрать из потока персональные данные. Трассировка без идентификатора пользователя, ошибка без тела запроса, аналитика на псевдонимах. Часто оказывается, что поток был нужен, а данные в нём — нет (качество и управление данными).
  3. И только потом переносить оставшееся в нужную юрисдикцию. Это самый дорогой шаг, и делать его первым — типичная ошибка.

Отдельная строка — ИИ-сервисы: отправка текста обращения во внешнюю модель есть передача данных внешнему обработчику, со всеми теми же последствиями — договор, перечень, право субъекта знать; то, что это выглядит как вызов библиотеки, ничего не меняет.

Персональные данные как инженерная задача

Регламенты пересказывать не будем — это в треке безопасности и у юриста. Инженерная часть сводится к четырём механикам, и все четыре — это код, а не текст.

1. Инвентаризация. Карта: какие категории данных, в каких хранилищах, с какой целью, на какой срок, кому передаются. Без неё не работает ничего остальное. Единственный способ, при котором карта не устаревает через месяц, — генерировать её из кода: аннотации на полях моделей, комментарии в схеме, теги в описании инфраструктуры.

# data-map.yaml — источник истины, из которого генерируются раздел политики обработки, анкета магазина
# и ответ на опросник клиента. Лежит в репозитории, проверяется в CI: новое поле без записи — красная сборка.
stores:
  - id: primary-db
    region: ru-central
    categories:
      - name: contact
        fields: [email, phone]
        purpose: "уведомления по договору"                # цель, а не «на всякий случай»
        retention: "3 года после расторжения"
        legal_basis_owner: "юрист, вопрос закрыт 2026-03-11"
      - name: usage_events
        fields: [tenant_id, user_id, action, ts]
        purpose: "тарификация по потреблению"
        retention: "18 месяцев, затем агрегат без user_id"
  - id: error-tracker
    region: eu-west                                       # ← поток пересекает границу
    processor: "внешний сервис"
    categories:
      - name: diagnostics
        fields: [stacktrace, url]
        purpose: "диагностика сбоев"
        retention: "90 дней"
        controls: ["скраббер тела запроса", "запрет отправки заголовков авторизации"]
# Правило проверки: поле есть в схеме, но нет в карте → сборка красная, а не «замечание в бэклог».

2. Основание, цель и минимизация. У каждого поля есть причина существования; поле без цели — это не «на всякий случай», а обязательство, которое вы взяли бесплатно и содержите вечно. Каждое несобранное поле — минус строка в политике, минус вопрос в опроснике клиента, минус риск в инциденте. Проверка при код-ревью: «что сломается, если мы это не сохраним» — если ответ «ничего, но вдруг пригодится», не сохраняем. Приём из биллинга работает и здесь: удалять раньше дешевле, чем защищать дольше.

3. Сроки. Ретеншн — это задача в расписании, а не абзац в документе. Если в политике написано «90 дней», а джоба нет, вы не исполняете собственную политику, и это хуже, чем если бы срок не был объявлен.

4. Права субъекта. Доступ, копия, исправление, удаление, возражение против обработки. Здесь важен не список, а срок ответа: он измеряется календарными днями, а значит, ручной процесс на почте разваливается на десятом запросе.

Удаление: три уровня и то, что делать с бэкапами

Уровень 1 — мягкое удаление. Флаг deleted_at. Это не удаление, это скрытие. Полезно для отмены ошибки, бесполезно как ответ субъекту. Опасно тем, что выглядит как выполненная работа.

Уровень 2 — жёсткое удаление из продакшна. Строки удалены, файлы стёрты, кэши инвалидированы, поисковый индекс перестроен, события в аналитике обезличены. Уже требует знания всех хранилищ — то есть карты данных из пункта 1.

Уровень 3 — бэкапы. Точечно удалить строку из архивной копии невозможно, а если возможно — копия перестаёт быть копией. Два рабочих решения, оба честные:

  • Ограниченный срок жизни копий плюс повтор удалений при восстановлении. В реестре записано: копии живут, скажем, 35 дней; список удалённых субъектов («надгробия») хранится дольше и применяется повторно сразу после любого восстановления. Тогда честная формулировка звучит так: из активных систем — немедленно, из резервных копий — по истечении срока их жизни, и данные не возвращаются в оборот даже при аварийном восстановлении.
  • Криптографическое стирание. Данные субъекта шифруются отдельным ключом; удаление ключа делает нечитаемыми все копии сразу, включая те, до которых вы физически не дотянетесь.
"""Криптографическое стирание: уничтожаем ключ, а не строки.

Конвертное шифрование: у каждого субъекта свой ключ данных, обёрнутый на мастер-ключе в хранилище секретов.
Стирание субъекта — уничтожение его ключа: все копии и архивы, где лежит шифротекст, становятся
нерасшифровываемыми одновременно, включая те, до которых вы физически не дотянетесь. Цена, о которой надо
знать заранее: по зашифрованным полям нельзя искать без отдельного слоя, потеря ключа неотличима от
стирания, а ключей столько же, сколько субъектов.
"""
import os
from dataclasses import dataclass
from cryptography.hazmat.primitives.ciphers.aead import AESGCM


@dataclass(frozen=True)
class SubjectCrypto:
    kms: "KeyStore"                                          # мастер-ключ и обёрнутые ключи субъектов

    def encrypt(self, subject_id: str, plaintext: bytes, aad: bytes) -> bytes:
        dek, nonce = self.kms.get_or_create_dek(subject_id), os.urandom(12)
        return nonce + AESGCM(dek).encrypt(nonce, plaintext, aad)

    def decrypt(self, subject_id: str, blob: bytes, aad: bytes) -> bytes | None:
        dek = self.kms.get_dek(subject_id)                   # None — ключ уничтожен,
        if dek is None:                                      # это штатный ответ, а не ошибка
            return None
        return AESGCM(dek).decrypt(blob[:12], blob[12:], aad)

    def erase(self, subject_id: str, ticket: str) -> None:
        """Необратимое стирание, сразу с доказательством: кто, когда, по какому обращению."""
        self.kms.destroy_dek(subject_id)
        self.kms.audit(action="dek_destroyed", subject_id=subject_id, ticket=ticket)

Сложность операции — константная по числу записей субъекта: одно удаление ключа против обхода всех хранилищ. Память — один ключ на субъекта, порядка 32 байт плюс метаданные; для миллиона субъектов это десятки мегабайт в хранилище ключей, что несопоставимо дешевле, чем перебор бэкапов. Управление самими ключами — в главе про секреты и прикладной криптографии.

Оператор и обработчик: одна строка договора, много кода

Роль определяет, кто решает, зачем обрабатываются данные. Если решаете вы — вы оператор. Если решает клиент, а вы выполняете его инструкции — вы обработчик. SaaS почти всегда одновременно и то и другое: оператор по данным своих пользователей (регистрация, биллинг, поддержка) и обработчик по данным, которые клиент загрузил в систему.

Что меняется в системе, если вы обработчик:

  • Настраиваемые сроки хранения. Не ваша политика, а параметр тенанта: клиент вправе потребовать «удалять через 30 дней».
  • Экспорт и удаление по команде клиента, а не только по запросу субъекта, — и с подтверждающим отчётом.
  • Список субобработчиков публичный и версионируемый. Это ровно те двенадцать строк со схемы выше. Появился новый — клиента надо уведомить заранее, и у него может быть право возразить.
  • Уведомление об инциденте в срок, измеряемый часами. Значит, вы обязаны уметь ответить «какие именно записи затронуты», а это требует журналов доступа, которые пишутся до инцидента, а не после (реакция на инциденты).

Формулировки договора — к юристу; шаблон разбора договорных обязательств есть в материале о контрактах. Ваша часть — сделать так, чтобы каждое обещание в договоре имело исполнителя в коде и способ доказать исполнение.

Экспортный контроль: почему инженера спрашивают про шифрование

Эта тема сваливается на разработчика внезапно — обычно в виде обязательного поля при загрузке сборки в магазин (магазины приложений, релизы мобильных). Три вещи ломают интуицию. Экспорт — не обязательно физическая отправка. Публикация файла, который можно скачать из другой страны, предоставление доступа к репозиторию иностранному сотруднику, демонстрация технологии — в разных режимах всё это может считаться передачей. «Мы ничего не отправляли, оно само скачалось» — не аргумент.

Классификация идёт по функции, а не по намерению. Мессенджер со сквозным шифрованием классифицируется по криптографии, даже если вы считаете его «просто чатом». Резервное копирование с шифрованием архива — тоже криптография. Функция определяет режим.

Исключение надо заявить, а не предположить. Для массового рынка почти везде существуют упрощённые режимы, но у них есть условия, а у условий — процедура: самоклассификация, уведомление, регистрационный номер. Разница между «мы попадаем под исключение» и «мы заявили исключение и у нас есть подтверждение» — это разница между спокойным релизом и остановленной сборкой.

Что от вас требуется конкретно, и это всё инженерные задачи:

  1. Знать всю криптографию в продукте, включая транзитивную. Не «мы используем TLS», а полный список: библиотеки, алгоритмы, длины ключей, где хранятся ключи. Источник истины — спецификация состава сборки (упаковка, цепочка поставки).
  2. Отличать «крипта для защиты канала» от «крипта как функция продукта». Это разные ответы в анкете и разные режимы.
  3. Не встраивать собственную криптографию. Помимо очевидной причины — своя реализация лишает вас права ссылаться на стандартные компоненты и превращает разговор в исследование.
  4. Хранить ответ анкеты рядом с версией сборки. Состав меняется — ответ может измениться, а ответ, данный полтора года назад, относится к той версии, для которой он был дан. Заодно держите наготове ответ на вопрос «есть ли обход или закладка»: он должен быть «нет» и подтверждаться процессом ревью, а не памятью тимлида.

Вопрос юристу формулируется так, а не иначе. Не «подпадаем ли мы под экспортный контроль», а: «вот список криптографических компонентов и их назначение, вот страны, где доступна загрузка, вот страны, где находятся наши сотрудники с доступом к репозиторию, — какая классификация применима, какая процедура требуется и кто её выполняет».

Списочные ограничения: скрининг сторон, а не блокировка по адресу

Отдельный класс — ограничения по конкретным лицам, организациям и направлениям. Инженерная суть проста и неприятна: обязанность привязана к стороне сделки, а не к её сетевому адресу.

Отсюда прямое следствие: блокировка по геолокации — гигиена, а не соответствие требованиям. Она отсекает случайных, не отсекает намеренных и не защищает вас, если сторона в списке пришла через соседнюю страну. Работающий механизм устроен иначе.

Четыре вещи отличают работающий скрининг от имитации:

  • Точки проверки — три, а не одна. Регистрация, оплата, продление. Плюс регулярная перепроверка всей базы: списки обновляются, и вчера чистый контрагент сегодня может быть в списке.
  • Нечёткое сравнение и человек в цикле. Сравнение имён даёт много ложных срабатываний — транслитерация, порядок частей имени, однофамильцы. Автоматический отказ по совпадению строк создаёт свою проблему; нужна очередь разбора с целевым сроком.
  • Журнал решений — это и есть доказательство. Кто проверил, по какой версии списка, что решил, на каком основании. Без журнала проверка была, но доказать её нельзя, а значит, для проверяющего её не было. И отказ оформляется аккуратно: право не выдаётся, деньги не удерживаются, формулировка ответа пользователю согласована с юристом заранее.
  • Открытый код — отдельный режим. Публикация исходных текстов во многих режимах регулируется иначе, чем поставка бинарного продукта. Это не значит «на open source ничего не распространяется» — это значит, что режим другой, и уточнять его надо до публикации репозитория (открытый код как способ поставки).

Отраслевые режимы: ограничения, которые приезжают договором

Самый частый способ получить жёсткие требования — продать клиенту из регулируемой отрасли. Никакой регулятор к вам не придёт; придёт отдел закупок с приложением к договору на сорок пунктов.

Отрасль клиента Типичное требование Что меняется в системе Чем доказываете
Финансы журнал доступа, длительное хранение, право аудита, план выхода неизменяемый журнал, ретеншн по годам, экспорт всех данных клиента выгрузка журнала, отчёт о тестовом экспорте
Медицина разграничение доступа к записям, шифрование, журнал обращений атрибутный контроль доступа, шифрование на уровне полей политика доступа в коде плюс тест
Госзакупки резидентность, доступность интерфейса, иногда наличие в реестре отдельный региональный контур, соответствие интерфейса стандартам отчёт о проверке доступности, документы на контур
Дети и образование ограничение профилирования и рекламы, возрастная проверка режим без трекинга, флаг возраста как атрибут конфигурация режима и её журнал изменений
Критическая инфраструктура уведомление об инцидентах в срок, непрерывность процедуры оповещения, регулярные учения протоколы учений, записи оповещений

Обратите внимание на последний столбец: доказательство — это отдельный артефакт, который надо производить, а не побочный эффект хорошей работы. Требование доступности интерфейса, кстати, всё чаще встречается именно в закупках, и там оно перестаёт быть этическим вопросом и становится условием допуска — см. зачем нужна доступность и какие есть стандарты.

Аудиты и сертификаты — это про продажи, а не про безопасность

Отраслевой аудит покупатель приобретает не ради вашей защищённости, а ради снятия собственной ответственности: у него в процедуре записано, что поставщик должен иметь такой отчёт. Отсюда трезвый взгляд:

  • Сертификат не делает систему безопаснее — он фиксирует, что заявленные процессы существуют и выполняются. Безопасность делают практики из трека security.
  • Стоимость — годовая, а не разовая. Подготовка, внешний аудитор, поддержание процессов, ежегодное подтверждение. По состоянию на середину 2026 года для маленькой команды это ощутимая доля годового бюджета: сам аудит — сотни тысяч рублей и выше, подготовка обычно дороже аудита, а постоянная нагрузка на команду измеряется человеко-неделями в квартал. Конкретные цифры запрашивайте у аудиторов на текущий год: рынок и требования меняются.
  • Решение принимается арифметикой. Если сертификат открывает сегмент, в котором средний контракт кратно больше годовой стоимости соответствия, — считайте. Если нет — не начинайте (юнит-экономика).

Между «ничего нет» и «есть аудит» существует дешёвая промежуточная ступень — пакет доверия, подготовленный заранее: описание архитектуры и модели угроз, список субобработчиков, политика хранения и удаления, результаты последней проверки на уязвимости, спецификация состава сборки, порядок уведомления об инцидентах. Он не заменяет аудит, но сокращает цикл сделки с месяцев до недель, потому что закрывает восемьдесят процентов опросника без участия людей. Как это устроено на уровне платформы — в главе про безопасность и соответствие.

Соответствие как код: проверки в конвейере и реестр обязательств

Ограничения, живущие в головах, исчезают вместе с людьми: работает только то, что проверяется автоматически и хранится рядом с артефактом. Ворота в конвейере устроены так, что каждая проверка даёт машиночитаемый результат, и этот результат живёт столько же, сколько само обязательство (конвейер и безопасность, тесты в CI).

#!/usr/bin/env bash
# Ворота соответствия в конвейере. Ни одна проверка не «предупреждает»: предупреждение никто не читает.
set -euo pipefail
OUT="artifacts/compliance/${VERSION}"; mkdir -p "$OUT"

# 1. Состав сборки: без него невозможны ни лицензии, ни разговор про криптографию.
syft packages "dist/app:${VERSION}" -o cyclonedx-json > "$OUT/sbom.cdx.json"
# 2. Политика лицензий: копилефт в проприетарной сборке останавливает выпуск.
license-check --sbom "$OUT/sbom.cdx.json" --policy policy/licenses.yaml --report "$OUT/licenses.json"
# 3. Криптографические компоненты отдельным списком — он идёт прямо в анкету площадки.
jq '[.components[] | select(.name | test("openssl|libsodium|bouncycastle|boringssl"; "i"))]' \
   "$OUT/sbom.cdx.json" > "$OUT/crypto-components.json"
# 4. Карта данных: новое поле с персональными данными без записи в data-map.yaml — ошибка.
datamap-lint --schema db/schema.sql --map data-map.yaml --report "$OUT/datamap.json"
# 5. Реестр обязательств: просроченный пересмотр — тоже красная сборка.
python tools/check_obligations.py obligations.yaml --report "$OUT/obligations.json"
# Доказательство без подписи — просто файл, поэтому подписываем вместе с артефактом.
cosign sign-blob --yes "$OUT/sbom.cdx.json" > "$OUT/sbom.sig"

Реестр обязательств — единственный документ, который в этой теме действительно нужен. Не политика на двадцать страниц, а таблица, где у каждого обязательства есть источник, владелец, механизм, доказательство и дата пересмотра.

Модель читается просто: обязательство без механизма — это обещание; механизм без доказательства — это надежда; доказательство без даты пересмотра — это архив. Проверка такого реестра пишется за час и вешается в те же ворота (шаг 5 в скрипте выше): пройти по записям и упасть, если у обязательства нет владельца, нет механизма, доказательство не производится, доказательство старше своего допустимого возраста или дата пересмотра уже в прошлом. Сложность линейная по числу обязательств, а их у нормального продукта десятки, не тысячи. Дорого не выполнять проверку — дорого впервые составить список: обычно это две-три недели работы одного человека с юристом на подхвате.

Считать стоимость соответствия до выхода на рынок

Рынок — это не «ещё одна локаль». Стоимость входа складывается из трёх слагаемых с разным поведением:

$$ C_{\text{рынок}} = C_{\text{вход}} ;+; C_{\text{год}} \cdot T ;+; c_{\text{сделка}} \cdot N $$

  • $C_{\text{вход}}$ — разовое: регистрации, юридическая проработка, региональный контур, локализация договора.
  • $C_{\text{год}}$ — ежегодное и самое коварное: отчётность, продление аудита, поддержание отдельного стека, дежурство внутри юрисдикции. Оно не уменьшается от роста выручки.
  • $c_{\text{сделка}} \cdot N$ — на каждую сделку: опросники, проверки контрагента, индивидуальные приложения к договору.

Правило принятия решения простое: выходить, если ожидаемая валовая маржа за два года превышает сумму трёх слагаемых с запасом, а не впритык, потому что $C_{\text{год}}$ имеет свойство расти. И второе правило, которое редко проговаривают вслух: отказ от рынка — это нормальное инженерное решение, а не поражение. Половина проблем этой главы решается фразой «мы не продаём в эту юрисдикцию», записанной честно и реализованной в коде, а не подразумеваемой.

Квадрант в правом нижнем углу — самый опасный не деньгами, а вниманием: небольшой рынок с дорогим соответствием съедает время тех же людей, которые могли бы закрывать сделки в левом верхнем (приоритизация).

Что спросить у юриста

Юрист отвечает точно на точный вопрос. Вот формулировки, после которых ответ можно превратить в задачу в трекере.

  1. При нашей структуре продаж — в каких юрисдикциях у нас возникает обязанность регистрации, и какая именно?
  2. Мы оператор или обработчик по каждой из этих категорий данных? Вот карта данных, пройдите по строкам.
  3. Какие признаки места нахождения покупателя мы обязаны хранить, в каком виде и сколько лет?
  4. Требование клиента «данные не покидают страну» — это какой из пяти уровней? Что именно мы обязаны подтвердить?
  5. Каков предельный срок ответа на запрос субъекта и что считается надлежащим подтверждением исполнения?
  6. Наша формулировка про бэкапы — «из активных систем немедленно, из копий по истечении срока их жизни» — приемлема? Если нет, какая приемлема?
  7. Вот список криптографических компонентов и их назначение: какая экспортная классификация применима, какая процедура нужна и меняется ли что-то от публикации нашего репозитория в открытом доступе?
  8. По каким спискам мы обязаны проверять контрагентов, с какой периодичностью, что фиксировать в журнале и что при отказе вправе сообщить пользователю?
  9. Какие обязательства переживают расторжение договора, на какой срок, и кого надо уведомить, если мы добавим подрядчика в другой стране?

И зеркальное правило: ни один пункт этой главы не является юридической консультацией. Здесь описан механизм — как требование превращается в поле, ветку, артефакт и срок. Содержание требования устанавливает юрист вашей юрисдикции применительно к вашей ситуации.

Типичные ошибки

  • Одно поле country на всё. Через год выясняется, что налог, резидентность и правила канала разошлись, а миграция трогает каждую таблицу.
  • «База в нужной стране, значит, резидентность есть». Двенадцать потоков со схемы выше говорят обратное.
  • Политика написана раньше механизма. Опубликованное обещание, которое вы не исполняете, хуже отсутствия обещания.
  • Мягкое удаление вместо удаления. Флаг deleted_at — это скрытие; субъекту отвечено «удалено», данные на месте.
  • Ретеншн живёт в документе, а не в расписании. Срок объявлен, джобы нет, данные копятся годами.
  • Ответ на анкету об экспорте «по памяти». Состав сборки изменился, ответ остался прежним — и относится он к другой версии.
  • Геоблок вместо скрининга. Отсекает случайных, не отсекает намеренных, доказательством не является; а скрининг без человека в цикле, наоборот, отказывает по совпадению строк и создаёт поток конфликтов на пустом месте.
  • Доказательство не производится. Проверка выполняется, результат нигде не сохраняется — для проверяющего проверки не было.
  • Требования собирают под конкретную сделку и забывают. Обязательство осталось, знание о нём ушло вместе с менеджером, а пересмотр никто не запланировал — реестр без даты пересмотра гниёт молча.
  • Внешний ИИ-сервис не считают обработчиком. Вызов библиотеки выглядит невинно, передача данных от этого не исчезает.

Мини-итог

Ограничение — условие, при котором работающий продукт нельзя поставить конкретному покупателю. В системе оно принимает одну из четырёх форм: атрибут, ветка, доказательство, срок; если требование не ложится ни в одну, оно ещё не понято. Источников шесть, и быстрее всех бьёт площадка, дороже всех обходится отрасль клиента, тише всех работает содержимое артефакта. Юрисдикций в сделке четыре, и в схеме им нужны четыре разных поля. Резидентность имеет пять уровней с разницей в стоимости в десятки раз, и нарушается она не в базе, а у самого незаметного подрядчика. Персональные данные — это карта, основание, минимизация, срок и исполнимая процедура удаления, включая честный ответ про бэкапы. Экспортный контроль требует знать собственную криптографию и заявлять исключение, а не предполагать его. Списочные ограничения проверяются по стороне сделки, а не по адресу, и доказательством служит журнал решений. Всё вместе живёт в реестре обязательств: источник, владелец, механизм, доказательство, дата пересмотра — и проверяется теми же воротами, что и сборка. А где кончается механизм и начинается норма, ваша работа — задать юристу вопрос, на который можно ответить одним предложением.

Источники

Что дальше

Трек «Поставка софта» на этом закончен. От карты трека и моделей поставки мы дошли до упаковки и ограничений: теперь у вас есть язык, на котором обсуждают цену владения моделью, обязательства лицензии, механику денег и границы, за которые продукт не едет. Куда идти дальше — в зависимости от того, что оказалось узким местом:

Если непонятно, какой трек брать следующим, посмотрите общую карту курсов — там треки расставлены по уровням и связям между собой.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов