Quantum Resistant Email: วิธีที่เราใช้กล่องจดหมาย SQLite ที่เข้ารหัสเพื่อรักษาความปลอดภัยอีเมลของคุณ

ภาพประกอบบริการอีเมลที่เข้ารหัสปลอดภัยจากควอนตัม

คำนำ

Important

บริการอีเมลของเราเป็น โอเพนซอร์ส 100% และเน้นความเป็นส่วนตัวผ่านกล่องจดหมาย SQLite ที่ปลอดภัยและเข้ารหัส

จนกระทั่งเราเปิดตัว การรองรับ IMAP เราใช้ MongoDB สำหรับความต้องการจัดเก็บข้อมูลถาวรของเรา

เทคโนโลยีนี้ยอดเยี่ยมและเรายังคงใช้งานอยู่จนถึงวันนี้ – แต่เพื่อให้มีการเข้ารหัสข้อมูลขณะพักกับ MongoDB คุณต้องใช้ผู้ให้บริการที่มี MongoDB Enterprise เช่น Digital Ocean หรือ Mongo Atlas – หรือจ่ายค่าลิขสิทธิ์แบบองค์กร (และต้องทำงานกับทีมขายที่มีความล่าช้า)

ทีมงานของเราที่ Forward Email ต้องการโซลูชันการจัดเก็บที่เป็นมิตรกับนักพัฒนา, ขยายตัวได้, เชื่อถือได้ และเข้ารหัสสำหรับกล่องจดหมาย IMAP ในฐานะนักพัฒนาโอเพนซอร์ส การใช้เทคโนโลยีที่ต้องจ่ายค่าลิขสิทธิ์เพื่อให้ได้ฟีเจอร์การเข้ารหัสข้อมูลขณะพักนั้นขัดกับ หลักการของเรา – ดังนั้นเราจึงทดลอง วิจัย และพัฒนาโซลูชันใหม่ตั้งแต่ต้นเพื่อตอบสนองความต้องการเหล่านี้

แทนที่จะใช้ฐานข้อมูลร่วมกันเพื่อเก็บกล่องจดหมายของคุณ เราจะเก็บและเข้ารหัสกล่องจดหมายของคุณแต่ละกล่องด้วยรหัสผ่านของคุณ (ซึ่งมีเพียงคุณเท่านั้นที่มี) บริการอีเมลของเราปลอดภัยมากจนถ้าคุณลืมรหัสผ่าน คุณจะสูญเสียกล่องจดหมายของคุณ (และต้องกู้คืนด้วยการสำรองข้อมูลออฟไลน์หรือเริ่มต้นใหม่)

อ่านต่อไปขณะที่เราลงลึกด้านล่างด้วย การเปรียบเทียบบริการอีเมล, วิธีการทำงานของบริการเรา, เทคโนโลยีของเรา และอื่นๆ

การเปรียบเทียบบริการอีเมล

เราเป็นผู้ให้บริการอีเมลที่เป็นโอเพนซอร์ส 100% และเน้นความเป็นส่วนตัวเพียงรายเดียวที่เก็บกล่องจดหมาย SQLite ที่เข้ารหัสแยกกัน, เสนอชื่อโดเมน, อีเมลแฝง และผู้ใช้ไม่จำกัด และรองรับ SMTP ขาออก, IMAP และ POP3:

แตกต่างจากผู้ให้บริการอีเมลรายอื่น คุณไม่จำเป็นต้องจ่ายค่าพื้นที่เก็บข้อมูลแยกตามโดเมนหรืออีเมลแฝงกับ Forward Email พื้นที่เก็บข้อมูลถูกแชร์ทั่วทั้งบัญชีของคุณ – ดังนั้นถ้าคุณมีชื่อโดเมนที่กำหนดเองหลายชื่อและหลายอีเมลแฝงในแต่ละชื่อ เราคือโซลูชันที่สมบูรณ์แบบสำหรับคุณ โปรดทราบว่าคุณยังสามารถบังคับใช้ขีดจำกัดพื้นที่เก็บข้อมูลได้หากต้องการแยกตามโดเมนหรืออีเมลแฝง

อ่านการเปรียบเทียบบริการอีเมล

