مقدمه

این کتاب شامل منابعی برای افراد مختلف جهت یادگیری اموزش استفاده یا توسعه نوستر میباشد.

در بخش نوستر به زبان ساده کوشش میکنیم تا نوستر را به سادگی برای کاربران تازه اموزش دهیم.

در بخش NIP ها تمامی امکانات پیاده سازی نوستر را به پارسی برگردادنده ایم.

و در بخش نوستر برای توسعه دهندگان توضیحات و مقدماتی بر پیاده سازی رله و کلاینت یا اجرا کد های موجود اراعه کرده ایم.

مقدمه

در این بخش ما نوستر رو به زبان ساده برای افرادی که میخوان اشنایی اولیه با نوستر پیدا کنن و خیلی ساده استفاده از اون رو شروع کنن توضیح میدیم. به مرور توی هر قسمت یک مفهوم جدید معرفی میشه و کار باهاش اموزش داده میشه.

هدف این بخش اینه که سادگی حداکثری داشته باشه.

نوستر به زبان ساده

در این بخش سعی میکنیم کلیات نوستر رو به زبان ساده بیان کنیم و خیلی ساده نحوه شروع به کار با اون رو اموزش بدیم. تلاش میشه تمامی بخش های نوستر به زبان ساده از نظر نوشتار و توضیحات واضح و ساده باشن. اگر توی هر بخش نیاز به راهنمایی یا کمک دارید میتونید از روش های زیر استفاده کنید:

باز کردن ایشو روی مخزن این کتاب و پرسیدن سوال:

https://github.com/persianccbook/nips-persian/issues

توجه کنید که با جستجو مشکل در ایشو ها و یا مروگر ممکنه به جواب سوالتون برسید.

نوستر چیست؟

قبل از خوندن این بخش اگر میخواید اشنایی با پروتکل رو کوتاه و سریع یادبگیرید من توییت زیر رو پیشنهاد میدم:

https://x.com/Ali2kCom/status/1608110985314697218

در ادامه ما سعی میکنیم خیلی ساده اما با مثال و کمی مفصل تر نوستر رو توضیح بدیم.

!نکته: بیشتر موارد توضیح داده شده ممکنه پیچیده بنظر بیاد برای انجام دادن اما در نظر بگیرید که ابزار های زیادی هستن که این کار رو برای شما انجام میدن به سادگی که بعد از درک اولیه معرفی میکنیم.

شبکه های اجتماعی فعلی

نوستر یک پروتکل برای ایجاد شبکه های اجتماعی غیرمتمرکز و آزاد هست. زمانی که ما از یک شبکه اجتماعی متمرکز استفاده میکنیم برای نمونه x یک نرم افزار روی دستگاه اندروید خودمون نصب میکنیم و بعد میتونیم شروع به انتشار پست و ارتباط با افراد دیگه کنیم.

در این حالت نرم افزار نصب شده روی دستگاه شما به یک سرور که یک کامپیوتر دیگه در جای دیگه ای از دنیا هست از طریق اینترنت متصل میشه و این دو میتونن با همدیگه از طریق مجموعه ای از قوانین باهم صحبت کنن. که ما بهش میگیم پروتکل. بر اساس این پروتکل نرم افزار روی دستگاه شما از سرور درخواست میکنه که داخل سرور برای شما یک حساب ساخته بشه و یا براتون یک پست رو منتشر کنه. زمانی که دوستتون ادرس شمارو از روی دستگاه خودش به سرور x میده اون سرور پست هایی که شما ازش خواستید منتشر کنه رو به دوستتون نشون میده.

در این حالت شما اجازه ندارید یک نرم افزار مشابه نرم افزار x بنویسید و به اون سرور متصل بشید. نمیتونید یه سرور مثل سرور x بسازید که دوستتون بجای اون سرور مشخص به سرور شما متصل بشه. و تمامی اطلاعات و محتوا خصوصی و عمومی شما برای سرور x و یا صاحبانش شفاف و قابل رویت هست.

همچنین این سرور یه ادرس داره که شما میتونید از طریق اینترنت باهاش صحبت کنید. دسترسی شما به اینترنت از طریق اراعه دهنده اینترنت هست که اون اراعه دهنده با دونستن ادرس x میتونه دسترسی نرم افزار شمارو به اون سرور مسدود کنه یا صحبت های شمارو سانسور کنه.

در نظر بگیرید که به نرم افزاری که شما نصب کردید میگیم کلاینت یا مشتری به زبان خودمون که به نحوی با سرور ارتباط میگیره.

نوستر و تفاوت ها

روند در نوستر کمی متفاوت هست. نوستر یک پروتکل هست. چیزی دقیقا مشابه همون چیزی که نرم افزار x با سرور میتونست صحبت کنه. عده ای شروع به تعریف این مجموعه قوانین کردن و جزییاتش رو بصورت شفاف و عمومی منتشر کردن و هرکسی از جمله خود شما میتونید اونهارو تغییر بدید و یا بهشون اضافه کنید.

