Кукбук BPMkitStand

Свободный диспетчер стендов BPMSoft: что это, как поставить, как жить с ним каждый день и как вынести управление на удалённые хосты. Документ для оператора и администратора сразу — всё по плиткам, ищите через поиск сверху.

01

Что такое BPMkitStand

Коротко

  • Бесплатный (MIT) диспетчер стендов BPMSoft
  • Локальный веб-дашборд в браузере вместо команд в PowerShell
  • Локальные и удалённые стенды — одним списком
  • Ядро — чистая стандартная библиотека Python, Windows и Linux

🛠️Что делает

  • Список стендов из общего реестра и состояние в реальном времени
  • Старт / стоп / рестарт с честной обратной связью
  • Логи текущей сессии, открытие папок логов
  • Очистка Redis-кэша стенда
  • Регистрация уже существующего стенда в реестре

🚧Чего не делает

  • Не разворачивает стенд «с нуля» — это провижининг, зона BPMkit
  • Не деплоит пакеты и не правит конфигурацию платформы
  • Не делает операций с БД, кроме проверки, что порт отвечает
  • Полная разделительная линия — раздел Границы

Три части продукта

⚙️standkit — ядро

  • Движок жизненного цикла над реестром
  • Health-пробы: процесс / HTTP / порт БД / порт Redis
  • Чтение хвоста лога, Secret-first доступ к секретам
  • Без веб-слоя — годится как библиотека

🖥️standkit_hub — дашборд

  • То, что видит оператор: веб-интерфейс в браузере
  • Сам себя отдаёт по HTTP, без CDN и сборки
  • Ставится только на машину оператора
  • Опционально — нативное окно (--desktop)

📡standkit_agent — агент

  • Лёгкий headless-демон на хосте удалённого стенда
  • То же ядро, обёрнутое в крошечный HTTP/RPC-слой
  • Нужен только для удалённого управления
  • Windows и Linux, только стандартная библиотека

Как это собрано — за один взгляд

Машина оператора Браузер: BPMkit Дашборд вкладка, PWA или нативное окно standkit_hub http.server на 127.0.0.1:8770 standkit — ядро жизненный цикл, пробы, логи projects.json — общий реестр Хосты удалённых стендов standkit_agent на каждом хосте transport: agent HTTPS + Bearer-токен, mTLS Локальные стенды transport: local — та же машина kestrel / iis / docker / k8s
Дашборд — федеративный клиент: локальными стендами управляет через ядро напрямую, удалёнными — через агентов. Реестр projects.json у оператора один и тот же для обоих случаев.

Для кого

⚙️Администратор

  • Стенды под контролем без консоли
  • Удалённые контуры через агентов

💻Разработчик

  • Быстрый рестарт и хвост лога
  • Очистка Redis в один клик

🎯РП / пресейл

  • Демо-стенды: поднять перед показом
  • Видно, что живо, а что нет

🤖Пользователь BPMkit

  • Общий реестр с AI-агентом
  • Стенд из дашборда сразу виден агенту
🧩 Место в экосистеме. BPMkitStand — бесплатная часть экосистемы BPMkit и полноценный диспетчер сам по себе. Companion-версия на той же кодовой базе дополнительно даёт автообновление MCP BPMkit и контроль лицензии; она поставляется в составе установщика MCP-клиента.
02

Установка и первый запуск

🖥️Что нужно на машине оператора

  • Python 3.10+
  • Windows 10 / 11 или Linux
  • Браузер (Chrome, Edge, Firefox — любой современный)
  • Сетевой доступ до стендов и их БД

📦Что нужно на хосте стенда

  • kestrel: установленный dotnet
  • iis: Windows + IIS, диспетчер от администратора
  • docker: Docker Engine, для compose — Compose V2
  • k8s: kubectl в PATH и рабочий kubeconfig

🔓Что не нужно

  • Никаких сторонних веб-фреймворков и GUI-тулкитов
  • Ни одной pip-зависимости у ядра и агента
  • Интернета в рантайме: фронтенд без CDN, работает офлайн
  • Отдельного сервера — дашборд локальный

Шаг 1 — поставить пакет (по-разному в Windows и Linux)

Пакет один и называется standkit — в нём сразу ядро, дашборд и агент, обязательных зависимостей у него нет. Отличается только способ установки: в Windows пакет обычно ставят прямо в пользовательский Python, в Linux системный Python трогать нельзя.

🪟Windows

  • Python 3.10+ с python.org; при установке отметьте «Add python.exe to PATH»
  • Ставим обычным pip:
pip install standkit
  • С дополнениями (хранилище секретов + нативное окно):
pip install "standkit[secrets,desktop]"
  • Запуск: standkit-hub; если консоль его не видит — py -m standkit_hub
  • Ярлык на рабочий стол: standkit-hub --install-shortcut

🐧Linux

В современных дистрибутивах системный Python помечен как «externally managed» (PEP 668), и обычный pip install в него откажет с error: externally-managed-environment. Это не про standkit — так ведут себя все пакеты. Ставим в изолированное окружение.

Вариант A — pipx (рекомендуется: изоляция + команды сразу в PATH):

sudo apt install pipx # Debian/Ubuntu; иначе: python3 -m pip install --user pipx pipx ensurepath # добавляет ~/.local/bin в PATH — нужен новый вход в shell pipx install "standkit[secrets]" standkit-hub

Вариант B — venv (если pipx нет или окружение нужно своё):

python3 -m venv ~/.venvs/standkit ~/.venvs/standkit/bin/pip install "standkit[secrets]" ~/.venvs/standkit/bin/standkit-hub # при желании — короткая команда: sudo ln -s ~/.venvs/standkit/bin/standkit-hub /usr/local/bin/standkit-hub

Дополнение [desktop] (нативное окно) на Linux требует WebKitGTK в системе; без него просто пользуйтесь браузером. К уже поставленному pipx-пакету дополнение добавляется инъекцией: pipx inject standkit keyring.

--break-system-packages на рабочем сервере — антипаттерн. На Debian/Ubuntu системный Python обслуживает сам apt; сломав его, вы теряете управление пакетами машины. Флаг существует для контейнеров и одноразовых песочниц, не для хоста со стендами.
🐍 Скрипты запускайте питоном того окружения, куда поставили пакет. Системный python3 модуля не видит — будет No module named 'standkit_agent'. Для pipx это ~/.local/share/pipx/venvs/standkit/bin/python, для venv — ~/.venvs/standkit/bin/python.
🖧 Дашборд на headless-сервере. Хаб слушает только 127.0.0.1 и это правильно: не открывайте его порт наружу. Запустите standkit-hub --no-browser, а с рабочей машины пробросьте порт по SSH — ssh -L 8770:127.0.0.1:8770 user@host — и откройте http://127.0.0.1:8770 у себя. URL с сессионным токеном возьмите из вывода хаба на сервере.

Шаги 2–5 — одинаково в обеих ОС