มันทำงานอย่างไร

  1. ใช้ไคลเอนต์อีเมลของคุณ เช่น Apple Mail, Thunderbird, Gmail หรือ Outlook – คุณเชื่อมต่อกับเซิร์ฟเวอร์ IMAP ที่ปลอดภัยของเราโดยใช้ชื่อผู้ใช้และรหัสผ่านของคุณ:

    • ชื่อผู้ใช้ของคุณคืออีเมลแฝงเต็มรูปแบบพร้อมโดเมน เช่น hello@example.com
    • รหัสผ่านของคุณถูกสร้างแบบสุ่มและแสดงให้คุณเห็นเพียง 30 วินาทีเมื่อคุณคลิก สร้างรหัสผ่าน จาก บัญชีของฉัน โดเมน อีเมลแฝง.
  2. เมื่อเชื่อมต่อแล้ว โปรแกรมรับส่งอีเมลของคุณจะส่ง คำสั่งโปรโตคอล IMAP ไปยังเซิร์ฟเวอร์ IMAP ของเราเพื่อให้กล่องจดหมายของคุณซิงค์กัน ซึ่งรวมถึงการเขียนและเก็บร่างอีเมลและการกระทำอื่นๆ ที่คุณอาจทำ (เช่น การติดป้ายอีเมลว่า สำคัญ หรือการทำเครื่องหมายอีเมลว่าเป็น สแปมหรือจดหมายขยะ)

  3. เซิร์ฟเวอร์แลกเปลี่ยนอีเมล (ที่รู้จักกันทั่วไปว่า "เซิร์ฟเวอร์ MX") จะรับอีเมลขาเข้าที่ใหม่และเก็บไว้ในกล่องจดหมายของคุณ เมื่อเกิดเหตุการณ์นี้ โปรแกรมรับส่งอีเมลของคุณจะได้รับการแจ้งเตือนและซิงค์กล่องจดหมาย เซิร์ฟเวอร์แลกเปลี่ยนอีเมลของเราสามารถส่งต่ออีเมลของคุณไปยังผู้รับหนึ่งหรือมากกว่านั้น (รวมถึง เว็บฮุค) เก็บอีเมลของคุณไว้ในพื้นที่เก็บข้อมูล IMAP ที่เข้ารหัสกับเรา หรือทั้งสองอย่าง!

  4. เบื้องหลัง การออกแบบพื้นที่เก็บอีเมลที่ปลอดภัยของเราทำงานในสองวิธีเพื่อให้กล่องจดหมายของคุณถูกเข้ารหัสและเข้าถึงได้เฉพาะคุณเท่านั้น:

    • เมื่อได้รับอีเมลใหม่สำหรับคุณจากผู้ส่ง เซิร์ฟเวอร์แลกเปลี่ยนอีเมลของเราจะเขียนไปยังกล่องจดหมายชั่วคราวส่วนตัวและเข้ารหัสสำหรับคุณ

    • เมื่อคุณเชื่อมต่อกับเซิร์ฟเวอร์ IMAP ของเราด้วยโปรแกรมรับส่งอีเมล รหัสผ่านของคุณจะถูกเข้ารหัสในหน่วยความจำและใช้เพื่ออ่านและเขียนไปยังกล่องจดหมายของคุณ กล่องจดหมายของคุณสามารถอ่านและเขียนได้เฉพาะด้วยรหัสผ่านนี้เท่านั้น โปรดทราบว่าเนื่องจากคุณเป็นคนเดียวที่มีรหัสผ่านนี้ มีเพียงคุณเท่านั้น ที่สามารถอ่านและเขียนกล่องจดหมายของคุณเมื่อคุณเข้าถึงมัน ครั้งถัดไปที่โปรแกรมรับส่งอีเมลของคุณพยายามตรวจสอบอีเมลหรือซิงค์ ข้อความใหม่ของคุณจะถูกโอนจากกล่องจดหมายชั่วคราวนี้และเก็บไว้ในไฟล์กล่องจดหมายจริงของคุณโดยใช้รหัสผ่านที่คุณให้ไว้ โปรดทราบว่ากล่องจดหมายชั่วคราวนี้จะถูกล้างและลบทิ้งหลังจากนั้นเพื่อให้มีเพียงกล่องจดหมายที่ป้องกันด้วยรหัสผ่านของคุณเท่านั้นที่มีข้อความเหล่านั้น

    • หากคุณเชื่อมต่อกับ IMAP (เช่น ใช้โปรแกรมรับส่งอีเมลอย่าง Apple Mail หรือ Thunderbird) เราไม่จำเป็นต้องเขียนลงในพื้นที่เก็บข้อมูลชั่วคราวบนดิสก์ รหัสผ่าน IMAP ที่เข้ารหัสในหน่วยความจำของคุณจะถูกดึงมาใช้แทน ในเวลาจริง เมื่อมีข้อความพยายามส่งถึงคุณ เราจะส่งคำขอ WebSocket ไปยังเซิร์ฟเวอร์ IMAP ทั้งหมดเพื่อถามว่ามีเซสชันที่ใช้งานสำหรับคุณหรือไม่ (นี่คือส่วนของการดึงข้อมูล) และจากนั้นจะส่งต่อรหัสผ่านที่เข้ารหัสในหน่วยความจำนี้ – ดังนั้นเราจึงไม่จำเป็นต้องเขียนลงในกล่องจดหมายชั่วคราว เราสามารถเขียนลงในกล่องจดหมายที่เข้ารหัสจริงของคุณโดยใช้รหัสผ่านที่เข้ารหัสของคุณได้

  5. การสำรองข้อมูลกล่องจดหมายที่เข้ารหัสของคุณ จะถูกทำทุกวัน คุณยังสามารถขอสำรองข้อมูลใหม่ได้ทุกเมื่อหรือดาวน์โหลดสำรองข้อมูลล่าสุดได้จาก บัญชีของฉัน โดเมน อลิอาส หากคุณตัดสินใจเปลี่ยนไปใช้บริการอีเมลอื่น คุณสามารถย้าย ดาวน์โหลด ส่งออก และล้างกล่องจดหมายและการสำรองข้อมูลของคุณได้อย่างง่ายดายทุกเมื่อ

เทคโนโลยี

ฐานข้อมูล

เราได้สำรวจชั้นเก็บข้อมูลฐานข้อมูลอื่นๆ แต่ไม่มีตัวใดที่ตอบสนองความต้องการของเราได้ดีเท่า SQLite:

Database Encryption-at-rest Sandboxed Mailboxes License Used Everywhere
SQLite ✅ ใช่กับ SQLite3MultipleCiphers ✅ สาธารณสมบัติ
MongoDB "มีเฉพาะใน MongoDB Enterprise เท่านั้น" ❌ ฐานข้อมูลเชิงสัมพันธ์ ❌ AGPL และ SSPL-1.0
rqlite เฉพาะเครือข่าย ❌ ฐานข้อมูลเชิงสัมพันธ์ MIT
dqlite ยังไม่ได้ทดสอบและยังไม่รองรับ? ยังไม่ได้ทดสอบและยังไม่รองรับ? LGPL-3.0-only
PostgreSQL ใช่ ❌ ฐานข้อมูลเชิงสัมพันธ์ PostgreSQL (คล้ายกับ BSD หรือ MIT)
MariaDB สำหรับ InnoDB เท่านั้น ❌ ฐานข้อมูลเชิงสัมพันธ์ GPLv2 และ BUSL-1.1
CockroachDB ฟีเจอร์เฉพาะ Enterprise ❌ ฐานข้อมูลเชิงสัมพันธ์ BUSL-1.1 และอื่นๆ

นี่คือ บทความบล็อกที่เปรียบเทียบตัวเลือกการจัดเก็บฐานข้อมูล SQLite หลายตัว ในตารางด้านบน

Security

ตลอดเวลาที่เราใช้ encryption-at-rest (AES-256), encryption-in-transit (TLS), DNS over HTTPS ("DoH") โดยใช้ 🍊 Tangerine, และ sqleet (ChaCha20-Poly1305) การเข้ารหัสบนกล่องจดหมาย นอกจากนี้เรายังใช้การยืนยันตัวตนสองปัจจัยแบบใช้โทเค็น (แตกต่างจาก SMS ซึ่งเสี่ยงต่อ man-in-the-middle-attacks), การหมุนเวียนกุญแจ SSH พร้อมปิดการเข้าถึง root, การเข้าถึงเซิร์ฟเวอร์เฉพาะผ่านที่อยู่ IP ที่จำกัด และอื่นๆ อีกมากมาย ในกรณีที่เกิด การโจมตีแบบ evil maid หรือพนักงานที่ไม่ซื่อสัตย์จากผู้ให้บริการภายนอก, กล่องจดหมายของคุณยังคงเปิดได้เฉพาะด้วยรหัสผ่านที่คุณสร้างขึ้นเท่านั้น. โปรดมั่นใจว่า เราไม่พึ่งพาผู้ให้บริการภายนอกใดๆ นอกจากผู้ให้บริการเซิร์ฟเวอร์ที่เป็นไปตามมาตรฐาน SOC Type 2 ของเรา ได้แก่ Cloudflare, DataPacket, Digital Ocean, GitHub และ Vultr

เป้าหมายของเราคือการมี single point of failures ให้น้อยที่สุดเท่าที่จะเป็นไปได้

Mailboxes

สรุปสั้นๆ; เซิร์ฟเวอร์ IMAP ของเราใช้ฐานข้อมูล SQLite ที่เข้ารหัสแยกกันสำหรับแต่ละกล่องจดหมายของคุณ

SQLite เป็นฐานข้อมูลฝังตัวที่ได้รับความนิยมอย่างมาก – ปัจจุบันมันทำงานอยู่บนโทรศัพท์และคอมพิวเตอร์ของคุณ – และถูกใช้โดยเทคโนโลยีหลักเกือบทั้งหมด

ตัวอย่างเช่น บนเซิร์ฟเวอร์ที่เข้ารหัสของเรา มีฐานข้อมูล SQLite สำหรับกล่องจดหมาย linux@example.com, info@example.com, hello@example.com เป็นต้น – หนึ่งฐานข้อมูลสำหรับแต่ละกล่องในรูปแบบไฟล์ .sqlite เราไม่ได้ตั้งชื่อไฟล์ฐานข้อมูลด้วยที่อยู่อีเมล แต่ใช้ BSON ObjectID และ UUID ที่ไม่ซ้ำกันซึ่งไม่เปิดเผยว่าเป็นของใครหรือเป็นอีเมลใด (เช่น 353a03f21e534321f5d6e267.sqlite)

ฐานข้อมูลแต่ละชุดนี้ถูกเข้ารหัสด้วยรหัสผ่านของคุณ (ซึ่งมีเพียงคุณเท่านั้นที่มี) โดยใช้ sqleet (ChaCha20-Poly1305) ซึ่งหมายความว่ากล่องจดหมายของคุณถูกเข้ารหัสแยกกัน, เป็นอิสระ, sandboxed และพกพาได้

เราได้ปรับแต่ง SQLite ด้วย PRAGMA ดังต่อไปนี้:

PRAGMA จุดประสงค์
cipher=chacha20 การเข้ารหัสฐานข้อมูล SQLite ด้วย ChaCha20-Poly1305. ดู better-sqlite3-multiple-ciphers ในส่วน Projects เพื่อข้อมูลเชิงลึกเพิ่มเติม
key="****************" นี่คือรหัสผ่านที่ถอดรหัสในหน่วยความจำเท่านั้นซึ่งถูกส่งผ่านการเชื่อมต่อ IMAP ของไคลเอนต์อีเมลของคุณไปยังเซิร์ฟเวอร์ของเรา อินสแตนซ์ฐานข้อมูลใหม่จะถูกสร้างและปิดสำหรับแต่ละเซสชันอ่านและเขียน (เพื่อให้แน่ใจในเรื่อง sandboxing และการแยกกัน)
journal_model=WAL Write-ahead-log ("WAL") ซึ่งช่วยเพิ่มประสิทธิภาพและอนุญาตให้เข้าถึงการอ่านพร้อมกันได้
busy_timeout=5000 ป้องกันข้อผิดพลาดล็อกเขียน ในขณะที่มีการเขียนอื่นๆ กำลังดำเนินอยู่
synchronous=NORMAL เพิ่มความทนทานของธุรกรรม โดยไม่มีความเสี่ยงของการเสียหายของข้อมูล
foreign_keys=ON บังคับใช้การอ้างอิงคีย์ต่างประเทศ (เช่น ความสัมพันธ์จากตารางหนึ่งไปยังอีกตารางหนึ่ง) โดยปกติจะไม่เปิดใช้งานใน SQLite แต่เพื่อการตรวจสอบและความสมบูรณ์ของข้อมูลควรเปิดใช้งานไว้
encoding='UTF-8' การเข้ารหัสเริ่มต้น ที่ใช้เพื่อให้แน่ใจว่านักพัฒนาจะเข้าใจและใช้งานได้อย่างถูกต้อง

ค่าปริยายอื่น ๆ ทั้งหมดมาจาก SQLite ตามที่ระบุไว้ใน เอกสาร PRAGMA อย่างเป็นทางการ

การทำงานพร้อมกัน

สรุปสั้น ๆ; เราใช้ WebSocket สำหรับการอ่านและเขียนพร้อมกันไปยังกล่องจดหมาย SQLite ที่เข้ารหัสของคุณ

การอ่าน

ไคลเอนต์อีเมลของคุณบนโทรศัพท์อาจแก้ไข imap.forwardemail.net ไปยังที่อยู่ IP ของ Digital Ocean หนึ่งในของเรา – และไคลเอนต์บนเดสก์ท็อปของคุณอาจแก้ไขไปยัง IP แยกต่างหากจาก ผู้ให้บริการ รายอื่นโดยสิ้นเชิง

ไม่ว่าจะเชื่อมต่อกับเซิร์ฟเวอร์ IMAP ใด เราต้องการให้การเชื่อมต่อนั้นอ่านจากฐานข้อมูลของคุณแบบเรียลไทม์ด้วยความถูกต้อง 100% ซึ่งทำได้ผ่าน WebSockets

การเขียน

การเขียนไปยังฐานข้อมูลของคุณจะแตกต่างกันเล็กน้อย – เนื่องจาก SQLite เป็นฐานข้อมูลฝังตัวและกล่องจดหมายของคุณอยู่ในไฟล์เดียวโดยค่าเริ่มต้น

เราได้สำรวจตัวเลือกต่าง ๆ เช่น litestream, rqlite และ dqlite ด้านล่าง – แต่ไม่มีตัวเลือกใดที่ตอบสนองความต้องการของเรา

เพื่อให้สามารถเขียนด้วยการเปิดใช้งาน write-ahead-logging ("WAL") – เราต้องมั่นใจว่าเซิร์ฟเวอร์เพียงตัวเดียว ("Primary") เป็นผู้รับผิดชอบในการทำเช่นนั้น WAL ช่วยเร่งความเร็วในการทำงานพร้อมกันอย่างมากและอนุญาตให้มีผู้เขียนหนึ่งคนและผู้อ่านหลายคน

Primary ทำงานบนเซิร์ฟเวอร์ข้อมูลที่มีโวลุ่มเมานต์ซึ่งบรรจุกล่องจดหมายที่เข้ารหัส จากมุมมองการกระจาย คุณสามารถพิจารณาเซิร์ฟเวอร์ IMAP แต่ละตัวที่อยู่เบื้องหลัง imap.forwardemail.net เป็นเซิร์ฟเวอร์รอง ("Secondary")

เราทำการสื่อสารสองทางด้วย WebSockets:

  • เซิร์ฟเวอร์ Primary ใช้ตัวอย่างของเซิร์ฟเวอร์ WebSocketServer จาก ws
  • เซิร์ฟเวอร์ Secondary ใช้ตัวอย่างของไคลเอนต์ WebSocket จาก ws ที่ถูกห่อด้วย websocket-as-promised และ reconnecting-websocket ตัวห่อหุ้มสองตัวนี้ช่วยให้ WebSocket สามารถเชื่อมต่อใหม่และส่ง/รับข้อมูลสำหรับการเขียนฐานข้อมูลเฉพาะได้

การสำรองข้อมูล

สรุปสั้น ๆ; การสำรองข้อมูลกล่องจดหมายที่เข้ารหัสของคุณจะทำทุกวัน คุณยังสามารถขอสำรองข้อมูลใหม่ทันทีหรือดาวน์โหลดสำรองข้อมูลล่าสุดได้ทุกเมื่อจาก บัญชีของฉัน โดเมน อีเมลแฝง

สำหรับการสำรองข้อมูล เราเพียงแค่รันคำสั่ง SQLite VACUUM INTO ทุกวันในระหว่างการประมวลผลคำสั่ง IMAP ซึ่งใช้รหัสผ่านที่เข้ารหัสของคุณจากการเชื่อมต่อ IMAP ในหน่วยความจำ การสำรองข้อมูลจะถูกเก็บไว้หากไม่พบการสำรองข้อมูลเดิมหรือหากแฮช SHA-256 ของไฟล์เปลี่ยนแปลงไปเมื่อเทียบกับการสำรองข้อมูลล่าสุด

โปรดทราบว่าเราใช้คำสั่ง VACUUM INTO แทนคำสั่ง backup ที่มีอยู่ในตัว เพราะถ้ามีการแก้ไขหน้าในระหว่างการดำเนินการคำสั่ง backup จะต้องเริ่มใหม่ คำสั่ง VACUUM INTO จะทำการถ่ายภาพสถานะ (snapshot) ดูความคิดเห็นเหล่านี้ใน GitHub และ Hacker News เพื่อข้อมูลเชิงลึกเพิ่มเติม

นอกจากนี้เราใช้ VACUUM INTO แทน backup เพราะคำสั่ง backup จะทำให้ฐานข้อมูลไม่ถูกเข้ารหัสชั่วคราวจนกว่าจะเรียกใช้ rekey (ดูความคิดเห็นใน GitHub นี้ comment เพื่อข้อมูลเชิงลึก)

Secondary จะสั่งให้ Primary ผ่านการเชื่อมต่อ WebSocket ให้ดำเนินการสำรองข้อมูล – และ Primary จะได้รับคำสั่งนั้นและดำเนินการดังนี้:

  1. เชื่อมต่อกับกล่องจดหมายที่เข้ารหัสของคุณ
  2. ได้รับล็อกสำหรับการเขียน
  3. รันจุดตรวจสอบ WAL ผ่าน wal_checkpoint(PASSIVE)
  4. รันคำสั่ง SQLite VACUUM INTO
  5. ตรวจสอบว่าไฟล์ที่คัดลอกสามารถเปิดได้ด้วยรหัสผ่านที่เข้ารหัส (เพื่อความปลอดภัย/ป้องกันข้อผิดพลาด)
  6. อัปโหลดไปยัง Cloudflare R2 เพื่อเก็บรักษา (หรือผู้ให้บริการของคุณเองหากระบุไว้)

โปรดจำไว้ว่ากล่องจดหมายของคุณถูกเข้ารหัส – และแม้ว่าเราจะมีข้อจำกัด IP และมาตรการการตรวจสอบสิทธิ์อื่น ๆ สำหรับการสื่อสาร WebSocket – ในกรณีที่มีผู้ประสงค์ร้าย คุณมั่นใจได้ว่า เว้นแต่ payload ของ WebSocket จะมีรหัสผ่าน IMAP ของคุณ มันจะไม่สามารถเปิดฐานข้อมูลของคุณได้

ในขณะนี้จะเก็บสำรองเพียงชุดเดียวต่อกล่องจดหมายเท่านั้น แต่ในอนาคตเราอาจเสนอการกู้คืนจุดเวลา ("PITR")

เซิร์ฟเวอร์ IMAP ของเรารองรับคำสั่ง SEARCH พร้อมกับการค้นหาที่ซับซ้อน, นิพจน์ปกติ และอื่น ๆ

ประสิทธิภาพการค้นหาที่รวดเร็วเป็นผลมาจาก FTS5 และ sqlite-regex

เราจัดเก็บค่า Date ในกล่องจดหมาย SQLite เป็นสตริง ISO 8601 ผ่าน Date.prototype.toISOString (โดยใช้โซนเวลา UTC เพื่อให้การเปรียบเทียบความเท่าเทียมทำงานได้อย่างถูกต้อง)

ดัชนียังถูกจัดเก็บสำหรับคุณสมบัติทั้งหมดที่อยู่ในคำค้นหา

โครงการ

นี่คือตารางแสดงโครงการที่เราใช้ในซอร์สโค้ดและกระบวนการพัฒนา (เรียงตามลำดับตัวอักษร):