حالا ما مجموعه از قوانینی برای سرور ها و کلاینت ها داریم که باهم دیگه سازگاری دارن. یعنی اگر من یه نرم افزار بنویسم که از این قوانین پیروی میکنه و ینفر یه سرور بنویسه و اجرا کنه که با این قوانین سازگار باشه این دو میتونن باهم صحبت کنن و اگر کلاینت دومی هم نوشته بشه باز هم میتونه با اون سرور صحبت کنه.

شما توی این مورد ازادید از ده ها کلاینت و سرور موجود استفاده کنید که توسعه دهنده ها و مالک های متفاوتی دارن. این کلاینت ها میتونن روی گوشی یا کامپیوتر شخصی شما نصب بشن یا از طریق محیط وب بصورت یک وبسایت در دسترس شما باشه.

با این حالت حتی خود شما و دوستانتون میتونید یه سرور اجرا کنید که کلاینت های مختلف بهش متصل بشن.

قبل صحبت از مزایای نوستر یک نگاهی به احراز هویت شما بندازیم. وقتی از x استفاده میکنید شما ایمیلتون رو به اون سرور میدید و ثابت میکنید مالک اون هستید که اون ایمیل شامل حجم عظیمی از اطلاعات هست که بعدا باهاش برای شما تبلیغات و محتوای هرزنامه فرستاده میشه. بعد ها شما با یک کلمه عبور مختص x مالکیت اون حساب رو ثابت میکنید و دسترسی به اطلاعاتتون میگیرید.

توی نوستر شما از امضای دیجیتال استفاده میکنید. فرض کنید دوتا عدد خیلی طولانی با یکسری محاسبات ریاضیاتی تولید میشه که یکیش رو بهش میگیم کلید عمومی و یکیش رو بهش میگیم کلید خصوصی. کلید عمومی شما در اصل حکم ایدی x شمارو داره.

وقتی شما یک پست میزارید داخل چندتا سرور نوستر تنها چیزی که از شما وجود داره اون کلید عمومیه و اگر اسمتون یا چیز دیگه ای درکنارش باشه خود شما خواستید قطعا. من که پست شمارو میبنیم با استفاده از فرمول های ریاضیاتی دیگه ای میتونیم مطمن بشم که اون پست رو مالک این کلید عمومی گزاشته نه شخص دیگه ای.

حالا این دو کلید رو ما میتونیم برای رمزنگاری هم استفاده کنیم. اگر شما بخواید پستی بزارید که فقط من ببینم میاید با کلید عمومی من رمزنگاریش میکنید و اونوقت من که کلید خصوصی اون کلید عمومی رو دارم میتونم متنش رو ببینم. یجورایی مثل صندوق پست میمونه. همه میتونن داخلش نامه بزارن و فقط من که مالک صندوق پست هستم میتونم اون رو باز کنم و نامه هارو بخونم.

در این شرایط هیچ واسطی وجود نداره که پیام های مارو ببینه یا بتونه از طرف ما تحت هیچ شرایطی حرف بزنه چون همه چیز با فرمول های ریاضیاتی ای اثبات و رمزنگاری میشه. توی شبکه های اجتماعی کنونی هم رمزنگاری وجود داره اما شما کلید های خودتون رو ندارید و کلید هاتون رو مالک اون شبکه اجتماعی نگه میداره. در اصل پیام های خصوصی شمارو از چشم افرادی غیر از شما کسانی که خواستید و صاحب پلتفرم پنهان خواهد بود. و مورد سوم از اختیار شما خارجه. و اثبات اینکه حرفی رو کسی زده یا نزده با اعتماد به اون پلتفرم که راست میگه مشخص میشه.

اما نوستر صاحبی برای اعتماد نداره. کلید های شما واقعا کلید های شما هستن و پیش خودتون نگهداری میشن.

ما اشاره کردیم که در نوستر چندین سرور و کلاینت مختلف وجود داره که بدون نیاز به اعتماد کردن بهشون (بر اساس داشتن کلید های عمومی خصوصی) وجود دارن و ما به اونا متصل میشیم که هرکدوم ادرس متفاوتی روی اینترنت دارن.

حالا اراعه دهنده اینترنت شما اگر چندین تا از اینارو مسدود کنه شما به چندین به علاوه یکمینش دسترسی خواهید داشت. اگر بیست تا از رله ها شمارو مسدود کنن یا پستی از شمارو منتشر نکنن و ردش کنن یا پاکش کنن شما از طریق بیست و یکمی یا حتی رله شخصی خودتون میتونید پستتون رو منتشر کنید.

توی این شرایط هویت شما همونقدر مشخصه که خودتون میخواید. پیام خصوصی شما خصوصی خواهد موند. حرف شما و ارتباط شما سانسور و فیلتر نخواهد شد. هویت شما به تبلیغ کنندگان و ربات ها فروخته نخواهد شد.

چطوری از نوستر استفاده کنیم؟

