ما بعد EAS: هندسة محرك تحديثات OTA مخصص ومستضاف ذاتيًا لتطبيقات Expo للمؤسسات
غوص عميق في تجاوز EAS Update لتصميم وهندسة بنية تحتية خاصة، آمنة ومستضافة ذاتيًا لتحديثات Over-The-Air (OTA). تعرّف على كيفية تلبية إرشادات الامتثال الصارمة، وقيود الشبكات المعزولة (air-gapped)، ومتطلبات الأمان العالية.

لا تفهمني بشكل خاطئ: حزمة خدمات Expo Application Services (EAS) هي مجموعة أدوات استثنائية. بالنسبة لـ 90% من الفرق، تعتبر EAS Update المعيار الذهبي لإرسال الإصلاحات العاجلة (hotfixes) والميزات الصغيرة دون انتظار مراجعات App Store أو Google Play.
ولكن عندما تعمل في بيئات خاضعة لقوانين تنظيمية صارمة—مثل الدفاع، أو الخدمات المصرفية، أو الرعاية الصحية—تتغير القصة تمامًا.
خلال مسيرتي في تصميم البنية التحتية لتطبيقات الجوال للمؤسسات الكبرى، اصطدمت مرارًا بجدار الامتثال الصارم. فقوانين سيادة البيانات (data residency)، ومتطلبات البيئات المعزولة تمامًا عن الإنترنت (air-gapped environments) حيث تعمل الأجهزة على شبكات داخلية خاصة ومغلقة، وحدود شهادة SOC2 Type II، تفرض جميعها غالبًا عدم السماح بمرور أو تخزين أي ملكية فكرية (مثل حزم JS bundles المجمّعة) عبر بنية تحتية سحابية تابعة لجهات خارجية.
إذا وجدت نفسك في هذا الموقف، فلا داعي للتخلي عن Expo. بدلاً من ذلك، يمكنك بناء الحل الخاص بك.
في هذه المقالة، سأشاركك كيف قمنا بهندسة نظام تحديثات Over-The-Air (OTA) آمن، ذي كفاءة عالية ومستضاف ذاتيًا، مصمم خصيصًا لتطبيقات Expo للمؤسسات، مع تجاوز EAS بالكامل.
كشف أسرار بروتوكول تحديثات Expo (الإصدار v1)
لبناء بديل مباشر لـ EAS Update، يجب أولاً فهم بروتوكول الاتصال بين عميل التطبيق الأصلي (native client) والمتمثل في expo-updates وخادم التحديثات.
يتوقع بيئة تشغيل Expo (runtime) مصافحة (handshake) محددة. عند بدء تشغيل تطبيقك، يقوم موديول expo-updates الأصلي باعتراض تسلسل التشغيل وإرسال طلب إلى نقطة النهاية (endpoint) المكونة في ملف app.json تحت المفتاح updates.url.
ترويسات الطلب (Request Headers)
ستتلقى نقطة النهاية المستضافة ذاتيًا طلب GET يحتوي على عدة ترويسات (headers) حيوية:
expo-protocol-version: حاليًا1. يحدد هذا التنسيق المنظم للترويسة واستجابة الـ multipart.expo-runtime-version: إصدار بيئة التشغيل الأصلي (على سبيل المثال،1.0.0أو بصمة SDK). يضمن هذا عدم إرسال حزمة (bundle) تعتمد على موديولات أصلية لم يتم تجميعها في التطبيق الحالي (binary).expo-platform:iosأوandroid.expo-expect-signature: يطلب توقيعًا تشفيرياً للتحقق من سلامة وصحة بيان التحديث (manifest).
بنية الاستجابة (Response Structure)
يجب أن يستجيب خادمك باستجابة multipart mixed أو بنية JSON محددة للغاية في حال استخدام Protocol v1. تقدم الاستجابة ما يُعرف بالـ Manifest (بيان التحديث)—وهو بمثابة مخطط يحتوي على البيانات الوصفية (metadata) حول التحديث ويشير إلى موقع الملفات والموارد (JS bundles، الصور، والخطوط).
إليك نظرة معمارية مبسطة على خط أنابيب النشر المخصص (deployment pipeline):
+-------------------------+ 1. npx expo export +------------------------+
| CI/CD Build Runner | ---------------------------> | Private S3/MinIO |
| (Custom Signing & Prep) | | (Bundles & static assets) |
+-------------------------+
| ^
| 2. Register Metadata |
v | 3. Download Assets
+-------------------------+ 4. Fetch Signed Manifest +------------------------+
| Custom Update API | <----------------------------------- | Client App (iOS/And) |
| (Node/NestJS + Postgres)| | `expo-updates` Native |
+-------------------------+ +------------------------+
طبقة الأمان الحاسمة: توقيع الكود (Code Signing)
في البنية التحتية المستضافة ذاتيًا، يمثل الأمان أولويتك القصوى. نظرًا لأن تحديثات OTA تقوم بتشغيل JavaScript ديناميكي مباشرة على أجهزة المستخدمين، فإن اختراق خادم التحديثات يشكل تهديدًا وجوديًا.
لمنع هجمات رجل في المنتصف (MITM) وحقن الحزم الخبيثة، فإننا نفرض توقيعًا تشفيريًا صارمًا للكود. تدعم مكتبة عميل Expo الأصلية بشكل افتراضي التحقق من هذه التواقيع باستخدام زوج من المفاتيح العامة والخاصة (public/private key pair).
الخطوة 1: توليد زوج المفاتيح
أولاً، قم بتوليد زوج مفاتيح RSA 2048-bit على خادم البناء الآمن الخاص بك (build runner) أو جهازك المحلي:
قم بتحويل مفتاحك العام إلى سلسلة base64. سيتم تضمين هذا المفتاح العام مباشرة في ملف التطبيق (binary) عبر ملف app.json:
هندسة خادم التحديث (NodeJS + Fastify)
لنقم ببناء خادم تحديث مخصص، خفيف الوزن، وجاهز للإنتاج باستخدام TypeScript و Fastify. سنقوم بمعالجة الطلبات الواردة، والتحقق من قيود بيئة التشغيل، وبناء بيان التحديث (manifest)، وتوقيع الاستجابة تشفيريًا.
متحكم البيانات الوصفية (Manifest Controller)
خط أنابيب التطوير والنشر الآلي (Automated CI/CD Pipeline)
الآن بعد أن أصبح خادمنا جاهزًا للاستماع للطلبات، كيف نقوم بتجميع التحديثات وشحنها بأمان دون الحاجة لتدخل يدوي من المطورين؟
بدلاً من استدعاء eas update، سنلجأ إلى واجهة سطر أوامر Expo CLI لإنشاء تصدير ثابت (static export) لحزم JS bundles والموارد (assets).
إليك نص برمي (bash script) يتعامل مع التجميع، وحساب قيم SHA-256 للموارد، وإعداد الملفات لرفعها إلى وحدة تخزين الكائنات الداخلية لدينا (MinIO أو AWS S3):
ندوب المعارك: دروس مستفادة من بيئات الإنتاج الفعلية
عندما قمنا بنشر هذه البنية التحتية المخصصة عبر آلاف الأجهزة اللوحية للمؤسسات التي تعمل داخل شبكات خاصة، واجهنا بعض العقبات غير المتوقعة. إليك النصائح التشغيلية التي تمنيت لو كنت أعرفها في اليوم الأول:
1. احذر من اختلافات لغة الآلة لـ Hermes (Hermes Bytecode)
إذا قمت بتمكين Hermes (وهو ما أنصحك به بشدة)، فسيتم تجميع كود JS مسبقًا إلى bytecode. تأكد من أن إصدار Hermes المستخدم في خط أنابيب CI يتطابق تمامًا وبدقة مع الإصدار الأصلي المجمع داخل ملفات الـ IPA/APK المستهدفة. أي اختلاف بسيط في إصدار المجمع (compiler) قد يؤدي إلى انهيار تطبيقك فورًا عند التشغيل أثناء تحميل الحزمة الديناميكية.
2. تطبيق النشر التدريجي (Canary Rollouts)
يمكن لتحديث OTA أن يسير بشكل كارثي إذا تسلل خطأ برمي لم يُكتشف. لا تقم بتقديم بيان التحديث الجديد لـ 100% من أجهزتك على الفور. قم بتهيئة قاعدة البيانات الداخلية الخاصة بك لدعم "التحديثات المستهدفة" بناءً على معرف العميل أو مجموعات الأجهزة. لقد قمنا ببناء متحكم /api/manifest لفحص ترويسات القياس عن بُعد المخصصة مثل x-device-id لنشر التحديثات على مراحل:
- المرحلة 1: أجهزة اختبار الجودة QA/Alpha (الاختبار الداخلي)
- المرحلة 2: 5% من مستخدمي بيئة الإنتاج فعليًا
- المرحلة 3: 100% من مستخدمي بيئة الإنتاج فعليًا
3. التراجع السلس والآمن (Graceful Rollbacks)
إذا كان هناك خلل في التحديث، فإن حذف سجل من قاعدة البيانات على خادمك المستضاف ذاتيًا يجب أن يتراجع تلقائيًا ببيان التحديث النشط إلى الإصدار المستقر السابق. تأكد من أن تطبيقك الأصلي يتعامل مع الانهيارات غير المتوقعة في جهة العميل بمرونة وسلاسة. إذا انهار التطبيق أكثر من 3 مرات عند بدء التشغيل، فاستخدم واجهة برمجية expo-updates API للتراجع عن الحزم المحلية ديناميكيًا:
الخاتمة
إن بناء بنية تحتية خاصة بك لتحديثات Expo OTA ليس بالأمر السهل. فهو يتطلب التعامل مع سلاسل التشفير المخصصة، وإعداد شبكات توصيل محتوى (CDNs) قابلة للتوسع للموارد الثابتة، وصيانة طبقة قاعدة بيانات قوية.
ومع ذلك، فإن العوائد هائلة. بالنسبة لعملائنا من المؤسسات الكبرى، فقد ساهم هذا الحل في تجاوز عقبات الامتثال تمامًا، ووفر مئات الآلاف من تكاليف نقل البيانات (bandwidth) الخاصة بالجهات الخارجية، وجلب كامل صلاحيات التنفيذ مباشرة إلى الحدود السحابية الآمنة للمؤسسة حيث تنتمي.
إذا كنت تعمل على نطاق واسع وتخضع لمتطلبات امتثال صارمة، فإن تملك مسار تقديم تحديثات OTA الخاصة بك ليس مجرد تحسين—بل هو تطور معماري لا غنى عنه.