Доставка и лицензия

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

Как устроена защита поставки

Три независимых, дополняющих друг друга слоя (полная спецификация — в DELIVERY-VERIFY.md внутри вашей поставки):

  1. In-file канарейки (watermark). В launchkit.config.ts и LICENSE.md вшиты уникальные для вашей покупки токены (LKWM-cfg-..., ваш Watermark.id в LICENSE.md). Они лучше-усилие (best-effort) для атрибуции утечки — определённый нарушитель может их вычистить — но при обычном копировании/форке остаются на месте.
  2. Подписанный манифест (treeSha256 + Ed25519-подпись). Детерминированный Merkle-хеш по всем файлам доставленного дерева плюс подпись приватным ключом, который хранится только на стороне провижининга (никогда не передаётся покупателю). Это основной, авторитетный источник истины для споров о доставке — не для атрибуции утечки, а для доказательства «именно это дерево было доставлено именно этому покупателю в это время».
  3. RFC-3161 таймстамп, привязывающий момент подписи манифеста к доверенному времени (в проде — внешний TSA; в текущей поставке может быть локальная заглушка с тем же интерфейсом).

Что покрывает манифест

Точная спецификация treeSha256

Чтобы независимо пересчитать хеш, нужно повторить canonicalization побитово:

  1. Перечислить каждый обычный файл в дереве, путь — относительно корня, разделитель /.
  2. Исключить: каталоги .git/, node_modules/, .next/, dist/ (в любом месте пути), файлы *.log, и весь lock-файл (pnpm-lock.yaml / package-lock.json / yarn.lock целиком — он не хешируется вообще, а не построчно фильтруется).
  3. Для каждого оставшегося файла прочитать байты в бинарном режиме, без преобразования переводов строк (важно для Windows/core.autocrlf — хешировать нужно байты доставленного архива, а не байты рабочей копии после чекаута) и вычислить fileSha = sha256(bytes) в нижнем регистре hex.
  4. Собрать строку-лист: "<relpath>" + "\0" + "<fileSha>" + "\n".
  5. Отсортировать листья по relpath в байтовом порядке (сравнение UTF-8 байт).
  6. treeSha256 = sha256(concat(отсортированные листья)).

Всё исключённое (lock-файл, .git/, node_modules/, .next/, dist/, *.log) находится вне подписанного конверта — не является доказательством доставки. Канарейка в commit-трейлере тоже живёт в .git и, соответственно, вне treeSha256.

Как проверить свою копию

  1. Возьмите manifest.json (поле payload + signature + keyId + tsa) из письма/личного кабинета.
  2. Пересчитайте treeSha256 по вашей локальной копии по алгоритму выше.
  3. Сравните с payload.treeSha256 из манифеста.
  4. Проверьте подпись Ed25519 над каноническим JSON payload опубликованным публичным ключом.
  5. (Прод) проверьте RFC-3161 таймстамп у выпустившего TSA.

Референсная реализация шагов 2–4 на Node.js без внешних зависимостей — см. DELIVERY-VERIFY.md в вашей поставке (там же публикуется актуальный keyId и публичный ключ).

Канонический JSON тела манифеста — фиксированный порядок ключей, без лишних пробелов:

{"buyerId":"…","licenseId":"…","purchasedTag":"…","treeSha256":"…","deliverySeq":0,"issuedAt":"…","keyId":"…"}

Ротация ключа подписи

При ротации ключа выпускается новый keyId, старый помечается неактивным — но старые манифесты продолжают проверяться по архивному публичному ключу, доступному в реестре ключей.

Если проверка не сходится

Прежде чем считать это подменой — перепроверьте, что вы исключили ровно перечисленный набор файлов и читали байты в бинарном режиме без преобразования EOL, поверх байтов именно доставленного архива (а не пересобранной рабочей копии). Если после этого хеш всё ещё не совпадает — обратитесь в поддержку, указав ваш Watermark ID и полученный манифест.

Обновления

Каждое обновление — это новый архив с собственным свежим watermark и собственным подписанным манифестом (deliverySeq увеличивается на единицу) — так атрибуция сохраняется на каждой версии, а не только на первичной поставке.

Границы применимости

Подписанный манифест и серверная запись о водяном знаке — авторитетный источник для споров о факте и содержании доставки. In-file и commit-канарейки — best-effort механизм атрибуции утечки, а не техническое ограничение на использование кода в рамках вашей лицензии: определённый нарушитель, вычистивший канарейки, создаст копию, не совпадающую ни с одним манифестом хешем — 100%-я защита от копирования технически недостижима, и это не заявляется как таковая.