cmd/bj-license-server, cmd/bj-license и deploy/license перенесены в zuevav/lic_Bridge-and-Join-s (наша инфраструктура, клиентам не поставляется). Здесь остаются клиентская часть лицензирования, формат токена internal/license и контракт API в docs. TestTokenCompat — одинаковый тест формата токена в обоих репозиториях. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.1 KiB
Сервер лицензирования и обновлений bj — контракт API
Сервер лицензий живёт в отдельном репозитории zuevav/lic_Bridge-and-Join-s (наша инфраструктура, клиентам не поставляется). Здесь — контракт API для клиентской части bj (
internal/lkgateway/licenseclient.go). Формат токена общий:internal/license, тестTestTokenCompatодинаков в обоих репозиториях.
Сервер: https://lic.zetit.ru (ВМ 46.173.19.220, Ubuntu 26.04). Адрес зашит в bj
(internal/lkgateway, константа), в интерфейсе не меняется. С этого же сервера bj
получает обновления.
Модель
- Лицензия — прежний токен
internal/license: подписанный Ed25519 JSONLicense, строкаbase64(payload).base64(sig).keyid. Ключ подписи —~/keys/license.priv(keyidmain); открытый ключ зашит в bj. - Клиенту выдаётся код активации (
BJ-XXXX-XXXX-XXXX), один на лицензию; им активируется доmax_nodesустановок (0 — без лимита). - При активации сервер выдаёт токен на 60 дней (
expires_at), но не дольшеcontract_until; в токенеinstall_idустановки иcontract_untilдоговора. - bj продлевает токен раз в сутки. Отзыв = сервер больше не продлевает; уже выданный токен доживает свой срок (запас на недоступность сервера).
- Офлайн-ключи (
bj-license issue, безinstall_id) продолжают работать — для закрытых контуров без интернета.
Идентификатор установки
install_id — UUID v4, создаётся bj при первом запуске и хранится в
/var/lib/bj/.bj/setup.json (license.install_id). Вместе с ним bj передаёт
machine — sha256(/etc/machine-id) в hex (для учёта и обнаружения клонов, не для
отказа).
Запросы
Все тела — JSON, Content-Type: application/json. Ошибки — HTTP 4xx/5xx и
{"error": "<код>", "message": "<текст по-русски>"}.
POST /v1/activate
{"code": "BJ-7K3M-Q9XP-2HDA", "install_id": "…uuid…", "machine": "…sha256…",
"hostname": "bj-server-01", "version": "0.1.0"}
200:
{"token": "<payload.sig.main>", "license_id": "…uuid…", "tenant": "ИК Фонтвьель",
"expires_at": "2026-11-14T10:00:00Z", "contract_until": "2027-09-14T00:00:00Z",
"renew_after_sec": 86400}
Коды ошибок: bad_request, unknown_code, revoked, contract_expired,
max_nodes (лимит установок исчерпан). Повторная активация той же install_id —
не новая установка, просто новый токен.
POST /v1/renew
{"token": "<текущий токен>", "install_id": "…", "machine": "…", "hostname": "…", "version": "…"}
Сервер проверяет подпись токена (срок не проверяет — продлить можно и
истёкший), совпадение install_id с токеном, что установка известна и лицензия не
отозвана. 200 — тот же формат, что у activate. Ошибки: bad_token,
install_mismatch, unknown_install, revoked, contract_expired.
Каждый успешный renew обновляет last_seen, version, hostname, IP установки.
Обновления
GET /v1/{channel}/manifest.json— подписанный манифест (internal/release), без авторизации.GET /v1/{channel}/files/{file}— только с заголовкомAuthorization: License <token>: подпись верна, срок не истёк, лицензия не отозвана, в признаках естьupdates(или планenterprise). Иначе 401/403 с JSON-ошибкой. Ключ подписи манифестов отдельный от лицензионного.
GET /healthz
ok.
Кабинет администратора
https://lic.zetit.ru/admin/ — вход по логину и паролю (bcrypt), только для нас:
клиенты (название, ИНН), лицензии (план, признаки, max_nodes, contract_until, код
активации, отзыв/восстановление), установки (install_id, hostname, версия, IP,
последнее продление, срок токена), журнал событий, выпуск офлайн-ключа для
закрытого контура.