الغوص العميق: كيف نستخدم صناديق بريد SQLite المشفرة والآمنة لخدمة البريد الإلكتروني الآمنة التي تركز على الخصوصية

على عكس خدمات البريد الإلكتروني الأخرى ، فإننا نضمن أنك وحدك من يمكنه الوصول إلى صندوق البريد الخاص بك في جميع الأوقات.

مقدمة

تلدر؛ خدمة البريد الإلكتروني لدينا هي 100% مفتوح المصدر وتركز على الخصوصية من خلال صناديق بريد SQLite الآمنة والمشفرة.

حتى أطلقنا دعم IMAP، استخدمنا MongoDB لتلبية احتياجات تخزين البيانات المستمرة لدينا.

هذه التكنولوجيا مذهلة وما زلنا نستخدمها حتى اليوم - ولكن للحصول على تشفير ثابت مع MongoDB، يتعين عليك استخدام موفر يقدم MongoDB Enterprise، مثل Digital Ocean أو Mongo Atlas - أو الدفع مقابل ترخيص مؤسسة (و يتعين عليك لاحقًا العمل مع زمن وصول فريق المبيعات).

فريقنا في إعادة توجيه البريد الإلكتروني كنت بحاجة إلى حل تخزين سهل الاستخدام وقابل للتطوير وموثوق ومشفر لصناديق بريد IMAP. باعتبارك مطورًا مفتوح المصدر، فإن استخدام التكنولوجيا التي تحتاج إلى دفع رسوم ترخيص للحصول على ميزة التشفير غير النشط يعد أمرًا مخالفًا مبادئنا - ولذلك قمنا بتجربة وبحث وتطوير حل جديد من الصفر لحل هذه الاحتياجات.

بدلاً من استخدام قاعدة بيانات مشتركة لتخزين صناديق البريد الخاصة بك، نقوم بشكل فردي بتخزين وتشفير صناديق البريد الخاصة بك باستخدام كلمة المرور الخاصة بك (والتي تمتلكها أنت فقط). خدمة البريد الإلكتروني لدينا آمنة جدًا لدرجة أنك إذا نسيت كلمة المرور الخاصة بك، فستفقد صندوق البريد الخاص بك (وتحتاج إلى الاسترداد باستخدام النسخ الاحتياطية دون اتصال بالإنترنت أو البدء من جديد).

استمر في القراءة بينما نتعمق في الأسفل باستخدام أ مقارنة بين مقدمي خدمة البريد الإلكتروني, كيف تعمل خدمتنا, كومة التكنولوجيا لدينا، و اكثر.

مقارنة مزود خدمة البريد الإلكتروني

نحن المزود الوحيد لخدمات البريد الإلكتروني مفتوحة المصدر والتي تركز على الخصوصية بنسبة 100% والتي تقوم بتخزين صناديق بريد SQLite مشفرة بشكل فردي، وتقدم نطاقات وأسماء مستعارة ومستخدمين غير محدودين، كما أنها تتمتع بدعم SMTP وIMAP وPOP3 الصادر:

على عكس موفري خدمة البريد الإلكتروني الآخرين، لا تحتاج إلى الدفع مقابل التخزين على أساس كل نطاق أو اسم مستعار باستخدام إعادة توجيه البريد الإلكتروني. تتم مشاركة مساحة التخزين عبر حسابك بالكامل - لذا إذا كان لديك العديد من أسماء النطاقات المخصصة والأسماء المستعارة المتعددة في كل منها، فنحن الحل الأمثل لك. لاحظ أنه لا يزال بإمكانك فرض حدود التخزين إذا رغبت في ذلك على أساس كل نطاق أو اسم مستعار.

قراءة مقارنة خدمة البريد الإلكتروني