اینجا خیلی ساده توضیح میدیم چطوری توی نوستر فعالیت کنیم و ازش استفاده کنیم. شما کافیه یه کلاینت مناسب پیدا کنید و یک کلید خصوصی و عمومی بسازید و به یکسری رله متصل بشید.

تولید کلید های عمومی و خصوصی میتونه با استفاده از اون کلاینت اتفاق بیفته و هیچگونه پیچیدگی نداره. توی این بخش ما یک کلاینت وب معرفی میکنیم اما شما میتونید کلاینت های بیشتری رو از سایت زیر پیدا کنید و اونی که براتون مناسب تر هست رو انتخاب کنید.

https://nostrapps.com

شما میتونید برای با وارد شدن به سایت:

https://snort.social/login/sign-up

یک جفت کلید تولید کنید و به راحتی از این کلاینت استفاده کنید. کلید خصوصی شما یک متنه به هم ریخته هست که پسوند nsec داره رو یک جای امن نگهداری کنید و ترجیحا روی دستگاهی دیجیتالی نگه ندارید.

میتونید به این قسمت برید و کلید خصوصی خودتون رو پیدا کنید:

https://snort.social/settings/keys

همچنین میتونید ۲۴ کلمه منومیک رو نگهدارید که با کلید خصوصی شما برابره اما برای نگهداری روی کاغذ یا به خاطر سپردن ساده تر هست.

این کلید عمومی با گم شدن قابل بازیابی نیست و بشدت مهمه که ازش بصورت امن نگهداری کنید.

همچنین شما یک کلید عمومی با پسوند npub دارید. این حکم ایدی شمارو داره و میتونید خیلی راحت با بقیه به اشتراکتش بزارید تا صفحتون رو پیدا کنن.

میتونید کلید عمومیتون رو به فایل README گیتهاب این کتاب بیفیزایید و کلید عمومی دوستانی که پیشتر این مطلب رو خوندن و کلیدشون رو اونجا به اشتراک گذاشتن رو پیدا کنید و اونار دنبال کنید:

https://github.com/persianccbook/nips-persian

ادامه

در ادامه بخش نوستر به زبان ساده مفاهیم کمی پیچیده تر نوستر رو به زبان ساده توضیح میدیم تا بتونید از همه امکانات نوستر استفاده کنید.

برای مثال کلیدتون رو با نرم افزار های امن تر تولید کنید یا اینکه از لایتنینگ و بیتکوین برای پرداخت و دونیت داخل نوستر استفاده کنید و یا ادرس های شخصی سازی شده و کوتاه برای خودتون بزارید و خیلی ساده با دوستاتون به اشتراک بزارید.

برگردان پارسی NIP ها

این بخش یک ترجمه پارسی از مخزن اصلی میباشد:

https://github.com/nostr-protocol/nips

تغییرات مخزن اصلی در این مخزن هم به طور پیوسته اعمال میشود.

نیپ شماره ۱

روشنگری بنیادی روند پروتکل

پیشنویس ‍‍‍‍بایسته(الزامی)

این نیپ پروتکل آغازینی را میشناساند که باید توسط همه پیاده سازی شود. نیپ های تازه تر ممکن است زمینه ها پیام ها و یا قابلیت های دلخواهی به روند شناسانده شده (معرفی شده) در این نیپ بیفزایند.

رویداد ها و امضا ها

هر کاربر یک جفت کلید دارد. امضا ها کلید های عمومی و رمزگذاری ها بر اساس استاندارد Schnorr signatures standard for the curve secp256k1 انجام میشود.

تنها شی (حالت داده) موجود ‍‍رویداد است که فرمت زیر را دارد:

{
  "id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>, // شناسه
  "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, // کلید عمومی
  "created_at": <unix timestamp in seconds>, // زمان ساخت شدن رویداد
  "kind": <integer between 0 and 65535>, // نوع

  "tags": [ // برچسب ها
    [<arbitrary string>...],
    // ...
  ],
  "content": <arbitrary string>, // محتوا
  "sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field> // امضا
}

برای محاسبه شناسه رویداد هش sha256 حالت سریالایز شده ان را محاسبه میکنیم.

فرایند سریالایز کردن رویداد ها با استفاده از استاندارد UTF-8 و رشته سریالایز شده جیسان با ساختار زیر اتفاق می افتد:

[
  0,
  <pubkey, as a lowercase hex string>, // کلید عمومی
  <created_at, as a number>, // زمان ساخت بصورت عدد
  <kind, as a number>, // گونه به صورت عدد
  <tags, as an array of arrays of non-null strings>, // برچسب ها له صورت ارایه ای از رشته ها ناتهی
  <content, as a string> // محتوا بصورت رشته
]

