ساخت یه مسنجر غیرمتمرکز از صفر
خلاصهٔ کاملتر
نویسنده تو این مقاله سراغ یه سؤال ساده میره: چطور میشه شبکهای ساخت که هر سرور بتونه بدون هیچ ارکستریتور مرکزی بهش وصل شه؟ جوابش federation ـه، همون کاری که SMTP سالهاست انجام میده. تو ایمیل بخش @domain تزئین نیست؛ سرور فرستنده از روی همون دامنه رکوردهای MX رو پیدا میکنه و مستقیم میره سراغ سرور مقصد. تو این پروتکل هم هویت کاربر شکل @ داره تا بقیه بدونن باید سراغ کدوم سرور برن.
قدم بعدی کشفه. هر سرور موظفه یه فایل تو مسیر .well-known/lnchat.json منتشر کنه که نسخهٔ پروتکل، آدرس اندپوینتها و کلید عمومی امضای خودش توش بیاد. نویسنده میگه بهتره مسیر اندپوینتها تو خود پروتکل سفتوسخت حبس نشه، چون ممکنه با روتهای موجود سرور تداخل پیدا کنه.
{
"protocol": "lnchat.federation.v1",
"endpoints": {
"resolve_users": "https://example.com/federation/v1/users/resolve",
"send_messages": "https://example.com/federation/v1/message"
}
}برای رمزنگاری سرتاسری، کلید خصوصی باید پیش کلاینت بمونه و هیچوقت خام به سرور نره، وگرنه سرور میتونه پیامها رو بخونه و دیگه E2E نداریم. سرور فقط کلید عمومی کاربرها رو از طریق اندپوینت resolve_users در اختیار بقیه میذاره، اون هم آرایهای تا چند کاربر با یه درخواست حل بشن. هر کلید یه key_id داره که اگه عوض شد کلاینت بفهمه و هشدار بده. خود پیام هم با HPKE رمز میشه: یه کلید متقارن تازه ساخته میشه، متن باهاش رمز میشه و همون کلید با کلید عمومی گیرنده encapsulate میشه.
تیکهٔ آخر اعتماده، چون هر کسی میتونه یه درخواست HTTP بفرسته و ادعا کنه از طرف یه دامنهٔ دیگهست. برای همین هر سرور یه جفتکلید امضای جدا (Ed25519) داره که با کلیدهای کاربرها قاطی نمیشه، و کنار هر درخواست دامنهٔ مبدأ و مقصد، key_id، timestamp، یه request id و هش SHA-256 بدنه رو امضا و ارسال میکنه. نویسنده تأکید میکنه هر دو طرف باید دقیقاً همون رشتهٔ canonical رو بسازن، وگرنه تأیید شکست میخوره؛ و موقع گرفتن فایل discovery هم حواستون به SSRF باشه.
نکات کلیدی:
- فدریشن یعنی کشف سرور مقصد و بعد گفتوگوی مستقیم سرور به سرور، بدون مرجع مرکزی
- هویت کاربر باید دامنه داشته باشه تا مقصد قابل تشخیص باشه
- فایل .well-known نسخهٔ پروتکل، اندپوینتها و کلید عمومی سرور رو منتشر میکنه
- کلید خصوصی کاربر هیچوقت از کلاینت بیرون نمیره؛ key_id تغییر کلید رو قابل تشخیص میکنه
- message_id برای idempotency و امضای Ed25519 برای اثبات هویت سرور فرستندهست