كيف يعمل

  1. باستخدام برنامج البريد الإلكتروني الخاص بك مثل Apple Mail، أو Thunderbird، أو Gmail، أو Outlook - يمكنك الاتصال بخادمنا الآمن IMAP الخوادم التي تستخدم اسم المستخدم وكلمة المرور الخاصة بك:

    • اسم المستخدم الخاص بك هو الاسم المستعار الكامل للمجال الخاص بك مثل hello@example.com.
    • يتم إنشاء كلمة المرور الخاصة بك بشكل عشوائي ويتم عرضها لك لمدة 30 ثانية فقط عند النقر عليها إنشاء كلمة المرور من حسابي المجالات اسماء مستعارة.
  2. بمجرد الاتصال، سوف يرسل عميل البريد الإلكتروني الخاص بك أوامر بروتوكول IMAP إلى خادم IMAP الخاص بنا للحفاظ على مزامنة صندوق البريد الخاص بك. يتضمن ذلك كتابة وتخزين مسودات رسائل البريد الإلكتروني والإجراءات الأخرى التي قد تقوم بها (على سبيل المثال، تصنيف رسالة بريد إلكتروني على أنها مهمة أو وضع علامة على بريد إلكتروني كبريد عشوائي/بريد غير هام).

  3. تتلقى خوادم تبادل البريد (المعروفة باسم خوادم "MX") رسائل البريد الإلكتروني الواردة الجديدة وتقوم بتخزينها في صندوق البريد الخاص بك. عندما يحدث هذا، سيتم إخطار عميل البريد الإلكتروني الخاص بك وسيقوم بمزامنة صندوق البريد الخاص بك. يمكن لخوادم تبادل البريد لدينا إعادة توجيه بريدك الإلكتروني إلى واحد أو أكثر من المستلمين (بما في ذلك خطافات الويب)، قم بتخزين بريدك الإلكتروني في مخزن IMAP المشفر لدينا، او كلاهما!

    هل أنت مهتم بمعرفة المزيد؟ يقرأ كيفية إعداد إعادة توجيه البريد الإلكتروني, كيف تعمل خدمة تبادل البريد لدينا، أو عرض مرشدينا.

  4. خلف الكواليس، يعمل تصميم تخزين البريد الإلكتروني الآمن الخاص بنا بطريقتين للحفاظ على صناديق البريد الخاصة بك مشفرة ولا يمكن الوصول إليها إلا بواسطتك:

    • عندما يتم استلام بريد جديد لك من أحد المرسلين، تقوم خوادم تبادل البريد لدينا بالكتابة إلى صندوق بريد فردي ومؤقت ومشفر لك.

      sequenceDiagram
          autonumber
          actor Sender
          Sender->>MX: Inbound message received for your alias (e.g. you@yourdomain.com).
          MX->>SQLite: Message is stored in a temporary mailbox.
          Note over MX,SQLite: Forwards to other recipients and webhooks configured.
          MX->>Sender: Success!
      
    • عند الاتصال بخادم IMAP الخاص بنا من خلال برنامج البريد الإلكتروني الخاص بك، يتم بعد ذلك تشفير كلمة المرور الخاصة بك في الذاكرة واستخدامها للقراءة والكتابة في صندوق البريد الخاص بك. لا يمكن قراءة صندوق البريد الخاص بك والكتابة إليه إلا باستخدام كلمة المرور هذه. ضع في اعتبارك أنه نظرًا لأنك الوحيد الذي لديه كلمة المرور هذه، أنت فقط يمكنه القراءة والكتابة في صندوق البريد الخاص بك عند الوصول إليه. في المرة التالية التي يحاول فيها برنامج البريد الإلكتروني الخاص بك استطلاع البريد أو المزامنة، سيتم نقل رسائلك الجديدة من صندوق البريد المؤقت هذا وتخزينها في ملف صندوق البريد الفعلي باستخدام كلمة المرور التي قدمتها. لاحظ أنه تتم إزالة صندوق البريد المؤقت هذا وحذفه بعد ذلك بحيث لا يحتوي على الرسائل سوى صندوق البريد المحمي بكلمة مرور.

    • إذا كنت متصلاً بـ IMAP (على سبيل المثال، باستخدام برنامج بريد إلكتروني مثل Apple Mail أو Thunderbird)، فلن نحتاج إلى الكتابة إلى وحدة تخزين القرص المؤقتة. بدلاً من ذلك، يتم جلب كلمة مرور IMAP المشفرة في الذاكرة واستخدامها. في الوقت الفعلي، عندما نحاول تسليم رسالة إليك، نرسل طلب WebSocket إلى جميع خوادم IMAP لنسألهم عما إذا كانت لديهم جلسة نشطة لك (هذا هو جزء الجلب)، وبعد ذلك سنمرر ذلك كلمة المرور المشفرة في الذاكرة - لذلك لا نحتاج إلى الكتابة إلى صندوق بريد مؤقت، يمكننا الكتابة إلى صندوق البريد المشفر الفعلي الخاص بك باستخدام كلمة المرور المشفرة.

      sequenceDiagram
          autonumber
          actor You
          You->>IMAP: You connect to IMAP server using an email client.
          IMAP->>SQLite: Transfer message from temporary mailbox to your alias' mailbox.
          Note over IMAP,SQLite: Your alias' mailbox is only available in-memory using IMAP password.
          SQLite->>IMAP: Retrieves messages as requested by email client.
          IMAP->>You: Success!
      
  5. النسخ الاحتياطية لصناديق البريد المشفرة الخاصة بك مصنوعة يوميا. يمكنك أيضًا طلب نسخة احتياطية جديدة في أي وقت أو تنزيل أحدث نسخة احتياطية من حسابي المجالات اسماء مستعارة. إذا قررت التبديل إلى خدمة بريد إلكتروني أخرى، فيمكنك بسهولة ترحيل وتنزيل وتصدير وإزالة صناديق البريد والنسخ الاحتياطية الخاصة بك في أي وقت.