โครงการ จุดประสงค์
Ansible แพลตฟอร์มอัตโนมัติ DevOps สำหรับการดูแลรักษา, ขยาย และจัดการเซิร์ฟเวอร์ทั้งหมดของเราได้อย่างง่ายดาย
Bree ตัวจัดตารางงานสำหรับ Node.js และ JavaScript พร้อมการรองรับ cron, วันที่, ms, later และใช้งานง่ายสำหรับมนุษย์
Cabin ไลบรารีล็อก JavaScript และ Node.js ที่เป็นมิตรกับนักพัฒนาด้วยความปลอดภัยและความเป็นส่วนตัวในใจ
Lad เฟรมเวิร์ก Node.js ที่ขับเคลื่อนสถาปัตยกรรมและการออกแบบวิศวกรรมทั้งหมดของเราด้วย MVC และอื่น ๆ
MongoDB โซลูชันฐานข้อมูล NoSQL ที่เราใช้สำหรับเก็บข้อมูลอื่น ๆ นอกเหนือจากกล่องจดหมาย (เช่น บัญชีของคุณ, การตั้งค่า, โดเมน และการกำหนดค่าอีเมลแฝง)
Mongoose การสร้างแบบจำลองเอกสารวัตถุ MongoDB ("ODM") ที่เราใช้ทั่วทั้งสแตก เราเขียนตัวช่วยพิเศษที่ช่วยให้เราสามารถใช้ Mongoose กับ SQLite ต่อไปได้ 🎉
Node.js Node.js คือสภาพแวดล้อมรันไทม์ JavaScript แบบโอเพนซอร์สข้ามแพลตฟอร์มที่รันกระบวนการเซิร์ฟเวอร์ทั้งหมดของเรา
Nodemailer แพ็กเกจ Node.js สำหรับส่งอีเมล, สร้างการเชื่อมต่อ และอื่น ๆ เราเป็นผู้สนับสนุนอย่างเป็นทางการของโครงการนี้
Redis ฐานข้อมูลในหน่วยความจำสำหรับแคช, ช่องทางเผยแพร่/สมัครรับ และคำขอ DNS ผ่าน HTTPS
SQLite3MultipleCiphers ส่วนขยายการเข้ารหัสสำหรับ SQLite เพื่อให้ไฟล์ฐานข้อมูลทั้งหมดถูกเข้ารหัส (รวมถึง write-ahead-log ("WAL"), journal, rollback, …)
SQLiteStudio ตัวแก้ไข SQLite แบบกราฟิก (ซึ่งคุณก็สามารถใช้ได้) สำหรับทดสอบ, ดาวน์โหลด และดูการพัฒนากล่องจดหมาย
SQLite ชั้นฐานข้อมูลฝังตัวสำหรับการจัดเก็บ IMAP ที่ปรับขนาดได้, เป็นอิสระ, รวดเร็ว และทนทาน
Spam Scanner เครื่องมือป้องกันสแปม, กรองอีเมล และป้องกันฟิชชิ่งบน Node.js (ทางเลือกของเราสำหรับ Spam Assassin และ rspamd)
Tangerine คำขอ DNS ผ่าน HTTPS ด้วย Node.js และแคชโดยใช้ Redis – ซึ่งช่วยให้ความสอดคล้องทั่วโลกและอื่น ๆ อีกมากมาย
Thunderbird ทีมพัฒนาของเราใช้ (และแนะนำ) โปรแกรมนี้เป็น ไคลเอนต์อีเมลที่แนะนำให้ใช้กับ Forward Email
UTM ทีมพัฒนาของเราใช้สร้างเครื่องเสมือนสำหรับ iOS และ macOS เพื่อทดสอบไคลเอนต์อีเมลต่าง ๆ (พร้อมกัน) กับเซิร์ฟเวอร์ IMAP และ SMTP ของเรา
Ubuntu ระบบปฏิบัติการเซิร์ฟเวอร์ Linux แบบโอเพนซอร์สสมัยใหม่ที่ขับเคลื่อนโครงสร้างพื้นฐานทั้งหมดของเรา
WildDuck ไลบรารีเซิร์ฟเวอร์ IMAP – ดูบันทึกเกี่ยวกับ การลบซ้ำไฟล์แนบ และ การรองรับโปรโตคอล IMAP
better-sqlite3-multiple-ciphers ไลบรารี API ที่รวดเร็วและเรียบง่ายสำหรับ Node.js เพื่อโต้ตอบกับ SQLite3 โดยโปรแกรม
email-templates เฟรมเวิร์กอีเมลที่เป็นมิตรกับนักพัฒนาเพื่อสร้าง, แสดงตัวอย่าง และส่งอีเมลที่กำหนดเอง (เช่น การแจ้งเตือนบัญชีและอื่น ๆ)
json-sql-enhanced ตัวสร้างคำสั่ง SQL โดยใช้ไวยากรณ์สไตล์ Mongo ช่วยประหยัดเวลาทีมพัฒนาด้วยการเขียนแบบ Mongo ทั่วทั้งสแตกด้วยแนวทางที่ไม่ขึ้นกับฐานข้อมูล ยังช่วยป้องกันการโจมตี SQL injection โดยใช้พารามิเตอร์คำสั่ง
knex-schema-inspector เครื่องมือ SQL สำหรับดึงข้อมูลเกี่ยวกับโครงสร้างฐานข้อมูลที่มีอยู่ ช่วยให้เราตรวจสอบได้ง่ายว่าดัชนี, ตาราง, คอลัมน์, ข้อจำกัด และอื่น ๆ ถูกต้องและตรงกับที่ควรจะเป็น 1:1 เรายังเขียนตัวช่วยอัตโนมัติสำหรับเพิ่มคอลัมน์และดัชนีใหม่หากมีการเปลี่ยนแปลงโครงสร้างฐานข้อมูล (พร้อมแจ้งเตือนข้อผิดพลาดอย่างละเอียด)
knex ตัวสร้างคำสั่ง SQL ที่เราใช้เฉพาะสำหรับการย้ายฐานข้อมูลและตรวจสอบโครงสร้างผ่าน knex-schema-inspector
mandarin การแปลวลี i18n อัตโนมัติพร้อมรองรับ Markdown โดยใช้ Google Cloud Translation API
mx-connect แพ็กเกจ Node.js สำหรับแก้ไขและสร้างการเชื่อมต่อกับเซิร์ฟเวอร์ MX และจัดการข้อผิดพลาด
pm2 ตัวจัดการกระบวนการผลิต Node.js พร้อมโหลดบาลานเซอร์ในตัว (ปรับแต่ง เพื่อประสิทธิภาพ)
smtp-server ไลบรารีเซิร์ฟเวอร์ SMTP – เราใช้สำหรับเซิร์ฟเวอร์แลกเปลี่ยนอีเมล ("MX") และเซิร์ฟเวอร์ SMTP ขาออก
ImapTest เครื่องมือที่มีประโยชน์สำหรับทดสอบเซิร์ฟเวอร์ IMAP กับเกณฑ์มาตรฐานและความเข้ากันได้กับโปรโตคอล IMAP ตาม RFC โครงการนี้สร้างโดยทีม Dovecot (เซิร์ฟเวอร์ IMAP และ POP3 แบบโอเพนซอร์สที่เปิดตัวในเดือนกรกฎาคม 2002) เราได้ทดสอบเซิร์ฟเวอร์ IMAP ของเราอย่างละเอียดด้วยเครื่องมือนี้

