مقدمه

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

مقدمه

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

چگونه یک آدرس نیپ ۵ داشته باشیم؟

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

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

example@dezh.tech

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

  • https://njump.me/example@dezh.tech

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

نکته: شما میتوانید چند آدرس نیپ ۵ مرتبط با حساب خود داشته باشید.

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

سرویس های رایگان (نام دامنه سرویس)

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

فهرست سرویس های موجود:

  • https://bitcoinnostr.com

  • https://nostrcheck.me

  • https://zaps.lol

  • https://www.nostr-check.com

  • https://nl.nostraddress.com

  • https://cosanostr.com

در تمامی این سرویس ها شما میتوانید کلید عمومی خود و یک نام دلخواه را وارد کنید و آدرس نیپ ۵ خود را دریافت کنید.

نکته: شرایط یا تعهد سرویس ها (به ویژه سرویس های رایگان) میتواند متفاوت باشد.

ممکن است برخی از کلاینت ها همچون 0xchat بصورت داخلی به شما اجازه دریافت ادرس رایگان نیپ ۵ را بدهند.

سرویس های پولی (نام دامنه سرویس)

استفاده از سرویس های پولی برای دریافت ادرس نیپ ۵ هم ممکن است:

  • https://nostrplebs.com

  • https://nl.nostraddress.com

  • https://getalby.com

  • https://nostr.directory

  • https://nostr.com.au

  • https://stacker.news

  • https://nostrich.house

نکته: شرایط ویژه ممکن است در سرویس های پولی هم وجود داشته باشد.

استفاده از دامنه شخصی و گیت هاب

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

در ادامه به روند انجام این کار و چند نمونه عملی میپردازیم.

۱. یک دامنه تهیه کنید.

۲. رکورد های DNS دامنه خود را به شکل زیر تنظیم کنید:

| Type | Host           | Answer          | TTL | Priority |
|------|----------------|-----------------|-----|----------|
| A    | YOURDOMAIN.COM | 185.199.108.153 | 300 |          |
| A    | YOURDOMAIN.COM | 185.199.109.153 | 300 |          |
| A    | YOURDOMAIN.COM | 185.199.110.153 | 300 |          |
| A    | YOURDOMAIN.COM | 185.199.111.153 | 300 |          |

۳. یک مخزن جدید در گیتهاب ایجاد کنید.

۴. در مخزن یک فایل جدید با مسیر your-repo/.well-known/nostr.json‍ ایجاد کنید.

۵. فایل nostr.json را با کلید عمومی و اطلاعات دلخواه خود پر کنید:

{
 "names": {
   "bob": "e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411"
 }
}

میتوانید bob را با نام دلخواه خود (پیش از @) جایگزین کنید. و کلید عمومی خود را (بصورت hex) در مقابل ان قرار دهید.

برای تبدیل کلید عمومی از npub به hex از این ابزار میتوانید استفاده کنید: https://nostrcheck.me/converter/

بر اساس نیپ ۵ شما میتوانید بصورت key/value کلید ها و نام های بیشتری در فایل مورد نظر قراردهید و برای دوستان و یا حساب های دیگر خود نیز آدرس نیپ ۵ ایجاد کنید.

۶. در root مخزن خود یک فایل با نام _config.yml‍ ایجاد کنید و محتوای زیر را در آن قرار دهید:

‍‍‍```yaml include: [".well-known"]


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

github.com/[USER]/[YOUR-REPO]/settings/pages

۸. پایین قسمت Build and deployment گزینه Deploy from branch را انتخاب کنید و پایین آن برنچ Main/Master را انتخاب کنید. (این موارد ممکن است در پنل جدید گیت هاب تغییر کنند. در نظر داشته باشید که از پنل قدیمی استفاده کنید یا مکان جدید موارد گفته شده را پیدا کنید.)

۹. در پایین قسمت Custom domain نام دامنه خالی خود را (همچون YOURDOMAIN.COM) وارد کنید. (ممکن است از طرف گیتهاب خطا دریافت کنید. ان را نادیده بگیرید.)

۱۰. پایین آن گزینه Enforce HTTPS را فعال کنید. گاهی ممکن است کمی زمان بر باشد.

اکنون آدرس نیپ ۵ شما بصورت `name@yourdomain.com` در دسترس است و در کلاینت های مختلف قابل جستجو خواهد بود. برای نمایش این آدرس به کلاینت مد نظر خود بروید و پروفایل خود را بروزرسانی کنید . ادرس خود را در قسمت نیپ ۵ قرار دهید.

نمونه های عملی:

مخزن خام:

* https://github.com/kehiy/kehiy

پروژه react ای:

* https://github.com/dezh-tech/website/blob/main/public/.well-known/nostr.json

منبع اصلی آموزش: 

* https://nvk.org/n00b-nip5

Web of Trust

در این قسمت به مفهوم WoT را شبکه اعتماد و کاربرد و عملکرد آن در پروتکل نوستر میپردازیم.

شبکه اعتماد یا Web of Trust چیست؟

شبکه اعتماد یک راه حل نایکجا (decentralized) برای اعتبار سنجی کلید های رمزنگاری میباشد. در یک سیستم رمزنگاری مانند PGP شما ممکن است با یک کلید عمومی مواجه شوید که از اعتبار آن خبر نداشته باشید. در چنین مواقعی یک راه حل یکجا (centralized) به نام زیرساخت کلید عمومی یا (PKI) وجود دارد که یک نهاد ويژه با کلیدی مشخص کلید عمومی ای را امضا میکند و با استفاده از این ام‍‍ضا یا سلسه مراتبی از امضا های نهاد هایی ویژه ما به کلید امضا شده اعتماد میکنیم.

در راه حل شبکه اعتماد یا Web of Trust هر کاربر با ورود به این شبکه تعدادی از کلید های دیگر را میشناسد و آنها را قابل اعتماد در نظر میگیرد. هر کلید معتمد مجموعه ای از کلید ها معتمد خود را امضا میکند. شما برای ورود به یک شبکه اعتماد به فردی در آن شبکه نیاز دارید تا کلید شمارا امضا کند. پس از این شما با دیدن کلید های جدید یا خود شما یا یکی از کلید های معتمد شما ممکن است کلید مد نظر را امضا کرده باشد و به آن اعتماد مستقیم داشته باشد. در نهایت شما در یک شبکه اعتماد برای برقراری اعتماد به جای تکیه بر یک نهاد ويژه به امضا های افراد مورد اعتماد خود تکیه میکنید که ممکن شما بصورت مستقیم یا غیرمستقیم به یک کلید اعتماد داشته باشید.

کاربر شبکه اعتماد در نوستر چیست؟

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

برای این منظور یکی از راه حل های اراعه شده شبکه اعتماد است. هر کلاینت میتواند رویداد ها را بر اساس شبکه اعتماد کاربر رتبه بندی کند و به کاربر اجازه دهد تا رتبه های پایین تر از عدد ویژه ای نمایش داده نشود. در نوستر فهرست دنبال شوندگان شما همان فهرست کلید هایی به حساب می آید که شما به آنها اعتماد مستقیم دارید و به دنبال شوندگان این کلید ها ارتباط غیر مستقیم. یک کلاینت میتواند برای بدست آوردن رتبه WoT از فهرست کلید های مسدود شده و گزارش شده توسط افراد معتمد شما نیر استفاده کند.

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

منابع

  1. https://www.geeksforgeeks.org/what-is-web-of-trust

  2. https://freakoverse.github.io/wotonnostr

Outbox model چیست؟

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

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

در مراحل ابتدایی کاربران دو روش متفاوت را برای حل این مشکل ایجاد کردند که هر دو مشکلاتی داشت:

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

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

۲. اتصال اکثر کاربر ها به رله هایی ویژه و شناخته شده: این روش قدرت و ویژگی سانسورناپذیر بودن نوستر را کاهش میداد چرا که نفر حمله کننده با محدود کردن دسترسی به رله های مد نظر بخش زیادی از رویداد هارا از دسترسی خارج میکرد.

مدل Outbox و NIP-65

پس از استفاده از روش های مختلف و و معرفی نیپ ۶۵ راه حل اوت باکس مدل استفاده و شناخته شد. این روش اجازده میدهد هر کابر فهرستی از رله هایی که از انها برای خواندن (دریافت کردن) و نوشتن (فرستادن) استفاده میکند را به رله های مختلف بفرستد.

در نیپ ۶۵ رله های خواندن را inbox و رله های نوشتن را outbox صدا میزنند.

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

شما فهرست nip-65 خود را روی حداکثر رله ها میفرستید. به همین دلیل کلاینت مدنظر برای دسترسی به رویداد های شما یا فرستادن یک رویداد به شما تنها نیاز به پیدا کردن فهرست نیپ ۶۵ شما دارد. پس از ان میتواند توقع داشته باشد که رویداد های شما روی رله های مشخص شده پیدا شوند و شما از ان رله ها رویداد دریافت کنید. این روش اجازه میدهد که ما نیازی به فرستادن تمام رویداد های خود به تمام رله های موجود نداشته باشیم (راه حل یکم کاربر ها برای این مشکل.).

توضیحات با تصویر

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

اوت باکس مدل در پروتکل نوستر

منابع

  • https://mikedilger.com/gossip-model

  • https://github.com/nostr-protocol/nips/blob/master/65.md

برگردان پارسی 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 جدیدی برای کلید عمومی یکسانی دریافت کردند.

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

  • یک گونه با شماره ‍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", "..."] جایگذاری شود. برچسب ها به بیرون نشت می شود و کاربران نام برده پیام را در صندوق ورودی خود دریافت میکنند.

نیپ شماره 5

نگاشت کلیدهای Nostr به شناسه های اینترنتی مبتنی بر DNS

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

در رویداد های گونه ۰ (متادیتا کاربر) یک کلید به نام nip05 (https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1)[میتواند ادرس شناسه گر اینترنتی] (ادرسی ایمیل مانند) را به عنوان مقدار داشته باشد. هرچند که لینک به مشخصات شناسه گر اینترنتی در بالا وجود دارد اما نیپ ۵ تصور میکند که <local-part> به کاراکتر های a-z0-9-_. حساس است و حساستی به کوچکی و بزرگی کاراکتر ها ندارد.

با دیدن این ادرس کلاینت ادرس را به دو قسمت domain و <local-part> تقسیم میکند و از این مقادیر برای ایجاد یک درخواست GET به https://<domain>/.well-known/nostr.json?name=<local-part> استفاده میکند.

خروجی باید یک شی جیسان با کلید به نام "names" داشته باشد که به کلید های عمومی ای در مبنای ۱۶ اشاره کند. اگر کلید عمومی مربوط به <name> با کلید عمومی کاربری که این شناسه در رویداد متادیتا او قرارداشته برابر باشد کلاینت میتواند نتیجه بگیرد که کلید عمومی با این شناسه هم میتواند شناخته شود.

نمونه

اگر کلاینت رویدادی همچون رویداد زیر دید:

{
  "pubkey": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9",
  "kind": 0,
  "content": "{\"name\": \"bob\", \"nip05\": \"bob@example.com\"}"
  ...
}

یک درخواست GET به ادرس https://example.com/.well-known/nostr.json?name=bob ایجاد میکند و چنین پاسخی میگیرد:

{
  "names": {
    "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
  }
}

یا به همراه ویژگی رله ها پیشنهاد شده:

{
  "names": {
    "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
  },
  "relays": {
    "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ]
  }
}

اگر کلید عمومی با کلید عمومی داده شده در "names" برابر بود به این معنا است که وابستگی معتبر است و نشانی نیپ ۵ میتواند نمایش داده شود.

ویژگی رله های پیشنهادی میتواند شامل کلید های عمومی به عنوان کلید و ارایه ای از ادرس رله ها به عنوان مقدار باشد. اگر این مقدار وجود داشت میتواند به کلاینت کمک کند تا بفهمد در کدام رله ها میتواند رویداد های مربوط به کلید عمومی داده شده را پیدا کند. وب سرور هایی که .well-known/nostr.json را بصورت پویا اراعه میکنند باید فهرست رله های پیشنهادی را هم در صورت وجود در همان درخواست برگردانند.

پیدا کردن کاربر ها با استفاده از شناسه نیپ ۵

یک کلاینت ممکن است پشتیبانی از یافتن کاربران با استفاده از شناسه گر اینترنتی را پیاده سازی کند. روند همچون پیش است اما برعکس: نخست کلاینت ادرس well-known را میگیرد سپس از خروجی کلید عمومی کاربر را بدست می اورد و کوشش میکند تا رویداد گونه ۰ کاربر را پیدا کند و بررسی کند که ادرس نیپ ۵ با ادرس نیپ ۵ در رویداد گونه ۰ کاربر برابر است یا نه.

یادداشت ها

کلاینت همیشه باید کلید های عمومی را دنبال کند نه ادرس های نیپ ۵ را

برای نمونه بعد از یافتن نشانی bob@bob.com که کلید عمومی abc...def را دارد کاربر بر روی دکمه دنبال کردن در آن پروفایل کلیک میکند. کلاینت باید مرجع اولیه و اصلی abc...def را نگهدارد و نه نشانی bob@bob.com را. اگر بنا به هر دلیلی در اینده ادرس https://bob.com/.well-known/nostr.json?name=bob شروع به بازگرداندن کلید عمومی 1d2...e3fکرد کلاینت نباید در فهرست دنبال شوندگان خود کلید عمومی abc...def را برای برای ان کاربر جایگزین کند. (اما باید از نمایش دادن ادرس bob@bob.com برای ان کاربر دست نگه دارد زیرا این دیگر به یک ویژگی "nip05" نامعتبر تبدیل شده است.)

کلید عمومی باید در مبنای ۱۶ (hex) باشد

کلید ها باید در مبنای ۱۶ (hex) برگردانده شوند. کلید های NIP-19 در فرمت npub برای نمایش در رابط کاربری کلاینت ها به وجود امده اند نه برای استفاده در این نیپ.

پیشنهاد پیاده سازی کاوش/کشف کاربر

یک کلاینت همچنین میتواند از این استفاده کند تا به کاربر اجازه دهند پروفایل کاربران را جستجو کند. اگر یک کلاینت سرچ باکس ای داشت کاربر میتواند "bob@bob.com"را انجا جستجو کند و کلاینت میتواند این را تشخیص دهد و درخواست های لازم برای بدست اوردن کلید عمومی را ایجاد کند و ان را به کاربر پیشنهاد دهد.

نشان دادن دامنه تنها به عنوان نشانی

کلاینت ها ممکن است شناسه _@domain را به عنوان شناسه ریشه در نظر بگیرند. و بخواهند که ان را بصورت <domain> نمایش دهند. برای نمونه اگر bob مالک دامنه bob.com است او شاید نخواهد نشانی به صورت bob@bob.comداشته باشد زیرا بخشی اضافی دارد. بجای این باب میتواند از شناسه "_@bob.com" استفاده کنید و توقع داشته باشد که کلاینت های نوستر از "bob.com" برای نمایش استفاده کنند و برای همه استفاده ها از این استفاده کنند.

دلیل استفاده از ساختار /.well-known/nostr.json?name=<local-part>

با افزودن <local-part> به صورت یک رشته پرس و جو (query string) بجای بخشی از مسیر ادرس پروتکل میتواند بصورت همزمان از سرور هایی که بصورت پویا و برحسب تقاضا JSON تولید میکنند و هم از سرور هایی که بصورت ایستا JSON ای که شامل چند نام میباشد را داشته باشند پشتبانی کند.

اجازه دسترسی از برنامه های جاوا اسکریپت

برنامه های نوستر جاوااسکریپتی ممکن است به دلیل سیاست های(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)[CORS] مرورگر از دسترسی به مسیر /.well-known/nostr.json در دامنه کاربر محدود شده باشند. زمانی که جاوااسکریپت به دلیل CORS نمیتواند منبعی را بارگیری کند برنامه ان را خطایی مربوط به شبکه میشناسد میگویید منبع وجود ندارد. پس برنامه نمیتواند به کاربر بگویید که خطا به دلیل مشکل CORS است. برنامه های جاوااسکریپتی نوستر که خطا های مربوط به شبکه در گرفتن فایل موجود در مسیر /.well-known/nostr.json بر میخورند میتوانند به کاربر پیشنهاد دهند که سیاست های CORS سرور خود را مورد بررسی قرار دهند. برای نمونه:

$ curl -sI https://example.com/.well-known/nostr.json?name=bob | grep -i ^Access-Control
Access-Control-Allow-Origin: *

کاربر باید مطمن شوند که مسیر /.well-known/nostr.json با HTTP header Access-Control-Allow-Origin: * سرو میشود تا مطمن باشند که برنامه های جاوااسکریپتی که بر روی مرورگر های به روز اجرا میشوند میتوانند ان را اعتبارسنجی کنند.

محدودیت های امنیتی

مسیر /.well-known/nostr.json نباید هیچ گونه تغییر مسیر HTTP ای را برگرداند. صدا زنندگان این ادرس باید هرگونه تغییر مسیر مربوط به /.well-known/nostr.json را نادیده بگیرند.

نیپ شماره 6

اشتقاق کلید از واژگان نمونیک

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

بیپ ۳۹ (https://bips.xyz/39) برای تولید واژگان نمونیک (mnemonic) و مشتق کردن یک دانه باینری از انها استفاده میشود.

بیپ ۳۲ (https://bips.xyz/32) برای مشتق کردن مسیر ‍‍m/44'/1237'/<account>'/0/0 استفاده میشود. (مطابق با ورودی نوستر در سلیپ ۴۴ (https://github.com/satoshilabs/slips/blob/master/slip-0044.md))

یک کلاینت ابتدایی یا ساده میتواند account ای برابر با 0 را برای تولید یک کلید استفاده کند. برای استفاده های حرفه ای تر شما میتوانید account را افزایش دهید برای امکان تولید کلیدهای عملا بی نهایت از مسیر 5 سطحی با استخراج سخت شده.

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

بردار های تست (نمونه های تست)

mnemonic: leader monkey parrot ring guide accident before fence cannon height naive bean private key (hex): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp public key (hex): 17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917 npub: npub1zutzeysacnf9rru6zqwmxd54mud0k44tst6l70ja5mhv8jjumytsd2x7nu


mnemonic: what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade private key (hex): c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add nsec: nsec1c9wh8xy5eqdzln7n5t0ctgxjcrdug73gp5yj0x03gntn67h83twssdfhel public key (hex): d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573 npub: npub16sdj9zv4f8sl85e45vgq9n7nsgt5qphpvmf7vk8r5hhvmdjxx4es8rq74h

نیپ شماره 7

قابلیت window.nostr برای مرورگر های وب

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

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

این شی (Object) باید متد های زیر را تعریف کند:

async window.nostr.getPublicKey(): string // returns a public key as hex
async window.nostr.signEvent(event: { created_at: number, kind: number, tags: string[][], content: string }): Event // takes an event object, adds `id`, `pubkey` and `sig` and returns it

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

async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip44.encrypt(pubkey, plaintext): string // returns ciphertext as specified in nip-44
async window.nostr.nip44.decrypt(pubkey, ciphertext): string // takes ciphertext as specified in nip-44

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

برای اطمینان حاصل کردن از اینکه nostr.window برای کلاینت ها در حین بارگیری صفحه در دسترس است توسعه دهنده اکستنشن که اکتنشن هایی برای فایرفاکس و کرومیوم مینویسند باید اسکریپت خود را با مشخص کردن "run_at": "document_end" در ‍‍manifest اکستنشن خود بارگیری کنند.

پیاده سازی ها

ببینید: https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions

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

نیپ شماره 8

مدیریت اشاره ها (mention)

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

این مستند روش منشن کردن (اشاره کردن/نام بردن) رویداد ها یا کلید های عمومی دیگر را در فیلد کانتنت هر رویداد یادداشت متنی را برای کلاینت ها استاندارد میکند.

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

زمانی که منشن شناسایی شد. برای نمونه کلید عمومی ‍‍27866e9d854c78ae625b867eefdfa9580434bc3e675be08d2acb526610d96fbe کلاینت باید کلید عمومی را به برچسب ها بیفزاید. با یک برچسب p. و سپس مرجع متنی درون فیلد محتوا را با نشاشنه #[index] جایگزین کند. جایی که index شماره قرار گرفتن تگ p مد نظر در تگ ها میباشد (شمارش ایندکس ها از صفر).

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

کلاینتی که یک یادداشت متنی دریافت میکند که محتوای ان شامل #[index] می باشد میتواند محتوا اصلی را در برچسب های p و یا e جستجو کند و با ان جایگزین کند. و انجام هر فرایند دلخواهی همچون نمایش پیش نمایشی از رویداد منشن شده یا افزدودن پیوند به ان نمایه.

جایی که #[index] خارج از تعداد اجزای برچسب ها بود و یا به برچسب هایی غیر از e و یا ‍p اشاره کند کلاینت نباید ان را جایگزین کند یا چیز ویژه ای نمایش دهد و متن را به حالت اصلی نشان دهد.

نیپ شماره 9

درخواست پاک کردن رویداد

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

یک رویداد ویژه با شماره گونه ۵ به معنای درخواست پاک کردن یک رویداد است. که فهرستی از یک یا چند تگ e و/یا a دارد که ارجاع داده میشود به رویدادی که مالک ان میخواهد پاک شود. درخواست پاک کردن باید یک تگ k را شامل شود که شماره گونه هر رویدادی است که درخواست میشود که پاک شود.

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

برای نمونه:


{
  "kind": 5,
  "pubkey": <32-bytes hex-encoded public key of the event creator>,
  "tags": [
    ["e", "dcd59..464a2"],
    ["e", "968c5..ad7a4"],
    ["a", "<kind>:<pubkey>:<d-identifier>"],
    ["k", "1"],
    ["k", "30023"]
  ],
  "content": "these posts were published by accident",
  // other fields...
}

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

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

وقتی از یک تگ a استفاده میشود رله باید تمام نگارش های رویداد جایگزین پذیر را تا زمان created_at رویداد پاک کنند.

استفاده کلاینت ها

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

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

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

کلاینت‌ ها رویداد درخواست پاک کردن را به هر شکلی که انتخاب کنند میتوانند نمایش دهند، برای نمونه اصلا نشان ندهند یا با یک اطلاعیه برجسته ان را نمایش دهند.

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

استفاده رله ها

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

درخواست پاک کردن یک رویداد درخواست پاک کردن

انتشار یک رویداد برای پاک کردن یک درخواست پاک کردن بی تاثیر است. رله ها و کلاینت ها مجبور به پشتیبانی ویژگی لغو درخواست پاک کردن نیستند.

نیپ شماره 10

رویداد های متنی و رشته ها

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

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

چکیده

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

مقدار content شامل متن قابل خواندن توسط انسان است.

برچسب های e و p میتوانند برای تعریف یادداشت ها رشته ها و منشن ها استفاده شوند.

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

تگ e موقعیتی (منسوخ شده)

این رویه در استفاده است اما باید به عنوان منسوخ شده در نظر گرفته شود.

["e", , ] همانطور که در نیپ ۱ آمده.

به طوری که:

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

موقعیت تگ e در رویداد معنا های متفاوتی دارد:

  • بدون تگ e: این رویداد یک پاسخ نیست و به رویدادی ارجاعی ندارد.

  • یک تگ e: ["e", ]: ایدی رویدادی که این رویداد پاسخ به ان است.

  • دو تگ e ["e", ], ["e", ]: : شناسه رویدادی که در ریشه زنجیره پاسخ ها قرار دارد. : شناسه رویدادی که این رویداد به ان اشاره میکند.

  • چند تگ e ["e", ] ["e", ], ..., ["e", ]: ممکن است هر تعداد شناسه وجود داشته باشد که میتواند در زنجیره پاسخ ها باشند یا نباشند که از این رویداد استناد میکنند. root-id و reply-id همچون موارد قبل اند.

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

تگ e علامت گزاری شده (ترجیحی)

["e", , , , ]

به طوری که:

*: شناسه رویداد ارجاع داده شده است.

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

  • : دلبخواهی است و اگر وجود داشته باشد یکی از موارد reply یا root یا mention است.

  • : دلبخواهی است و باید شناسه مالک رویداد ارجاع شده باشد.

مواردی که با "reply" نشانه گذاری شده اند نشان دهنده شناسه رویدادی است که به آن پاسخ داده شده است. موارد نشانه گذاری شده با "root" شناسه رویداد ریشه رویدادی است که به ان پاسخ داده شده است. برای پاسخ های سطح بالا (رویداد هایی که مستقیما به ریشه اشاره میکنند.) تنها نشانه root باید استفاده شود. مواردی که با "mention" نشانه گذاری شده اند نشان دهنده شناسه یک رویداد نقل قول شده یا بازنشر شده اند.

یک پاسخ مستقیم به رویداد ریشه باید تنها یک تگ e با نشانه گذاری root داشته باشد.

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

تگ p

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

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

برای نمونه: یک رویداد متنی با مالکیت a1 با تگ های p: [p1, p2, p3] سپس تگ های p پاسخ به این رویداد باید به صورت [a1, p1, p2, p3] بدون اهمیت ترتیب باشد.

نیپ شماره 98

احرازهویت HTTP

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

این نیپ یک رویداد گذرموقت را تعریف میکند که برای احرازهویت درخواست ها به HTTP سرور ها با استفاده از رویداد های نوستر استفاده میشود.

این نیپ برای خدمات HTTP ای که برای نوستر ساخته شده اند و با حساب های نوستر کاربران سر و کار دارند کاربردی است.

رویداد نوستر

یک رویداد با گونه 27235 (به دلیل RFC 7235) استفاده میشود.

فیلد content باید خالی باشد.

برچسب های زیر باید در رویداد وجود داشته باشند:

  • u - url مطلق سرویس
  • method - متد درخواست HTTP

رویداد نمونه:

{
  "id": "fe964e758903360f28d8424d092da8494ed207cba823110be3a57dfe4b578734",
  "pubkey": "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
  "content": "",
  "kind": 27235,
  "created_at": 1682327852,
  "tags": [
    ["u", "https://api.snort.social/api/v1/n5sp/list"],
    ["method", "GET"]
  ],
  "sig": "5ed9d8ec958bc854f997bdc24ac337d005af372324747efe4a00e24f4c30437ff4dd8308684bed467d9d6be3e5a517bb43b1732cc7d33949a3aaf86705c22184"
}

سرور باید بررسی های زیر را برای اعتبارسنجی رویداد انجام دهد:

۱. گونه باید 27235 باشد. ۲. تاریخ created_at باید در یک بازه زمانی منطقی باشد. (زمان پیشنهادی یک دقیقه است.) ۳. برچسب u باید url کامل مسیر صدا زده شده باشد. (با query parameter ها.) ۴. برچسب method باید با متد درخواست برابر باشد.

زمانی که درخواست شامل body است (متد های POST/PUT/PATCH) کلاینت ها باید یک هش SHA256 از آن را در تگ payload بر مبنای 16 قرار دهند. نمونه: (["payload", "<sha256-hex>"]). سرور ممکن است آن را بررسی کند تا اعتبارسنجی کند که payload درخواست احرازهویت شده است.

اگر هرکدام از بررسی ها ناموفق بود سرور باید درخواست با کد 401 احرازهویت نشده پاسخ دهد.

سرور ممکن است بررسی های افزوده مربوط به پیاده سازی خودش را هم انجام دهد.

روند درخواست

با استفاده از Authorization HTTP header رویداد باید بر مبنای ۶۴ باشد و Authorization scheme Nostr باشد.

نمونه HTTP Authorization header:

Authorization: Nostr 
eyJpZCI6ImZlOTY0ZTc1ODkwMzM2MGYyOGQ4NDI0ZDA5MmRhODQ5NGVkMjA3Y2JhODIzMTEwYmUzYTU3ZGZlNGI1Nzg3MzQiLCJwdWJrZXkiOiI2M2ZlNjMxOGRjNTg1ODNjZmUxNjgxMGY4NmRkMDllMThiZmQ3NmFhYmMyNGEwMDgxY2UyODU2ZjMzMDUwNGVkIiwiY29udGVudCI6IiIsImtpbmQiOjI3MjM1LCJjcmVhdGVkX2F0IjoxNjgyMzI3ODUyLCJ0YWdzIjpbWyJ1IiwiaHR0cHM6Ly9hcGkuc25vcnQuc29jaWFsL2FwaS92MS9uNXNwL2xpc3QiXSxbIm1ldGhvZCIsIkdFVCJdXSwic2lnIjoiNWVkOWQ4ZWM5NThiYzg1NGY5OTdiZGMyNGFjMzM3ZDAwNWFmMzcyMzI0NzQ3ZWZlNGEwMGUyNGY0YzMwNDM3ZmY0ZGQ4MzA4Njg0YmVkNDY3ZDlkNmJlM2U1YTUxN2JiNDNiMTczMmNjN2QzMzk0OWEzYWFmODY3MDVjMjIxODQifQ

پیاده سازی های مرجع

C# ASP.NET AuthenticationHandler NostrAuth.cs ‍‍