التقنيات

قواعد بيانات

لقد استكشفنا طبقات تخزين قاعدة البيانات المحتملة الأخرى، ولكن لم يلبي أي منها متطلباتنا بقدر ما فعلته SQLite:

قاعدة البياناتالتشفير في الراحةوضع الحماية صناديق البريدرخصةتستخدم في كل مكان
سكليتي✅ نعم مع SQLite3MultipleCiphers✅ المجال العام
MongoDB"متوفر في MongoDB Enterprise فقط"❌ قاعدة بيانات علائقية❌AGPL و SSPL-1.0
rqliteالشبكة فقط❌ قاعدة بيانات علائقيةMIT
com.dqliteلم تختبر ولم تدعم بعد؟لم تختبر ولم تدعم بعد؟LGPL-3.0-only
PostgreSQLنعم❌ قاعدة بيانات علائقيةPostgreSQL (مشابه ل BSD أو MIT)
ماريا دي بيلInnoDB فقط❌ قاعدة بيانات علائقيةGPLv2 و BUSL-1.1
صرصورDBميزة خاصة بالمؤسسات فقط❌ قاعدة بيانات علائقيةBUSL-1.1 و اخرين

هنا أ منشور مدونة يقارن بين العديد من خيارات تخزين قاعدة بيانات SQLite في الجدول أعلاه.

الأمان

في جميع الأوقات نستخدمها التشفير في الراحة (AES-256), التشفير أثناء النقل (TLS), DNS عبر HTTPS ("DoH") باستخدام 🍊 يوسفي، و com.sqleet (ChaCha20-Poly1305) التشفير على صناديق البريد. بالإضافة إلى ذلك، نستخدم المصادقة الثنائية القائمة على الرمز المميز (على عكس الرسائل النصية القصيرة التي يشتبه فيها هجمات الرجل في المنتصف)، تدوير مفاتيح SSH مع تعطيل الوصول إلى الجذر، والوصول الحصري إلى الخوادم من خلال عناوين IP المقيدة، والمزيد.

في حالة هجوم الخادمة الشريرة أو موظف مارق من بائع طرف ثالث، لا يزال من الممكن فتح صندوق البريد الخاص بك فقط باستخدام كلمة المرور التي تم إنشاؤها. كن مطمئنًا، فنحن لا نعتمد على أي موردين خارجيين بخلاف موفري خوادم الشكاوى SOC Type 2 مثل Cloudflare وDigital Ocean وVultr.

هدفنا هو الحصول على أقل عدد ممكن نقطة واحدة من الفشل بقدر الإمكان.

صناديق البريد

تلدر؛ تستخدم خوادم IMAP الخاصة بنا قواعد بيانات SQLite مشفرة بشكل فردي لكل صندوق من صناديق البريد الخاصة بك.

SQLite تحظى بشعبية كبيرة قاعدة البيانات المضمنة – وهي قيد التشغيل حاليًا على هاتفك وجهاز الكمبيوتر – وتستخدمها جميع التقنيات الرئيسية تقريبًا.

على سبيل المثال، يوجد على خوادمنا المشفرة صندوق بريد لقاعدة بيانات SQLite linux@example.com, info@example.com, hello@example.com وهكذا - واحد لكل منهما .sqlite ملف قاعدة البيانات. نحن لا نقوم بتسمية ملفات قاعدة البيانات بعنوان البريد الإلكتروني أيضًا - بدلاً من ذلك نستخدم BSON ObjectID ومعرفات UUID الفريدة التي تم إنشاؤها والتي لا تشارك من ينتمي صندوق البريد أو عنوان البريد الإلكتروني الموجود ضمنه (على سبيل المثال: 353a03f21e534321f5d6e267.sqlite).

يتم تشفير كل قاعدة من قواعد البيانات هذه بنفسها باستخدام كلمة المرور الخاصة بك (التي تمتلكها أنت فقط). com.sqleet (ChaCha20-Poly1305). وهذا يعني أن صناديق البريد الخاصة بك مشفرة بشكل فردي، ومكتفية بذاتها، وضع الحماية، والمحمولة.

لقد قمنا بضبط SQLite بما يلي PRAGMA:

PRAGMAغاية
cipher=chacha20ChaCha20-Poly1305 تشفير قاعدة بيانات SQLite. مرجع better-sqlite3-multiple-ciphers تحت المشاريع لمزيد من البصيرة.
key="****************"هذه هي كلمة المرور التي تم فك تشفيرها في الذاكرة فقط والتي يتم تمريرها عبر اتصال IMAP الخاص بعميل البريد الإلكتروني الخاص بك بخادمنا. يتم إنشاء مثيلات قاعدة البيانات الجديدة وإغلاقها لكل جلسة قراءة وكتابة (من أجل ضمان وضع الحماية والعزل).
journal_model=WALسجل الكتابة المسبقة ("WAL") مما يعزز الأداء ويسمح بالوصول المتزامن للقراءة.
busy_timeout=5000يمنع أخطاء قفل الكتابة بينما تجري عمليات كتابية أخرى.
synchronous=NORMALيزيد من متانة المعاملات دون خطر تلف البيانات.
foreign_keys=ONيفرض فرض مراجع المفاتيح الخارجية (مثل العلاقة من جدول إلى آخر). بشكل افتراضي، لا يتم تشغيل هذا في SQLiteولكن للتحقق من صحة البيانات وتكاملها يجب تمكينها.
encoding='UTF-8'التشفير الافتراضي لاستخدامها لضمان سلامة المطور.

جميع الإعدادات الافتراضية الأخرى هي من SQLite كما هو محدد من ملف وثائق براغما الرسمية.

التزامن

تلدر؛ نحن نستخدم rclone و WebSocket للقراءة والكتابة المتزامنة في صناديق بريد SQLite المشفرة الخاصة بك.

يقرأ

قد يتم حل مشكلة عميل البريد الإلكتروني الخاص بك على هاتفك imap.forwardemail.net إلى أحد عناوين IP الرقمية الخاصة بنا - وقد يقوم عميل سطح المكتب الخاص بك بحل عنوان IP منفصل من عنوان IP مختلف مزود كليا.

بغض النظر عن خادم IMAP الذي يتصل به عميل البريد الإلكتروني الخاص بك، فإننا نريد أن تتم قراءة الاتصال من قاعدة بياناتك في الوقت الفعلي وبدقة 100%:

  • يتم تحقيق ذلك باستخدام rclone مع --vfs-cache-mode off (الافتراضي).

  • بدلاً من استخدام ذاكرة التخزين المؤقت على القرص المحلي، تتم قراءة ذاكرة التخزين المؤقت مباشرةً من التثبيت البعيد (قاعدة البيانات الخاصة بك) في الوقت الفعلي.

  • وفي حالة عدم العثور على الملف المحلي، فهذا يشير إلى ذلك rclone فشل في التحميل أو لديه مشكلة. في هذه الحالة نستخدم أ WebSocket احتياطي للقراءات (مما يؤدي إلى انخفاض الأداء قليلاً، لكنه لا يزال يحافظ على سلامة الخدمة).

  • تم تكوين كل خادم من خوادمنا ليتم تشغيله بشكل متسق وينبهنا في الوقت الفعلي بأي أخطاء.

يكتب

تختلف الكتابة إلى قاعدة البيانات الخاصة بك قليلًا – نظرًا لأن SQLite عبارة عن قاعدة بيانات مضمنة ويعيش صندوق البريد الخاص بك في ملف واحد افتراضيًا.

لقد استكشفنا خيارات مثل litestream, rqlite، و dqlite أدناه - ولكن لم يلبي أي منها متطلباتنا.

لإنجاز عمليات الكتابة باستخدام تسجيل الكتابة المسبق ("WAL") ممكّن - نحتاج إلى التأكد من أن خادمًا واحدًا فقط ("الأساسي") مسؤول عن القيام بذلك. WAL يعمل على تسريع التزامن بشكل كبير ويسمح لكاتب واحد والعديد من القراء.

يتم تشغيل الأساسي على خوادم البيانات مع وحدات التخزين المحملة التي تحتوي على صناديق البريد المشفرة. من وجهة نظر التوزيع، يمكنك مراعاة جميع خوادم IMAP الفردية الموجودة خلفها imap.forwardemail.net أن تكون خوادم ثانوية ("ثانوية").

نحن ننجز التواصل في اتجاهين مع WebSockets:

  • تستخدم الخوادم الأساسية مثيلاً لـ wsWebSocketServer الخادم.
  • تستخدم الخوادم الثانوية مثيلاً لـ wsWebSocket العميل الذي تم تغليفه به websocket كما وعدت و إعادة الاتصال websocket. يضمن هذان الغلافان أن WebSocket يعيد الاتصال ويمكنه إرسال واستقبال البيانات لعمليات الكتابة في قاعدة بيانات محددة.

النسخ الاحتياطية

تلدر؛ يتم عمل نسخ احتياطية لصناديق البريد المشفرة الخاصة بك يوميًا. يمكنك أيضًا طلب نسخة احتياطية جديدة على الفور أو تنزيل أحدث نسخة احتياطية في أي وقت من حسابي المجالات اسماء مستعارة.

بالنسبة للنسخ الاحتياطية، نقوم ببساطة بتشغيل SQLite VACUUM INTO الأمر كل يوم أثناء معالجة أوامر IMAP، مما يعزز كلمة المرور المشفرة من اتصال IMAP في الذاكرة. يتم تخزين النسخ الاحتياطية في حالة عدم اكتشاف نسخة احتياطية موجودة أو في حالة ظهور خطأ SHA-256 تم تغيير التجزئة في الملف مقارنة بأحدث نسخة احتياطية.

