مانیفست افزونه
مانیفست، nexora-addon.json، قرارداد میان یک افزونه و پنل، دایرکتوری افزونهها و فرم نصب پنل است. میگوید افزونه کیست، چه میخواهد و چطور نصب میشود. این صفحه همهٔ فیلدها را فهرست میکند؛ ساختن افزونه توضیح میدهد چطور از آنها استفاده کنید.
بستهٔ manifest کیت افزونه این قرارداد را اجرا میکند، و پنل با همان کد اعتبارسنجی میکند. پیش از انتشار، مانیفست را بررسی کنید:
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، _ و -، که با حرف یا رقم شروع میشود، حداکثر ۳۲ نویسه. |
name | بله | حداکثر ۶۴ نویسه. |
version | نسخه، به صورت semver. دایرکتوری با مقایسهٔ آن نشان میدهد بهروزرسانی موجود است. | |
publisher | حداکثر ۶۴ نویسه. | |
scopes | یکی از scopes یا webhook | [{"scope": …, "purpose": …}]: دسترسیهای توکن API، هر کدام با دلیلی که در صفحهٔ تأیید نشان داده میشود. |
events، webhook | با هم | رویدادهایی که افزونه میشنود و مسیری که به آن فرستاده میشوند. رویدادها و دامنههای دسترسی را ببینید. |
setup | بله | مسیری که اطلاعات ورود همراه با کد ادعا به آن تحویل داده میشود. |
health | مسیری که وقتی افزونه سالم است 200 جواب میدهد. هر دقیقه پرسیده میشود. | |
ui | نقطهٔ ورود افزونه: یک مسیر، یا یک URL مطلق http(s). | |
description | v1 | به تفکیک زبان (en، fa، ru، zh)، اگر داده شود انگلیسی الزامی است، هر کدام حداکثر ۵۰۰ نویسه. |
license | v1 | یک شناسهٔ SPDX یا proprietary، حداکثر ۶۴ نویسه. |
paid | v1 | افزونه فروشی است. مجوزش مال خودش است؛ پنل هیچ چیزی را بررسی نمیکند. |
docs | v1 | یک URL مطلق http(s). |
requires.panel | v1 | ">=X.Y.Z". پنلی قدیمیتر از آن، افزونه را رد میکند و دلیلش را میگوید. |
install | v1 | افزونه چطور نصب میشود (پایینتر). افزونهٔ بدون آن فقط با آدرسش قابل ثبت است. |
rateLimit | v1 | تعداد درخواست در دقیقه که توکن مجاز است، ۱ تا ۶۰۰. اگر نباشد، پیشفرض پنل یعنی ۱۲۰. در صفحهٔ تأیید نشان داده میشود؛ مانیفست جدیدتری که بیشتر بخواهد منتظر تأیید میماند. |
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
سؤالهایی که نصب میپرسد، حداکثر ۳۲، به ترتیب نمایش:
{ "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 و _، که با حرف شروع میشود، حداکثر ۳۲ نویسه؛ یکتا. |
type | یکی از نوعهای پایین. |
label | به تفکیک زبان، انگلیسی الزامی، هر کدام حداکثر ۸۰ نویسه. |
help | به تفکیک زبان، هر کدام حداکثر ۳۰۰ نویسه. |
default | یک مقدار JSON از نوع گزینه. هرگز روی secret یا password. |
required | نصب بدون جواب ادامه پیدا نمیکند. |
choices | مقدارهای یک choice، ۱ تا ۳۲، هر کدام حداکثر ۶۴ نویسه. |
when | {"<key>": "<value>"}: فقط وقتی نشان داده میشود که آن گزینه، که پیش از این یکی اعلام شده و از نوع choice یا bool است، آن مقدار را داشته باشد (برای bool، "true" یا "false"). |
نوع گزینهها
| نوع | چه چیزی میپرسد |
|---|---|
string | متن آزاد. |
secret | متنی که پنل هرگز ذخیره نمیکند؛ دستوری که آن را در خود دارد یک بار نشان داده میشود. |
password | یک secret که گذرواژهٔ مدیر افزونه میشود: دستکم ۱۰ نویسه و حداکثر ۷۲ بایت. پنل در فرمش مقداری بیرون از این بازه را رد میکند. |
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
یک بخش از حروف، ارقام، - و _ (حداکثر ۶۴)، با یا بدون اسلش، یا خالی برای ریشه. فرم نصب پنل وقتی گزینه default ندارد یک مسیر تصادفی پیشنهاد میدهد، و جواب را در آدرسی که افزونه را با آن ثبت میکند میگذارد، همانطور که با port میکند. نصب با اسکریپت، اگر دستور مسیری ندهد، خودش یکی انتخاب میکند.
افزونه بخش مدیریت، API و مسیرهایی را که پنل فرا میخواند (مانیفست، setup، وبهوک، health) زیر آن سرو میکند، و صفحههای مشتریهایش را بیرون از آن.
نصب تازه
کد ادعای تازه روی دادههای یک نصب قبلی (افزونه بدون دادههایش حذف و بعد دوباره نصب شده) یک نصب تازه است: افزونه ثبت قبلیای را که نگه داشته بود کنار میگذارد، تا پنلی که کد را صادر کرده بتواند ثبتش کند، و جوابهایی را که در حالت عادی فقط یک بار استفاده میکند، مثل گذرواژهٔ اولین مدیرش، اعمال میکند. بهروزرسانی کد ادعا را نگه میدارد.
امضا
مانیفست
- افزونهٔ رسمی را نکسورا امضا میکند.
- افزونهٔ تأییدشده با کلید خود توسعهدهنده امضا میشود (
nexora-addon keygen،nexora-addon sign)، که فهرست امضاشدهٔ دایرکتوری آن را تضمین میکند. - افزونهٔ بدون امضا دستی ثبت میشود.
امضا همهٔ فیلدها جز signature را پوشش میدهد، که با کلیدهای مرتبشده دوباره کدگذاری میشوند، پس فایل را میتوان آزادانه تورفتگی داد یا جابهجا کرد.
checksumهای نسخه
هر نسخه 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 امضاشده با کلیدی که مانیفست همان نسخه را امضا کرده مطابقت داشته باشند. |
| نصب با اسکریپت که سرور خودش نسخه را دانلود میکند | checksum باینری با --sha256 به اسکریپت نصب داده میشود و اسکریپت آن را بررسی میکند. |
| Docker | ایمیج با تگش کشیده میشود و پوشش داده نمیشود. |
نسخهٔ بدون SHA256SUMS.sig | فقط با دستورش نصب میشود. |
خود مانیفست هیچ digestی را نام نمیبرد: افزونه مانیفستش را درون باینریاش جای میدهد، پس digest باینری نمیتواند در آن باشد.
