Доставка и лицензия
Каждая поставка LaunchKit — первая покупка и каждое последующее обновление — сопровождается подписанным манифестом доставки: криптографической записью того, какие именно файлы, с каким именно содержимым, были доставлены вам и в какой момент времени. Это позволяет независимо проверить, что ваша локальная копия совпадает с тем, что было фактически доставлено.
Как устроена защита поставки
Три независимых, дополняющих друг друга слоя (полная спецификация — в DELIVERY-VERIFY.md
внутри вашей поставки):
- In-file канарейки (watermark). В
launchkit.config.tsиLICENSE.mdвшиты уникальные для вашей покупки токены (LKWM-cfg-..., вашWatermark.idвLICENSE.md). Они лучше-усилие (best-effort) для атрибуции утечки — определённый нарушитель может их вычистить — но при обычном копировании/форке остаются на месте. - Подписанный манифест (
treeSha256+ Ed25519-подпись). Детерминированный Merkle-хеш по всем файлам доставленного дерева плюс подпись приватным ключом, который хранится только на стороне провижининга (никогда не передаётся покупателю). Это основной, авторитетный источник истины для споров о доставке — не для атрибуции утечки, а для доказательства «именно это дерево было доставлено именно этому покупателю в это время». - RFC-3161 таймстамп, привязывающий момент подписи манифеста к доверенному времени (в проде — внешний TSA; в текущей поставке может быть локальная заглушка с тем же интерфейсом).
Что покрывает манифест
- Merkle-хеш (
treeSha256) по каждому доставленному файлу. - Подпись Ed25519 над телом манифеста.
- Таймстамп, фиксирующий момент подписи.
- Ваш
Watermark ID(значение{{WATERMARK_LICENSE_ID}}вLICENSE.md, заменённое на реальный ID при доставке) иdeliverySeq(0 — первичная поставка, 1..N — каждое следующее обновление).
Точная спецификация treeSha256
Чтобы независимо пересчитать хеш, нужно повторить canonicalization побитово:
- Перечислить каждый обычный файл в дереве, путь — относительно корня, разделитель
/. - Исключить: каталоги
.git/,node_modules/,.next/,dist/(в любом месте пути), файлы*.log, и весь lock-файл (pnpm-lock.yaml/package-lock.json/yarn.lockцеликом — он не хешируется вообще, а не построчно фильтруется). - Для каждого оставшегося файла прочитать байты в бинарном режиме, без преобразования
переводов строк (важно для Windows/
core.autocrlf— хешировать нужно байты доставленного архива, а не байты рабочей копии после чекаута) и вычислитьfileSha = sha256(bytes)в нижнем регистре hex. - Собрать строку-лист:
"<relpath>" + "\0" + "<fileSha>" + "\n". - Отсортировать листья по
relpathв байтовом порядке (сравнение UTF-8 байт). treeSha256 = sha256(concat(отсортированные листья)).
Всё исключённое (lock-файл,
.git/,node_modules/,.next/,dist/,*.log) находится вне подписанного конверта — не является доказательством доставки. Канарейка в commit-трейлере тоже живёт в.gitи, соответственно, внеtreeSha256.
Как проверить свою копию
- Возьмите
manifest.json(полеpayload+signature+keyId+tsa) из письма/личного кабинета. - Пересчитайте
treeSha256по вашей локальной копии по алгоритму выше. - Сравните с
payload.treeSha256из манифеста. - Проверьте подпись Ed25519 над каноническим JSON
payloadопубликованным публичным ключом. - (Прод) проверьте 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%-я защита от копирования технически недостижима, и это не заявляется как таковая.