لاحظ أننا نستخدم ملف VACUUM INTO الأمر بدلا من المدمج في backup الأمر لأنه إذا تم تعديل الصفحة أثناء backup عملية القيادة، ثم عليه أن يبدأ من جديد. ال VACUUM INTO سوف يأخذ الأمر لقطة. انظر هذه التعليقات على جيثب و أخبار القراصنة لمزيد من البصيرة.

بالإضافة إلى ذلك نستخدم VACUUM INTO في مقابل backup، بسبب ال backup سيترك الأمر قاعدة البيانات غير مشفرة لفترة وجيزة حتى rekey تم استدعاؤه (راجع GitHub تعليق للبصيرة).

سيقوم الثانوي بإرشاد الابتدائية على WebSocket اتصال لتنفيذ النسخ الاحتياطي - وسيتلقى البرنامج الأساسي بعد ذلك الأمر للقيام بذلك وسيقوم بعد ذلك بما يلي:

  1. الاتصال بصندوق البريد المشفر الخاص بك.
  2. الحصول على قفل الكتابة.
  3. قم بتشغيل نقطة تفتيش WAL عبر wal_checkpoint(PASSIVE).
  4. تشغيل VACUUM INTO أمر سكليتي.
  5. تأكد من إمكانية فتح الملف المنسوخ باستخدام كلمة المرور المشفرة (الحماية/الحماية من الدمى).
  6. قم بتحميله إلى Cloudflare R2 للتخزين (أو المزود الخاص بك إذا تم تحديده).
  7. قم بضغط ملف النسخ الاحتياطي الناتج باستخدام gzip.
  8. قم بتحميله إلى Cloudflare R2 للتخزين (أو المزود الخاص بك إذا تم تحديده).

تذكر أن صناديق البريد الخاصة بك مشفرة - وعلى الرغم من وجود قيود IP وإجراءات مصادقة أخرى مطبقة لاتصالات WebSocket - في حالة وجود ممثل سيئ، يمكنك الاطمئنان إلى أنه ما لم تكن حمولة WebSocket تحتوي على كلمة مرور IMAP الخاصة بك، فلن تتمكن من فتح قاعدة البيانات الخاصة بك .

يتم تخزين نسخة احتياطية واحدة فقط لكل صندوق بريد في هذا الوقت، ولكن في المستقبل قد نقدم إمكانية الاسترداد في وقت محدد ("PITR").

تدعم خوادم IMAP لدينا SEARCH الأمر مع الاستعلامات المعقدة والتعبيرات العادية والمزيد.

أداء البحث السريع بفضل FTS5 و sqlite-regex.

نحن نخزن Date القيم في علب بريد SQLite مثل ISO 8601 سلاسل عبر Date.prototype.toISOString (مع المنطقة الزمنية UTC لتعمل مقارنات المساواة بشكل صحيح).

يتم أيضًا تخزين الفهارس لجميع الخصائص الموجودة في استعلامات البحث.

المشاريع

فيما يلي جدول يوضح المشاريع التي نستخدمها في كود المصدر وعملية التطوير (مرتبة أبجديًا):

