Skip to content

مانیفست افزونه ​

مانیفست، nexora-addon.json، قرارداد میان یک افزونه و پنل، دایرکتوری افزونه‌ها و فرم نصب پنل است. می‌گوید افزونه کیست، چه می‌خواهد و چطور نصب می‌شود. این صفحه همهٔ فیلدها را فهرست می‌کند؛ ساختن افزونه توضیح می‌دهد چطور از آن‌ها استفاده کنید.

بستهٔ manifest کیت افزونه این قرارداد را اجرا می‌کند، و پنل با همان کد اعتبارسنجی می‌کند. پیش از انتشار، مانیفست را بررسی کنید:

bash
nexora-addon check nexora-addon.json

کجا قرار دارد ​

  • در ریشهٔ مخزن عمومی افزونه. دایرکتوری و فرم نصب پیش از اجرای هر چیزی آن را همان‌جا می‌خوانند. برای افزونهٔ بسته، این مخزن عمومی توزیع آن است.
  • سرو شده توسط افزونهٔ در حال اجرا در /.well-known/nexora-addon.json، که ثبت آن را می‌خواند.

یک نمونهٔ کامل ​

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).
descriptionv1به تفکیک زبان (en، fa، ru، zh)، اگر داده شود انگلیسی الزامی است، هر کدام حداکثر ۵۰۰ نویسه.
licensev1یک شناسهٔ SPDX یا proprietary، حداکثر ۶۴ نویسه.
paidv1افزونه فروشی است. مجوزش مال خودش است؛ پنل هیچ چیزی را بررسی نمی‌کند.
docsv1یک URL مطلق http(s).
requires.panelv1">=X.Y.Z". پنلی قدیمی‌تر از آن، افزونه را رد می‌کند و دلیلش را می‌گوید.
installv1افزونه چطور نصب می‌شود (پایین‌تر). افزونهٔ بدون آن فقط با آدرسش قابل ثبت است.
rateLimitv1تعداد درخواست در دقیقه که توکن مجاز است، ۱ تا ۶۰۰. اگر نباشد، پیش‌فرض پنل یعنی ۱۲۰. در صفحهٔ تأیید نشان داده می‌شود؛ مانیفست جدیدتری که بیشتر بخواهد منتظر تأیید می‌ماند.
signatureیک امضای Ed25519 به صورت base64 (امضا).

مسیرها ​

setup، webhook و health سه مسیر تمیز و متفاوت‌اند، و هیچ‌کدام مسیر خود مانیفست نیست. هر کدام:

  • با / شروع می‌شود؛
  • هیچ بخش خالی، . یا .. ندارد (/ انتهایی اشکالی ندارد)؛
  • هیچ‌کدام از ?، #، %، {، }، \ یا فاصله را ندارد.

رویدادها و دامنه‌های دسترسی ​

هر رویداد با دامنه‌ای داده می‌شود که فهرست رویدادهای پنل برایش نام می‌برد (GET /api/events؛ فهرست رویدادها را ببینید). آن دامنه، یا :write آن، باید میان scopes باشد، وگرنه پنل مانیفست را رد می‌کند. panel.addon_removed استثناست: افزونه، هر دسترسی‌ای داشته باشد، از حذف خودش خبردار می‌شود.

کلید تکراری ​

مانیفستی که در هر شیئی یک کلید را دو بار نام ببرد رد می‌شود.

install ​

json
"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 ​

سؤال‌هایی که نصب می‌پرسد، حداکثر ۳۲، به ترتیب نمایش:

json
{ "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" } }
فیلدمعنی
keya-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، یعنی امضای آن که بعد از ساخت با همان کلیدی که مانیفست را امضا می‌کند ساخته می‌شود:

bash
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 باینری نمی‌تواند در آن باشد.

متن و تصویرها با مجوز CC BY 4.0.