NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Философия: почему 29, а не 290 Язык сценариев NKT — построчный декларативный язык развёртывания. Он не пытается быть полноценным языком программирования: в нём нет условий, циклов и функций. Это осознанный выбор.

Принцип: язык решает одну задачу — «Разверни сервис, проверь, что он работает, и перенеси, если нужно». Для этой задачи не нужны сотни абстракций. Нужны 29 глаголов.

Каждый глагол покрывает атомарное действие:

  • Установить пакет
  • Управлять службой
  • Развернуть стек
  • Создать ВМ
  • Перенести контейнер
  • Проверить доступность

Из 29 глаголов собираются сценарии любой сложности. Если нужна логика сложнее — есть описания сервисов и внешние плагины, которые расширяют язык без изменения его ядра.

Все 29 команд

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

set создаёт переменную для подстановки как ${ИМЯ}. Вариант ask спрашивает значение при запуске и не записывает его в текст и журнал — для паролей и токенов.

param создаёт параметр, значение которого спрашивается при запуске с подсказкой. Так один сценарий разворачивает разные стенды. Незаполненные параметры сверяются при запуске (Script.Unresolved).

Пример:

set IMAGE ubuntu-24.04 set DB_PASS ask param ADDR "адрес нового сервера" param NAME "имя хоста в хабе" host ${NAME} ${ADDR} user root password ask
NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

group создаёт группу хостов на хабе. С профилем — машины, создаваемые в ней, делаются по нему.

host добавляет хост на хаб по SSH — то же, что «Добавить хост» в интерфейсе. Варианты аутентификации: password ask, password "…", key hub.

install ставит nkt на перечисленные хосты и ждёт, пока каждый ответит.

wait имеет пять вариантов:

wait ХОСТ online [ДЛИТЕЛЬНОСТЬ] wait ХОСТ port ПОРТ [ДЛИТЕЛЬНОСТЬ] wait ХОСТ http URL [КОД] [ДЛИТЕЛЬНОСТЬ] wait ХОСТ service ИМЯ active [ДЛИТЕЛЬНОСТЬ] wait url URL [КОД] [ДЛИТЕЛЬНОСТЬ]

Последний вариант — проверка адреса снаружи: запрос с хаба по настоящему имени (DNS, шлюз, сертификат проверяются), хост указывать не нужно.

Пример:

host web1 192.0.2.10 user root password ask group web-farm host web2 192.0.2.11:2222 user deploy key hub install web1 web2 wait web1 online 5m wait web1 port 443 30s wait web1 service nginx active wait url https://git.example.com/api/healthz 200 10m
NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Пакеты ставятся одной транзакцией. Файрвол работает через ufw или firewalld — в зависимости от того, что установлено на хосте.

Пример:

on web1 web2 web3 packages install nginx htop on web1 web2 web3 service nginx enable on web1 web2 web3 service nginx start on web1 web2 web3 firewall allow 80/tcp on web1 web2 web3 firewall allow 443/tcp
NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

docker stack имеет четыре действия:

  • up — поднять (скачав образы, если нужно)
  • down — остановить и убрать
  • start — запустить имеющееся без обращения к registry
  • stop — остановить, оставив контейнеры

С блоком до end команда сначала записывает compose‑файл по указанному пути:

on web1 docker stack /srv/app/docker-compose.yml up services: web: image: nginx:alpine ports: - "8080:80" end

Стек, выложенный конвейером, запускают и останавливают командой deploy КОНВЕЙЕР start|stop — «Проверить» об этом напомнит.

vm create создаёт виртуальную машину из облачного образа. Опции: cpu, mem, disk, user, network, profile, install (поставить nkt внутрь).

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

cert issue работает через certbot --standalone: службы на 80/443 останавливаются на время проверки и запускаются снова.

git clone для приватных репозиториев: token ask (спросят при запуске) или key hub (deploy‑ключ хоста).

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

k8s create — самая «тяжёлая» команда языка. Создаёт кластер на виртуальных машинах: машины из облачного образа, control plane, присоединение узлов, ожидание Ready, проброс портов, скачивание kubeconfig на хаб.

Опции: flavor (k3s или kubeadm), nodes (размещение по хостам), image, cpu/mem/disk, network, cni, no-kube-proxy, version, expose, api/http/https порты.

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