مشروعغاية
غير مقبولمنصة أتمتة DevOps لصيانة أسطولنا الكامل من الخوادم وتوسيع نطاقه وإدارته بسهولة.
بريأداة جدولة المهام لـ Node.js وJavaScript مع cron، والتواريخ، وms، والإصدارات الأحدث، ودعم سهل الاستخدام.
الطائرةمكتبة تسجيل JavaScript وNode.js سهلة الاستخدام للمطورين مع وضع الأمان والخصوصية في الاعتبار.
دعإطار عمل Node.js الذي يعمل على تشغيل الهندسة المعمارية والتصميم الهندسي بالكامل باستخدام MVC والمزيد.
MongoDBحل قاعدة بيانات NoSQL الذي نستخدمه لتخزين جميع البيانات الأخرى خارج صناديق البريد (مثل حسابك والإعدادات والمجالات وتكوينات الاسم المستعار).
النمسنمذجة مستند كائن MongoDB ("ODM") الذي نستخدمه عبر مجموعتنا بأكملها. لقد كتبنا مساعدين خاصين يسمحون لنا بمواصلة الاستخدام ببساطة النمس مع SQLite 🎉
Node.jsNode.js هي بيئة تشغيل JavaScript مفتوحة المصدر ومتعددة الأنظمة الأساسية والتي تدير جميع عمليات الخادم لدينا.
ملاحظة الارسالحزمة Node.js لإرسال رسائل البريد الإلكتروني وإنشاء الاتصالات والمزيد. نحن الراعي الرسمي لهذا المشروع.
ريديسقاعدة بيانات في الذاكرة للتخزين المؤقت وقنوات النشر/الاشتراك وDNS عبر طلبات HTTPS.
SQLite3MultipleCiphersملحق التشفير لـ SQLite للسماح بتشفير ملفات قاعدة البيانات بأكملها (بما في ذلك سجل الكتابة المسبق ("WAL")، مجلة، التراجع، ...).
SQLiteStudioمحرر Visual SQLite (والذي يمكنك استخدامه أيضًا) لاختبار صناديق بريد التطوير وتنزيلها وعرضها.
سكليتيطبقة قاعدة بيانات مضمنة لتخزين IMAP قابل للتطوير ومكتفي بذاته وسريع ومرن.
ماسح البريد المزعجأداة Node.js لمكافحة البريد العشوائي وتصفية البريد الإلكتروني ومنع التصيد الاحتيالي (بديلنا لـ قاتل البريد العشوائي و com.rspamd).
يوسفيDNS عبر طلبات HTTPS باستخدام Node.js والتخزين المؤقت باستخدام Redis - مما يضمن الاتساق العالمي وغير ذلك الكثير.
ثندربيرديستخدم فريق التطوير لدينا هذا (ويوصي بهذا أيضًا) كـ عميل البريد الإلكتروني المفضل لاستخدامه مع إعادة توجيه البريد الإلكتروني.
UTMيستخدم فريق التطوير لدينا إنشاء أجهزة افتراضية لنظامي التشغيل iOS وmacOS من أجل اختبار عملاء البريد الإلكتروني المختلفين (بالتوازي) مع خوادم IMAP وSMTP الخاصة بنا.
أوبونتونظام تشغيل خادم حديث مفتوح المصدر قائم على نظام Linux والذي يعمل على تشغيل جميع بنيتنا التحتية.
البطة البريةمكتبة خادم IMAP – راجع ملاحظاتها على إلغاء تكرار المرفقات و دعم بروتوكول IMAP.
أفضل-sqlite3-أصفار متعددةمكتبة API سريعة وبسيطة لـ Node.js للتفاعل مع SQLite3 برمجيًا.
قوالب البريد الإلكترونيإطار بريد إلكتروني صديق للمطورين لإنشاء رسائل بريد إلكتروني مخصصة ومعاينتها وإرسالها (مثل إشعارات الحساب والمزيد).
json-sqlمنشئ استعلام SQL باستخدام بناء جملة نمط مونغو. وهذا يوفر وقت فريق التطوير لدينا حيث يمكننا الاستمرار في الكتابة بأسلوب Mongo عبر المجموعة بأكملها باستخدام نهج حيادي لقاعدة البيانات. كما أنه يساعد على تجنب هجمات حقن SQL باستخدام معلمات الاستعلام.
مفتش مخطط knexأداة SQL لاستخراج المعلومات حول مخطط قاعدة البيانات الموجودة. يتيح لنا ذلك التحقق بسهولة من صحة جميع المؤشرات والجداول والأعمدة والقيود وغيرها 1:1 مع كيف ينبغي أن يكونوا. حتى أننا كتبنا مساعدين آليين لإضافة أعمدة وفهارس جديدة إذا تم إجراء تغييرات على مخططات قاعدة البيانات (مع تنبيهات تفصيلية للغاية للأخطاء أيضًا).
knexمنشئ استعلام SQL الذي نستخدمه فقط لعمليات ترحيل قاعدة البيانات والتحقق من صحة المخطط من خلاله knex-schema-inspector.
الماندرينتلقائي i18n ترجمة العبارة مع دعم استخدام Markdown واجهة برمجة تطبيقات الترجمة السحابية من Google.
mx-connectحزمة Node.js لحل وإنشاء الاتصالات مع خوادم MX ومعالجة الأخطاء.
مساء2مدير عملية إنتاج Node.js مع موازن تحميل مدمج (ضبطها بدقة للأداء).
خادم SMTPمكتبة خادم SMTP - نستخدمها لتبادل البريد ("MX") وخوادم SMTP الصادرة.
ImapTestأداة مفيدة لاختبار خوادم IMAP مقابل المعايير وتوافق بروتوكول IMAP لمواصفات RFC. تم إنشاء هذا المشروع بواسطة حمامة فريق (خادم IMAP وPOP3 مفتوح المصدر نشط من يوليو 2002). لقد اختبرنا خادم IMAP الخاص بنا على نطاق واسع باستخدام هذه الأداة.

يمكنك العثور على مشاريع أخرى نستخدمها فيها كود المصدر الخاص بنا على GitHub.

مقدمي الخدمات

مزودغاية
كلاود فليرمزود DNS، والفحوصات الصحية، وموازنات التحميل، وتخزين النسخ الاحتياطي باستخدام كلاودفلير R2.
المحيط الرقمياستضافة خادم مخصص، وتخزين كتلة SSD، وقواعد البيانات المُدارة.
فولتراستضافة خادم مخصص وتخزين كتلة SSD.