Запустите дашборд Хаб слушает 127.0.0.1:8770, печатает URL с сессионным токеном и открывает браузер. Если команда «не распознана» — тот же запуск модулем работает всегда: python -m standkit_hub (разбор — в разделе Траблшутинг).
standkit-hub
Зарегистрируйте стенды Кнопка «Зарегистрировать стенд» на вкладке «Стенды» — или правка projects.json руками (раздел Реестр стендов). Регистрируется уже существующий стенд: каталог, БД и дистрибутив должны быть готовы заранее.
Задайте секреты В реестре хранятся только ссылки на секреты. Самый простой путь — кнопка «Задать секрет…» в «Настройках» дашборда. Из терминала (нужно дополнение [secrets]) — так значение не попадёт в историю команд:
python -c "import getpass; from standkit.secrets import set_secret; set_secret('standkit:my-stand:db', getpass.getpass('Значение: '))"
На машине без keyring (headless-сервер, служба) секрет задаётся переменной окружения — см. плитку «Секреты» ниже.
Сделайте ярлык и проверьте Ярлык: standkit-hub --install-shortcut. В Chrome/Edge дашборд дополнительно ставится как приложение (PWA): своё окно, иконка в панели задач, место в Alt+Tab. Дальше — «Первая проверка» ниже.

Секреты: три источника, приоритет сверху вниз

🌱1. Переменная окружения

  • Имя — STANDKIT_SECRET__ + ссылка на секрет в верхнем регистре, все не-буквенно-цифровые символы заменены на _
  • standkit:client-uat:agent-token
    STANDKIT_SECRET__STANDKIT_CLIENT_UAT_AGENT_TOKEN
  • Единственный рабочий путь на headless-хосте и для службы

🔐2. Системный keyring

  • Нужен extra standkit[secrets]
  • Задать: кнопка «Задать секрет…» в дашборде или однострочник выше
  • Служба под сервисной учёткой без сессии до keyring обычно не достучится

📄3. Открытое поле в реестре

  • Явный фолбэк (например db_password) — сознательно наименьший приоритет
  • Для прод-контуров не использовать
  • ⚠️ Отдельного CLI (python -m standkit.secrets …) в пакете нет — такая команда молча ничего не делает

Первая проверка

  • В консоли — строка дашборд слушает 127.0.0.1:8770 и URL с токеном
  • В браузере открылась вкладка «BPMkit Дашборд», индикатор в правом верхнем углу — зелёный «онлайн»
  • На вкладке «Стенды» видны ваши стенды (или пустая таблица, если реестр пуст)
  • Клик по строке стенда наполняет панель «Текущее состояние» внизу

🔁Повторный запуск

  • Хаб — single-instance: второй клик по ярлыку не поднимает второй диспетчер, а открывает браузер на уже работающем
  • Закрытие окна браузера не останавливает хаб — процесс живёт дальше (у него нет idle-выключения)
  • Остановить: Ctrl+C в консоли хаба (завершается штатно, без трейсбека)
  • Если порт 8770 занял чужой сервис — хаб честно напишет об этом и возьмёт свободный

Обновление диспетчера

⬆️Как обновиться

  1. Остановите работающий диспетчер: Ctrl+C в его консоли (закрытие окна браузера процесс не останавливает)
  2. Обновите пакет — командой того способа, которым ставили:
    pip install -U standkit # Windows / обычный pip pipx upgrade standkit # Linux, установка через pipx ~/.venvs/standkit/bin/pip install -U standkit # Linux, установка в venv
  3. Запустите заново standkit-hub и обновите вкладку — Ctrl+F5, чтобы браузер взял свежую статику
  4. Проверьте версию: кнопка в шапке → «О программе» (или pip show standkit / pipx list)

🧭Что важно знать

  • Ваши данные не трогаются: реестр стендов, конфиг дашборда и секреты живут в профиле пользователя, а не внутри пакета
  • Агентов обновляйте вместе с хабом — на каждом хосте тем же обновлением пакета плюс рестарт службы (sudo systemctl restart standkit-agent / nssm restart standkit-agent)
  • Ярлык и установленное PWA-приложение пересоздавать не нужно
  • Что изменилось — docs/CHANGELOG.md в репозитории
  • Свежий main до релиза (только если нужна ещё не выпущенная правка):
    pip install --force-reinstall "git+https://github.com/thinkquattro/BPMkitStand.git"
🤖 Не путать с обновлением MCP BPMkit. Бесплатный диспетчер обновляется вручную командой выше. Автообновление самого MCP BPMkit и контроль лицензии — это Companion-версия из установщика MCP-клиента, а не pip.

Ключи запуска standkit-hub

КлючПо умолчаниюЗачем
--host127.0.0.1 Адрес, на котором слушать. Loopback — безопасный дефолт; менять без TLS нельзя (см. Безопасность)
--port8770 Порт фиксирован осознанно: браузер узнаёт дашборд по origin и помнит тему и кэш. 0 — эфемерный порт
--configпрофиль пользователя Свой путь к конфигу хаба вместо %APPDATA%\BPMkit\standkit-hub.json
--no-browserвыклНе открывать браузер автоматически
--desktopвыкл Нативное окно вместо браузера. Требует extra standkit[desktop]; без него хаб предупредит и откроет браузер
--insecureвыкл Осознанный обход fail-closed-проверки bind. Только dev/тест
--install-shortcut / --uninstall-shortcut Создать/удалить ярлык на рабочем столе и выйти, не поднимая сервер

Где что лежит

ЧтоWindowsLinux
Реестр стендов %APPDATA%\BPMkit\projects.json ~/.config/BPMkit/projects.json
Конфиг дашборда %APPDATA%\BPMkit\standkit-hub.json ~/.config/BPMkit/standkit-hub.json
Каталог pid-файлов и логов запуска Задаются в «Настройках» (поля «Каталог pid-файлов» и «Каталог логов»)
Аудит-лог агента ~/.standkit/audit.log, если не задан --audit-log
📌 Путь к реестру резолвится по порядку: переменная окружения BPMSOFT_PROJECTS_FILE → профиль пользователя → ./projects.json в текущем каталоге. Первый запуск сам создаёт папку реестра, чтобы показанный путь вёл в реальное место, а не «в никуда».
03

Дашборд: экран за экраном

Три вкладки: Стенды — повседневная работа, Локальный агент — нужен только хосту удалённых стендов, Настройки — пути, интервал опроса, федерация.

Дашборд BPMkitStand, вкладка «Стенды», тёмная тема Дашборд BPMkitStand, вкладка «Стенды», светлая тема
Вкладка «Стенды»: таблица состояния, действия построчно и панель «Текущее состояние» с хвостом лога выбранного стенда. Скриншот меняется вместе с темой этого документа.

Колонки таблицы

КолонкаЧто показывает
СтендИмя записи в реестре. Рядом может быть бейдж вне диспетчера — стенд поднят мимо дашборда
ТранспортКак дашборд дотягивается до стенда: local (напрямую через ядро) или agent (по HTTP к агенту хоста)
ПроцессБейдж вердикта: up down unknown (состояние выяснить не удалось) или «проверяется…» на первом опросе. Причина от бэкенда хостинга — во всплывающей подсказке бейджа. Во время прогрева после «Запустить» вместо бейджа — «Запускается…» со спиннером
HTTPАдрес web-хоста стенда ссылкой — открывает стенд в новой вкладке. Цвет ссылки = результат HTTP-пробы. Схема берётся из stand_scheme записи: у стенда за TLS и проба, и ссылка идут на https://
БДИмя базы стенда из реестра, окрашенное по результату пробы (открыт ли TCP-порт БД)
RedisНомер базы Redis стенда — тот самый, который будет очищен кнопкой 🗑. Прочерк — Redis у стенда не настроен
ДействияКнопки-иконки: ▶ запустить · ■ остановить · ⟳ перезапустить · 🗑 очистить Redis. Неприменимые гаснут: ▶ у запущенного, ■ и ⟳ у остановленного, 🗑 — если Redis не настроен

🎬Честный старт

  • Дашборд поднимает процесс стенда и держит спиннер до реального ответа по HTTP
  • «Запущено» не рапортуется по факту создания процесса — прогрев не выдаётся за готовность
  • Остановка мягкая: сначала штатное завершение с ожиданием, принудительное — только потом
  • Стоп, Рестарт и Очистка Redis спрашивают подтверждение

🤝Стенд «вне диспетчера»

  • Стенд, поднятый руками, не имеет pid-файла — обычные Стоп/Рестарт по нему невозможны
  • Диспетчер предлагает усыновление: найти владельца порта и взять процесс под управление
  • Берёт только с явного подтверждения и только при совпадении трёх улик сразу — см. Безопасность
  • Не совпало — отказ с указанием, какой именно процесс занимает порт

Мелочи, которые экономят время

📂Два места логов

  • Сплит-кнопка «Открыть папку логов» помнит последний выбор
  • Стрелка — выбор источника: логи стенда или логи BPMkit-проекта
  • Пункт BPMkit гаснет, если проекта у стенда нет

Компактный режим

  • Кнопка ▭ в шапке — узкое окно-виджет
  • Только имена, состояния и старт/стоп
  • Открывается и напрямую: ?view=compact

Справка под рукой

  • Кнопка ? в шапке открывает этот кукбук
  • Он входит в поставку и работает офлайн — файл можно открыть с диска и при остановленном диспетчере
  • Та же ссылка — в окне «О программе» (ⓘ)

🌓Тема и обновление

  • Тема хранится в конфиге хаба и применяется до загрузки скриптов — светлой вспышки нет
  • Интервал автообновления — в «Настройках»
  • Кнопка «Обновить» форсирует опрос
⚠️ Красный индикатор и баннер сверху означают, что вкладка потеряла связь с диспетчером: процесс хаба остановлен или перезапущен. Таблица в этот момент гасится — то, что на экране, уже неактуально. Если появился возраст снапшота («данные от …»), пробы отстают: проверьте доступность стендов и агентов.
04

Реестр стендов

Реестр — единственный источник правды о стендах. Один и тот же файл projects.json используют дашборд, агент и MCP BPMkit: стенд, заведённый в диспетчере, сразу виден AI-агенту — и наоборот.

📄Где лежит

  • Порядок резолва: BPMSOFT_PROJECTS_FILE (если файл существует) → канонический путь профиля → ./projects.json в текущем каталоге
  • Windows: %APPDATA%\BPMkit\projects.json
  • Linux: $XDG_CONFIG_HOME/BPMkit/projects.json, иначе ~/.config/BPMkit/projects.json
  • Стенды — под ключом projects, имя записи = имя стенда
  • На чистой машине файла нет — это норма: чтение несуществующего реестра даёт пустой список, а сам файл (вместе с каталогом) создаётся при первой записи

🔐Secret-first

  • В реестре нет паролей и токенов — только ссылки на секреты (secret_ref_*, agent_secret_ref)
  • Значение живёт в хранилище машины и задаётся отдельно
  • Порядок поиска значения: переменная окружения STANDKIT_SECRET__… → keyring → открытый фолбэк из реестра
  • Задать: кнопка «Задать секрет…» в «Настройках» дашборда или однострочник (нужен extra [secrets]):
python -c "import getpass; from standkit.secrets import set_secret; set_secret('standkit:my-stand:db', getpass.getpass('Значение: '))"

Подробнее — плитки «Секреты» в разделе Установка.

Регистрация ≠ провижининг

  • Кнопка «Зарегистрировать стенд» привязывает уже существующий стенд к реестру
  • Каталог, база и дистрибутив должны существовать заранее
  • Развернуть стенд «с нуля» диспетчер не умеет — это зона BPMkit
  • Отдельной CLI-команды регистрации в пакете нет — три рабочих пути: кнопка в дашборде, правка projects.json руками по образцу projects.sample.json, Registry.add_existing() из Python (рецепт ниже — для сервера без дашборда)
  • Обязательные поля: name и stand_dir; для host_kind=dockerdocker_container (или compose-пара), для kestrelstand_dll и dotnet

Ключевые поля записи

ПолеЗначенияСмысл
transportlocal · agent Где управлять стендом: тем же процессом или через агента на его хосте. ssh/winrm схема допускает, но логика не реализована
host_kindkestrel (по умолчанию) · iis · docker · k8s Как стенд хостится. Поле независимо от transport — см. Виды хостинга
stand_dirпутьКаталог стенда: логи, pid, проверка усыновления
stand_dll, dotnetBPMSoft.WebHost.dll, dotnet Чем и что запускать в режиме kestrel
stand_host, stand_port127.0.0.1, 5000 Адрес web-хоста: HTTP-проба, ссылка в таблице, поиск владельца порта при усыновлении
stand_scheme, verify_tlshttp · https; true/false Схема HTTP-пробы и ссылки «Открыть стенд»; для самоподписанного сертификата — verify_tls: false. Дефолты http/true сохраняют прежнее поведение — см. Виды хостинга
db_type, db_host, db_port, db_namepostgres · mssql Куда стучаться пробой БД (в бесплатной версии — только «открыт ли порт»)
redis_host, redis_port, redis_dbхост, порт, номер БД Проба Redis и очистка кэша. Если redis_db не задан, дашборд пробует вытащить его из конфигурации стенда
secret_ref_db, secret_ref_adminstandkit:<стенд>:db Ссылки на секреты, не сами секреты
agent_url, agent_secret_refhttps://host:8765 Только при transport: agent — адрес агента и ссылка на его токен
description, customerтекстЧеловеческие пометки, ни на что не влияют

Один стенд — две разные записи в двух реестрах

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

ЧтоРеестр на хосте стенда (читает агент)Реестр оператора (читает дашборд)
transportlocal — агент поднимает стенд у себяagent — дашборд ходит к агенту по HTTP
Адрес агентаagent_url + agent_secret_ref
Как запускаетсяstand_dll/dotnet, docker_container, IIS-сайт…Не важно — запуск делает агент
СекретыПароль БД стенда (secret_ref_db)Токен агента (agent_secret_ref)
Путь файлаЗадаётся агенту явно: --registry /opt/standkit/projects.jsonПрофиль оператора: %APPDATA%\BPMkit\projects.json
🔁 Агент читает реестр при старте. Поправили projects.json на хосте — перезапустите службу агента, иначе увидите прежнюю картину и будете искать несуществующую проблему.
🐍 Регистрация стенда на сервере без дашбордарецепт