pipeline сохраняет конвейер выкладки. Значения env: уходят в секреты конвейера. generate: создаёт значение (пароль, токен) на хабе.

deploy выкладывает конвейер и ждёт итога. С start или stop — запускает или останавливает уже выложенное на всех его хостах.

migrate переносит стек на другой хост: план с препятствиями до остановки стека, остановка, данные через хаб, запуск на новом. Упавший перенос откатывается сам. Опции: switch-fly, switch-pipeline, clean-leftovers.

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

k8s apply в сухом прогоне выполняет kubectl diff и показывает изменения без применения.

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Виды маршрутов: https, http, tcp, udp, tcpudp. Цели: targets cluster КЛАСТЕР port N (все узлы кластера) или target ХОСТ container|vm|lxd ИМЯ ПОРТ (конкретная цель).

Новый маршрут записывается и применяется. У существующего цели меняются плавно: новые добавляются и проверяются, прежние выводятся. В сухом прогоне — проверка карты с маршрутом.

Реальный пример: полный цикл

Из репозитория, examples/scripts/forgejo-fly.nkt:

# Forgejo за Traffic Fly: выкладка и перенос на другой хост param FROM "хост, где Forgejo будет выложен сначала" param TO "хост, куда его перенести" param DOMAIN "имя Forgejo — A-запись на адрес шлюза Traffic Fly" pipeline forgejo repo: https://github.com/piqab/nkt.git ref: main action: compose compose: file: examples/forgejo/deploy/docker-compose.yml project: forgejo hosts: [${FROM}] wait_timeout: 5m env: POSTGRES_PASSWORD: generate:password FORGEJO_DOMAIN: ${DOMAIN} FORGEJO_ROOT_URL: https://${DOMAIN}/ FORGEJO_ADMIN_USER: gitadmin FORGEJO_ADMIN_PASSWORD: generate:password fly: - route: forgejo-web kind: https names: [${DOMAIN}] tls: gateway service: forgejo target_port: 3000 check: http check_path: /api/healthz - route: forgejo-ssh kind: tcp port: 2222 service: forgejo target_port: 2222 end deploy forgejo 20m wait url https://${DOMAIN}/api/healthz 200 10m migrate ${FROM} forgejo-forgejo-1 to ${TO} switch-fly switch-pipeline wait url https://${DOMAIN}/api/healthz 200 5m

Что делает язык в этом сценарии:

  • param — спрашивает хосты и домен при запуске
  • pipeline — сохраняет конвейер с Docker Compose и генерацией паролей
  • deploy — выкладывает и ждёт до 20 минут
  • wait url — проверяет доступность через шлюз с проверкой сертификата
  • migrate — переносит контейнер с переключением маршрутов и конвейера
  • Финальная проверка доступности

47 строк. Вместо связки docker-compose.yml + deploy.sh + migrate.sh + healthcheck.sh.

Использование через веб‑интерфейс

Где найти

Профили → вкладка «Сценарии» → «Новый сценарий»

Редактор

  • «Готовые сценарии ▾» — выпадающий список с примерами (Forgejo, Kubernetes). Пример вставляется целиком, с вопросом, если в тексте уже есть свои правки.
  • «?» — значок в заголовке окна редактора и на карточке сценария открывает страницу «Команды сценариев» на сайте документации.
  • Вкладка «Справка» — показывает ту же страницу с сайта. Переключатель «Сайт / Из nkt» — встроенная справка, если сайт недоступен. Выбор запоминается.
  • Проверка — кнопка анализирует синтаксис и показывает ошибки. Имена хостов из параметров (on ${HOST}) не ругаются при проверке — они сверяются при запуске. Предупреждения не мешают сохранить и выполнить.
  • История версий — таблица: №, время, автор, заметка, размер, метка «текущая». У каждой редакции: «Содержимое» (текст целиком), «Разница» с текущей или предыдущей, «Вернуть в редактор».
  • Кнопка «ИИ ▾» — три действия:

1.Объяснить — разбор написанного кода

2.Написать по # ИИ: — генерация кода из комментариев

3.Написать по описанию — создание сценария из текстового описания задачи

Результат вставляется через дифф — пользователь проверяет и подтверждает. Сценариям от ИИ предупреждения показываются как ошибки на исправление.

Запуск