أفكار

مبادئ

تم تصميم إعادة توجيه البريد الإلكتروني وفقًا لهذه المبادئ:

  1. كن دائمًا صديقًا للمطورين، مع التركيز على الأمان والخصوصية، والشفافية.
  2. الالتزام MVC, يونكس, KISS, DRY, YAGNI, اثنا عشر عامل, الحلاقة أوكام، و تطبيق تجريبي
  3. استهدف Scrapy، Bootstrapped، و رامين مربح مطور

التجارب

تلدر؛ في نهاية المطاف، فإن استخدام تخزين الكائنات المتوافق مع S3 و/أو الجداول الافتراضية ليس ممكنًا من الناحية الفنية لأسباب تتعلق بالأداء ويكون عرضة للخطأ بسبب قيود الذاكرة.

لقد أجرينا بعض التجارب التي أدت إلى حل SQLite النهائي كما تمت مناقشته أعلاه.

واحدة من هذه كانت محاولة استخدام com.rclone وSQLite مع طبقة تخزين متوافقة مع S3.

قادتنا هذه التجربة إلى فهم واكتشاف حالات الحافة المحيطة بـ rclone وSQLite و VFS الاستخدام:

  • إذا قمت بتمكين --vfs-cache-mode writes ضع علامة باستخدام rclone، وستكون عمليات القراءة على ما يرام، ولكن سيتم تخزين عمليات الكتابة مؤقتًا.
    • إذا كان لديك عدة خوادم IMAP موزعة عالميًا، فسيتم إيقاف تشغيل ذاكرة التخزين المؤقت عبرها ما لم يكن لديك كاتب واحد ومستمعين متعددين (على سبيل المثال، أسلوب النشر/الفرع).
    • هذا أمر معقد بشكل لا يصدق، وإضافة أي تعقيد إضافي مثل هذا سيؤدي إلى المزيد من نقاط الفشل الفردية.
    • لا يدعم موفرو التخزين المتوافقون مع S3 التغييرات الجزئية للملفات - مما يعني أي تغيير في .sqlite سيؤدي الملف إلى تغيير كامل وإعادة تحميل قاعدة البيانات.
    • حلول أخرى مثل rsync موجودة، لكنها لا تركز على سجل الكتابة المسبق ("WAL") الدعم - لذلك انتهينا من مراجعة Litestream. ولحسن الحظ، فإن استخدام التشفير لدينا يقوم بالفعل بتشفير ملف WAL الملفات لنا، لذلك لا نحتاج إلى الاعتماد على Litestream لذلك. ومع ذلك، لم نكن واثقين بعد من استخدام Litestream في الإنتاج ولدينا بعض الملاحظات أدناه حول ذلك.
    • باستخدام هذا الخيار من --vfs-cache-mode writes (ال فقط طريقة لاستخدام SQLite أكثر rclone للكتابة) سيحاول نسخ قاعدة البيانات بأكملها من نقطة الصفر في الذاكرة - يعد التعامل مع صندوق بريد واحد بسعة 10 جيجابايت أمراً مقبولاً، إلا أن التعامل مع صناديق بريد متعددة ذات مساحة تخزين عالية للغاية سيؤدي إلى تشغيل خوادم IMAP في قيود الذاكرة و ENOMEM الأخطاء وأخطاء التجزئة وفساد البيانات.
  • إذا حاولت استخدام SQLite الجداول الافتراضية (على سبيل المثال باستخدام s3db) من أجل نشر البيانات على طبقة تخزين متوافقة مع S3، فسوف تواجه العديد من المشكلات الأخرى:
    • ستكون القراءة والكتابة بطيئة للغاية حيث يجب الوصول إلى نقاط نهاية S3 API باستخدام HTTP GET, PUT, HEAD، و POST طُرق.
    • أظهرت اختبارات التطوير أن ما يزيد عن 500 ألف - 1 مليون+ من السجلات على إنترنت الألياف لا يزال محدودًا بإنتاجية الكتابة والقراءة لمقدمي الخدمات المتوافقين مع S3. على سبيل المثال، ركض مطورونا for حلقات للقيام بكلا SQL المتسلسل INSERT البيانات وتلك التي كتبت بكميات كبيرة من البيانات. وفي كلتا الحالتين كان الأداء بطيئا بشكل مذهل.
    • الجداول الافتراضية لا يمكن أن يكون لها فهارس, ALTER TABLE تصريحات، و آخر محددات – مما يؤدي إلى تأخير يصل إلى 1-2 دقيقة أو أكثر حسب كمية البيانات.
    • تم تخزين الكائنات غير مشفرة ولا يتوفر دعم التشفير الأصلي بسهولة.
  • لقد استكشفنا أيضًا استخدام sqlite-s3vfs وهو مشابه من الناحية المفاهيمية والفنية للنقطة السابقة (لذلك فهو يحتوي على نفس المشكلات). سيكون الاحتمال هو استخدام العرف sqlite3 بناء ملفوف بالتشفير مثل wxSQLite3 (الذي نستخدمه حاليًا في حلنا أعلاه) من خلال تحرير ملف الإعداد.
  • كان النهج المحتمل الآخر هو استخدام تمديد متعدد، ولكن هذا له حد قدره 32 جيجابايت وسيتطلب مشكلات معقدة في البناء والتطوير.
  • ALTER TABLE البيانات مطلوبة (لذا فإن هذا يستبعد تمامًا استخدام الجداول الافتراضية). نحن نحتاج ALTER TABLE البيانات من أجل ربطنا مع knex-schema-inspector للعمل بشكل صحيح - مما يضمن عدم تلف البيانات وإمكانية تحويل الصفوف المستردة إلى مستندات صالحة وفقًا لمتطلباتنا mongoose تعريفات المخطط (والتي تتضمن القيد ونوع المتغير والتحقق التعسفي من صحة البيانات).
  • تقريبًا جميع المشاريع المتوافقة مع S3 والمتعلقة بـ SQLite في مجتمع مفتوح المصدر موجودة بلغة Python (وليست JavaScript التي نستخدمها بنسبة 100% في مجموعتنا).
  • مكتبات الضغط مثل sqlite-zstd (يرى تعليقات) تبدو واعدة، ولكن قد لا يكون جاهزًا لاستخدام الإنتاج بعد. بدلاً من ذلك، يتم الضغط من جانب التطبيق على أنواع البيانات مثل String, Object, Map, Array, Set، و Buffer سيكون أسلوبًا أنظف وأسهل (وسيكون من الأسهل ترحيله أيضًا، حيث يمكننا تخزين ملف Boolean العلم أو العمود – أو حتى استخدامه PRAGMA user_version=1 للضغط أو user_version=0 لعدم الضغط كبيانات تعريف قاعدة البيانات).
    • لحسن الحظ، قمنا بالفعل بتنفيذ إلغاء تكرار المرفقات في مخزن خادم IMAP الخاص بنا - وبالتالي فإن كل رسالة تحتوي على نفس المرفق لن تحتفظ بنسخة من المرفق - وبدلاً من ذلك، يتم تخزين مرفق واحد لرسائل وسلاسل رسائل متعددة في صندوق بريد (وملف خارجي) يتم استخدام المرجع لاحقا).
  • يعد مشروع Litestream، وهو حل النسخ الاحتياطي والنسخ الاحتياطي لـ SQLite، واعدًا جدًا وسنستخدمه على الأرجح في المستقبل.
  • يجب أن تكون استعادة النسخة الاحتياطية خالية من الاحتكاك وتافهة. باستخدام حل مثل MongoDB مع mongodump و mongoexport ليست مملة فحسب، بل تتطلب وقتًا طويلاً وتتسم بتعقيد التكوين.
    • تجعل قواعد بيانات SQLite الأمر بسيطًا (إنه ملف واحد).
    • أردنا تصميم حل حيث يمكن للمستخدمين أخذ صندوق البريد الخاص بهم والمغادرة في أي لحظة.
      • أوامر Node.js البسيطة لـ fs.unlink('mailbox.sqlite')) ويتم مسحه نهائيًا من مساحة تخزين القرص.
      • يمكننا بالمثل استخدام واجهة برمجة التطبيقات المتوافقة مع S3 مع HTTP DELETE لإزالة اللقطات والنسخ الاحتياطية للمستخدمين بسهولة.
    • كان SQLite هو الحل الأبسط والأسرع والأكثر فعالية من حيث التكلفة.

عدم وجود البدائل

على حد علمنا، لم يتم تصميم أي خدمات بريد إلكتروني أخرى بهذه الطريقة كما أنها ليست مفتوحة المصدر.

نحن أعتقد أن هذا قد يكون بسبب إلى خدمات البريد الإلكتروني الحالية التي تتمتع بتكنولوجيا قديمة في الإنتاج كود السباغيتي 🍝.

معظم موفري خدمة البريد الإلكتروني الحاليين، إن لم يكن جميعهم، إما أن يكونوا مصدرًا مغلقًا أو يعلنون على أنهم مفتوح المصدر، ولكن في الواقع فإن واجهتهم الأمامية هي فقط مفتوحة المصدر.

الجزء الأكثر حساسية في البريد الإلكتروني (التخزين الفعلي/تفاعل IMAP/SMTP) يتم كل ذلك على الواجهة الخلفية (الخادم)، و لا على الواجهة الأمامية (العميل).

جرب إعادة توجيه البريد الإلكتروني

سجل اليوم في https://forwardemail.net! 🚀