Дашборд на headless-хосте неудобен (слушает только 127.0.0.1), CLI регистрации нет — поэтому запись добавляется из Python. Питон берём из того окружения, куда поставлен пакет.

/opt/standkit/venv/bin/python - <<'PY' from standkit.models import Stand from standkit.registry import Registry reg = Registry.load("/opt/standkit/projects.json") reg.add_existing(Stand.from_dict("survey9", { "transport": "local", "host_kind": "docker", "docker_container": "survey9-web", "stand_dir": "/opt/bpmsoft/survey9", "stand_host": "127.0.0.1", # адрес ОБРАЩЕНИЯ с хоста, не 0.0.0.0 "stand_port": 5010, # левый порт из docker ps "db_type": "postgres", "db_host": "127.0.0.1", "db_port": 5433, "db_name": "survey9", "secret_ref_db": "standkit:survey9:db", "redis_host": "127.0.0.1", "redis_port": 6379, "redis_db": 2, })) reg.save("/opt/standkit/projects.json") print("ok") PY
  • Файл и каталог создаются сами при save() — заводить их заранее не нужно
  • add_existing() валидирует запись: пропущенное обязательное поле для выбранного host_kind вылезет сразу, а не при первом старте
  • После правки реестра — перезапуск агента
📁 Минимальная запись локального стенда (kestrel)
{ "default": "", "projects": { "dev-local": { "transport": "local", "stand_dir": "C:\\BPMSoft\\dev-local", "stand_dll": "BPMSoft.WebHost.dll", "dotnet": "dotnet", "stand_host": "127.0.0.1", "stand_port": 5000, "db_type": "postgres", "db_host": "127.0.0.1", "db_port": 5432, "db_name": "dev_local", "secret_ref_db": "standkit:dev-local:db" } } }
  • Полный образец всех вариантов — файл projects.sample.json в репозитории
  • Реальный projects.json в git не коммитится
🧭 Модалка «Зарегистрировать стенд» — что в ней
  • Имя стенда, транспорт и вид хостинга — два выпадающих списка сверху
  • Условные поля появляются по выбору: agent_* для агента, iis_* / docker_* / k8s_* для хостинга
  • Для IIS есть кнопка «Определить автоматически»: сайт и пул находятся по каталогу стенда и биндингу порта
  • Каталог, host/port и параметры БД — остальная часть формы
  • Запись уходит в общий projects.json, править файл руками не обязательно
Форма «Зарегистрировать стенд» с выбранным хостингом iis
Форма с выбранным хостингом iis: поля iis_site / iis_app_pool и кнопка «Определить автоматически» появились по значению списка «Хостинг».
05

Виды хостинга

Два независимых измерения: transportгде вы управляете стендом, host_kindкак стенд хостится на своей машине. Их можно комбинировать свободно: transport=agent + host_kind=docker — это удалённый контейнер, которым управляет агент на его хосте.

transport — ГДЕ управлять local процессом самого диспетчера agent через standkit_agent по HTTPS ssh · winrm задел, логика не реализована × host_kind — КАК стенд хостится kestrel dotnet + pidfile (дефолт) iis appcmd, управление сайтом docker docker / compose k8s kubectl scale / rollout Общее правило вердикта ответ CLI авторитетнее открытого TCP-порта
Поля независимы. Неизвестное значение host_kind не роняет чтение реестра — откат на kestrel.

Что делает диспетчер под капотом

host_kindОбязательные поляСтарт / стоп / рестарт и «жив ли»
kestrel Запуск dotnet <stand_dll> скрытым процессом + pidfile. «Жив» — процесс по pid и HTTP-ответ
iisiis_site и/или iis_app_pool Через appcmd. «Стенд» = его Site: диспетчер стартует и останавливает сайт и намеренно не трогает App Pool (пул может быть общим с другими приложениями). Пул задействуется, только если iis_site не задан вовсе
dockerdocker_container или пара docker_compose_file + docker_compose_service docker start|stop|restart, состояние — State.Status (приостановленный контейнер зелёным не показывается). Для compose — docker compose up -d|stop|restart с точным сравнением имени сервиса
k8sk8s_deployment Старт — scale --replicas=N, стоп — scale --replicas=0, рестарт — rollout restart с ожиданием выката. «Жив» — число готовых реплик

Docker: какой адрес писать в реестр

Здесь ошибаются практически все, а расплата — ложный http: down при живом стенде. Диспетчер (и агент) работают на хосте, вне контейнера, поэтому в stand_host/stand_port идёт адрес, по которому до стенда достучаться с хоста. Не адрес слушания и не внутренний IP контейнера.

🚫0.0.0.0 — неверно

  • Это адрес для bind внутри контейнера, а не адрес для обращения
  • Проба по нему не ходит — стенд живой, а в таблице down
  • Пишите 127.0.0.1 (порт опубликован на хост) или реальное имя/адрес хоста

🔎Где взять порт

docker ps --format '{{.Names}}\t{{.Ports}}'
  • В 127.0.0.1:5010->5002/tcp нужен левый порт — 5010
  • Строка вида 5000/tcp без стрелки = порт объявлен, но наружу не опубликован; с хоста его нет

📌Внутренний IP не писать

  • 172.17.x.x работает, но меняется при пересоздании контейнера
  • То же правило для db_host/db_port и redis_host/redis_port
  • Нет redis_host/redis_port — проба честно вернёт unknown, а не down: это «не настроено», а не авария

Стенд за TLS: stand_scheme и verify_tls

🔐Два поля записи

  • stand_schemehttp (по умолчанию) или https: схема, по которой идёт HTTP-проба и строится ссылка «Открыть стенд» в таблице
  • verify_tls — проверять ли цепочку сертификатов; по умолчанию true, для самоподписанного сертификата дев-контура нужен false
  • Дефолты полностью повторяют прежнее поведение — старые реестры править не нужно
  • Мусор в stand_scheme не роняет чтение реестра: откат на http, а несоответствие поймает валидация записи
"stand_host": "127.0.0.1", "stand_port": 5001, "stand_scheme": "https", "verify_tls": false

🩺Как это выглядит без настройки

  • Стенд за TLS на http:// не отвечает вовсе — проба честно возвращает down при живых process и db
  • С самоподписанным сертификатом и verify_tls: true проба падает на проверке сертификата — тоже down
  • Симптом один и тот же: «стенд открывается в браузере, а в дашборде красный HTTP»
  • verify_tls: false ослабляет проверку только для https://-адресов — на обычный http это не влияет

🛡️IIS: только от администратора

  • appcmd читает конфигурацию IIS — нужны права администратора
  • Членства в IIS_IUSRS недостаточно
  • Запускайте диспетчер «от имени администратора», иначе любая IIS-операция падает по правам
  • Если IIS-службы остановлены — диспетчер прямо посоветует поднять WAS/W3SVC, а не спишет всё на права

🐳Docker и k8s: нужен CLI

  • docker (и плагин compose) либо kubectl должны быть в PATH процесса диспетчера
  • Для k8s — доступный kubeconfig и права на get/scale/rollout/logs
  • Нет CLI — состояние честно помечается как «не выяснено», а не выдаётся за «жив»

💬Состояние объясняется словами

  • «контейнер приостановлен», «контейнер в цикле перезапуска», «деплоймент масштабирован в 0 реплик», «готово 1 из 3 реплик»
  • Вердикт по TCP-порту помечается как неопределённый — «зелёный по порту» не выдаётся за подтверждённый ответ CLI
  • Порт может держать инфраструктура (http.sys у IIS, Service у k8s) даже при погашенном стенде
06

Удалённые стенды и агент

Стенды на других хостах — виртуалки, серверы, контуры заказчика — управляются из того же дашборда через федерацию лёгких агентов. На хосте стенда поднимается standkit_agent, дашборд ходит к нему по HTTPS с Bearer-токеном.

Машина оператора standkit_hub федеративный клиент локальное ядро + N агентов один список стендов на экране Хост A (Windows) standkit_agent start / stop / restart / status / logs Хост N (Linux) standkit_agent то же ядро standkit внутри HTTPS + Bearer-токен, mTLS HTTPS + Bearer-токен, mTLS
Агент — это то же ядро standkit в крошечной HTTP-обёртке: поведение старта, стопа и логов идентично локальному. Стенд объявляется удалённым одним полем transport: "agent".
🛑 Правило №1. Агент по HTTP запускает и останавливает процессы на своём хосте — это поверхность удалённого исполнения кода по дизайну. Никогда не выставляйте его в недоверенную сеть открытым HTTP. Только TLS с клиентской аутентификацией (mTLS) — либо loopback внутри VPN / SSH-туннеля.

Связка --insecure + --host 0.0.0.0 на публичном IP означает, что Bearer-токен уходит открытым текстом через интернет. Поэтому: (1) порт закрыть firewall'ом на конкретные адреса (ufw allow from <IP> to any port 8765 proto tcp); (2) --insecure — только на первую проверку связи в доверенной сети, для постоянной работы TLS обязателен; (3) agent.envchmod 600, токен не показывать в консоли и на скриншотах, при утечке перевыпускать; (4) пароли БД в projects.json (db_password) не писать — только secret_ref_db.

Где что лежит на хосте агента

📄Реестр стендов агента

  • Агент читает свой projects.json — в нём перечислены стенды этого хоста, и они остаются transport: "local": агент поднимает их у себя. Удалённым стенд становится только в реестре оператора
  • Путь задаётся флагом --registry. Без флага действует тот же порядок, что у дашборда: BPMSOFT_PROJECTS_FILE → профиль пользователя → ./projects.json
  • Для службы всегда задавайте --registry абсолютным путём: у сервисной учётной записи свой профиль, и «тот самый» файл из вашей домашней папки она не увидит
  • Реестр агента и реестр оператора — разные файлы, синхронизировать их не нужно

🗂️Рекомендуемая раскладка

Linux /opt/standkit/venv/ # окружение с пакетом standkit /opt/standkit/projects.json # реестр стендов этого хоста /opt/standkit/tls/agent.crt # серверный сертификат агента /opt/standkit/tls/agent.key # ключ, chmod 600, владелец — сервисный аккаунт /opt/standkit/tls/clients-ca.crt # CA клиентских сертификатов (mTLS) /opt/standkit/logs/audit.log # аудит-лог /opt/standkit/run/ # pid-файлы стендов Windows C:\ProgramData\standkit\projects.json C:\ProgramData\standkit\tls\... C:\ProgramData\standkit\logs\audit.log

Каталоги должны принадлежать сервисной учётной записи агента, а не root/Администратору.

Установка агента — на каждом хосте стенда

Поставить пакет в изолированное окружение Тот же пакет standkit, что у оператора, — агент входит в него. Нужен Python 3.10+ и dotnet для запуска стендов (для iis/docker/k8s — соответствующий CLI). Системный Python в Linux не трогаем: службе нужен предсказуемый путь к интерпретатору, поэтому для неё — именно venv, а не pipx.
Linux sudo useradd --system --no-create-home --shell /usr/sbin/nologin standkit sudo mkdir -p /opt/standkit/{tls,logs,run} sudo python3 -m venv /opt/standkit/venv sudo /opt/standkit/venv/bin/pip install standkit sudo chown -R standkit:standkit /opt/standkit Windows (PowerShell от администратора) python -m venv C:\ProgramData\standkit\venv C:\ProgramData\standkit\venv\Scripts\pip install standkit
Положить реестр стендов этого хоста Файл /opt/standkit/projects.json (Windows — C:\ProgramData\standkit\projects.json). Достаточно стендов этой машины; секретов в нём нет — только ссылки. Стенды здесь остаются transport: "local". Написать файл можно руками или скриптом — рецепт Registry.add_existing() в разделе Реестр стендов.
{ "default": "", "projects": { "client-uat": { "transport": "local", "stand_dir": "/opt/bpmsoft/client-uat", "stand_dll": "BPMSoft.WebHost.dll", "dotnet": "dotnet", "stand_host": "127.0.0.1", "stand_port": 5000, "db_type": "postgres", "db_host": "127.0.0.1", "db_port": 5432, "db_name": "client_uat", "secret_ref_db": "standkit:client-uat:db" } } }
⚠️ В stand_host — адрес обращения к стенду с этого хоста, а не адрес слушания: 0.0.0.0 здесь даёт ложный http: down. Для контейнеров порт берётся из docker ps — см. Виды хостинга.
Задать токены агента Токены не передаются в командной строке открытым текстом — только ссылкой на секрет. Заведите разные токены для управления и для чтения. На сервере без keyring (обычный случай) значение задаётся переменной окружения; имя — STANDKIT_SECRET__ плюс ссылка в верхнем регистре, где всё, кроме букв и цифр, заменено на _.
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # сгенерировать токен # /etc/standkit/agent.env — chmod 600, владелец = пользователь, под которым идёт агент STANDKIT_SECRET__STANDKIT_CLIENT_UAT_AGENT_TOKEN=<control-токен> STANDKIT_SECRET__STANDKIT_CLIENT_UAT_AGENT_READONLY_TOKEN=<readonly-токен>
Ссылка на секрет в реестреПеременная окружения
standkit:survey9:dbSTANDKIT_SECRET__STANDKIT_SURVEY9_DB
standkit:survey9:agent-tokenSTANDKIT_SECRET__STANDKIT_SURVEY9_AGENT_TOKEN
standkit:client-uat:agent-readonly-tokenSTANDKIT_SECRET__STANDKIT_CLIENT_UAT_AGENT_READONLY_TOKEN
Три грабли подряд, все живые. (1) Забывают префикс STANDKIT_SECRET__ — имя выглядит правильным, а на старте SecretError. (2) Владелец agent.env должен совпадать с пользователем службы: файл 600 под root и агент под ubuntu дают Permission denied (sudo chown ubuntu:ubuntu /etc/standkit/agent.env). (3) Имя переменной, посчитанное через echo … | tr, получает лишний _ на конце из-за перевода строки — используйте printf '%s' или просто впишите имя руками. Keyring на headless-хосте обычно нерабочий (нет DBus и кошелька) — для сервера основной путь именно env. На десктопе можно положить в keyring: python -c "import getpass; from standkit.secrets import set_secret; set_secret('standkit:client-uat:agent-token', getpass.getpass())" (нужно дополнение [secrets]).
Выпустить сертификаты Нужны для доступа по сети: серверный сертификат агента и, крайне желательно, CA клиентских сертификатов для mTLS. Агент только потребляет готовые PEM — выпуск и ротация на вашей PKI. Минимальный самоподписанный вариант для закрытого контура:
openssl req -x509 -newkey rsa:4096 -nodes -days 365 \ -keyout /opt/standkit/tls/agent.key -out /opt/standkit/tls/agent.crt \ -subj "/CN=client-host" openssl req -x509 -newkey rsa:4096 -nodes -days 365 \ -keyout /opt/standkit/tls/clients-ca.key -out /opt/standkit/tls/clients-ca.crt \ -subj "/CN=BPMkitStand clients CA" sudo chmod 600 /opt/standkit/tls/*.key && sudo chown standkit:standkit /opt/standkit/tls/*
CN серверного сертификата — то имя хоста, по которому к агенту будет ходить дашборд.
Проверить запуск руками Сначала убедитесь, что агент поднимается и видит стенды, и только потом оформляйте службу. --token-ref обязателен — без него argparse просто откажет, это самая частая первая ошибка. Каталоги по умолчанию — ~/.standkit/run, ~/.standkit/logs, ~/.standkit/audit.log: у службы свой $HOME, поэтому --run-dir/--log-dir/--audit-log задавайте явно. Все ключи — --help.
sudo -u standkit env $(cat /etc/standkit/agent.env | xargs) \ /opt/standkit/venv/bin/python -m standkit_agent \ --host 0.0.0.0 --port 8765 \ --registry /opt/standkit/projects.json \ --token-ref standkit:client-uat:agent-token \ --readonly-token-ref standkit:client-uat:agent-readonly-token \ --tls-cert /opt/standkit/tls/agent.crt \ --tls-key /opt/standkit/tls/agent.key \ --tls-client-ca /opt/standkit/tls/clients-ca.crt \ --audit-log /opt/standkit/logs/audit.log
Для локальной отладки достаточно loopback без TLS: python -m standkit_agent --registry ./projects.json --token-ref standkit:client-uat:agent-token.
Оформить службой Linux: в пакете есть готовый least-privilege юнит — в репозитории standkit_agent/deploy/standkit-agent.service (в установленный пакет он не входит, возьмите из репозитория или напишите по образцу ниже). Подставьте свои пути, добавьте EnvironmentFile с токенами и включите службу.
[Service] Type=simple User=standkit Group=standkit WorkingDirectory=/opt/standkit EnvironmentFile=/etc/standkit/agent.env ExecStart=/opt/standkit/venv/bin/python -m standkit_agent \ --host 0.0.0.0 --port 8765 \ --registry /opt/standkit/projects.json \ --token-ref standkit:client-uat:agent-token \ --tls-cert /opt/standkit/tls/agent.crt \ --tls-key /opt/standkit/tls/agent.key \ --tls-client-ca /opt/standkit/tls/clients-ca.crt \ --run-dir /opt/standkit/run \ --log-dir /opt/standkit/logs \ --audit-log /opt/standkit/logs/audit.log Restart=on-failure NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/opt/standkit/run /opt/standkit/logs
В ExecStart — только абсолютные пути: у службы нет ни ~, ни вашего PATH. User= должен совпадать с владельцем agent.env, иначе Permission denied на файле 600. Каталоги --run-dir/--log-dir задаём явно — иначе агент уйдёт в $HOME сервисной учётки, которого при ProtectHome=yes фактически нет.
sudo cp standkit-agent.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now standkit-agent systemctl status standkit-agent
Windows: проще всего через NSSM под выделенной учётной записью (никогда не под LocalSystem): nssm install standkit-agent C:\ProgramData\standkit\venv\Scripts\python.exe -m standkit_agent --registry C:\ProgramData\standkit\projects.json --token-ref standkit:client-uat:agent-token …, затем nssm set standkit-agent ObjectName .\standkit-agent <пароль>. Учётной записи нужно право «Log on as a service»; полные заметки — standkit_agent/deploy/windows-service.md.
Открыть доступ и проверить с машины оператора Firewall: входящий порт агента — только для адресов управляющего контура, не «любой адрес». Проверять лучше readonly-токеном, чтобы ничего не запустить.
sudo ufw allow from <IP оператора> to any port 8765 proto tcp # Linux / macOS curl --cacert agent.crt --cert client.crt --key client.key \ -H "Authorization: Bearer <readonly-токен>" \ https://client-host:8765/stands
В PowerShell curl — это алиас Invoke-WebRequest, флаги -s -H он не понимает и падает с ParameterBindingException. Нужен либо настоящий curl.exe, либо родная команда:
curl.exe -s -H "Authorization: Bearer <токен>" https://client-host:8765/stands Invoke-RestMethod -Uri https://client-host:8765/stands ` -Headers @{ Authorization = "Bearer <токен>" }

Подключение из дашборда

🔗Запись в реестре оператора

{ "projects": { "client-uat": { "transport": "agent", "agent_url": "https://client-host:8765", "agent_secret_ref": "standkit:client-uat:agent-token", "stand_dir": "/opt/bpmsoft/client-uat", "stand_port": 5000, "db_type": "postgres", "db_host": "db-host", "db_port": 5432, "db_name": "client_uat" } } }
  • Тот же токен задайте секретом и на стороне оператора
  • Стенд появится в общем списке, в колонке «Транспорт» будет agent
  • Кнопки старт/стоп/рестарт, статус и логи работают так же — запросы прозрачно уходят к агенту

🧾Что умеет агент

ПутьСкоупДействие
GET /standsreadсписок стендов реестра агента
GET /stand/{имя}/statusreadhealth-статус
GET /stand/{имя}/logs?n=100readпоследние N строк лога
POST /stand/{имя}/startcontrolзапустить
POST /stand/{имя}/stopcontrolостановить
POST /stand/{имя}/restartcontrolперезапустить
POST /stand/{имя}/adoptcontrolусыновить процесс, поднятый вне диспетчера

Эндпоинта /health у агента нет — не ищите. Живость проверяется GET /stands с токеном; без заголовка Authorization любой маршрут ответит отказом. Мониторингу отдавайте readonly-токен: он видит статусы и логи, но ничего не запускает и не гасит.

409 adopt_required — это фича, а не ошибка. Так агент отвечает на stop/restart стенда, поднятого мимо диспетчера: он возвращает описание найденного процесса-кандидата и ничего не трогает. Согласие передаётся явно — повторить с ?force=1 либо вызвать POST /stand/<имя>/adopt. Тихого kill по номеру порта не бывает.

🎛️ Вкладка «Локальный агент» в дашборде запускает и останавливает агента на текущей машине с параметрами из «Настроек» — она нужна администратору хоста, где живут стенды. Если вы управляете стендами с той же машины, где они запущены (transport: local), агент не нужен вообще: ядро работает со стендами напрямую. Блок «Агент (расширенное)» в настройках скрыт, пока в реестре нет удалённых стендов.
07

Безопасность

И дашборд, и агент управляют процессами — к обоим применяется модель угроз управляющего контура, а не обычного прикладного API. Защита включена по умолчанию; ослабить её можно только явным флагом.

🖥️Дашборд: secure-defaults

  • Слушает только 127.0.0.1
  • Fail-closed: внешний адрес без TLS — хаб не стартует; обход только явным --insecure
  • Сессионный токен в URL, дальше — сессионная cookie
  • Мутации защищены проверкой токена и заголовка Origin (CSRF)
  • Стоп, Рестарт и Очистка Redis — с подтверждением

📡Агент: secure-defaults

  • Bind по умолчанию 127.0.0.1, fail-closed на внешнем адресе без TLS
  • TLS 1.2+ с AEAD/ECDHE-шифрами, сжатие выключено; mTLS отсекает чужого клиента на хендшейке
  • Bearer-токен сравнивается только константным по времени сравнением
  • Два скоупа: control (управление + чтение) и readonly
  • Lockout по IP после серии неудач; append-only JSON-аудит — без токенов и секретов
  • Лимиты тела запроса, таймауты, валидация имени стенда

Усыновление: три улики сразу

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

  • порт процесса совпадает со stand_port записи;
  • имя образа входит в allowlist (dotnet, BPMSoft.WebHost, w3wp);
  • рабочий каталог, путь исполняемого файла или командная строка ведут внутрь stand_dir.

Не совпало хотя бы одно — усыновления нет, вы получите отказ с указанием, какой процесс занимает порт. Тихого kill не бывает: без явного подтверждения процесс не трогается, согласие не запоминается и не переиспользуется. У агента adopt отнесён к управляющим действиям — readonly-токен усыновлять не может, каждая попытка идёт в аудит.

Рекомендуемые топологии

1️⃣Loopback + управляющий контур

  • Агент слушает 127.0.0.1, доступ — через SSH-туннель / VPN / WireGuard
  • Порт агента наружу не публикуется вообще
  • Самый простой безопасный вариант, он же дефолт

2️⃣mTLS за firewall

  • TLS + обязательный клиентский сертификат (--tls-client-ca)
  • Подключится только держатель сертификата от доверенного CA
  • Источники ограничены allowlist на firewall

Категорически нельзя

  • Публиковать порт агента в интернет
  • Выставлять его в недоверенную сеть открытым HTTP
  • Запускать агента под root / LocalSystem
  • Держать токен в открытых конфигах и в истории команд

Чек-лист перед продом

  • Агент на loopback либо с TLS (лучше mTLS); порт не в интернете
  • Отдельный сервис-аккаунт без root, hardening из systemd-юнита применён
  • Токены криптостойкие, в secret-store; control и readonly — разные
  • Firewall-allowlist источников
  • Аудит-лог пишется, собирается и ротируется; мониторятся denied и 429
  • Права на ключ TLS корректные (0600), ротация продумана
  • --insecure на этом хосте не используется
🔎 Полная модель угроз, разбор активов и остаточные пункты — SECURITY.md в репозитории. О проблемах безопасности сообщайте приватно мейнтейнеру, не через публичные issue.
08

Траблшутинг

Сначала таблица «симптом → причина → что сделать», ниже — разборы частых случаев. Общее правило: диспетчер старается объяснять причину словами, поэтому читайте текст ошибки целиком — в нём обычно уже есть рецепт.

Быстрая таблица

СимптомВероятная причинаЧто сделать
standkit-hub: «не является внутренней или внешней командой» / command not found Каталог, куда pip кладёт консольные команды (Scripts), не в PATH Запускать модулем: python -m standkit_hub. Разбор — плитка ниже
Linux: error: externally-managed-environment при pip install Системный Python защищён от установки пакетов (PEP 668) — так во всех свежих дистрибутивах Ставить в изолированное окружение: pipx install "standkit[secrets]" или venv (см. Установка). Не ломать защиту флагом --break-system-packages
Linux: поставил через pipx, команды нет ~/.local/bin не в PATH pipx ensurepath и новый вход в shell
Агент пишет, что секрет не найден (служба) У сервисной учётной записи нет доступа к keyring — типично для headless-хоста Передать токен переменной STANDKIT_SECRET__… через EnvironmentFile (chmod 600) — см. Удалённые стенды
SecretError при, казалось бы, верной ссылке Забыт префикс STANDKIT_SECRET__ либо лишний _ в конце от echo … | tr Сверить имя по таблице в разделе Удалённые стенды; вместо echoprintf '%s'
/etc/standkit/agent.env: Permission denied Файл 600 принадлежит root, а служба идёт под другим пользователем sudo chown <пользователь службы> /etc/standkit/agent.env
the following arguments are required: --token-ref У агента нет значения по умолчанию для control-токена Добавить --token-ref standkit:<стенд>:agent-token
No module named 'standkit_agent' в скрипте Скрипт запущен системным python3, а пакет стоит в venv/pipx Звать питон окружения: /opt/standkit/venv/bin/python или ~/.local/share/pipx/venvs/standkit/bin/python
RegistryError: Стенд не найден Регистрация не выполнялась — или ушла в другой файл реестра Проверить путь: агенту всегда задавать --registry явно; рецепт регистрации — в разделе Реестр стендов
http: down при живом стенде stand_host=0.0.0.0, порт не опубликован наружу — или стенд за TLS без stand_scheme Взять адрес обращения с хоста и левый порт из docker ps; для стенда за TLS — stand_scheme: "https"verify_tls: false для self-signed), см. Виды хостинга
Статус не изменился после правки реестра Агент читает projects.json при старте sudo systemctl restart standkit-agent
409 adopt_required от агента Стенд поднят вне диспетчера — согласия на усыновление не было Это штатный отказ: повторить с ?force=1 или вызвать /adopt
No module named standkit_hub Пакет поставлен в другой интерпретатор или в venv, который не активирован Ставить тем же питоном, которым запускаете: python -m pip install standkit
После обновления интерфейс прежний Старый процесс хаба ещё жив либо браузер держит статику Остановить хаб (Ctrl+C), запустить заново, обновить вкладку Ctrl+F5
Дашборд открылся не на 8770Порт занял чужой сервис Прочитать строку в консоли — там реальный порт; либо освободить 8770
Баннер «нет связи», таблица погашенаПроцесс диспетчера остановлен или перезапущен Запустить standkit-hub заново и обновить вкладку
«Сессия не подтверждена» (401)Cookie не пережила полное закрытие браузера или хаб перезапущен Перезапустить диспетчер и открыть URL с токеном из консоли
IIS-операция падает по правамДиспетчер запущен без elevation Запустить «от имени администратора». Членства в IIS_IUSRS недостаточно
IIS: «состояние не выяснено», службы IISОстановлены WAS / W3SVC sc start WAS или iisreset /start
Стоп/Рестарт отвечает отказом с описанием процессаСтенд поднят вне диспетчера, pid неизвестен Подтвердить усыновление — или остановить процесс тем же способом, каким запускали
Кнопка «Очистить Redis» неактивнаRedis у стенда не настроен в реестре Добавить redis_host / redis_port / redis_db в запись
Агент не стартует, пишет про fail-closedВнешний адрес без TLS Добавить --tls-cert/--tls-key (или --insecure — только dev)
401 unauthorizedТокен у оператора ≠ токену на агенте Сверить секрет по одному и тому же *-ref с обеих сторон
403 forbidden: insufficient scopeУправление readonly-токеном Использовать control-токен (--token-ref)
429 too many failed attemptsСработал lockout по IP Подождать окно блокировки, проверить токен
В дашборде стенд с ошибкой связиАгент недоступен, таймаут или сертификат Проверить сеть и firewall, срок действия и CN сертификата
TLS-хендшейк отклонёнНет валидного клиентского сертификата (mTLS) Выпустить клиентский сертификат из того же CA, что в --tls-client-ca

Разборы частых случаев

⌨️ standkit-hub — «команда не распознана»частое

Пакет установлен, но консоль команду не видит. Причина почти всегда одна: pip кладёт консольные команды (standkit-hub, standkit-agent) в свой каталог Scripts, а его нет в PATH. Особенно часто — при установке с ключом --user и в свежем окружении.

  • Работает всегда — запуск модулем. Ключи те же, что у standkit-hub:
    python -m standkit_hub
    На Windows, если python тоже не резолвится, — через лаунчер: py -m standkit_hub. Агент точно так же: python -m standkit_agent.
  • Узнать, куда положены команды:
    python -c "import sysconfig; print(sysconfig.get_path('scripts'))"
    Вывод — тот самый каталог; в нём должен лежать standkit-hub.exe (Windows) или standkit-hub (Linux). Нет файла — пакет поставлен другим питоном, см. следующий пункт.
  • Добавить каталог в PATH. Windows: «Параметры» → «Переменные среды» → Path → добавить путь из команды выше, затем открыть новую консоль (старая PATH не перечитывает). Linux: строка export PATH="$HOME/.local/bin:$PATH" в ~/.bashrc.
  • Сверить интерпретаторы. Ставить нужно тем же питоном, которым запускаете:
    python -m pip install standkit python -m pip show standkit
    Если pip show ничего не находит — установка ушла в другой Python (частый случай: несколько версий или venv), поставьте заново командой выше.
  • Ярлык вместо команды. Один раз создать ярлык на рабочем столе — и консоль больше не нужна:
    python -m standkit_hub --install-shortcut
🟢 Процесс «жив», а стенд не открывается
  • Посмотрите колонку HTTP: жив процесс ≠ отвечает web-хост, стенд может ещё прогреваться
  • Откройте панель «Текущее состояние» — в хвосте лога обычно видно, на чём он встал (БД, лицензия, порт)
  • Если вердикт помечен как неопределённый, стенд «зелёный по порту»: порт держит инфраструктура (http.sys у IIS, Service у k8s), а сам стенд погашен
  • Проверьте пробы БД и Redis — часто стенд не поднимается именно из-за них
⏳ Стенд долго стартует, спиннер не гаснет
  • Это нормальное поведение: диспетчер ждёт реального HTTP-ответа, а не факта создания процесса
  • BPMSoft на .NET Framework прогревается заметно дольше, чем kestrel-стенд
  • Остановка пула IIS у прогретого стенда может занять десятки секунд — у изменяющих IIS-операций свой увеличенный таймаут
  • Если ожидание затянулось — смотрите хвост лога, а не перезапускайте вслепую
📜 В панели состояния пусто, хотя логи есть
  • BPMSoft под .NET Framework пишет логи в подпапки-даты (Logs\ГГГГ_ММ_ДД\Application.log) — они читаются
  • Плоские логи kestrel имеют приоритет; если в каталоге нет ни того, ни другого, панель останется пустой
  • Кнопка «Открыть папку логов» откроет каталог в проводнике — там видно, пишется ли файл вообще
  • Второй источник в меню кнопки — логи BPMkit-проекта; он гаснет, если проекта у стенда нет
👯 Кажется, диспетчер запущен дважды
  • Так больше не бывает: перед стартом проверяется, не работает ли диспетчер уже, и второй экземпляр не поднимается
  • Ярлык стартует хаб без окна, поэтому закрытие браузера процесс не останавливает
  • Полностью остановить — Ctrl+C в консоли хаба или снять процесс
🔑 Секрет задан, но операция всё равно падает
  • Проверьте, что ссылка в реестре и ключ, под которым вы клали значение, совпадают дословно
  • Значение ищется по порядку: переменная окружения → keyring → фолбэк; переменная перекрывает keyring
  • Для агента ссылка должна совпадать на обеих сторонах — у оператора и на хосте агента
  • Хранилище секретов ставится extra: pip install "standkit[secrets]"
🌡️ Данные в таблице подозрительно старые
  • Возраст снапшота показывается, только когда данные заметно устарели или пробы ещё не выполнялись
  • Частая причина — недоступный стенд или агент за firewall: опрос агентов последовательный, таймауты складываются
  • Кнопка «Обновить» форсирует проход; интервал автообновления — в «Настройках»
09

Границы и что дальше

BPMkitStand — молодой проект и честно об этом пишет. Ниже — что входит в бесплатную версию, что относится к платному BPMkit и что пока каркас.

Бесплатно (MIT) и платно

BPMkitStand — бесплатно, MITBPMkit — платно
Старт / стоп / рестарт стендаПровижининг нового стенда «с нуля»
Health-пробы: процесс, HTTP, TCP-порт БД и RedisГлубокие операции с БД: создание, бэкап, восстановление
Хвост и открытие логовДеплой пакетов, кастомизация JS и C#
Реестр стендов: чтение и записьГенерация документов, git-онбординг пакетов
Secret-first доступ к секретамАдминистративные операции над живым стендом: роли, права, данные
Федерация агентов, TLS/mTLS, аудитАвтообновление MCP и контроль лицензии — Companion

🟡 каркас / заглушки

  • Нет CLI регистрации стенда — только кнопка в дашборде, правка projects.json или Registry.add_existing() из Python
  • Глубокие пробы БД и Redis (SELECT 1 / PING) — пока только проверка порта
  • Живой follow лога — сейчас периодический tail
  • UI-индикатор host_kind в таблице
  • Браузинг и скачивание логов удалённых стендов

🔴 не реализовано

  • Транспорты ssh и winrm — схема допускает, логики нет
  • PKI: выпуск и ротация сертификатов, ротация токенов «на лету»
  • Per-stand ACL: скоуп бинарный на весь реестр агента
  • Маппинг CN клиентского сертификата в скоуп
  • Параллельный опрос агентов (сейчас последовательный)

🟢 где смотреть правду

  • docs/BACKLOG.md — «упомянуто в коде, но не реализовано», со ссылками на символы
  • docs/ROADMAP.md — что сделано, ближайшее, бэклог
  • docs/CHANGELOG.md — история по версиям
  • docs/adr/ — почему сделано именно так
📈 Документ описывает поведение версии 0.6.x. Если что-то в кукбуке разошлось с тем, что вы видите на экране, источник правды — docs/CHANGELOG.md в репозитории; сообщите о расхождении, и кукбук поправят.