คุณสามารถค้นหาโปรเจกต์อื่น ๆ ที่เราใช้ได้ใน ซอร์สโค้ดของเราบน GitHub

ผู้ให้บริการ

ผู้ให้บริการ จุดประสงค์
Cloudflare ผู้ให้บริการ DNS, การตรวจสอบสุขภาพ, ตัวโหลดบาลานเซอร์ และการจัดเก็บข้อมูลสำรองโดยใช้ Cloudflare R2
GitHub โฮสต์ซอร์สโค้ด, CI/CD และการจัดการโปรเจกต์
Digital Ocean โฮสต์เซิร์ฟเวอร์เฉพาะและฐานข้อมูลที่มีการจัดการ
Vultr โฮสต์เซิร์ฟเวอร์เฉพาะ
DataPacket โฮสต์เซิร์ฟเวอร์เฉพาะ

ความคิด

หลักการ

Forward Email ถูกออกแบบตามหลักการเหล่านี้:

  1. เป็นมิตรกับนักพัฒนาเสมอ มุ่งเน้นความปลอดภัยและความเป็นส่วนตัว และโปร่งใส
  2. ปฏิบัติตาม MVC, Unix, KISS, DRY, YAGNI, Twelve Factor, Occam's razor, และ dogfooding
  3. มุ่งเป้าไปที่นักพัฒนาที่มีความขยันขันแข็ง เริ่มต้นด้วยงบประมาณจำกัด และ ทำกำไรจากราเมน

การทดลอง

สรุป; ในที่สุดการใช้ที่เก็บข้อมูลแบบวัตถุที่เข้ากันได้กับ S3 และ/หรือ Virtual Tables ไม่สามารถทำได้ทางเทคนิคเนื่องจากเหตุผลด้านประสิทธิภาพและมีแนวโน้มเกิดข้อผิดพลาดเนื่องจากข้อจำกัดของหน่วยความจำ

เราได้ทำการทดลองหลายอย่างจนได้โซลูชัน SQLite สุดท้ายตามที่กล่าวไว้ข้างต้น

หนึ่งในนั้นคือการลองใช้ rclone และ SQLite ร่วมกับชั้นเก็บข้อมูลที่เข้ากันได้กับ S3

