Манифест дополнения
Манифест nexora-addon.json — это договор между дополнением и панелью, каталогом дополнений и формой установки в панели. Он сообщает, кто такое дополнение, что оно запрашивает и как устанавливается. Эта страница перечисляет все поля; как ими пользоваться, объясняет Создание дополнения.
Пакет manifest из addon kit следит за соблюдением этого договора, и панель проверяет манифест тем же кодом. Проверяйте манифест перед публикацией:
nexora-addon check nexora-addon.jsonГде он лежит
- В корне публичного репозитория дополнения. Каталог и форма установки читают его там ещё до того, как что-либо запущено. Для закрытого дополнения это его публичный репозиторий распространения.
- Его отдаёт работающее дополнение по адресу
/.well-known/nexora-addon.json; оттуда его читает регистрация.
Полный пример
{
"api": 1,
"slug": "template",
"name": "Addon template",
"version": "0.1.0",
"publisher": "Example Ltd",
"license": "Apache-2.0",
"description": { "en": "A minimal addon to start from." },
"docs": "https://github.com/nexora-vpn/addon-template",
"install": {
"docker": { "image": "ghcr.io/nexora-vpn/addon-template", "compose": "deploy/compose.yml" },
"binary": { "linux-amd64": "addon-template-linux-amd64.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": "/nexora/events",
"setup": "/nexora/setup",
"health": "/health",
"ui": "/",
"rateLimit": 60,
"signature": "…"
}Поля
| Поле | Обязательно | Значение |
|---|---|---|
api | да | Версия манифеста: 1 (эта страница) или 0 (прежняя версия, в которой не может быть полей, отмеченных ниже как v1). |
slug | да | Идентификатор дополнения: a-z, 0-9, _ и -, начинается с буквы или цифры, не длиннее 32 символов. |
name | да | Не длиннее 64 символов. |
version | Выпуск в формате semver. Каталог сравнивает его, чтобы показать доступное обновление. | |
publisher | Не длиннее 64 символов. | |
scopes | одно из scopes или webhook | [{"scope": …, "purpose": …}]: права API-токена, каждое с причиной, которая показывается на экране одобрения. |
events, webhook | вместе | События, которые получает дополнение, и путь, на который они отправляются. См. События и области. |
setup | да | Путь, на который доставляются учётные данные вместе с кодом привязки. |
health | Путь, который отвечает 200, когда дополнение исправно. Опрашивается раз в минуту. | |
ui | Точка входа дополнения: путь или абсолютный URL http(s). | |
description | v1 | По языкам (en, fa, ru, zh); если поле задано, английский обязателен; не длиннее 500 символов каждое. |
license | v1 | Идентификатор SPDX или proprietary, не длиннее 64 символов. |
paid | v1 | Дополнение продаётся. Лицензия у него своя; панель ничего не проверяет. |
docs | v1 | Абсолютный URL http(s). |
requires.panel | v1 | ">=X.Y.Z". Панель старше этой версии отклоняет дополнение и объясняет почему. |
install | v1 | Как устанавливается дополнение (ниже). Дополнение без этого раздела можно только зарегистрировать по его адресу. |
rateLimit | v1 | Сколько запросов в минуту может делать токен, от 1 до 600. Если не задано — значение панели по умолчанию, 120. Показывается на экране одобрения; более новый манифест, который просит больше, ждёт одобрения. |
signature | Подпись Ed25519 в base64 (Подпись). |
Пути
setup, webhook и health — три разных чистых пути, и ни один из них не совпадает с путём самого манифеста. Каждый:
- начинается с
/; - не содержит пустых сегментов,
.или..(завершающий/допустим); - не содержит
?,#,%,{,},\и пробелов.
События и области
Каждое событие выдаётся по области, которую называет для него каталог событий панели (GET /api/events; см. Каталог событий). Эта область или её :write должна быть среди scopes, иначе панель отклоняет манифест. Исключение — panel.addon_removed: о собственном удалении дополнение узнаёт при любых правах.
Повторяющийся ключ
Манифест, в котором какой-либо объект дважды называет один ключ, отклоняется.
install
"install": {
"docker": { "image": "ghcr.io/nexora-vpn/shop", "compose": "deploy/compose.yml" },
"binary": { "linux-amd64": "shop-linux-amd64.tar.gz", "linux-arm64": "shop-linux-arm64.tar.gz" },
"options": [ … ]
}Нужен хотя бы один из docker и binary.
| Поле | Значение |
|---|---|
docker.image | Путь в реестре без тега: тегом служит версия выпуска. |
docker.compose | Относительный путь к файлу compose в репозитории и в выпуске. |
binary | Сопоставляет платформе имя файла из выпуска. Скрипт установки дополнения ставит его под systemd (--method script). |
options | Вопросы, которые задаёт установка (ниже). |
Платформы для binary: linux-amd64, linux-arm64, linux-armv7, linux-armv6, linux-armv5, linux-386, linux-s390x, linux-riscv64.
install.options
Вопросы, которые задаёт установка, не больше 32, в порядке показа:
{ "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" } }| Поле | Значение |
|---|---|
key | a-z, 0-9 и _, начинается с буквы, не длиннее 32 символов; уникален. |
type | Один из типов ниже. |
label | По языкам, английский обязателен, не длиннее 80 символов каждое. |
help | По языкам, не длиннее 300 символов каждое. |
default | Значение JSON того же типа, что и параметр. Никогда не задаётся для secret и password. |
required | Без ответа установка не продолжится. |
choices | Значения для choice, от 1 до 32, каждое не длиннее 64 символов. |
when | {"<key>": "<value>"}: показывается, только пока тот параметр, объявленный раньше этого и имеющий тип choice или bool, имеет это значение ("true" или "false" для bool). |
Типы параметров
| Тип | Что запрашивает |
|---|---|
string | Произвольный текст. |
secret | Текст, который панель никогда не хранит; команда, содержащая его, показывается один раз. |
password | secret, который становится паролем администратора дополнения: не меньше 10 символов и не больше 72 байт. Форма панели отклоняет пароль вне этих границ. |
number | Число. |
port | Номер порта. |
bool | Да или нет. |
choice | Одно из choices. |
url | Адрес. |
path | Базовый путь, под которым дополнение отдаёт свою админку. Не больше одного на манифест (ниже). |
Это полный список. Панель, которая старше какого-либо типа, отклоняет манифест с ним ещё до того, как прочитает requires.panel. Всё равно задавайте requires.panel и указывайте в описании выпуска, какая панель нужна вашему дополнению.
Как ответы попадают в дополнение
Ответы приходят как переменные окружения NEXORA_OPT_<KEY> — в .env рядом с файлом compose или в EnvironmentFile юнита systemd. Рядом с ними приходят NEXORA_PANEL_URL и NEXORA_CLAIM_CODE — код, который панель выдала для этой установки. bool передаётся как true или false, число — в записи JSON.
Тип path
Один сегмент из букв, цифр, - и _ (не длиннее 64), со слешами или без, либо пусто для корня. Форма установки в панели предлагает случайный путь, если у параметра нет default, и включает ответ в адрес, по которому регистрирует дополнение, так же как port. Установка скриптом выбирает путь сама, если команда его не передала.
Дополнение отдаёт под ним свою админку, свой API и маршруты, которые вызывает панель (манифест, setup, вебхук, health), а страницы для своих клиентов — вне его.
Новая установка
Новый код привязки поверх данных прежней установки (дополнение удалено без удаления данных, затем установлено снова) — это новая установка: дополнение отбрасывает сохранённую прежнюю регистрацию, чтобы его могла зарегистрировать выдавшая код панель, и применяет ответы, которые иначе использует только один раз, например пароль первого администратора. Обновление сохраняет код привязки.
Подпись
Манифест
- Официальное дополнение подписывает Nexora.
- Проверенное дополнение подписано собственным ключом разработчика (
nexora-addon keygen,nexora-addon sign), за который ручается подписанный индекс каталога. - Дополнение без подписи регистрируется вручную.
Подпись покрывает все поля, кроме signature, перекодированные с отсортированными ключами, поэтому файл можно свободно форматировать и переупорядочивать.
Контрольные суммы выпуска
Выпуск публикует SHA256SUMS, который покрывает все файлы (в том числе install.sh и бинарные файлы) в формате sha256sum, и SHA256SUMS.sig — подпись над ним, сделанную после сборки тем же ключом, которым подписан манифест:
nexora-addon sign-sums -key addon.key SHA256SUMS > SHA256SUMS.sig
gh release upload v1.2.3 SHA256SUMS.sigЭто подпись Ed25519 в base64 над строкой nexora-addon SHA256SUMS v1\n, за которой следуют байты файла, поэтому её никогда нельзя выдать за подпись манифеста.
| Установка | Что проверяется |
|---|---|
| По SSH или на сервере самой панели | install.sh выпуска, а при установке как службы и его бинарный файл, должны совпадать с SHA256SUMS, подписанным тем же ключом, что и манифест выпуска. |
| Установка скриптом, когда сервер сам скачивает выпуск | Скрипт установки получает контрольную сумму бинарного файла как --sha256 и проверяет её. |
| Docker | Образ скачивается по тегу и не покрывается. |
Выпуск без SHA256SUMS.sig | Устанавливается только своей командой. |
Сам манифест не содержит хеша: дополнение встраивает манифест в свой бинарный файл, поэтому хеш бинарного файла не может в нём быть.
