عن المشروع

Wappie هو خادم WhatsApp API متعدد المستأجرين مع أرشيف مختوم، بالإضافة إلى عميل ويب يستهلك نفس واجهة برمجة التطبيقات (API). يقوم بربط أنظمة الأعمال بـ WhatsApp عبر HTTP و WebSocket، مع توفير مساحات عمل مشتركة، وأذونات لكل رقم، وعميل مراسلة. الخادم، وواجهة السطر البرمجي (CLI)، وعميل الويب، والإدارة الأساسية هي مفتوحة المصدر تحت رخصة Apache-2.0؛ بينما تتم إدارة الاستضافة المدارة والفواتير التجارية بشكل منفصل، وتكون النسخة التجريبية المستضافة بدعوات فقط ومجانية، وتكون المدفوعات فيها محاكاة. من الناحية الوظيفية، يقوم الخادم باقتران جهاز WhatsApp من الطرفية باستخدام إما رمز مكون من ثماني خانات (يُكتب تحت الأجهزة المرتبطة) أو رمز QR يتم عرضه في الطرفية، ثم يقوم بتشفير (ختم) حركة مرور ذلك الجهاز عند الدخول. يقوم النظام باستيعاب مزامنة السجل، وتعديلات المشاريع، والإلغاءات، والتفاعلات، وتسجيل الإيصالات، وتتبع أي مراجعة كانت تظهر على شاشة كل قارئ. يتم تخزين الوسائط الواردة تماماً كما قدمتها Meta CDN، كما يتم دعم رفع وإرسال الوسائط الصادرة، وكذلك ميزة "العرض لمرة واحدة". تم تنفيذ المحتوى المهيكل (الموقع، الاستطلاع، جهة الاتصال، الحدث)، وجهات الاتصال، والأسماء وصور الملف الشخصي، والملء الخلفي عند الطلب، وطبقة محادثة تتضمن أعداد الرسائل غير المقروءة، وعلامات القراءة، والحضور، والمجموعات والاستطلاعات. يتم الوصول عبر أداة CLI (`wsctl`) وواجهة HTTP/WebSocket API، مع نقطة نهاية مستضافة و `/v1/ws` لـ WebSocket. نموذج حماية الأرشيف هو حجر الزاوية في المشروع. تصل وسائط WhatsApp مشفرة بالفعل بـ AES-256-CBC مع HMAC (تشفير ثم توقيع) باستخدام مفتاح وسائط بطول 32 بايت؛ يتم تخزين النص المشفر حرفياً ويتم ختم مفتاح الوسائط فقط بمفتاح عام للجهاز. يتم ختم أجسام الرسائل باستخدام HPKE (RFC 9180, X25519 + HKDF-SHA256 + AES-256-GCM) تحت مفتاح محتوى يغطي دفعة من الرسائل، وهو ما يبرره ملف README لأسباب تتعلق بالتكلفة بدلاً من سرعة النقل. يحتفظ الخادم بالمفاتيح العامة فقط: يمكنه الختم ولا يمكنه الفتح. يمتلك كل جهاز زوج مفاتيح أرشيف يتم إنشاؤه بواسطة العميل الذي قام باقترانه؛ يتم ختم النصف الخاص بالمفتاح بالمفتاح العام لكل حساب يمكنه قراءة ذلك الجهاز (منح المفتاح) ثم يتم نسيانه. يتم إنشاء المفاتيح الخاصة بالحساب في المتصفح عند التسجيل، وتُغلف بمفتاح مشتق من Argon2id مرتبط بعنوان الحساب، ولا يتم نقلها أبداً؛ ويقوم رمز استرداد بتغليف نفس المفتاح مرة ثانية. يقوم المتصفح بتنفيذ HPKE، ومعالجة مفاتيح المحتوى، وأجسام الرسائل، وأسماء جهات الاتصال، وصور الملف الشخصي، وفك تشفير المرفقات داخل الصفحة. الأذونات متعددة الطبقات: تحمل مفاتيح API نطاق `read` أو `send` أو `full`؛ لا يصل العضو إلا إلى الأجهزة الممنوحة له؛ بينما يمكن للمالك أو المسؤول الوصول إلى غلاف كل جهاز ويمكنه الاقتران، ومنح الصلاحيات، وصك المفاتيح، والتبديل بين وضع الجهاز "الكتوم" و"الصاخب"؛ ولا يصل أي نطاق إلى تكوين المستأجر. يتم منح الطرف الثالث حساب خدمة — وهو زوج مفاتيح بدون كلمة مرور، يُمنح أجهزة مثل الشخص ويتم الوصول إليه عبر مفتاح API يعمل نيابة عنه. ميزة الاحتفاظ بالبيانات معطلة افتراضياً؛ يمكن للمستأجر تحديد نافذة زمنية يطبقها الخادم كل ساعة على الرسائل والإيصالات وأحداث المجموعات والمرفقات، بينما تظل الدردشات وجهات الاتصال. يمكن مسح الشخص من الأرشيف عبر كل جهاز، وحذف جهاز أو إعادة تعيين أرشيف يؤدي إلى إزالة كائنات المرفقات الخاصة به من التخزين. تقوم السجلات بإخفاء المعرفات على جميع المستويات. يتطلب التشغيل محلياً PostgreSQL 18 أو أحدث لدعم `uuidv7()`، ودور غير مسؤول (non-superuser) لأن المسؤولين يتجاوزون أمن مستوى الصف (row-level security). تخزين الكائنات اختياري: في حال عدم تكوينه، يتم وضع المرفقات في قائمة انتظار في قاعدة البيانات حتى يتوفر التخزين. هدف `make dev-up` يقوم بتشغيل Postgres و MinIO في Docker. تغطي أهداف الاختبار التنسيق، والتدقيق، والتخطيط، والاختبارات الممكنة للتسابق (race-enabled)، وفحص الأنواع وبناء عميل المتصفح، والتغطية والاختبار العشوائي (fuzzing)، ويعمل كل اختبار في مخطط Postgres خاص به. يتم بناء عميل الويب باستخدام Vite في `web/dist` ويتم تقديمه بواسطة `WS_WEB_DIR`؛ لا يوجد شيء مدمج في ثنائي Go، وفي حالة عدم وجود بناء، يقدم الخادم واجهة API فقط. أثناء التطوير، يقوم خادم Vite بتوجيه `/v1` إلى منفذ Go، لأن معالج websocket يقبل اتصالات من نفس الأصل فقط. يوضح ملف README الحدود بصراحة بدلاً من الإيحاء بأكثر مما يتم تقديمه. المهاجم الذي يشغل كوداً على خادم نشط يرى النص الواضح أثناء الانتقال بين فك تشفير Signal والختم والتخزين، لذا فإن الختم عند السكون يحمي القرص المسروق، أو النسخة الاحتياطية المسربة، أو تفريغ قاعدة البيانات، وليس العملية المخترقة. يجب أن يظل مخزن جلسة whatsmeow قابلاً للقراءة من قبل العملية؛ وأي شخص يسرقه يمكنه انتحال صفة الجهاز وقراءة الرسائل الجديدة، ولكن ليس الأرشيف. تمر النصوص الصادرة بشكل واضح، وكذلك الوسائط الصادرة، لأن رفع WhatsApp يقبل النص الواضح فقط؛ ولا يتم فك تشفير الوسائط الواردة أبداً من جانب الخادم. تكون البيانات الوصفية للمرفقات (النوع، الحجم، الأبعاد، المدة، الهاشات) وبيانات التوجيه، بما في ذلك الإيصالات، قابلة للقراءة، لذا فإن تفريغ قاعدة البيانات يكشف عن الرسم البياني الاجتماعي ومن قرأ ماذا ومتى، ولكن ليس المحتوى. يؤدي إلغاء المنح إلى منع الحصول على المفتاح مرة أخرى ولكن لا يمكن استعادة نسخة تم فتحها بالفعل، لأن المفتاح كان موجوداً في المتصفح. فقدان كل مسارات الوصول يؤدي إلى فقدان الأرشيف نهائياً للجميع. توضع مواد جلسة المتصفح في IndexedDB كنص مشفر تحت مفاتيح WebCrypto غير قابلة للاستخراج، ويشحن العميل سياسة أمن محتوى (CSP) ولا يحمل أي JavaScript من طرف ثالث. يسرد جدول الحالة المراحل المكتملة من الهيكل، والترحيلات، وتشفير الوسائط، وصولاً إلى الاقتران، والاستيعاب، وإسقاط التعديل/الإلغاء/التفاعل، والوسائط الواردة والصادرة، ومزامنة السجل، وإعادة محاولة الوسائط، وجهات الاتصال، والملء الخلفي عند الطلب، وعميل الويب، ومفاتيح الأجهزة والحسابات، وطبقة المحادثة، والوضع الخفي والحصص، ومراجعة أمنية؛ بينما تدرج لوحة الإدارة كخطوة تالية. يتم إنشاء ناقلات اختبار عبر التنفيذات في Go ويتم فتحها بواسطة كلا التنفيذين، بما في ذلك الحالات السلبية مثل نقل كتلة (blob) إلى صف آخر أو تقديمها تحت نوع آخر، بحيث لا يمكن لعميل المتصفح أن يوافق بصمت مع نفسه فقط. تقوم أداة `seeddemo` بكتابة محادثة وهمية صغيرة عبر خط أنابيب الاستيعاب الحقيقي لتطوير العميل دون الحاجة لاقتران هاتف.