При нажатии «Запустить»:

  • Диалог с полями для незаполненных param
  • Чекбокс Сухой прогон — проверка без применения
  • Журнал задания — пошаговый лог с таймстампами

Использование через API

Запуск сценария

# Список сценариев curl -H "Authorization: Bearer $TOKEN" \ https://hub.example.com/api/hub/scripts # Запуск curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ https://hub.example.com/api/hub/scripts/42/run \ -d '{ "answers": { "param:FROM": "web1", "param:TO": "web2", "param:DOMAIN": "forgejo.example.com" }, "dry_run": true }'
NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Пределы — хосты и группы, к которым токен имеет доступ. Сценарии проверяются по разбору до запуска: хосты/группы вне предела — отказ (scriptScopeError).

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

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Интеграция с n8n

Готовые ноды для n8n:

NKT: Киллер фича № 7, скрипты: краткость — сестра таланта

Ответы на параметры передаются как JSON: {"param:FROM": "web1"}.

Описания сервисов

Помимо 29 встроенных команд, язык расширяется через описания сервисов — YAML‑файлы, которые добавляют поддержку новых сервисов без перекомпиляции.

name: myapp detect: any: - file: /etc/myapp/config.yml - unit: myapp.service sources: - type: file path: /etc/myapp/config.yml format: yaml extract: endpoints: - each: $.listeners[*] address: $.address port: $.port

Описания живут в /etc/netknownsthat/services.d/, в профиле хаба или встроены в nkt. Старшее описание перекрывает младшее. disabled: true выключает описание.

Встроенные описания: Traefik, RabbitMQ, Redis, PostgreSQL, Mosquitto, Envoy — с правилами безопасности.

Встроенные разборщики (nginx, HAProxy, Caddy, Docker, Podman, LXD, libvirt, Kubernetes, firewall, fail2ban) — тоже описания с engine:. Их можно выключить или настроить через описание на хосте или в профиле.

Внешние плагины

Плагины добавляют новые команды в скриптовый язык:

# Команда от плагина plugin cloudflare-dns create-record example.com A 192.0.2.1 plugin mail create-domain example.com plugin llm list-models

Плагины подключаются через конвейеры и предоставляют:

  • Команды сценариев (plugin ИМЯ СЛОВО …)
  • Инструменты MCP для ИИ‑агентов
  • Разделы в веб‑интерфейсе

ИИ‑интеграция

Ассистент в редакторе

Кнопка «ИИ ▾» в редакторе сценариев:

  • Объяснить — разбор написанного кода
  • Написать по # ИИ: — генерация кода из комментариев
  • Написать по описанию — создание сценария из текстового описания задачи

Результат вставляется через дифф — пользователь проверяет и подтверждает.

Ассистент хаба

Переписка с ассистентом: окно запуска, имена ссылками с меню, избранное. Имена объектов — ссылки с меню по типу: хост, контейнер и служба, конвейер, сценарий, кластер, маршрут, группа.

MCP‑сервер

Хаб предоставляет ~40 инструментов для ИИ‑агентов (Claude Code, Claude Desktop, Cursor):

  • Хосты, находки, службы, контейнеры
  • Конвейеры (создание, правка, сухой прогон)
  • Задания, сценарии, переносы
  • Traffic Fly, кластеры

Каждый инструмент — вызов того же API под тем же токеном, права и пределы — токена.

Честные ограничения языка

Чего нет

❌ Условий (if/else) — язык линейный

❌ Циклов (for) — для нескольких хостов используется перечисление ❌ Функций — повторное использование через готовые сценарии

❌ Состояния между запусками — каждый запуск изолирован

❌ Языка выражений — нет арифметики, конкатенации строк

Почему это осознанный выбор

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

Принцип: если в языке нет условия — значит, вам оно не нужно для этой задачи.

Заключение

29 команд. Одна страница документации. Пример к каждой. Готовые сценарии в редакторе. Сухой прогон перед применением. Журнал с таймстампами. История версий. ИИ‑ассистент. Описания сервисов. Плагины.

Это и есть «Краткость — сестра таланта» в инженерном исполнении: не добавлять абстракции, а убирать всё лишнее, пока не останутся только те 29 глаголов, которые реально нужны.

А когда нужна расширяемость — описания сервисов и плагины добавляют новые команды без изменения ядра языка.

Ссылки: