Что такое дополнение
Панель никогда не запускает код дополнения. Оператор устанавливает дополнение на сервер — на сервер панели или на другой, — и работу поддерживает systemd или Docker этого сервера. Затем панель его регистрирует: читает манифест, показывает оператору, что дополнение запрашивает, и после одобрения выдаёт ему API-токен и подписанную подписку на события.
- Пишите на любом языке: контракт — это HTTP и JSON. На Go контракт за вас реализует addon-kit.
- Храните данные в своей базе. Панель ничего не хранит за дополнение, и дополнение само делает резервные копии.
- Запрашивайте минимум нужных прав: оператор читает каждое право вместе с вашим объяснением перед одобрением.
Начните с шаблона
addon-template — полное минимальное дополнение: регистрируется, получает события, отвечает на проверку работоспособности и показывает страницу; в комплекте скрипт установки, файл compose и workflow выпуска. Скопируйте и запустите:
gh repo create my-addon --template nx-panel/addon-template --public --clone cd my-addon NX_OPT_PORT=8090 go run . # not registered yet — claim code 3F9A1C0B7E21
По коду привязки панель регистрирует только манифест, подписанный доверенным ключом. Чтобы попробовать своё дополнение с панелью, собранной с -tags addondev, подпишите его ключом разработки — ему доверяет только такая панель:
go run github.com/nx-panel/addon-kit/cmd/nx-addon sign -dev nx-addon.json > signed.json NX_MANIFEST_FILE=signed.json go run .
Манифест
nx-addon.json находится в двух местах: в корне вашего публичного репозитория, где его читают этот каталог и мастер установки панели ещё до запуска, и в работающем дополнении по адресу /.well-known/nx-addon.json, откуда его читает регистрация. Это должен быть один и тот же файл. Справочник по всем полям — SPEC.md; рабочий пример:
{
"api": 1,
"slug": "hello",
"name": "Hello",
"version": "0.1.0",
"publisher": "Your name",
"license": "Apache-2.0",
"description": { "en": "What it does, in a sentence or two." },
"docs": "https://github.com/you/hello",
"install": {
"docker": { "image": "ghcr.io/you/hello", "compose": "deploy/compose.yml" },
"binary": { "linux-amd64": "hello-linux-amd64.tar.gz", "linux-arm64": "hello-linux-arm64.tar.gz" },
"options": [
{ "key": "port", "type": "port", "default": 8090, "label": { "en": "Port" } }
]
},
"scopes": [ { "scope": "users:read", "purpose": "to show how many accounts the panel has" } ],
"events": ["user.created", "panel.addon_removed"],
"webhook": "/nx/events",
"setup": "/nx/setup",
"health": "/health",
"ui": "/"
}
slug— идентичность дополнения: в каталоге, в панели и при проверке обновлений. Никогда его не меняйте; новый slug — это новое дополнение.version— номер выпуска безv; по нему панели показывают значок обновления.scopes— права API-токена, каждое с объяснением для экрана согласия;eventsдоставляются наwebhook. Запросите одно или оба.descriptionи каждыйlabelпринимаютen,fa,ruиzh; английский обязателен, а если есть язык оператора, панель покажет его.- Проверьте так же, как проверит панель:
nx-addon check nx-addon.json.
Параметры установки
install.options — вопросы, которые мастер панели задаёт перед установкой: порт, база данных, токен бота. Типы: string, secret, password (a secret that becomes the addon's admin password: at least 10 characters, at most 72 bytes), number, port, bool, choice, url, path (базовый путь, под которым дополнение отдаёт свою админку: один сегмент или пусто для корня; не более одного на манифест). Параметр с when спрашивается, только пока предыдущий choice или bool имеет указанное значение.
{ "key": "database", "type": "choice", "choices": ["sqlite", "postgres"], "default": "sqlite",
"label": { "en": "Database" } },
{ "key": "database_dsn", "type": "secret", "required": true, "when": { "database": "postgres" },
"label": { "en": "PostgreSQL DSN" } }
# reaches the addon as
NX_OPT_DATABASE=postgres
NX_OPT_DATABASE_DSN=postgres://…
NX_PANEL_URL=https://panel.example
NX_CLAIM_CODE=K7QF-2M9D-XW4H-8RTA
Каждый ответ попадает в дополнение как переменная окружения NX_OPT_<KEY>, вместе с адресом панели и кодом привязки, выданным для этой установки. Панель никогда не хранит secret: он есть только в команде, показанной один раз, или передаётся по SSH.
Выпуски и install.sh
Оба способа установки — команда, которую показывает панель, и установка самой панелью по SSH — запускают ваш install.sh из последнего выпуска на GitHub. Скрипт шаблона уже всё это делает; сохраните его флаги:
sh install.sh --method script|docker # binary + systemd, or the compose stack
--version v0.1.0 # a release tag (default: the latest)
--binary-file FILE # use this archive instead of downloading it
--opt KEY=VALUE # an answer to an install option; repeat
--panel-url URL # → NX_PANEL_URL
--claim-code CODE # → NX_CLAIM_CODE
sh install.sh # again: update in place, answers and data kept
sh install.sh --uninstall [--purge] # remove; --purge deletes the data too
Что должно быть в выпуске, всё под одним тегом:
nx-addon.json the manifest, its version = the tag install.sh at the root of the repository, at the tag deploy/compose.yml what "install.docker.compose" names hello-linux-amd64.tar.gz every asset "install.binary" names (release assets) hello-linux-arm64.tar.gz SHA256SUMS sha256sum output for the assets — the panel checks it ghcr.io/you/hello:0.1.0 the image, tagged with the version
При установке как сервиса по SSH панель сама скачивает бинарный файл для архитектуры сервера, сверяет его с SHA256SUMS и загружает через --binary-file, так что серверу не нужен доступ к GitHub. Скрипт работает от root; держите всё в /opt/nx-addons/<slug>: ответы в .env, данные в data/. Повторный запуск скрипта должен обновлять на месте.
Регистрация, события и проверка работоспособности
Когда оператор одобряет, панель отправляет учётные данные POST-запросом на ваш путь setup вместе с кодом привязки. Принимайте их только с тем кодом, с которым вас запустили (или который вы сгенерировали и записали в лог), после чего забудьте код. Храните токен и секрет вебхука в файле, доступном только вам.
POST /nx/setup
{
"api": 1,
"claimCode": "K7QF-2M9D-XW4H-8RTA",
"panel": { "url": "https://panel.example", "version": "1.8.0", "id": "…" },
"token": "nx_…",
"scopes": ["users:read"],
"webhook": { "id": 7, "url": "…/nx/events", "events": ["user.created"], "secret": "…" }
}
События приходят на ваш путь webhook с подписью:
X-Nx-Delivery: 9b1f… # the same on every retry: handle it once X-Nx-Signature: t=1791000000,v1=<hex> # v1 = HMAC-SHA256(secret, "<t>.<body>"); refuse a t more than 5 minutes off
Путь health опрашивается каждую минуту и должен отвечать 200, когда всё в порядке; сбой виден оператору на странице дополнений. Обращайтесь к панели со своим токеном по /api/v1 и отправляйте Idempotency-Key с каждой записью, чтобы повтор никогда не сработал дважды:
GET https://panel.example/api/v1/users?limit=1 Authorization: Bearer nx_… Idempotency-Key: <one per logical write> # on POST / PUT / PATCH / DELETE
Когда оператор удаляет дополнение, оно получает panel.addon_removed до удаления токена: забудьте учётные данные, чтобы его можно было зарегистрировать снова. На Go всё это — addon.New, Mount, OnEvent и Forget.
Официальные, проверенные, неофициальные
| Уровень | Кто размещает | Чем подписан манифест | В панели |
|---|---|---|---|
| Официальное | NX | ключом NX | установка командой или автоматически по SSH; экран согласия называет NX |
| Проверенное | вы, после проверки NX | вашим ключом, за который ручается подписанный индекс | как официальное, с вашим именем |
| Неофициальное | вы, после беглой проверки | вашим ключом или ничем | предупреждение на карточке и экране согласия; установка только командой |
Манифест, подписанный ключом, за который каталог не ручается, считается неподписанным; неподписанное дополнение регистрируется только вручную, а токен для него создаёт оператор.
Как попасть в каталог
- Сделайте репозиторий публичным, с манифестом в корне и хотя бы одним выпуском.
- Для проверенного размещения создайте свой ключ подписи и подпишите манифест. Подпись покрывает все поля, включая версию, поэтому подписывайте заново для каждого выпуска:
go run github.com/nx-panel/addon-kit/cmd/nx-addon keygen -out addon.key # keep addon.key out of the repository go run github.com/nx-panel/addon-kit/cmd/nx-addon sign -key addon.key nx-addon.json > signed.json && mv signed.json nx-addon.json go run github.com/nx-panel/addon-kit/cmd/nx-addon check nx-addon.json go run github.com/nx-panel/addon-kit/cmd/nx-addon verify -pub <your public key> nx-addon.json
- Отправьте репозиторий через форму, а для проверенного размещения укажите свой публичный ключ. Форма сразу читает репозиторий и сообщает, если что-то не так.
- NX проверяет заявку. После размещения каталог перечитывает последний выпуск каждые десять минут, а панели читают подписанный индекс раз в день или по запросу оператора.
Если последний выпуск перестаёт проходить проверки, размещение показывает последнюю исправную версию с пометкой «устарело», пока новый выпуск снова не пройдёт.
Обновления
Опубликуйте выпуск с большей version: каталог увидит его в течение десяти минут, а каждая панель при следующем чтении поставит на ваше дополнение значок обновления. Оператор обновляет командой или, для официального и проверенного дополнения, из панели; скрипт установки снова запускается на месте.
Обновление, запрашивающее новые права или события, устанавливается, но панель придерживает новые права до одобрения оператора; до тех пор дополнение работает со старыми. Запрашивайте права в том выпуске, которому они нужны.