برای جلوگیری از ساخت شناسه های متفاوت برای یک رویداد در پیاده سازی های متفاوت قوانین زیر باید دنبال شود:

  • برار رمزگذاری بایذ از UTF-8 استفاده شود.

  • فضا های خالی و خطوط جدید و دگیر فرمت های غیر ضرروی نباید در ساختار جیسان خروجی در نظر گرفته شوند.

  • نشان های زیر در فیلد محتوا (content) باید به شکل زیر نادیده گرفته شوند و دیگر نشان ها باید واژه به واژه در محتوا فراگرفته شوند:

    • برای نشان رفتن به خط بعد (0x0A) باید از \n استفاده شود.

    • برای نشان نقل قول (0x22) باید از \" استفاده شود.

    • برای نشان بک اسلش باید (0x5C) باید از \\ استفاده شود.

    • برای نشان بازگشت carriage (0x0D) باید از \r استفاده شود.

    • برای نشان تب (0x09) باید از \t استفاده شود.

    • برای نشان بک اسپیس باید از (0x08) باید از \b استفاده شود.

    • برای نشان حالت فید (feed) (0x0C) باید از \f استفاده شود.

برچسب ها

هر برچسب ارایه ای از رشته ها با مجموعه ای از قرار داد های مربوط به ان می باشد. به نمونه زیر نگاه کنید:

{
  "tags": [
    ["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"],
    ["p", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca"],
    ["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
    ["alt", "reply"],
    // ...
  ],
  // ...
}

بخش یکم هر برچسب نام یا کلید برچسب نامیده میشود و بخش دوم مقدار ان. پس ما میتوانیم با خیال راحت بگوییم در نمونه بالا رویداد ما یک برچسب e با مقدار "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36" و یک برچسب alt با مقدار "reply" دارد.

تمامی بخش ها بعد از دومین نام اصولی ندارند.

این نیپ سه برچسب استاندارد معرفی میکند که در همه رویداد ها با هر گونه ای با معنی یکی استفاده شود:

  • برچسب e برای واگذاری (ارجاع) به رویدادی استفاده میشود: ‍‍["e", <32-bytes lowercase hex of the id of another event>, <recommended relay URL, optional>]

  • برچسب p برای واگذاری به کاربری استفاده میشود: ["p", <32-bytes lowercase hex of a pubkey>, <recommended relay URL, optional>]

  • برچسب a برای واگذاری به یک رویداد (شاید پارامتر شده) قابل جایگزین استفاده میشود:

    • برای رویداد پارامتر شده قابل جایگزین: ["a", <kind integer>:<32-bytes lowercase hex of a pubkey>:<d tag value>, <recommended relay URL, optional>]

    • برای رویداد پارامتر نشده قابل جایگزین: ‍["a", <kind integer>:<32-bytes lowercase hex of a pubkey>:, <recommended relay URL, optional>]

به عنوان یک اصل تمامی بر چسب های تک حرفی با نام های بندواژه (حروف البفا) انگلیسی کوچک و بزرگ توقع میشود کلید ها یا نام ها توسط رله ها فهرست (index) شوند.

به گونه ای که پرس و جو یا دنبال کردن رویداد هایی که به رویداد با شناسه "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36" واگذاری دارند با استفاده از صافیه (filter) {"#e": ["5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"]} ممکن باشد.

گونه ها

یک گونه مشخص میکند هر کلاینت چگونه باید معنای هر رویداد و فیلد های ان را تفسیر کند. (برای مثال برچسب در گونه ۱ معنایی داشته باشد و در گونه ۱۰۰۰۲ معنایی کاملا متفاوت داشته باشد."r")

هر نیپ ممکن است معنای مجموعه ای گونه هارا تعریف کند که در جای دیگری تعریف نشده اند. این نیپ گونه های بنیادی ای را تعریف میکند:

  • 0: ‍‍ابرداده(metadata) کاربر: فیلد محتوا با ساختار جیسان رشته شده تنظیم میشود: {name: <username>, about: <string>, picture: <url, string>} کاربری که رویداد را ساخته است را تعریف میکند. فیلد های ابر داده بیشتری میتواند تنظیم شود. رله ها ممکن است رویداد های گونه 0 را حذف کنند اگر رویداد گونه 0 جدیدی برای کلید عمومی یکسانی دریافت کردند.

  • 1: ‍یادداشت متنی: فیلد محتوا با متن خام پر میشود. هر چیزی که کاربر بخواهد بگوید. محتوایی که باید تجزیه شوند (pars) نباید استفاده شود. و کلاینت ها نباید این محتوا را تجزیه کنند.

و همچنین یک اصل برای محدود شماره گونه ها که ازمایش رله هارا اسان تر و پیاده سازی انهارا منعطف تر میکند:

  • یک گونه با شماره ‍n به طوری که: 1000 <= n < 10000 || 4 <= n < 45 || n == 1 || n == 2, رویداد های منظم هستند که انتظار می رود رله همه انهارا نگهداری کند.

  • یک گونه با شماره n به طوری که: 10000 <= n < 20000 || n == 0 || n == 3, رویداد های جایگزین پذیر هستند. یعنی در ازای هر رویداد با این گونه و یک کلید عمومی باید اخرین نسخه ان توسط رله نگهداری شود. نگارش های قدیمی تر ممکن است پاک شوند.

  • یک گونه با شماره n به طوری که 20000 <= n < 30000, رویداد موقت است. یعنی انتظار نمی روند که توسط رله ها ذخیره شوند.

  • یک رویداد با گونه n به طوری که: 30000 <= n < 40000, رویداد جایگزین پذیر پارامتر شده است. یعنی در ازای هر رویداد با این گونه و یک کلید عمومی که یکمین مقدار برچسب d آن هم یکی باشد تنها اخرین رویداد باید توسط رله ذخیره شود. نگارش های قدیمی تر ممکن است پاک شوند.

در مورد گونه جایگزین پذیر دو رویداد با زمان های یکسان رویدادی با با کمترین شناسه (first in lexical order) باید نگهداری شود و دیگری پاک شود.

در زمان پاسخ به پیام REQ برای یک رویداد جایگزین پذیر برای نمونه: ‍{"kinds":[0],"authors":[<hex-key>]} در صورتی که رله چند نسخه از رویداد را داشته باشد باید نسخه اخر را بازگرداند.

اینها تنها اصول هستند. پیاده سازی رله ها میتواند تفاوت داشته باشد.

ارتباط بین رله ها و کلاینت ها

رله ها یک پورت وب سوکت را برای اتصال کلاینت ها آزاد میگزارند. هر کلاینت باید یک اتصال وب سوکت به هر رله برای تمامی اشتراک هایش ایجاد کند. رله ها ممکن است تعداد اتصال های ممکن را برای IP ادرس یا کلاینت خاصی محدود کنند.

از کلاینت به رله: فرستادن رویداد ها و ایجاد اشتراک (subscriptions)

کلاینت ها میتوانند سه نوع پیام ارسال کنند که به صورت یک ارایه جیسان است. این پیام ها به حالت های زیرند:

  • ‍‍["EVENT", <event JSON as defined above>], برای فرستادن رویداد استفاده میشود.
  • ["REQ", <subscription_id>, <filters1>, <filters2>, ...], برای گرفتن یک رویداد و یا ثبت اشتراک برای دریافت رویداد های جدید و بروزرسانی ها استفاده میشود.
  • ["CLOSE", <subscription_id>], برای لغو اشتراک های پیشین استفاده میشود.

‍‍<subscription_id>, مجموعه ای از کاراکتر های دلخواه و غیر خالی و تا حداکثر طول ۶۴ کاراکتر می باشد. که این نشان دهنده یک اشتراک برای دریافت رویداد های جدید در ازاری هر اتصال می باشد.

رله ها این نشانی هارا باید بصورت مستقل برای هر اتصال وب سوکت مدیریت کنند.

این نشانی ها تظمین نشده اند که بصورت همگانی یکتا باشند.

<filtersX> تایین میکند چه رویداد هایی باید در این اشتراک فرستاده شوند. این فیلد میتواند ویژگی های زیر را داشته باشد:

{
  "ids": <a list of event ids>,
  "authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,
  "kinds": <a list of a kind numbers>,
  "#<single-letter (a-zA-Z)>": <a list of tag values, for #e — a list of event ids, for #p — a list of pubkeys, etc.>,
  "since": <an integer unix timestamp in seconds. Events must have a created_at >= to this to pass>,
  "until": <an integer unix timestamp in seconds. Events must have a created_at <= to this to pass>,
  "limit": <maximum number of events relays SHOULD return in the initial query>
}

در زمان دریافت یک پیام REQ رله در پایگاه داده داخلی خود رویداد هایی با این ویژگی هارا پیدا میکند و ارسال میکند. سپس این فیلتر را نگهداری میکند و رویداد های اینده مربوط به این فیلتر را میفرستد تا زمانی که اتصال وب سوکت بسته شود.

وقتی یک پیام ‍CLOSE یا یک پیام REQ یا شناسه ای مانند فیلتر پیشین ارسال میشود رله باید فیلتر ذخیره شده را بروزرسانی و بازنویسی کند.

ویژگی هایی که آرایه هستند (مانند شناسه ها گونه ها و نویسنده ها و برچسب ها مانند #e) ارایه های جیسانی با بیش از یک مقدار هستند. حداقل یکی از این مقدار ها باید با یکی از فیلد های رویداد یکی باشد تا رویداد با فیلتر همخوانی داشته باشد. برای ویژگی های اسکالر مانند نویسنده یا شناسه همان ويژگی رویداد باید در فهرست فیلتر موجود باشد. برای ویژگی هایی مانند برچسب #e که هر رویداد میتواند برایش مقادیر متفاوتی داشته باشد رویداد و فیلتر باید حداقل یک مورد یکسان داشته باشند تا شرط بر قرار باشد.

شناسه‌ها نویسندگان فهرست های فیلتر #e و #p باید دارای مقادیر مبنای ۱۶ ۶۴ کاراکتری باشند.

خواص since و until برای محدوده زمانی رویداد های اشتراک استفاده میشوند. اگر فیلتر شامل مقدار since بود تنها رویداد هایی با مقدار created_at برابر و بیشتر از مقدار آن با فیلتر همخوانی خواهند داشت. برای ویژگی ‍‍until مشابه است اما رویداد هایی که ‍created_at کمتر و برابر آن با فیلتر همخوانی دارند.

تمامی شروط فیلتر باید برای یک رویداد همخوانی داشته باشند تا از فیلتر عبور کند. چند شرط در یک فیتلر به عنوان && به حساب میاید.

یک پیام REQ ممکن است چندین فیلتر داشته باشد. در این حالت رویدادی که با هرکدارم از این فیتلر ها همخوانی داشته باشد توسط رله به کلاینت فرستاده میشود. چند فیلتر در یک پیام به عنوان || تفسیر میشود.

مقدار limit تنها برای زمان فرستادن اولیه پیام معتبر است و بعد از آن باید نادیده گرفته شود. زمانی که در یک فیلتر ‍limit: n است توقع میرود n رویداد اخر بر اساس created_at بازگردادنده شود. رویداد های تازه تر باید جلوتر فرستاده شوند. در صورت برابر بودن زمان ساخت رویدادی با کمترین شناسه زودتر فرستاده میشود (first in lexical order). رویداد ها میتواند کمتر از مقدار n باشد اما نمیتواند زیادی بیشتر از آن باشد که کلاینت با مقدار زیاد اطلاعات غرق شود.

از رله به کلاینت: فرستادن رویداد ها و اطلاعیه ها

رله ها میتوانند ۵ نوع پیام بفرستند. که باید بصورت ارایه جیسان باشند بر اساس حالت زیر باشند:

  • ["EVENT", <subscription_id>, <event JSON as defined above>], برای فرستادن رویداد درخواست شده به کلاینت استفاده میشود.
  • ["OK", <event_id>, <true|false>, <message>], برای تایید یا رد شدن رویداد فرستاده شده استفاده میشود.
  • ["EOSE", <subscription_id>], برای اعلام پایان رویداد های ذخیره شده و شروع فرستادن رویداد های تازه در حالت بی درنگ استفاده میشود.
  • ["CLOSED", <subscription_id>, <message>], برای اعلام پایان یک اشتراک در سمت سرور (رله) استفاده میشود.
  • ["NOTICE", <message>], برای فرستادن متن های خطا قابل خواندن توسط انسان و چیز های دیگر به سمت کلاینت استفاده میشود.

این NIP هیچ قانونی برای نحوه رفتار و فرستادن پیام NOTICE تعریف نمیکند.

  • EVENT فقط زمانی باید فرستاده شود که با شناسه اشتراک که در یک پیام REQ که پیشتر تعریف شده فرستاده شده باشد.

  • OK این پیام باید در پاسخ به پیام EVENT فرستاده شود. مقدار سوم باید وجود داشته باشد که اگر رویداد با موفقیت فرستاده شده بود مقدار باید true و در غیر این صورت باید false باشد. مقدار چهارم میتواند در صورت موفقیت ارسال یک رشته خالی باشد یا یک رشته که با یک کلمه قابل خواندن توسط ماشین و یک : و بعد از آن یک متن قابل خواندن توسط انسان باشد. چند نمونه:

    • ["OK", "b1a649ebe8...", true, ""]
    • ["OK", "b1a649ebe8...", true, "pow: difficulty 25>=24"]
    • ["OK", "b1a649ebe8...", true, "duplicate: already have this event"]
    • ["OK", "b1a649ebe8...", false, "blocked: you are banned from posting here"]
    • ["OK", "b1a649ebe8...", false, "blocked: please register your pubkey at https://my-expensive-relay.example.com"]
    • ["OK", "b1a649ebe8...", false, "rate-limited: slow down there chief"]
    • ["OK", "b1a649ebe8...", false, "invalid: event creation date is too far off from the current time"]
    • ["OK", "b1a649ebe8...", false, "pow: difficulty 26 is less than 30"]
    • ["OK", "b1a649ebe8...", false, "error: could not connect to the database"]
  • CLOSED در پاسخ یک پیام REQ فرستاده میشود زمانی که یک رله به آن پاسخ داده یا ان را رد کرده است. همچنین زمانی که رله تصمیم بر از بین بردن اشتراک قبل از قطع اتصال یا دریافت پیام CLOSE توسط یک کلاینت را دارد استفاده میشود. این پیام از طرحی مشابه با پیام OK استفاده میکند که با یک پیشوند قابل خواندن توسط ماشین و ادامه ان بصورت قابل خواندن برای انسان می باشد. چند نمونه:

    • ["CLOSED", "sub1", "duplicate: sub1 already opened"]
    • ["CLOSED", "sub1", "unsupported: filter contains unknown elements"]
    • ["CLOSED", "sub1", "error: could not connect to the database"]
    • ["CLOSED", "sub1", "error: shutting down idle subscription"]
  • مقادیر استاندارد برای پیام های OK و CLOSE: duplicate, pow, blocked, rate-limited, invalid هستند و error برای زمانی که هیچ یک از مقادیر مناسب نیست استفاده میشود.

نیپ شماره ۲

فهرست دنبال شوندگان

پایانی ‍‍دلبخواهی

یک رویداد ویژه با گونه 3 به عنوان لیست دنبال شوندگان تعریف میشود. که دارای برچسب های p است که نشاندهنده افراد دنبال شده یا شناخته شده است.

هر برچسب باید کلید نمایه و ادرس رله ای که رویداد های ان نمایه را میتوان پیدا کرد (درصورت نبود نیاز میتواند خالی باشد.) و یک نام اختصاری (میتواند در صورت نبود نیاز خالی باشد یا اراعه نشود.) داشته باشد. برای مثال:

["p", <32-bytes hex key>, <main relay URL>, <petname>]

فیلد محتوا (.content) استفاده نمی شود.

برای مثال:

{
  "kind": 3,
  "tags": [
    ["p", "91cf9..4e5ca", "wss://alicerelay.com/", "alice"],
    ["p", "14aeb..8dad4", "wss://bobrelay.com/nostr", "bob"],
    ["p", "612ae..e610f", "ws://carolrelay.com/ws", "carol"]
  ],
  "content": "",
  ...other fields
}

هر رویداد فهرست دنبال شوندگان موارد قبلی خود را بازنویسی میکند. (overwrite) پس باید تمام دنبالشوندگان را شامل شود. رله ها و کلاینت ها باید تا اینکه فهرست جدیدی دریافت کردند باید فهرست قبلی را پاک کنند.

هر زمان که دنبال شونده ای به فهرست افزوده شد کلاینت باید ان را به پایان ارایه بفزاید (append) پس فهرست به ترتیب زمانی نگهداری میشود.

استفاده ها

پشتبان گیری فهرست دنبالشوندگان

اگر کسی بر این باور باشد که یک رله رویداد های ان را برای زمان مناسبی نگه میدارد میتواند از رویداد گونه ۳ برای پشتبانی گیری فهرست دنبالشونگان خود و بازیابی ان در دستگاه های دیگر خود استفاده کنند.

کشف نمایه و تقویت زمینه

یک کلاینت میتواند از گونه ۳ برای نمایش فهرست دنبال شوندگان استفاده کند. یا افرادی را برای دنبال کردن بر اساس دنباشوندگان افرادی که شخصی دنبال میکند یا مرور میکند پیشنهاد دهد. یا داده هارا در پس زمینه های دیگری نشان دهد.

اشتراک گذاری رله

یک کلاینت ممکن فهرست دنبالشوندگانش را با یک رله خوب برای هر یک از دنبالشوندگان خود منتشر کند. کلاینت های دیگر میتواندد فهرست داخلی رله های خود را برای افزایش مقاومت در برابر سانسور در صورت نیاز بروزرسانی کنند.

طرح نام اختصاری

یک کلاینت میتواند از فهرست دنبالشوندگان افراد دیگر استفاده کند تا یک جدول از نام (http://www.skyhunter.com/marcs/petnames/IntroPetNames.html)[اختصاری] بسازد. این کار نیاز به نام های همگانی قابل خوانده شدن توسط انسان را کاهش میدهد.

یک کاربر یک فهرست دنبالشوندگان درونی دارد:

[
  ["p", "21df6d143fb96c2ec9d63726bf9edc71", "", "erin"]
]

و دو فهرست دنبالشوندگان دریافت میکند یکی از 21df6d143fb96c2ec9d63726bf9edc71 که میگوید:

[
  ["p", "a8bb3d884d5d90b413d9891fe4c4e46d", "", "david"]
]

و یکی از a8bb3d884d5d90b413d9891fe4c4e46d که میگوید:

[
  ["p", "f57f54057d2a7af0efecc8b0b66f5708", "", "frank"]
]

وقتی کاربر 21df6d143fb96c2ec9d63726bf9edc71 را میبنید کلاینت میتواند به جای ان erin را نمایش دهد. وقتی کاربر a8bb3d884d5d90b413d9891fe4c4e46d را میبیند کلاینت میتواند david.erin را نمایش دهد. وقتی کاربر ‍‍f57f54057d2a7af0efecc8b0b66f5708 را میبنید کلاینت میتواند frank.david.erin را نمایش دهد.

نیپ شماره ۳

گواهی OpenTimestamps برای رویداد ها

پیشنویس دلبخواهی

این نیپ یک رویداد با گونه ‍kind:1040 تعریف میکند که یک اثبات OpenTimestamps برای هر رویداد دیگیری دارد:

{
  "kind": 1040
  "tags": [
    ["e", <event-id>, <relay-url>],
    ["alt", "opentimestamps attestation"]
  ],
  "content": <base64-encoded OTS file data>
}
  • مدرک OpenTimestamps باید شناسه رویداد مرجع را به عنوان هش خود ثابت کند.

  • محتوا (content) باید به شکل کامل محتوای یک فایل .ots باشد که حداقل شامل یک گواهی بیت کوین است. این فایل باید شامل یک گواهی بیت کوین باشد (زیرا وجود بیش از یک گواهی معتبر لازم نیست و حجم کمتر بهتر از بیشتر است.) و نباید به گواهی‌ های در انتظار اشاره کند زیرا آنها در این زمینه بی‌ فایده هستند.

روند نمونه بررسی سلامت OpenTimestamps

با استفاده از nak, jq و ots:

~> nak req -i e71c6ea722987debdb60f81f9ea4f604b5ac0664120dd64fb9d23abc4ec7c323 wss://nostr-pub.wellorder.net | jq -r .content | ots verify
> using an esplora server at https://blockstream.info/api
- sequence ending on block 810391 is valid
timestamp validated at block [810391]

نیپ شماره 4

اخطار پیشنهاد نشده: به دنبال نیپ شماره هفده منسوخ شده است.

پیام مستقیم رمزنگاری شده

نهایی پیشنهاد نشده ‍‍دلبخواهی

یک رویداد ویژه با گونه 4 به معنای پیام مستقیم رمزنگاری شده است. انتظار میرود که این رویداد ویژگی های زیر را داشته باشد:

متن باید برابر با رشته‌ای باشد که به صورت base64 رمزنگاری شده و با استفاده از aes-256-cbc رمزگذاری شده است و هر چیزی که یک کاربر می‌خواهد بنویسد را شامل می‌شود. این رمزگذاری با استفاده از یک رمز مشترک انجام می‌گیرد که با ترکیب کلید عمومی گیرنده و کلید خصوصی فرستنده ایجاد شده است. این متن باید به همراه رشته‌ ابتدایی (initialization vector) که به صورت base64 رمزنگاری شده است به عنوان یک پارامتر نام‌گذاری شده با عنوان "iv" اضافه شود. فرمت بدین صورت است: "content": "<encrypted_text>?iv=<initialization_vector>".

برچسب ها ممکن است یک شامل شناسه دریافت کننده پیام باشند (در این صورت رله ها ممکن است به طور طبیعی رویداد را به انها بفرستند.) به شکل: ["p", "<pubkey, as a hex string>"].

برچسب ها ممکن است شامل شناسه پیام قبلی مکالمه یا پیامی که ما صراحتا به ان پاسخ میدهیم (به طوری که ممکن است پیام های سازمان یافته تری رخ دهد.) به صورت ["e", "<event_id>"] باشند.

یادداشت: به طور پیشفرض در پیاده سازی libsecp256k1 ECDH کلید مخفی برابر با هش SHA256 نقطه مشترک (هر دو مختصات X و Y) است. در نوستر فقط مختصات X نقطه مشترک به عنوان کلید مخفی استفاده می‌شود و این مقدار هش نمی‌شود. اگر از کتابخانه libsecp256k1 استفاده می‌کنید، باید یک تابع سفارشی که مختصات X را کپی می‌کند به عنوان آرگومان hashfp در تابع secp256k1_ecdh منتقل کنید. ببینید.

کد منبع نمونه برای تولید چنین رویدادی با جاوااسکریپت:

import crypto from 'crypto'
import * as secp from '@noble/secp256k1'

let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey)
let sharedX = sharedPoint.slice(1, 33)

let iv = crypto.randomFillSync(new Uint8Array(16))
var cipher = crypto.createCipheriv(
  'aes-256-cbc',
  Buffer.from(sharedX),
  iv
)
let encryptedMessage = cipher.update(text, 'utf8', 'base64')
encryptedMessage += cipher.final('base64')
let ivBase64 = Buffer.from(iv.buffer).toString('base64')

let event = {
  pubkey: ourPubKey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 4,
  tags: [['p', theirPublicKey]],
  content: encryptedMessage + '?iv=' + ivBase64
}

اخطار امنیتی

این استاندارد به هیچ وجه به چیزی که به عنوان آخرین پیشرفت‌ ها در ارتباطات رمزگذاری شده بین همتاها در نظر گرفته می‌شود نزدیک نمی‌شود و metadata را در رویدادها نشت می‌کند. بنابراین، نباید برای هیچ چیزی که به واقع نیاز دارید محرمانه بماند استفاده شود و فقط باید با رله‌هایی که از AUTH برای محدود کردن اینکه چه کسی می‌تواند رویدادهای نوع:4 شما را برداشت کند استفاده شود.

اخطار پیاده سازی کلاینت

کلاینت نباید کلید های عمومی را در .content جستجو و جایگذاری کند. اگر مثل یک متن معمولی پردازش شود و @npub... با #[0] و یک برچسب ["p", "..."] جایگذاری شود. برچسب ها به بیرون نشت می شود و کاربران نام برده پیام را در صندوق ورودی خود دریافت میکنند.

مقدمه