تكامل العملاء مع مُرحِّل الدردشة في AeroNyx
مرجع تكاملي لمُرحِّل الدردشة في AeroNyx: المصادقة عبر WebSocket، والرسائل الفردية (1:1) ورسائل المجموعات المختومة، والتسليم دون اتصال، وإشعارات التسليم والقراءة، والتفاعلات، وحالة الاتصال، والمرفقات المشفّرة، والإشعارات الفورية، وحدود المعدل.
إنشاء تعليمات للتكامل
اختر تقنية العميل والمهمة، ثم انسخ التعليمات إلى مساعد البرمجة. تُنشأ محليًا ولا تُرسل إلى خدمة ذكاء اصطناعي.
التعليمات المُنشأة
هذا هو المرجع التكاملي لمُرحِّل الدردشة في AeroNyx: خدمة WebSocket وHTTPS على النطاق api.aeronyx.network التي تنقل بين هويات AeroNyx الرسائلَ الفردية (1:1) المشفّرة تشفيرًا تامًّا بين الطرفين، ورسائلَ المجموعات، وإشعارات التسليم والقراءة، والتفاعلات، وحالة الاتصال، والمرفقات المشفّرة.
وهو موجَّه إلى المهندسين الذين يبنون عملاء وروبوتات وخدمات متوافقة مع AeroNyx، وإلى وكلاء البرمجة بالذكاء الاصطناعي الذين ينفّذونها. ويعكس كل إطار وحقل وحدّ في هذه الصفحة المُرحِّلَ العامل في بيئة الإنتاج وAeroNyx App كما هما في أكتوبر 2026.
مُرحِّل الدردشة هو مسار التسليم المركزي، ويتعايش مع مسار العُقد اللامركزي (توجيه البصل وصناديق البريد المجهولة، راجع قسم «التسليم الموثَّق عبر قفزتين»)؛ ويجوز للعميل استخدام المسارين كليهما. وللتكامل عبر HTTPS بنمط الطلب/الاستجابة دون WebSocket، راجع واجهة HTTPS للدردشة المركزية v1.
نموذج الثقة
المُرحِّل أعمى عن المحتوى. إذ يشفّر العملاء كل شيء ويوقّعونه قبل وصوله إلى المُرحِّل، ويقتصر دور المُرحِّل على توجيه نص مشفّر معتم ووضعه في قوائم الانتظار وتطبيق حدود المعدل عليه.
لا يتلقّى المُرحِّل أبدًا:
- نص الرسائل أو الرموز التعبيرية للتفاعلات أو التعديلات أو حمولات المجموعات بصيغة نص صريح
- مفاتيح الدردشة أو مفاتيح المجموعات أو مفاتيح المرفقات أو قيم nonce
- محتويات المرفقات أو أسماء الملفات أو الصور المصغّرة أو الأشكال الموجية أو النصوص المفرَّغة
- مفاتيح الهوية الخاصة
غير أن المُرحِّل يرصد البيانات الوصفية للتسليم، وينبغي للمُدمِجين اعتبارها مرئية للمشغّل:
- المفاتيح العامة للمرسِل والمستقبِل، ومعرّفات المجموعات، ومعرّفات الرسائل
- الطوابع الزمنية، وأحجام الحمولات، وحالة التسليم والاستلام والقراءة
- حالة الاتصال، وحالة الواجهة الأمامية، ومؤشرات الكتابة (تُرسَل في إطارات بنص صريح)
- البيانات الوصفية لإشارات المكالمات (اسم الغرفة، ومعرّف المكالمة، وعلامة الفيديو)
- حجم النص المشفّر للمرفق، ونوع الوسائط المصرَّح به، وموعد انتهاء الصلاحية
- العلامة
contact_requestوالبيانات الوصفية للاتصال على مستوى IP
تنبع سرّية المحتوى من التشفير التام بين الطرفين، لا من التحكم في الوصول على المُرحِّل. صمِّم نظامك وفق ذلك: يجب أن يظل المهاجم الذي يحصل على نص مشفّر مخزَّن عاجزًا عن قراءته.
الهويات والمفاتيح
هوية الدردشة في AeroNyx هي زوج مفاتيح Ed25519. والمفتاح العام البالغ 32 بايت، المكتوب في صورة 64 حرفًا ست عشريًا بأحرف صغيرة، هو العنوان. ولِّد المفاتيح على الجهاز ولا ترسل المفتاح الخاص إلى أي جهة.
يُشتقّ من زوج الهوية مفتاحان:
- مفتاح الدردشة (1:1).
HKDF-SHA256(salt = empty, ikm = X25519(my_secret, peer_public), info = "AERONYX-P2P-KEY", length = 32)، حيث يُحوَّل مفتاحا Ed25519 كلاهما إلى X25519 (SHA-512(seed)[0..32]مع التقييد (clamping) للمفتاح السري، والتحويل من Edwards إلى Montgomery للمفتاح العام). ويشتقّ الطرفان المفتاح نفسه. - توقيعات الرسائل. Ed25519 بمفتاح الهوية، على سلاسل البايتات الدقيقة المعرَّفة في هذه الصفحة.
استخدم دائمًا الترميز الست عشري بأحرف صغيرة للمفاتيح العامة في الإطارات. فالمُرحِّل لا يوحِّد حالة الأحرف في جميع مفاتيح قوائم الانتظار.
المصادقة
توقيع RelayAuth
يستخدم تسجيل الدخول عبر WebSocket وكل نقطة نهاية HTTPS تتطلب المصادقة التوقيعَ نفسه:
digest = SHA256("AeroNyx-RelayAuth-v1" || pubkey[32] || timestamp as u64 little-endian)
signature = Ed25519(identity_key, digest)
السلسلة "AeroNyx-RelayAuth-v1" هي 20 بايت ASCII دون محرف إنهاء. والحقل timestamp مقدَّر بثواني Unix، ويجب أن يكون ضمن 300 ثانية من توقيت الخادم.
يربط التوقيع الهوية والوقت فقط. فهو لا يربط الطريقة أو المسار أو جسم الطلب أو الاتصال، ولا يتضمن nonce. ولِّد طابعًا زمنيًا جديدًا لكل طلب، وأرسله عبر TLS فقط، ولا تسجّله في السجلات أبدًا.
ترويسة HTTPS
Authorization: Relay <pubkey-hex>:<timestamp>:<signature-hex>
يعيد الفحص الفاشل رمز HTTP 401 مع {"success": false, "error": "<reason>"}. ومن الأسباب: missing_auth_header وmalformed_auth_header وinvalid_timestamp وtimestamp_expired وinvalid_pubkey وinvalid_signature.
اتصال WebSocket
نقطة النهاية
wss://api.aeronyx.network/ws/relay/
تتصل العملاء الأصلية دون ترويسة Origin. ويجب على المتصفحات الاتصال من أصل مسموح به؛ وأي أصل آخر، أو ترويسة Host غير معروفة، يؤدي إلى إغلاق الاتصال بالرمز 1008.
تسجيل الدخول
يقبل الخادم المقبس، ثم ينتظر إطار auth خلال 30 ثانية:
{
"type": "auth",
"pubkey": "<64 hex>",
"timestamp": 1780000000,
"signature": "<128 hex>"
}
عند النجاح:
{ "type": "auth_ack", "session_id": "<uuid>", "server_ts": 1780000001 }
استخدم server_ts لتقدير فرق الساعة؛ إذ تُفحَص الطوابع الزمنية للرسائل ضمن النافذة نفسها البالغة ±300 ثانية.
بعد auth_ack يبدأ الخادم نبضات القلب، ويُشرِك الاتصال في قنواته، ويعيد فورًا تشغيل قائمة الانتظار دون اتصال (راجع قسم «التسليم دون اتصال»).
عند الفشل يُرسَل {"type": "auth_error", "reason": "<reason>"} ويُغلَق المقبس بالرمز 4001. والأسباب هي: missing_fields وtimestamp_expired وinvalid_pubkey وinvalid_signature_length وinvalid_signature_encoding وinvalid_signature وinternal_error. وعند انتهاء مهلة تسجيل الدخول يُرسَل السبب timeout ويُغلَق الاتصال بالرمز 4002.
يُجاب عن أي إطار آخر يسبق تسجيل الدخول بالإطار auth_error مع السبب authentication_required؛ ويبقى المقبس مفتوحًا.
نبضات القلب وحالة الواجهة الأمامية
| الاتجاه | الإطار | السلوك |
|---|---|---|
| من الخادم إلى العميل | {"type":"ping"} كل 30 ثانية | الرد بـ{"type":"pong"}. |
| من العميل إلى الخادم | {"type":"ping"} | يرد الخادم بـ{"type":"pong"}. |
| من العميل إلى الخادم | {"type":"presence_state","foreground":true} | يعلِّم هذا الاتصال بأنه نشط. |
| من العميل إلى الخادم | {"type":"presence_state","foreground":false} | يعلِّمه بأنه في الخلفية، فيجوز للمُرحِّل إرسال إشعار فوري للرسائل الجديدة. |
لا يَعُدّ المُرحِّل الهوية متصلة إلا ما دام اتصالها يرسل ping أو pong أو presence_state مع foreground: true مرة كل 90 ثانية على الأقل. ويرسل AeroNyx App إطار ping كل 15 ثانية أثناء وجوده في الواجهة الأمامية، ويَعُدّ الاتصال منقطعًا إذا فُقد أكثر من ثلاثة ردود pong.
ينبغي للاتصالات طويلة الأمد إعادة الاتصال مرة واحدة على الأقل كل 24 ساعة. ولا تضيع الرسائل أبدًا عندما يتوقف الاتصال بصمت عن تلقي الإطارات الحية، لأن كل رسالة توضع أيضًا في قائمة الانتظار ويُعاد تشغيلها عند تسجيل الدخول، لكن التسليم الحي لا يُستأنف إلا بعد إعادة الاتصال.
قواعد الإطارات
- إطارات نصية فقط، بكائن JSON واحد في كل إطار. وتُتجاهَل الإطارات الثنائية.
- الحد الأقصى لحجم الإطار 1,048,576 محرفًا. وتُرفَض الإطارات الأكبر مع
{"type":"error","reason":"message_too_large"}. أبقِpayload_b64دون نحو 800 KiB لترك مساحة لبقية الإطار. - تعيد الإطارات المشوَّهة
errorمع السببinvalid_jsonأوinvalid_json_typeأوunknown_type.
إطار الخطأ العام:
{ "type": "error", "reason": "<reason>", "retry_after": 0 }
تحمل أخطاء حدود المعدل أيضًا الحقل scope.
رموز الإغلاق
| الرمز | المعنى |
|---|---|
1008 | المضيف (Host) أو الأصل (Origin) غير مسموح به. |
4000 | تعذّر على الخادم إرسال نبضات القلب. |
4001 | فشل تسجيل الدخول. |
4002 | انتهت مهلة تسجيل الدخول. |
أعد الاتصال بتراجع أُسّي مع تفاوت عشوائي (jitter). إذ ينتظر AeroNyx App مدة 2^(attempt-1) ثانية، محصورة بين 1 و60 ثانية، مضروبة في معامل عشوائي بين 0.8 و1.2.
إرسال رسالة فردية (1:1)
1. بناء الظرف المختوم
حمولة الرسالة الفردية هي ChatEnvelope موقَّع ومشفَّر. وتجد طريقة بنائه الدقيقة، وتنفيذًا مرجعيًا بلغة Python، ومتجه اختبار مرجعيًا (golden) في واجهة HTTPS للدردشة المركزية v1: صيغة الظرف المختوم. ويُستخدَم الظرف نفسه في الواجهتين كلتيهما.
باختصار: XChaCha20-Poly1305 بمفتاح الدردشة، وتوقيع Ed25519 على سجلّ (transcript) طوله 121 بايت، وتخطيط ثنائي ثابت. وقيمة content_type هي 0 لكل رسالة، بما في ذلك الرسائل ذات المرفقات.
النص الصريح هو نص UTF-8 في الرسالة العادية، أو كائن JSON في الرسائل التي تتضمن مرفقات أو ردودًا أو إعادة توجيه أو معاينات روابط:
{
"type": "aeronyx_message",
"text": "See the attached file",
"attachments": [ { "blob_id": "...", "file_key": "...", "media_type": "image/jpeg", "file_name": "photo.jpg", "file_size": 482113 } ],
"reply": { "msg_id": "...", "sender_pubkey": "...", "text": "..." },
"forwarded": true,
"forwarded_from_name": "...",
"forwarded_from_pubkey": "...",
"link_preview": { "url": "https://...", "title": "...", "description": "...", "site_name": "..." }
}
جميع الحقول عدا type وtext اختيارية. وينبغي للمستقبِلين عرض أي نص صريح ليس كائن JSON يحمل "type": "aeronyx_message" بوصفه نصًا عاديًا. ويُعرَّف كائن المرفق في قسم «المرفقات المشفّرة».
2. توقيع الإطار
يحمل كل إطار رسالة توقيعًا ثانيًا هو payload_sig، ويتحقق منه المُرحِّل قبل قبول الإطار:
payload_sig = Ed25519(identity_key,
SHA256(receiver_pubkey[32] || sender_pubkey[32] || discriminant as 1 byte
|| timestamp as u64 little-endian || envelope_bytes))
القيمة envelope_bytes هي payload_b64 بعد فك ترميزه. استخدم المميِّز 11 للرسائل والتعديلات و12 للتفاعلات. ويُرمَّز payload_sig بالنظام الست عشري.
3. الإرسال
{
"type": "relay_send",
"msg_id": "9e762ae0f5da43e6bec5eb8143f1f834",
"receiver_pubkey": "<64 hex>",
"discriminant": 11,
"payload_b64": "<base64 envelope>",
"timestamp": 1780000000,
"payload_sig": "<128 hex>"
}
| الحقل | القواعد |
|---|---|
msg_id | 32 حرفًا ست عشريًا بأحرف صغيرة: وهو message_id الخاص بالظرف والبالغ 16 بايت. ويخزّن التطبيق لدى المستقبِل الرسالة تحت هذا المعرّف، لذا يجب أن يطابق الظرف. |
receiver_pubkey | المفتاح العام للمستلم. |
discriminant | 11. |
timestamp | بثواني Unix، ضمن ±300 ثانية من توقيت الخادم. |
suppress_push | اختياري. القيمة true تمنع إرسال إشعار فوري. |
contact_request | اختياري. يعلِّم أول رسالة إلى شخص يشترط التحقق من جهات الاتصال (راجع قسم «التحقق من جهات الاتصال»). |
4. الإقرار
{ "type": "relay_delivered", "msg_id": "...", "delivered": true, "queued": true, "durable": true }
- تصبح قيمة
durable(وكذلكqueued) هيtrueبمجرد تخزين الرسالة في قائمة الانتظار دون اتصال الخاصة بالمستلم. اعتبرdurable: trueبمعنى «أُرسلت». - يعني الحقل
deliveredأن للمستلم اتصالًا نشطًا في الواجهة الأمامية. وهو ليس دليلًا على الاستلام؛ استخدم إشعارات التسليم لهذا الغرض.
انتظر الإقرار مدة تصل إلى 15 ثانية قبل اعتبار المحاولة فاشلة.
حالات الرفض والأخطاء
{ "type": "send_rejected", "msg_id": "...", "reason": "verification_required" }
| الاستجابة | السبب | الإجراء |
|---|---|---|
send_rejected | verification_required | لا يقبل المستلم الرسائل إلا من جهات اتصاله. لا تُعِد المحاولة. |
send_rejected | contact_request_rate_limited | بلغت طلبات الاتصال حدّها لهذا المستلم. لا تُعِد المحاولة. |
error | missing_fields | حقل مطلوب غائب أو فارغ. |
error | invalid_timestamp, timestamp_expired | أصلِح الساعة وأعد الختم الزمني. |
error | invalid_payload_sig | لا يجتاز payload_sig التحقق. |
error | rate_limited | أعد المحاولة بعد retry_after ثانية. |
إعادة المحاولة
أعد محاولة إرسال الرسالة غير المُقَرّ بها باستخدام msg_id نفسه وبايتات الظرف نفسها. ولأن المُرحِّل يرفض الإطارات التي يزيد عمرها على 300 ثانية، احسب قيمتين جديدتين للحقلين timestamp وpayload_sig الخاصين بالإطار في كل محاولة؛ بينما يحتفظ الظرف بطابعه الزمني الأصلي. ويزيل المُرحِّل والمستقبِلون التكرار استنادًا إلى msg_id.
البديل عبر HTTPS
عندما يتعذّر استخدام WebSocket، يمكن إرسال الرسالة نفسها عبر HTTPS:
POST /api/relay/push/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/json
{
"receiver_pubkey": "<64 hex>",
"discriminant": 11,
"payload_b64": "<base64 envelope>",
"timestamp": 1780000000,
"payload_sig": "<128 hex>"
}
يكون النجاح برمز HTTP 200 مع {"success": true}. وتعيد إخفاقات التحقق من جهات الاتصال الرمز 400 مع verification_required أو contact_request_rate_limited، ويعيد تجاوز حدود المعدل الرمز 429 مع Retry-After. والرسائل المُرسَلة بهذه الطريقة توضع في قائمة الانتظار للمستلم لكنها لا تُطلق إشعارًا فوريًا، لذا أعد إرسالها عبر WebSocket بمجرد إعادة اتصاله.
استقبال الرسائل
تصل الرسائل الواردة في صورة إطار relay_envelope:
{
"type": "relay_envelope",
"sender_pubkey": "<64 hex>",
"discriminant": 11,
"payload_b64": "<base64 envelope>",
"timestamp": 1780000000,
"msg_id": "9e762ae0f5da43e6bec5eb8143f1f834",
"from_offline": false
}
بالنسبة إلى discriminant: 11:
- أزِل التكرار استنادًا إلى
msg_id. فقد تصل الرسالة نفسها حيةً ثم مرة أخرى من قائمة الانتظار دون اتصال. - حلِّل الظرف وتحقق من أن
receiver_pubkeyفيه هو هويتك. - في الإطارات الحية (
from_offline: false)، أسقِط الرسالة إذا ابتعد الطابع الزمني للظرف عن ساعتك بأكثر من 300 ثانية. - تحقق من توقيع الظرف مقابل
sender_pubkeyالوارد في الظرف. فهذا المفتاح، لاsender_pubkeyالوارد في الإطار، هو المرسِل المُصادَق عليه. - فُكّ التشفير بمفتاح الدردشة المشتق من ذلك المرسِل. كانت إصدارات التطبيق القديمة جدًا تشفّر باستخدام ناتج X25519 الخام؛ فجرّبه إذا فشل مفتاح HKDF.
- خزِّن الرسالة تخزينًا دائمًا، ثم أرسل إشعار تسليم، وفي حالة
from_offline: trueأرسل أيضًا إقرارًا بالعنصر دون اتصال.
تُسلَّم الأظرف الفردية (1:1) دون payload_sig؛ فتوقيع الظرف هو فحص الأصالة. وقد تحمل الإطارات أيضًا contact_request: true.
إذا لم تكن الرسالة موجّهة إليك، أو فشل التحقق منها أو فك تشفيرها، فأسقِطها دون عرض أي شيء.
التسليم دون اتصال
تُكتَب كل رسالة وتعديل وسحب وتفاعل وإشعار تسليم وإشعار قراءة في قائمة الانتظار دون اتصال الخاصة بالمستلم قبل التسليم الحي. ويُعاد تشغيل قائمة الانتظار تلقائيًا بعد كل تسجيل دخول وعند الطلب:
{ "type": "relay_pull" }
يعيد المُرحِّل تشغيل جميع العناصر الموجودة في قائمة الانتظار بأنواع إطاراتها المعتادة مع from_offline: true، مرتبةً حسب الطابع الزمني، ثم يرسل:
{ "type": "relay_pull_done", "count": 12, "has_more": false }
يتم التسليم مرة واحدة على الأقل. وتبقى العناصر في قائمة الانتظار حتى يُقَرّ بها:
{ "type": "relay_offline_ack", "msg_id": "9e762ae0f5da43e6bec5eb8143f1f834" }
أقِرّ بالتفاعلات باستخدام "reaction_id" بدلًا من "msg_id". ولا تُقِرّ إلا بعد تخزين العنصر تخزينًا دائمًا على الجهاز. ويرد المُرحِّل بـ{"type":"relay_offline_ack","msg_id":"...","success":true}.
حدود قائمة الانتظار:
| الحد | القيمة |
|---|---|
| العناصر لكل مستلم | 1,000. وعند امتلاء القائمة تُرفَض العناصر الجديدة ويرى المرسِل durable: false. |
| مدة الاحتفاظ | 72 ساعة بعد إضافة أحدث عنصر. |
| حجم الحمولة | 1 MiB بعد فك الترميز، ضمن حد الإطار البالغ 1 MiB. |
المُرحِّل مخزن مؤقت للتسليم، وليس سجلًا للرسائل. احتفظ بالسجل على الجهاز.
إشعارات التسليم وإشعارات القراءة
إشعار التسليم
أرسله بعد تخزين الرسالة:
{ "type": "message_receipt", "receiver_pubkey": "<original sender>", "msg_id": "...", "timestamp": 1780000010 }
يرد المُرحِّل بـmessage_receipt_ack ويسلّم {"type":"message_receipt","sender_pubkey":"...","msg_id":"...","timestamp":...} إلى المرسِل الأصلي.
إشعار القراءة
{ "type": "message_read", "receiver_pubkey": "<original sender>", "msg_id": "<latest read message>", "timestamp": 1780000200, "enabled": true }
تعمل إشعارات القراءة بوصفها علامات حدّ (watermarks): إذ يعلِّم التطبيق الرسالة المحددة وكل رسالة صادرة سابقة لها في المحادثة بأنها مقروءة. ولا ترسل إشعار قراءة إلا إذا كان المستخدم قد فعّل إشعارات القراءة.
يفرض المُرحِّل قاعدة تبادلية عند الإرسال وعند التسليم الحي وعند إعادة التشغيل. فلا يُسلَّم إشعار القراءة إلا إذا كان المستخدمان جهتي اتصال متبادلتين وكان لدى كليهما read_receipts_enabled. وإلا يتلقى المرسِل:
{ "type": "message_read_ack", "msg_id": "...", "delivered": false, "suppressed": true, "reason": "receiver_read_receipts_disabled" }
| السبب | المعنى |
|---|---|
client_disabled | حمل الإطار enabled: false أو read_receipts_enabled: false. |
not_mutual_contact | المستخدمان ليسا جهتي اتصال متبادلتين. |
reader_read_receipts_disabled | أوقف القارئ إشعارات القراءة. |
receiver_read_receipts_disabled | أوقف المرسِل الأصلي إشعارات القراءة. |
invalid_pubkey | أحد المفاتيح العامة مشوَّه. |
يُقَرّ بإشعار القراءة المُسلَّم بالإطار {"type":"message_read_ack","msg_id":"...","delivered":true}.
التعديلات والسحب
التعديل
التعديل ظرف مختوم جديد (له message_id عشوائي خاص به) يحتوي المحتوى البديل كاملًا، ويُرسَل مقابل معرّف الرسالة الأصلية:
{
"type": "message_edit",
"receiver_pubkey": "<64 hex>",
"msg_id": "<original msg_id>",
"target_msg_id": "<original msg_id>",
"payload_b64": "<base64 envelope>",
"payload_sig": "<128 hex>",
"timestamp": 1780000400
}
يستخدم payload_sig الصيغة الواردة في قسم «توقيع الإطار» مع المميِّز 11. والتعديل الذي يزيل جميع المرفقات يضبط "attachments_edited": true في JSON النص الصريح الخاص به. ويرد المُرحِّل بـmessage_edit_ack. ويتحقق المستقبِلون من الظرف ويفكّون تشفيره كما في الرسالة، ويجب ألا يطبّقوا التعديل إلا إذا كان مرسِله هو كاتب الرسالة الأصلية.
السحب
{
"type": "message_revoke",
"receiver_pubkey": "<64 hex>",
"sender_pubkey": "<64 hex>",
"msg_id": "<original msg_id>",
"timestamp": 1780000500,
"payload_sig": "<128 hex>"
}
يُحسَب توقيع السحب على البايتات الخام، دون تجزئة:
payload_sig = Ed25519(identity_key,
"aeronyx-message-revoke-v1" || sender_pubkey[32] || receiver_pubkey[32]
|| UTF-8(msg_id) || timestamp as u64 little-endian)
يرد المُرحِّل بـmessage_revoke_ack. ولا يتحقق المُرحِّل من هوية الكاتب: لذا يجب على المستقبِلين التحقق من التوقيع وعدم تطبيق السحب إلا إذا كان مرسِله هو كاتب الرسالة الأصلية. ولحذف مرفقات رسالة مسحوبة، استدعِ POST /api/relay/blob/{blob_id}/delete/.
التفاعلات
التفاعل الفردي ظرف مختوم يكون message_id فيه هو معرّف التفاعل، مع content_type بالقيمة 2 والنص الصريح {"emoji": "❤️", "op": "add"} (أو "remove"):
{
"type": "message_reaction",
"receiver_pubkey": "<64 hex>",
"msg_id": "<message being reacted to>",
"reaction_id": "<32 hex>",
"payload_b64": "<base64 envelope>",
"payload_sig": "<128 hex>",
"timestamp": 1780000300
}
- يستخدم
payload_sigالمميِّز12. - يمثّل
reaction_idمفتاح منع التكرار (idempotency). وأيreaction_idمكرر خلال 72 ساعة يُقَرّ به مع"duplicate": trueولا يُسلَّم مرة أخرى. - يرد المُرحِّل بـ
{"type":"message_reaction_ack","msg_id":"...","reaction_id":"...","delivered":...,"queued":...}. - تقتصر التفاعلات على 20 لكل مرسِل ومحادثة ضمن نافذة المعدل.
تُوصَف تفاعلات المجموعات في قسم «المجموعات».
حالة الاتصال والكتابة
إطارات حالة الاتصال والكتابة بيانات وصفية بنص صريح، وهي مرئية للمُرحِّل.
حالة الاتصال
الاشتراك في جهات الاتصال:
{ "type": "presence_subscribe", "pubkeys": ["<64 hex>", "<64 hex>"] }
يُؤخذ في الحسبان ما يصل إلى 200 مفتاح في كل إطار؛ وتُتجاهَل المفاتيح المشوَّهة والمكررة. وتتراكم الاشتراكات طوال عمر الاتصال. لا تشترك إلا في جهات اتصالك.
{
"type": "presence_subscribe_ack",
"count": 2,
"updates": [
{ "pubkey": "<a>", "visible": true, "presence_visible": true, "last_seen_visible": true, "online": false, "last_seen_ts": 1780000000, "reason": "allowed" },
{ "pubkey": "<b>", "visible": false, "presence_visible": false, "last_seen_visible": false, "reason": "not_mutual_contact" }
],
"server_ts": 1780000001
}
لا تكون حالة الاتصال مرئية إلا بين جهات الاتصال المتبادلة، وبشرط أن يكون لدى الطرف المستهدف presence_enabled. ولا تحتوي الإدخالات المخفية (ذات reason بالقيمة not_mutual_contact أو presence_hidden) على online أو last_seen_ts. ولا يظهر last_seen_ts إلا عندما تكون قيمة last_seen_visible هي true؛ وإلا فاعرض حالة عامة مثل «آخر ظهور منذ وقت قريب».
تصل التغييرات الحية على النحو التالي:
{ "type": "presence_update", "pubkey": "<a>", "online": true, "presence_visible": true, "last_seen_visible": true, "last_seen_ts": 1780000100 }
الكتابة
{ "type": "typing", "receiver_pubkey": "<64 hex>", "is_typing": true }
{ "type": "group_typing", "group_id": "<uuid>", "is_typing": true }
لا يُمرَّر مؤشر الكتابة إلا إذا كانت حالة اتصال المرسِل مرئية للمستلم (جهات اتصال متبادلة مع presence_enabled)، أو إلى الأعضاء الآخرين في مجموعة ينتمي إليها المرسِل. ولا يُخزَّن أبدًا، ولا يوضع في قائمة الانتظار، ولا يُرسَل إشعارًا فوريًا.
الملف الشخصي وإعدادات الخصوصية
GET /api/relay/profile/
Authorization: Relay <pubkey>:<timestamp>:<signature>
{
"success": true,
"profile": {
"pubkey_hex": "<64 hex>",
"display_name": "AeroNyx User",
"bio": "",
"handle": "",
"avatar_url": "",
"status_text": "",
"privacy": { "presence_enabled": true, "last_seen_enabled": true, "read_receipts_enabled": true },
"updated_at": "2026-10-09T10:00:00Z"
}
}
تتلقى الهوية التي لا تملك ملفًا شخصيًا حقولًا فارغة، مع ضبط علامات الخصوصية الثلاث جميعها على true.
PATCH /api/relay/profile/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/json
{ "display_name": "Alice", "privacy": { "presence_enabled": true, "last_seen_enabled": false, "read_receipts_enabled": false } }
| الحقل | القواعد |
|---|---|
display_name | حتى 50 محرفًا. |
bio | حتى 200 محرف. |
avatar_url | عنوان URL يبدأ بـhttps:// أو قيمة فارغة. |
handle | الأحرف a-z والأرقام 0-9، من 5 إلى 24 محرفًا. وتخضع المعرّفات لقواعد العضوية ولفترات انتظار بين مرات التغيير. |
privacy.* | قيم منطقية. ويجوز أيضًا إرسال العلامات الثلاث في المستوى الأعلى. |
تشمل الأخطاء no_valid_fields و<flag>_invalid_boolean وhandle_taken وhandle_change_cooldown:<date>.
حافظ على اتساق العميل مع هذه الإعدادات: لا ترسل إشعارات القراءة عند إيقافها، ولا تعرض حالة القراءة لدى الطرف الآخر ما دامت إشعارات القراءة لديك متوقفة.
التحقق من جهات الاتصال
يمكن للمستخدم أن يشترط على الغرباء التحقق قبل مراسلته. وعندما يفعّل المستلم ذلك ولم يكن قد أضاف المرسِل جهةَ اتصال، يُرفَض relay_send مع verification_required.
لبدء محادثة، أرسل رسالة واحدة مع "contact_request": true. تتجاوز طلبات الاتصال هذا الفحص، وتقتصر على 3 لكل مرسِل ومستلم ضمن نافذة متحركة مدتها 24 ساعة؛ وتُرفَض الطلبات الإضافية مع contact_request_rate_limited. وتُسلَّم العلامة contact_request إلى المستلم حتى يتمكن التطبيق من عرض الرسالة بوصفها طلبًا.
ينطبق التحقق من جهات الاتصال على relay_send وعلى البديل عبر HTTPS.
المجموعات
رسائل المجموعات
يُشفَّر محتوى المجموعة بمفتاح مجموعة مشترك طوله 32 بايت باستخدام AES-256-GCM:
payload_b64 = base64(nonce[12] || AES-256-GCM(group_key, plaintext_json) || tag[16])
يحتوي JSON النص الصريح على text وtype (text أو media أو system أو reaction) وsender_pubkey وcreated_at، ويحتوي اختياريًا على attachments وmentions وreply وforwarded وforwarded_from_name وforwarded_from_pubkey وlink_preview.
{
"type": "group_send",
"msg_id": "<32 hex>",
"group_id": "<uuid>",
"payload_b64": "<base64>",
"timestamp": 1780000000,
"payload_sig": "<128 hex>",
"key_version": 3
}
payload_sig = Ed25519(identity_key,
SHA256(UTF-8(group_id) || sender_pubkey[32] || 0x19 || timestamp as u64 little-endian
|| decoded payload))
يتحقق المُرحِّل من التوقيع ومن أن المرسِل عضو نشط، ويخزّن الرسالة لكل عضو نشط آخر، ثم يسلّمها حيةً. ولا تستخدم حمولات المجموعات الظرف الفردي، وتُسلَّم أظرف المجموعات مع group_id وkey_version وpayload_sig حتى يتمكن المستقبِلون من التحقق من المرسِل قبل فك التشفير.
{
"type": "group_delivered",
"msg_id": "...",
"group_id": "...",
"accepted": true,
"accepted_count": 5,
"delivered_count": 2,
"queued_count": 5,
"failed_count": 0,
"member_count": 6
}
يتلقى المرسِل غير العضو error مع السبب not_a_member.
تعديلات المجموعات وسحبها وتفاعلاتها
| الإطار | التوقيع |
|---|---|
group_message_edit (group_id, msg_id, target_msg_id, payload_b64, payload_sig, key_version, timestamp) | صيغة المجموعة الواردة أعلاه. |
group_message_reaction (group_id, msg_id, reaction_id, payload_b64, payload_sig, key_version, timestamp) | صيغة المجموعة الواردة أعلاه. والحمولة حمولة مجموعة مع type: "reaction". |
group_message_revoke (group_id, sender_pubkey, msg_id, timestamp, payload_sig) | توقيع Ed25519 خام على `"aeronyx-group-message-revoke-v1" |
يُقَرّ بكل منها بإطار _ack المقابل الذي يحمل أعداد التسليم. ولا يطبّق المستقبِلون التعديلات والسحب إلا إذا صدرت عن الكاتب الأصلي.
مفاتيح المجموعات
يوزّع مالك المجموعة مفاتيح المجموعة في صورة حزم مفاتيح لكل عضو: base64(nonce[12] || AES-256-GCM(chat_key(owner, member), group_key) || tag[16]). وتتولى نقاط نهاية REST ضمن /api/relay/groups/ إنشاء المجموعات، وإدارة الأعضاء والدعوات، ورفع حزم المفاتيح، وتدوير المفاتيح (keys/rotate/)، وجلب الحزمة الحالية للمستدعي (keys/me/). ويشفّر المرسِلون بأحدث إصدار مفتاح لديهم؛ وعلى المستقبِلين الذين يفتقدون إصدار مفتاح ما جلب keys/me/ والاحتفاظ بالرسالة حتى يصل المفتاح.
المرفقات المشفّرة
تُشفَّر المرفقات على الجهاز، وتُرفَع في صورة نص مشفّر معتم، ويُشار إليها من داخل الرسالة المشفّرة.
تشفير الملف
لكل ملف، ولِّد مفتاحًا عشوائيًا طوله 32 بايت وقيمة nonce عشوائية طولها 12 بايت:
blob = nonce[12] || AES-256-GCM(file_key, file_bytes) || tag[16]
ارفع blob. وضع file_key وكل حقل وصفي داخل الرسالة المشفّرة، ولا تضعها أبدًا في طلب الرفع.
كائن المرفق
| المفتاح | مطلوب | المعنى |
|---|---|---|
blob_id | نعم | المعرّف الذي يعيده الرفع. |
file_key | نعم | ترميز Base64 لمفتاح الملف البالغ 32 بايت. |
media_type | نعم | نوع MIME للملف الصريح. |
file_name | نعم | الاسم المعروض. |
file_size | نعم | حجم الملف الصريح بالبايت. |
thumb_b64 | لا | صورة مصغّرة بصيغة JPEG مرمَّزة بـBase64، حتى 64 KiB. |
duration_ms | لا | مدة الصوت أو الفيديو. |
waveform | لا | حتى 96 رقمًا ضمن [0, 1] للرسائل الصوتية. |
sticker, sticker_pack, sticker_pose | لا | هوية الملصق. |
live | لا | الجزء المتحرك من Live Photo: {blob_id, file_key, media_type, file_size, duration_ms?, width?, height?}. |
يتجاهل المستقبِلون المرفقات التي تفتقر إلى blob_id أو file_key. والرسائل الصوتية الصادرة من التطبيق مرمَّزة بـAAC-LC ضمن حاوية MP4 (audio/mp4).
الرفع: طلب واحد (حتى 10 MiB)
POST /api/relay/blob/presign/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/json
{ "file_size": 482141, "media_type": "image/jpeg", "media_kind": "image", "ttl_days": 7 }
القيمة file_size هي حجم النص المشفّر. وتكون media_kind إحدى القيم voice أو image أو video أو file أو avatar أو other. وتُحصَر ttl_days بين 1 و30 (القيمة الافتراضية 7).
{
"blob_id": "<uuid>",
"upload_url": "https://...",
"upload_method": "PUT",
"storage": "r2",
"expires_at": "2026-10-16T10:00:00Z",
"media_type": "image/jpeg",
"media_kind": "image",
"access_mode": "capability",
"ttl_days": 7,
"max_bytes": 10485760
}
بعد ذلك:
- نفِّذ
PUTللنص المشفّر إلىupload_urlخلال 15 دقيقة، مع ترويسةContent-Typeفقط. ولا ترسل ترويسةAuthorizationإلى التخزين. - نفِّذ
POST /api/relay/blob/{blob_id}/complete/مع RelayAuth. فيؤكد المُرحِّل وجود الكائن ويعيد{blob_id, file_size, expires_at, storage}.
الرفع: متعدد الأجزاء (حتى 100 MiB)
POST /api/relay/blob/multipart/create/
{ "total_size": 52428800, "media_type": "video/mp4", "media_kind": "video", "ttl_days": 7 }
{
"blob_id": "<uuid>",
"upload_id": "...",
"part_size": 8388608,
"total_parts": 7,
"part_urls": ["https://...", "..."],
"storage": "r2",
"expires_at": "...",
"max_bytes": 104857600
}
القيمة الافتراضية لـpart_size هي 8 MiB، ويمكن طلب قيمة بين 5 و16 MiB. وعناوين URL للأجزاء صالحة لمدة 60 دقيقة. نفِّذ PUT لكل جزء إلى عنوانه وسجّل ترويسة الاستجابة ETag، ثم:
POST /api/relay/blob/multipart/complete/
{ "blob_id": "<uuid>", "upload_id": "...", "parts": [ { "part_number": 1, "etag": "\"...\"" } ] }
يستخدم AeroNyx App الرفع بطلب واحد حتى 8 MiB من النص المشفّر، والرفع متعدد الأجزاء لما يتجاوز ذلك.
التنزيل
GET /api/relay/blob/{blob_id}/
يجيب المُرحِّل بالرمز 302 مع ترويسة Location تشير إلى شبكة توصيل المحتوى، ومع الترويسات X-AeroNyx-Blob-Size وX-AeroNyx-Blob-Media-Type وX-AeroNyx-Blob-Storage. اتبع إعادة التوجيه بنفسك دون تمرير أي ترويسة Authorization، ثم تحقق من الكائن الثنائي (blob) وفُكّ تشفيره بمفتاحه file_key. وحدِّد سقف التنزيل بالحجم المتوقع.
يمثّل blob_id صلاحيةً لحامله (bearer capability): إذ يستطيع أي شخص يحمله جلب النص المشفّر، ولهذا لا ينتقل المفتاح إلا داخل الرسالة المشفّرة. ويُفرَض access_mode وانتهاء الصلاحية عند إعادة التوجيه من المُرحِّل. فلا تعتمد عليهما لتحقيق السرّية.
حذف المرفقات
POST /api/relay/blob/{blob_id}/delete/
لا يجوز حذف الكائن الثنائي إلا لمن رفعه. والاستجابة هي {"blob_id": "...", "deleted": true}.
أخطاء المرفقات
تستخدم الأخطاء الصيغة {"success": false, "error": "<text>", "error_code": "<code>"}. واعتمد في معالجة الحالات على error_code.
| HTTP | error_code | المعنى |
|---|---|---|
| 400 | blob_id_invalid | معرّف كائن ثنائي مشوَّه. |
| 400 | blob_missing_file_size, blob_missing_total_size, blob_missing_parts, blob_bad_parts, blob_bad_part_count | طلب رفع غير صالح. |
| 400 | blob_not_r2, blob_multipart_complete_failed | فشل الإكمال؛ ابدأ عملية رفع جديدة. |
| 401 | auth_required | يتطلب الكائن الثنائي RelayAuth. |
| 403 | blob_not_uploader, download_forbidden | المستدعي غير مسموح له. |
| 404 | blob_not_found, blob_not_uploaded | كائن ثنائي غير معروف، أو طلب إكمال قبل انتهاء الرفع. |
| 410 | blob_expired | انتهت صلاحية الكائن الثنائي. اطلب من المرسِل إعادة الإرسال. |
| 413 | blob_too_large | تجاوز الحد؛ وتتضمن الاستجابة max_bytes وchunked_max_bytes. |
| 503 | blob_r2_unavailable | التخزين غير متاح مؤقتًا؛ أعد المحاولة مع التراجع. |
نقاط نهاية الرفع القديمة
تظل نقطة النهاية POST /api/relay/blob/ (نموذج متعدد الأجزاء، حتى 10 MiB) وواجهة الجلسات القابلة للاستئناف ضمن /api/relay/blob/session/ (حتى 100 MiB، بأجزاء من 64 KiB إلى 4 MiB، والجلسات صالحة لمدة 24 ساعة) متاحتين كبديل احتياطي. وينبغي للعملاء الجدد استخدام نقاط النهاية الموضحة أعلاه.
الإشعارات الفورية
يرسل المُرحِّل إشعارات خدمة Apple Push Notification service (APNs) إلى iOS وmacOS. أما عملاء Android فيتلقون الرسائل عبر WebSocket فقط.
يُرسَل إشعار فوري عند relay_send وgroup_send إذا لم يكن للمستلم اتصال نشط في الواجهة الأمامية، ولم يضبط المرسِل suppress_push، ولم يكتم المستلم المحادثة. ولا تُرسَل إشعارات فورية للتعديلات والسحب والتفاعلات وإشعارات التسليم والقراءة. ولا تحتوي حمولات الإشعارات الفورية على أي نص مشفّر:
{
"aps": { "alert": { "title": "AeroNyx", "body": "New encrypted message" }, "sound": "default", "badge": 1, "content-available": 1 },
"kind": "p2p_message",
"sender_pubkey": "<64 hex>",
"message_id": "..."
}
القيمة kind هي p2p_message (مع sender_pubkey) أو group_message (مع group_id). وتستخدم إشعارات المكالمات missed_call وحمولات مخصصة للمكالمات. وعند تلقي إشعار فوري، اتصل بالمُرحِّل ونفِّذ relay_pull.
| نقطة النهاية | جسم الطلب |
|---|---|
POST /api/relay/push/register/ | token (64 حرفًا ست عشريًا)، وplatform (ios أو macos)، وbundle_id، وenvironment (production أو sandbox)، واختياريًا token_type (alert أو voip) وprovider (apns). |
POST /api/relay/push/unregister/ | token، واختياريًا platform وtoken_type وprovider. |
POST /api/relay/push/mute/ | kind (p2p أو group)، وtarget (مفتاح عام أو معرّف مجموعة)، وmuted (قيمة منطقية). |
تتطلب نقاط النهاية الثلاث جميعها RelayAuth. ويؤدي تسجيل رمز إلى نقله إلى الهوية المستدعية.
المكالمات
تنتقل إشارات المكالمات الصوتية والمرئية عبر اتصال WebSocket نفسه في صورة بيانات وصفية بنص صريح؛ بينما تتدفق الوسائط بشكل منفصل. والإطارات هي call_invite وcall_answer وcall_reject وcall_hangup وcall_busy للمكالمات الفردية، وgroup_call_invite وgroup_call_invite_broadcast وgroup_call_answer وgroup_call_hangup_broadcast للمجموعات، إضافة إلى إطارات قبول الحضور للاجتماعات المستضافة.
يتحقق المُرحِّل من room_name مقابل المشاركين: البادئة p2p_ متبوعةً بأول 16 حرفًا ست عشريًا من SHA256(lower_key + ":" + higher_key) للمكالمات الفردية، والصيغة grp_<first 8 characters of group_id>_<first 8 hex of SHA256(group_id)> للمجموعات. وتُحتجَز إشارات المكالمات الموجّهة إلى مستلم غير متصل مدة 120 ثانية.
حدود المعدل
| النطاق | الحد | الاستجابة |
|---|---|---|
relay_send وgroup_send والإشعارات عبر HTTPS، لكل هوية | 50 لكل نافذة قصيرة؛ و100,000 يوميًا | في WebSocket: error rate_limited مع retry_after. وفي HTTPS: 429 مع Retry-After. |
group_send لكل مرسِل ومجموعة | 10 لكل نافذة قصيرة | error rate_limited، scope: "sender". |
group_send لكل مجموعة | 50 لكل نافذة قصيرة | error rate_limited، scope: "group". |
| التفاعلات لكل مرسِل ومحادثة | 20 لكل نافذة قصيرة | error rate_limited، scope: "reaction". |
| طلبات الاتصال لكل مرسِل ومستلم | 3 لكل 24 ساعة | send_rejected contact_request_rate_limited. |
تراجَع مدة لا تقل عن retry_after ثانية. ولا تُعِد الإرسال في حلقة متلاحقة: فالعدّادات تستمر في العمل أثناء إعادة المحاولة.
قائمة التحقق للتنفيذ
- ولِّد هوية Ed25519 وخزّنها؛ واستخدم مفاتيح ست عشرية بأحرف صغيرة في كل مكان.
- نفِّذ RelayAuth وتسجيل الدخول عبر WebSocket ونبضات القلب و
presence_state. - نفِّذ الظرف المختوم وتحقق منه مقابل المتجه المرجعي في واجهة HTTPS للدردشة المركزية v1.
- أرسل باستخدام
relay_send، واعتبرdurable: trueبمعنى «أُرسلت»، وأعد حسابtimestampوpayload_sigعند إعادة المحاولة. - استقبل
relay_envelope: أزِل التكرار، وتحقق، وفُكّ التشفير، وخزِّن، ثم أرسلmessage_receiptوrelay_offline_ack. - نفِّذ
relay_pullبعد تسجيل الدخول وعند التنبيه بإشعار فوري؛ ولا تُقِرّ إلا بعد التخزين الدائم. - اقرأ علامات الخصوصية في الملف الشخصي والتزم بها في حالة الاتصال وإشعارات القراءة.
- شفّر المرفقات على الجهاز وارفعها عبر
presignأو الرفع متعدد الأجزاء؛ واحتفظ بـfile_keyداخل الرسالة المشفّرة. - تحقق من هوية الكاتب قبل تطبيق التعديلات والسحب.
- أبقِ البحث في الرسائل وسجلّها على الجهاز. فالمُرحِّل لا يوفّر بحثًا في المحتوى وليس أرشيفًا.
الأسئلة الشائعة
هل يستطيع مُرحِّل الدردشة في AeroNyx قراءة رسائلي؟
لا. تُشفَّر الرسائل والتعديلات والتفاعلات ورسائل المجموعات والمرفقات وتُوقَّع على جهاز المرسِل قبل وصولها إلى المُرحِّل، ولا تغادر المفاتيح أبدًا أجهزة المشاركين في المحادثة. ولا يخزّن المُرحِّل ولا يمرّر سوى النص المشفّر.
ما الذي يستطيع مُرحِّل الدردشة في AeroNyx رؤيته؟
يرى المُرحِّل البيانات الوصفية للتسليم: المفاتيح العامة للمرسِل والمستقبِل، ومعرّفات المجموعات والرسائل، والطوابع الزمنية، وأحجام الحمولات، وحالة التسليم والقراءة، وإشارات حالة الاتصال والكتابة، والبيانات الوصفية لإشارات المكالمات، وأحجام المرفقات، وعناوين IP للاتصالات. ولا يرى محتوى الرسائل ولا محتوى المرفقات ولا المفاتيح. وترد القائمة الكاملة في نموذج الثقة أعلى هذه الصفحة.
ما التشفير الذي تستخدمه دردشة AeroNyx؟
كل هوية عبارة عن زوج مفاتيح Ed25519. ويشتق الشخصان مفتاح دردشة مشتركًا باستخدام X25519 وHKDF-SHA256. وتُشفَّر الرسائل الفردية (1:1) باستخدام XChaCha20-Poly1305 وتُوقَّع باستخدام Ed25519. أما رسائل المجموعات والمرفقات فتُشفَّر باستخدام AES-256-GCM. وصيغ البايتات الدقيقة ومتجه اختبار منشورة في توثيق واجهة HTTPS للدردشة المركزية v1.
كيف تُحمى الصور ومقاطع الفيديو والملفات؟
يُشفَّر كل ملف على الجهاز بمفتاح AES-256-GCM عشوائي خاص به قبل رفعه. ولا يتلقّى التخزين سوى النص المشفّر. وينتقل مفتاح الملف داخل الرسالة المشفّرة تشفيرًا تامًّا بين الطرفين، فلا يستطيع فكّ تشفير الملف إلا المستلمون.
ماذا يحدث إذا كان المستلم غير متصل؟
يحتفظ المُرحِّل بالرسائل المشفّرة في قائمة الانتظار دون اتصال الخاصة بالمستلم لمدة تصل إلى 72 ساعة، ويسلّمها عندما يعاود المستلم الاتصال. وعلى iOS وmacOS يتلقّى المستلم أيضًا إشعارًا فوريًّا لا يتضمّن أي محتوى من الرسالة.
هل تحتفظ AeroNyx بسجل محادثاتي؟
لا. المُرحِّل مخزن مؤقت للتسليم: تُزال العناصر بعد أن يؤكّد جهاز المستلم أنه خزّنها. أما سجل المحادثات والبحث فيه فيوجدان على أجهزتك.
هل يمكنني بناء عميل أو روبوت خاص بي متوافق مع AeroNyx؟
نعم. يستطيع أي برنامج يمتلك هوية Ed25519 وينفّذ الصيغ الواردة في هذه الصفحة تبادلَ الرسائل مع مستخدمي AeroNyx App. وللتكامل الأبسط بنمط الطلب/الاستجابة دون WebSocket، استخدم واجهة HTTPS للدردشة المركزية v1.
هل مُرحِّل الدردشة في AeroNyx لامركزي؟
مُرحِّل الدردشة هو خدمة التسليم المركزية في AeroNyx. وتشغّل AeroNyx أيضًا شبكة عُقد مفتوحة المصدر (AGPL-3.0) يمكنها نقل النص المشفّر للدردشة عبر مسار من قفزتين متنوّع الشبكات. ويتعايش المساران: إذ يوفّر المُرحِّل تسليمًا سريعًا وموثوقًا وقوائم انتظار دون اتصال، بينما مسار العُقد مسار اختياري يقلّل ما يستطيع أي مشغّل منفرد رصده.
لماذا تُرفض رسالتي مع الخطأ verification_required؟
لا يقبل المستلم الرسائل إلا من جهات الاتصال. أرسل رسالة أولى واحدة مع contact_request: true، فيراها المستلم طلبَ اتصال. ويُسمح بما يصل إلى ثلاثة طلبات اتصال لكل مستلم خلال أي نافذة مدتها 24 ساعة.
التسليم الموثَّق عبر قفزتين
بالنسبة إلى حركة ChatRelay المؤهَّلة والمُصادَق عليها، يجوز للمصدر اختيار مسار من قفزتين عبر شبكات متنوعة، وعدم احتساب التسليم إلا بعد التحقق من الإيصال الموقَّع من الطرف النهائي المتوقَّع. وتتولى عُقد الترحيل توجيه النص المشفّر دون تحليل حمولة التشفير التام بين الطرفين (E2E). راجع نموذج الأدلة الكامل في اكتشاف العُقد والتسليم المشفّر الموثَّق عبر المُرحِّلات.
<!-- verified-two-hop-delivery-v1:end -->