Мобильное приложение (Android и iOS)
Нативный клиент хаба для телефона: тот же API, что у веб-интерфейса, но экраны сделаны под палец, а не под мышь. Сам хаб не меняется — приложение просто ещё один способ с ним говорить, как браузер. Один код на Kotlin Multiplatform для Android (8 и новее) и iOS (16 и новее): APK приложен к релизам, iOS-версия пока собирается из исходников — в App Store не публикуется.
Бета
Приложение новое. Всё, что оно умеет, проверено разбором настоящих ответов сервера в тестах, но на разных устройствах его гоняли мало — о странностях пишите в issues.
Установка
К каждому релизу nkt приложен подписанный nkt-android-<версия>.apk (сумма — в SHA256SUMS). Скачайте его на телефон и откройте — Android попросит разрешить установку из этого источника. Обновление — так же: новый APK встаёт поверх, вход и настройки сохраняются.
Версия приложения — версия nkt: приложение 1.11.150 написано под хаб 1.11.150. Обе видны в «О системе» приложения; если хаб новее, приложение работает, но новых разделов хаба в нём может не быть.
Язык
Интерфейс — на русском и английском. По умолчанию — язык телефона (русский; любой другой — английский). Сменить можно на экране входа и в «О системе», сразу, без перезапуска. Сообщения сервера — ошибки, журналы заданий, тексты оповещений и проблем — приходят на том же языке.
Вход
Адрес хаба — тот же, что в браузере (http://192.168.1.10:8077 или https://hub.example.com), логин и пароль — учётная запись хаба. Сессия переживает перезапуск приложения.
Если хаб отдаёт самоподписанный сертификат (NKT_TLS_ENABLED), приложение запоминает его при первом входе и дальше сверяет: подменённый сертификат — ошибка, а не тихое подключение. Отпечаток виден в «О системе»; после переустановки хаба запомненный сертификат сбрасывается там же.
Что внутри



Навигация. Внизу (на планшете и в ландшафте — сбоку) — разделы хаба: хосты, оповещения, мониторинг, задания, «ещё» (fail2ban, выкладка, о системе). Заголовок верхней панели — хлебные крошки, например Хосты › web-1 › Контейнеры › acme-app: каждое звено возвращает на свой уровень, а системная кнопка «Назад» — на шаг.
Маленький экран. В «О системе» — масштаб интерфейса: 80, 90 (по умолчанию), 100 или 115 % либо свой процент от 50 до 100 — текст и отступы уменьшаются вместе, и на экран помещается больше. Ряды кнопок переносятся на следующую строку, а не уходят за край. Поворот экрана не прерывает терминал и консоль, а в ландшафте телефона разделы хаба — лентой сбоку, чтобы не отнимать высоту.

Графики — как в вебе: «Нагрузка» хоста, история цели в «Доступности» (нажатие на цель — доступность в % и задержка за сутки, неделю, месяц) и графики хоста в «Мониторинге» хаба (нажатие на хост — CPU, память, load, заполнение дисков за период от суток до года). Палец на графике отмечает ближайший отсчёт и показывает его время и значения всех линий; ведите вбок — отметка идёт следом. Подсказка остаётся после того, как палец отпущен, и убирается касанием мимо графика; вверх-вниз страница прокручивается как обычно.
Фоновые операции. Пока где-то идёт задание — на хабе или на любом хосте, кем бы оно ни было запущено, — в верхней панели значок с их числом. Нажатие показывает список (хост, задание, шаг и полоса), выбор открывает живой журнал задания.
Список хостов — по группам: состояние, число проблем, роль в Kubernetes, sudo («без пароля» — красным), отставшая от хаба версия nkt. Значок списка сверху — группы (создать, переименовать, удалить).
Управление хостами (администратору): «+» добавляет хост — адрес, пользователь SSH и вход паролем, своим ключом или ключом хаба (тогда приложение покажет строку для authorized_keys на хосте), и по галочке сразу ставит nkt. В меню «⋮» строки — установка, обновление до версии хаба или переустановка nkt (заданием хаба с живым журналом), журнал последней установки, перенос в группу и удаление: только из хаба или вместе с nkt на самом хосте (служба, данные, доступ хаба; вход по паролю возвращается). Если на хосте уже стоит чужой nkt, приложение спросит, ставить ли поверх.
Хост открывается списком разделов по группам: обзор, проблемы (у каждой — переход туда, где она решается), терминал и btop, логи, сервисы, контейнеры, уязвимости, ClamAV, доступность, нагрузка, конфигурации, firewall, сертификаты, интерфейсы, карта ресурсов, пользователи, задания, fail2ban, журнал. Детали — отдельными страницами: контейнер («Инспект» с переменными окружения), файл конфигурации (правка, история, откат), задание (живой журнал и отмена), конвейер (история и откат). В меню «⋮» хоста — перезагрузка: сначала приложение покажет, что работает и что не поднимется само. В «Доступности» — свои цели (ping, TCP, HTTP, HTTPS — адрес, которого нет в конфигурациях хоста), у каждой цели — «проверить сейчас» и включение/выключение проверок. Остановка и перезапуск инстанса LXD и «Сохранить» у образов Docker идут заданием хоста — сразу открывается его журнал.
Терминал занимает весь экран. Если на хосте есть tmux, сессия открывается в нём и переживает обрыв связи: приложение само переподключается к той же сессии. На панели клавиш после стрелок — Enter. Кнопка «tmux ▾» на панели клавиш — действия tmux без набора Ctrl+B …: новое окно, следующее и предыдущее, список окон, разделение, прокрутка, отсоединиться, мышь вкл/выкл. Если tmux нет, открывается обычная оболочка с предложением установить tmux — установка идёт с живым выводом.


Консоль контейнера Docker и Podman, инстанса LXD и машины libvirt — кнопка «Консоль» на карточке запущенного, в «Контейнерах». Открывается тот же полноэкранный терминал. Docker и Podman сначала спрашивают пользователя (пусто — пользователь образа по умолчанию), у машины это последовательная консоль (virsh console): приложение само нажимает Enter, чтобы появилось приглашение входа, выход из virsh — Ctrl+]. Консоль, как и терминал, требует NKT_TERMINAL_ENABLED=true на хосте — иначе приложение так и скажет.
Экран машины (Android) — кнопка «Экран» на карточке запущенной машины libvirt (VNC, если он у машины есть, иначе SPICE) и виртуальной машины LXD (SPICE). Открывается на весь экран та же страница, что в веб-интерфейсе (noVNC и spice-html5), во встроенном браузере и уже с вашим входом. Касание — щелчок, движение пальцем — мышь, двумя пальцами — масштаб. Картинка занимает весь экран, а всё управление — в плавающем меню: круглая кнопка в углу (цвет рамки — состояние: зелёный — подключено, жёлтый — подключение, красный — ошибка), её можно перетащить пальцем, место запоминается. В меню — Esc, Tab, стрелки, ⌫, ⏎, Ctrl+Alt+Del и «⌨ Клавиатура» (экранная клавиатура: набранное уходит в машину как набор на английской раскладке), масштаб и подсказка; касание мимо меню его сворачивает. Ошибка подключения — полоской сверху на несколько секунд.
Масштаб, шаг 10%: в портрете 50–150%, 100% — картинка целиком вписана в экран; в ландшафте 50–200%, 150% — ровно по ширине экрана («↔»), 200% — на треть шире экрана. «⤢» — снова вписать целиком; помнится для каждой машины и ориентации. При масштабе больше 100% «Двигать» включает перелистывание картинки пальцем (касания тогда в машину не уходят).
Машина в общем списке хостов (внутри хоста, под «+» в вебе) — с кнопкой «Экран», он открывается через её хост. Если на машине нет nkt (Windows или nkt не поставлен), вместо пустых разделов — «на машине нет nkt» и тот же «Экран». Встроенный браузер открывает только страницы хаба; самоподписанный сертификат хаба он принимает, только если это тот, что закреплён при входе (на Android 10 и новее). На iOS экрана пока нет.
Разделы хаба:
| Раздел | Что |
|---|---|
| Оповещения | журнал хаба с фильтром по виду; «К хосту» открывает хост сразу на нужном разделе |
| Задания хаба | выкладки, баны по парку и другие задания хаба с живым журналом и отменой |
| Мониторинг | на что обратить внимание (диски, утечки, падение доступности), CPU, память и диски хостов, узлы кластеров |
| Fail2ban | забаненные адреса по всем хостам; бан и разбан везде — заданием хаба |
| Выкладка | конвейеры: выложить, сухой прогон, журнал, история с откатом, старые стеки на прежнем хосте |
Создание и правка конвейеров, профилей, сценариев, кластеров, экспорт и импорт хаба, настройки — в веб-интерфейсе: это редакторы с диффом, а не дело для телефона.
Уведомления
В «Оповещениях» — переключатель «Уведомления на телефоне». Включённый, он раз в 15 минут (чаще Android фоновые проверки не пускает) спрашивает у хаба новые события и показывает те виды, о которых хаб оповещает и в Slack/Telegram — выбор берётся из настроек оповещений хаба, отдельного списка в телефоне нет. Нажатие на уведомление открывает хост на разделе события. Первая проверка только запоминает, где журнал сейчас, — старые события не всплывают.
Push-сервисов (Firebase) приложение не использует: хабу не нужен ни Google-аккаунт, ни ключ сервера, а телефон ходит к хабу сам, как браузер.
Сборка из исходников
Код — в каталоге mobile/ (устройство — в mobile/README.md).
mobile/scripts/setup-toolchain.sh # JDK 17 и Android SDK в ~/.local, без root
mobile/scripts/build.sh testDebugUnitTest # тесты
mobile/scripts/build.sh # отладочный APKAPK — в mobile/androidApp/build/outputs/apk/debug/androidApp-debug.apk. Отладочная сборка подписана ключом разработчика и не встанет поверх релизной (и наоборот) — сначала удалите установленное приложение.
iOS собирается только на macOS с Xcode:
brew install xcodegen
cd mobile/iosApp && xcodegen generate && open iosApp.xcodeprojПоставить на свой iPhone можно из Xcode с бесплатной учётной записью Apple; публикация (TestFlight, App Store) требует платного аккаунта Apple Developer. Сборку приложения в CI (.github/workflows/mobile.yml) запускают вручную: Actions → «mobile» → Run workflow, платформа — android (тесты и отладочный APK), ios (сборка под симулятор) или both; из терминала — gh workflow run mobile.yml --ref main -f platform=ios.
Уведомления на iOS приходят «по возможности»: фоновое обновление запускает сама система, когда сочтёт нужным, и раз в 15 минут его не гарантировать.
Подписанный APK в релизе
Релиз собирает его сам (.github/workflows/release.yml, задание android-apk), если в секретах репозитория есть ключ подписи. Ключ заводится один раз:
keytool -genkeypair -keystore ~/nkt-release.jks -alias nkt \
-keyalg RSA -keysize 4096 -validity 36500
base64 -w0 ~/nkt-release.jks > ~/nkt-release.b64 # → секрет ANDROID_KEYSTORE_BASE64keytool — из JDK (после mobile/scripts/setup-toolchain.sh он в ~/.local/jdk-17/bin). Содержимое .b64 копируйте не из окна терминала, а в буфер обмена напрямую — clip.exe < ~/nkt-release.b64 в WSL, xclip -sel clip < ~/nkt-release.b64 или pbcopy < … — иначе в секрет попадают переносы строк.
Секреты: ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, и ANDROID_KEY_PASSWORD — только если пароль ключа отличается. Псевдоним ключа — переменная репозитория ANDROID_KEY_ALIAS (Variables, а не Secrets), по умолчанию nkt. Без секретов релиз выходит без APK; ключ, который не открывается паролем, сборка сообщает сразу. Ключ храните отдельно и надёжно: потерянный ключ — это невозможность обновить уже установленные приложения.
Бета-релиз только приложения
Чтобы выложить приложение отдельно от nkt: Actions → android-beta-release → Run workflow. Он гоняет тесты, подписывает APK тем же ключом и создаёт релиз Android <версия> (beta) с тегом android-v<версия>-beta, помеченный pre-release, с nkt-android-<версия>-beta.apk и SHA256SUMS; в «О системе» приложения версия видна как <версия>-beta. Хабы такой релиз не видят и обновляться на него не предлагают. Повторный запуск той же версии заменяет APK в том же релизе (и обновляет его заголовок).
Локально подписанная сборка — с теми же переменными окружения (NKT_ANDROID_KEYSTORE — путь к .jks, остальные — пароли и псевдоним):
NKT_ANDROID_KEYSTORE=$HOME/nkt-release.jks NKT_ANDROID_KEY_ALIAS=nkt \
NKT_ANDROID_KEYSTORE_PASSWORD=… NKT_ANDROID_KEY_PASSWORD=… \
mobile/scripts/build.sh :androidApp:assembleRelease