การทดลองนั้นทำให้เราเข้าใจและค้นพบกรณีขอบที่เกี่ยวข้องกับ rclone, SQLite และการใช้งาน VFS ดังนี้:

  • หากคุณเปิดใช้งานแฟล็ก --vfs-cache-mode writes กับ rclone การอ่านจะทำงานได้ดี แต่การเขียนจะถูกแคชไว้
    • หากคุณมีเซิร์ฟเวอร์ IMAP หลายตัวกระจายทั่วโลก แคชจะไม่ตรงกันระหว่างเซิร์ฟเวอร์เหล่านั้น เว้นแต่จะมีผู้เขียนเพียงคนเดียวและผู้ฟังหลายคน (เช่น วิธี pub/sub)
    • สิ่งนี้ซับซ้อนมาก และการเพิ่มความซับซ้อนเพิ่มเติมเช่นนี้จะทำให้เกิดจุดล้มเหลวเดียวมากขึ้น
    • ผู้ให้บริการเก็บข้อมูลที่เข้ากันได้กับ S3 ไม่รองรับการเปลี่ยนแปลงไฟล์บางส่วน – ซึ่งหมายความว่าการเปลี่ยนแปลงใด ๆ ของไฟล์ .sqlite จะทำให้ฐานข้อมูลถูกเปลี่ยนแปลงและอัปโหลดใหม่ทั้งหมด
    • มีโซลูชันอื่นเช่น rsync แต่ไม่ได้เน้นการรองรับ write-ahead-log ("WAL") – ดังนั้นเราจึงได้ทบทวน Litestream โชคดีที่การเข้ารหัสของเราเข้ารหัสไฟล์ WAL อยู่แล้ว ดังนั้นเราไม่จำเป็นต้องพึ่งพา Litestream สำหรับเรื่องนี้ อย่างไรก็ตาม เรายังไม่มั่นใจใน Litestream สำหรับการใช้งานในสภาพแวดล้อมจริงและมีบันทึกบางอย่างด้านล่าง
    • การใช้ตัวเลือก --vfs-cache-mode writes (วิธี เดียว ที่จะใช้ SQLite ผ่าน rclone สำหรับการเขียน) จะพยายามคัดลอกฐานข้อมูลทั้งหมดใหม่ในหน่วยความจำ – การจัดการกล่องจดหมายขนาด 10 GB ตัวเดียวถือว่าโอเค แต่การจัดการกล่องจดหมายหลายกล่องที่มีพื้นที่เก็บข้อมูลสูงมากจะทำให้เซิร์ฟเวอร์ IMAP ประสบปัญหาข้อจำกัดหน่วยความจำและเกิดข้อผิดพลาด ENOMEM, segmentation faults และข้อมูลเสียหาย
  • หากคุณพยายามใช้ SQLite Virtual Tables (เช่น การใช้ s3db) เพื่อให้ข้อมูลอยู่บนชั้นเก็บข้อมูลที่เข้ากันได้กับ S3 คุณจะพบปัญหาเพิ่มเติมหลายอย่าง:
    • การอ่านและเขียนจะช้ามากเนื่องจากต้องเรียกใช้ API ของ S3 ด้วย HTTP GET, PUT, HEAD และ POST
    • การทดสอบพัฒนาพบว่าการเกิน 500K-1M+ ระเบียนบนอินเทอร์เน็ตไฟเบอร์ยังถูกจำกัดด้วยอัตราการเขียนและอ่านไปยังผู้ให้บริการที่เข้ากันได้กับ S3 ตัวอย่างเช่น นักพัฒนาของเรารันลูป for เพื่อทำทั้งคำสั่ง SQL INSERT แบบเรียงลำดับและแบบเขียนข้อมูลจำนวนมาก ในทั้งสองกรณีประสิทธิภาพช้ามาก
    • ตารางเสมือน ไม่สามารถมีดัชนี, คำสั่ง ALTER TABLE และ ข้อจำกัดอื่น ๆ เพิ่มเติม – ซึ่งทำให้เกิดความล่าช้านานถึง 1-2 นาทีหรือมากกว่านั้นขึ้นอยู่กับปริมาณข้อมูล
    • วัตถุถูกเก็บโดยไม่เข้ารหัสและไม่มีการรองรับการเข้ารหัสในตัวที่พร้อมใช้งาน
  • เรายังได้สำรวจการใช้ sqlite-s3vfs ซึ่งมีแนวคิดและเทคนิคคล้ายกับข้อก่อนหน้า (ดังนั้นจึงมีปัญหาเหมือนกัน) ความเป็นไปได้คือการใช้ sqlite3 ที่สร้างขึ้นเองพร้อมการเข้ารหัสเช่น wxSQLite3 (ซึ่งเราใช้ในโซลูชันของเราข้างต้น) ผ่าน การแก้ไขไฟล์ setup
  • แนวทางที่เป็นไปได้อีกอย่างคือการใช้ ส่วนขยาย multiplex แต่มีข้อจำกัดที่ 32 GB และต้องการการสร้างและพัฒนาที่ซับซ้อน
  • คำสั่ง ALTER TABLE จำเป็นต้องใช้ (ดังนั้นจึงตัดการใช้ Virtual Tables ออกไปโดยสิ้นเชิง) เราต้องการคำสั่ง ALTER TABLE เพื่อให้ hook ของเรากับ knex-schema-inspector ทำงานอย่างถูกต้อง – ซึ่งช่วยให้มั่นใจว่าข้อมูลไม่เสียหายและแถวที่ดึงมาแปลงเป็นเอกสารที่ถูกต้องตามคำจำกัดความ schema ของ mongoose ของเรา (ซึ่งรวมถึงข้อจำกัด ประเภทตัวแปร และการตรวจสอบข้อมูลแบบกำหนดเอง)
  • เกือบทุกโปรเจกต์ที่เกี่ยวข้องกับ SQLite และเข้ากันได้กับ S3 ในชุมชนโอเพนซอร์สเป็นภาษา 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') และไฟล์จะถูกลบอย่างถาวรจากที่เก็บข้อมูลบนดิสก์
      • เราสามารถใช้ API ที่เข้ากันได้กับ S3 พร้อม HTTP DELETE เพื่อลบ snapshot และข้อมูลสำรองสำหรับผู้ใช้งานได้อย่างง่ายดาย
    • SQLite เป็นโซลูชันที่ง่ายที่สุด เร็วที่สุด และคุ้มค่าที่สุด

ขาดทางเลือก

เท่าที่เราทราบ ไม่มีบริการอีเมลอื่นใดที่ออกแบบในลักษณะนี้หรือเป็นโอเพนซอร์ส

เราคิดว่า อาจเป็นเพราะ บริการอีเมลที่มีอยู่ใช้เทคโนโลยีเก่าในระบบจริงพร้อมกับ โค้ดสปาเก็ตตี้ 🍝

ผู้ให้บริการอีเมลส่วนใหญ่ถ้าไม่ทั้งหมดจะเป็นซอร์สปิดหรือโฆษณาว่าเป็นโอเพนซอร์ส แต่ในความเป็นจริงมีเพียงส่วนหน้าของพวกเขาเท่านั้นที่เป็นโอเพนซอร์ส

ส่วนที่ละเอียดอ่อนที่สุดของอีเมล (การจัดเก็บจริง/IMAP/SMTP) ทั้งหมดทำงานบนฝั่งหลังบ้าน (เซิร์ฟเวอร์) และ ไม่ใช่ บนฝั่งหน้า (ไคลเอนต์)

ลองใช้ Forward Email

สมัครวันนี้ได้ที่ https://forwardemail.net! 🚀