يُباع Institflow بالاستضافة الذاتية حصرًا — قاعدة شيفرة واحدة، وعقد واجهة واحد، يعملان بالكامل على بنية تحتية تتحكّم بها أنت.

## النشر

حزمة Docker أحادية المستأجر (`api` و`worker` و`web` و`caddy`، إضافة إلى Postgres وRedis وGarage) تُشغّلها على خادمك الخاص، مقيّدة بترخيص موقّع صادر من منصّة مزوّدك. لا يوجد بديل مستضاف من المزوّد متعدد المستأجرين لتختاره بدلًا من ذلك.

## تثبيت النسخة الذاتية

المتطلّبات المسبقة: خادم جديد يعمل بنظام Ubuntu 22.04 أو Ubuntu 24.04 أو Ubuntu 26.04 أو Debian 12، مع منفذين 80/443 خاليين وقابلين للوصول، ورمز تثبيت (install token) من لوحة مزوّدك (Customer ← Install ← Generate install command — يُعرض مرّة واحدة).

> **صورة خادم يعمل عليها Apache/nginx مسبقًا على المنفذ 80**
>
> بعض صور خوادم VPS أو حزم لوحات التحكّم تُشغِّل Apache أو nginx على المنفذ 80 افتراضيًا — هذا المثبّت يحتاج المنفذين 80/443 خاليين من أجل Caddy. إن وجد أحدهما مشغولًا، يسمّي الخدمة بالضبط ويعرض إيقافها وتعطيلها لك (`y`/`N`، والافتراضي هو الرفض)؛ أمّا التشغيل غير التفاعلي/الآلي فلا يوقف شيئًا أبدًا من تلقاء نفسه، ويطبع بدلًا من ذلك الأمر الذي يجب تمريره، `--stop-conflicting-services`. إن كنت تفضّل ألا تدع المثبّت يلمسها، أجب بالرفض (أو تجاهل الخيار) وأوقف الخدمة المتعارضة بنفسك أولًا، أو استخدم خادمًا نظيفًا.

## أمر واحد

```bash
curl -fsSLo institflow-install.sh https://license.institflow.com/api/v1/install/script
sudo bash institflow-install.sh
```

يطلب منك السكربت رمز التثبيت (إدخال مخفي، فلا يبقى أثره في سجلّ الأوامر — يوجد أيضًا `--token` للأتمتة، وهو متعمَّد غيابه عن الأمر أعلاه)، ثم: يثبّت Docker من مستودع Docker الرسمي إن لم يكن مثبّتًا، يطالب بالرمز (claim) لدى منصّة التحكّم، يولّد كل الأسرار الأخرى على هذا الخادم نفسه (لا شيء سرّي ينتقل من المزوّد إليك أبدًا سوى الترخيص وبيانات اعتماد السجلّ، تمامًا كما كان الحال من قبل)، يكتب ملفّات `docker-compose.yml` و`.env` و`Caddyfile`، يشغّل الحزمة، ينتظر حتى تصبح كل خدمة سليمة (healthy) وحتى يُحلَّل عنوانك، ثم يطبع عنوان الإعداد ورمز `SETUP_TOKEN` لمرّة واحدة ومفتاح النسخ الاحتياطي الخاص لمرّة واحدة. يرفض العمل إلا بصلاحية الجذر (root)، وعلى غير الأنظمة الأربعة أعلاه، ولا يمرّر الشبكة مباشرة إلى صدفة أوامر أبدًا — أنت تنزّل السكربت أولًا ثم تشغّله، فيمكنك قراءته قبل ذلك.

يحصل خادمك تلقائيًا على عنوان خاص به تحت نطاق المزوّد `institflow.app` (بالصيغة `<slug-معهدك>.institflow.app`، مع سجلّ DNS وشهادته مُدارَين لك). مرّر `--domain your-own-domain.org` بدلًا من ذلك لاستخدام نطاق تملكه أنت — وجّه سجلّ DNS (**A**/**AAAA**) الخاص به إلى هذا الخادم أوّلًا؛ يتحقّق المثبّت من ذلك ويرفض المتابعة إن لم يكن كذلك.

إعادة تشغيل السكربت على خادم يحتوي أصلًا على `/opt/institflow/.env` تُرفَض ما لم تضِف `--reinstall`، التي لا تلمس وحدات تخزين بياناتك أبدًا وتُبقي كل قيمة موجودة أصلًا في `.env`.

> **سرّان يُطبَعان مرّة واحدة — احفظهما بالطريقة الصحيحة**
>
> يُطبَع كل من `SETUP_TOKEN` ومفتاح النسخ الاحتياطي الخاص على طرفيتك مرّة واحدة فقط، في نهاية هذا التشغيل. يُكتَب مفتاح النسخ الاحتياطي الخاص أيضًا في `/opt/institflow/BACKUP-KEY-COPY-ME-THEN-DELETE.txt` (بصلاحية 0600) — انسخه إلى خارج هذا الخادم ثم احذف ذلك الملفّ. أمّا `SETUP_TOKEN` فمحفوظ أيضًا في `/opt/institflow/.env` (بصلاحية 0600)، فلا تفقده إن أغلقت الطرفية قبل قراءته. لا يُكتَب أيٌّ من السرّين أبدًا في `/opt/institflow/install.log`.

التحديثات تصل من تلقاء نفسها بعد ذلك — انظر "التحديث" أدناه؛ لا شيء آخر تشغّله يدويًا.

## تسجيل الدخول الأوّل

افتح عنوان الإعداد الذي طبعه المثبّت، الصق `SETUP_TOKEN`، وأنشئ معهدك وحسابك كمالك من هناك. تتوقّف تلك الصفحة عن الوجود بعدها: لا يمكن إعادة استخدام الرمز لإنشاء معهد ثانٍ.

## التثبيت اليدوي (ملحق)

الأمر الواحد أعلاه هو المسار المدعوم. إن كان نموذج التهديد لديك يتطلّب تنفيذ كل خطوة يدويًا بدلًا من ذلك — دون تنزيل سكربت وتشغيله بصلاحية الجذر — فهذا هو التثبيت نفسه خطوة بخطوة:

1.

**احصل على ترخيص.** يُنشئ مزوّدك مستأجرك على منصّته الخاصة ويصدر ترخيصًا له — تحصل على `LICENSE_KEY` ويُعرض مرّة واحدة. لا يوجد مفتاح تحقّق تُدخله: المفتاح الذي تتحقّق به نسختك من هذا الترخيص مضمَّن داخل الصور المنشورة، فلا يمكن لأحد — بما في ذلك أنت — استبداله.

2.

**املأ متغيّرات البيئة.** انسخ `docker/self-hosted.env.example` إلى `docker/.env` واملأ كل قيمة — تُولَّد الأسرار بـ `openssl rand -base64 32`، وزوج مفاتيح JWT بسكربت `keys:generate` يُشغَّل مرّة واحدة.

3.

**شغّل الحزمة.**

```bash
docker compose -f docker/compose.self-hosted.yml --env-file docker/.env up -d
docker compose -f docker/compose.self-hosted.yml --env-file docker/.env ps
```

`api-init` مُشغِّل ترحيل لمرّة واحدة، ومن المتوقَّع أن يخرج بالرمز `0`؛ بقيّة الخدمات تبقى قيد التشغيل.

4.

**تسجيل الدخول الأوّل** — نفس صفحة الإعداد أعلاه.

التحديث هو رفع رقم الإصدار مع `docker compose pull && up -d` — `api-init` يُعاد تشغيله (بشكل غير تراكمي) عند كل بدء ويمنع الواجهة من خدمة الطلبات حتى تكتمل عمليات الترحيل.

## حجم الخادم

تقيس الواجهة الخلفية (API) وحدة المعالجة والذاكرة اللتين يمكنها استخدامهما فعليًا — حدّ الحاوية نفسها، لا كامل عتاد المضيف — وتقارن ذلك بما تتطلّبه أعداد الطلاب والموظفين النشطين في معهدك. هذا تحذير فقط، لا يحجب أي شيء أبدًا: يستمر التثبيت والتشغيل وكل ميزة بالعمل بصرف النظر عن حجم الخادم الذي تعمل عليه. عندما يكون خادمك أقل من الحجم المُوصى به، يظهر شريط تنبيه في صفحة الإعدادات ← الترخيص في التطبيق للمالك أو المدير، ويطبع `api-init` الفحص نفسه في سجلّه عند كل بدء تشغيل.

كل صفّ أدناه مُقاس فعليًا، لا مُقدَّر: شُغِّل مسار الكتابة (تسجيل حضور صفّ وتسجيل دفعة مالية — العمليتان اللتان يتنافس عليهما مكتب الاستقبال المزدحم فعليًا) على خادم عميل محاكى مبني من صور هذا المشروع نفسها، بأعداد متزايدة من الموظفين الذين يكتبون في اللحظة نفسها، حتى بقي زمن استجابة p95 لكل حجم دون 800 مللي ثانية (`docs/reports/sizing-2026-09-22.md`، البند K-32).

| حجم المعهد (الطلاب النشطون) | المُوصى به | عدد الموظفين المتزامنين الذين قِيس عندهم |
| --- | --- | --- |
| صغير — حتى 300 | 2 نواة معالجة افتراضية / 4 غيغابايت | 10، p95 بين 288 و462 مللي ثانية عبر 3 تشغيلات |
| متوسط — حتى 1,000 | 2 نواة معالجة افتراضية / 8 غيغابايت | 10، p95 بين 414 و636 مللي ثانية عبر 3 تشغيلات |
| كبير — حتى 3,000 | 4 نواة معالجة افتراضية / 8 غيغابايت | 20، p95 بين 456 و744 مللي ثانية عبر 3 تشغيلات |
| أكثر من 3,000 | 4 نواة معالجة افتراضية / 8 غيغابايت (حدّ أدنى، لا سقف مُثبَت) | لم يُقَس أبعد من الصفّ أعلاه |

> **الذاكرة الإضافية وحدها لم تُغيّر رقم مسار الكتابة**
>
> تساوى القياس بين 2 نواة/4 غيغابايت و2 نواة/8 غيغابايت عند 10 كاتبين متزامنين — عدد الأنوية هو ما حدّد بقاء الحجم دون 800 مللي ثانية، لا الذاكرة. رقم الذاكرة الأكبر للفئة المتوسطة مُوصى به لهامش أمان لم يقِسه هذا التقرير (جداول أكبر، نسخ احتياطية وتقارير متزامنة)، لا لأن مسار الكتابة نفسه احتاجه.

## تخزين الملفّات (Object storage)

الملفّات المرفوعة (مستندات الطلاب والموظفين، الفواتير، مواد الدورات) تُخزَّن في Garage — ثنائي (binary) واحد ثابت يعمل كخدمة مستقلّة، لا كتثبيت على مستوى المضيف أبدًا. لا ينشر أي منفذ على المضيف: يصل التطبيق إليه عبر شبكة compose فقط (واجهة S3 على المنفذ 3900، وواجهة إدارة على المنفذ 3903، كلتاهما داخليّتان)، وتُنشئ خدمة `object-store-init` أحادية التشغيل حاوية التخزين (bucket) ومفتاح تطبيق مقيّد النطاق عند كل إقلاع، بشكل غير تراكمي — لا شيء تشغّله يدويًا. هذا المفتاح يستطيع القراءة والكتابة داخل حاوية التخزين تلك وحدها ولا شيء غيرها: لا يستطيع إنشاء حاوية تخزين أو سردها أو حذفها.

يرفع متصفّحك الملفّات ويُنزّلها عبر نطاقك الخاص، لا عبر مضيف تخزين منفصل: `https://<نطاقك>/institflow-objects/…‎`، يمرّ عبر Caddy ومحدود هناك بسقف 20 ميغابايت. تُوقّع الواجهة الخلفية كل رابط لهذا العنوان نفسه.

يولّد التثبيت الجديد أربعة أسرار في `.env` — `GARAGE_RPC_SECRET` و`GARAGE_ADMIN_TOKEN` و`OBJECT_STORE_ACCESS_KEY_ID` و`OBJECT_STORE_SECRET_ACCESS_KEY` — ويكتب عناصر نائبة (placeholders) فارغة لـ `S3_BUCKET`/`S3_ACCESS_KEY_ID`/`S3_SECRET_ACCESS_KEY` لا يقرأها أبدًا إلا الترحيل لمرّة واحدة أدناه. لاستخدام مزوّد متوافق مع S3 خاص بك بدلًا من Garage المُرفَق، اضبط `OBJECT_STORE_ENDPOINT` و`OBJECT_STORE_PUBLIC_ENDPOINT` و`OBJECT_STORE_BUCKET` و`OBJECT_STORE_REGION`. يتطلّب هذا الإصدار Docker Compose بإصدار 2.23.1 أو أحدث — يتحقّق المثبّت من ذلك قبل فعل أي شيء آخر.

## الترقية من 1.5.x

التثبيتات على 1.5.x كانت تحتفظ بملفّاتها في MinIO مُرفَق. انقلها إلى Garage بالأمر:

```bash
curl -fsSLo institflow-install.sh https://license.institflow.com/api/v1/install/script
sudo bash institflow-install.sh --reinstall --adopt
```

هذا هو الأمر نفسه الذي يُنفَّذ مرة واحدة لنقل التثبيت إلى المحدِّث ذاتي الإصلاح (انظر [التحديثات والإصلاحات التلقائية](https://docs.institflow.com#automatic-updates-and-repairs) أدناه؛ `--refresh-bundle` اسمه القديم وما زال يعمل). على تثبيت 1.5.x يضيف أيضًا الأسرار الأربعة أعلاه إلى `.env` دون تغيير أي قيمة موجودة أصلًا، ويفعّل النسخ لمرة واحدة: ينسخ `api-init` كل ملفّ مرفوع من MinIO إلى Garage ويتحقّق من كل واحد — الحجم، والمجموع التحقّقي (checksum)، والصفّ المطابق في قاعدة البيانات — قبل السماح للواجهة الخلفية والعامل (worker) بالبدء. يبقى MinIO القديم لديك سليمًا وموثوقًا حتى تكتمل عملية النسخ والتحقّق بنجاح، ويبقى يعمل كخدمة `minio-legacy` كي تبقى إعادة نسخ الملفّات إليه ممكنة إن احتجت يومًا إلى التراجع.

يرفض هذا الأمر التغيير من الأساس إن لم تعد صورة MinIO موجودة على خادمك (توقّف المصدر الأصلي عن نشرها) — استعِدها من نسخة احتياطية لِـ `/var/lib/docker`، أو انسخها من خادم آخر بالأمر `docker save … | docker load`، ثم أعد التشغيل. إلى أن تنفّذه، يستمر تثبيت 1.6.0 على الحزمة القديمة بالعمل على MinIO تمامًا كما كان، ويرى المالك تنبيه "الحاجة إلى ترقية التخزين" في الإعدادات ← الترخيص ← التحديثات التلقائية.

## التراجع بعد الترقية

إن احتجت إلى العودة إلى إصدار قبل 1.6.0 بعد أن رفع مستخدموك ملفّات جديدة إلى Garage بالفعل، انسخها إلى الوراء أوّلًا:

```bash
cd /opt/institflow
sudo docker compose run --rm api-init node dist/scripts/storage-migrate.js --reverse
```

هذا ينسخ كل عنصر من Garage إلى MinIO ويتحقّق منه بالطريقة نفسها التي استُخدمت في الترقية، دون تغيير قاعدة بياناتك. عندئذٍ فقط أعِد `docker-compose.yml` و`Caddyfile` من `state/snapshots/*/‎` وتراجع. أبقِ ملفّ `.env` الحالي كما هو: ما أضافته الترقية إليه تتجاهله الحزمة القديمة، وأسرار التخزين الجديدة يجب أن تبقى كما هي لترقية لاحقة.

## التحديثات والإصلاحات التلقائية

ابتداءً من 1.7.0 يعمل على كل خادم **حارس** صغير على الخادم نفسه — سكربت يشغّله مؤقّت systemd (`institflow-guardian.timer`) كل دقيقتين — و**المحدِّث**، وهو حاوية يشغّلها الحارس بأمر `docker run`. يتأكّد الحارس أن المحدِّث موجود ويعمل، وأنه بالضبط الصورة التي وقّعها المزوّد لإصدارك، ويعيده إن لم يكن كذلك؛ ويعيد المحدِّثُ الحارسَ إن تلف.

يحمل كل إصدار وصفًا موقَّعًا للتثبيت السليم من ذلك الإصدار: أي ملفّات، بأي محتوى وصلاحيات، أي خدمات، أي مفاتيح في `.env`. لا يُصلح المحدِّث إلا ما يخالف هذا الوصف، ومن قائمة ثابتة من الأفعال الآمنة فقط — إعادة كتابة ملفّ للمزوّد مفقود، إعادة صلاحية، تشغيل خدمة متوقّفة، إضافة مفتاح ناقص في `.env` يمكن توليده بأمان، إزالة حاوية `updater` القديمة. لا يحذف أبدًا بيانات ولا وحدة تخزين ولا قاعدة بيانات ولا نسخة احتياطية؛ ولا يغيّر قيمة كتبتها في `.env` (يضيف الناقص فقط)؛ ولا يولّد كلمة مرور أو مفتاحًا يعني فقدانه حجب بياناتك (يبلّغ عنه بدل ذلك)؛ ولا يلمس الترخيص؛ ولا يكتب فوق ملفّ لا يستطيع أن يثبت أن المزوّد كتبه. الملفّ الذي غيّرته بيدك يُبلَّغ عنه ولا يُصلَح — وإن أردت نسخة المزوّد منه فنفّذ `sudo institflow-guardian accept-vendor-files` (تبقى نسختك في `state/quarantine/`). ضع إعدادات Caddy الخاصة بك في `caddy/custom.d/` وتعديلاتك على compose في `docker-compose.override.yml`؛ كلاهما لك ولا يُحكَم عليهما.

قبل أي إصلاح أو تحديث يأخذ المحدِّث نسخة احتياطية كاملة — قاعدة البيانات وملفّاتك وإعدادات الخادم نفسه — ويتحقّق منها؛ ومن دون نسخة متحقَّق منها لا يغيّر شيئًا. التحديث يشغّل محدِّث الإصدار الجديد بينما يبقى الحالي مسؤولًا: إن لم يعمل الإصدار الجديد سليمًا يُشغَّل السابق من جديد، ويبقى الخادم يخدم. قاعدة البيانات لا تُستعاد تلقائيًا أبدًا.

أوامر مفيدة، كمستخدم root: `institflow-guardian status` (ما الذي يعمل، والإصدار المثبَّت، وتحديث جارٍ)، `institflow-guardian plan` (ما الذي سيُصلحه المحدِّث الآن)، `institflow-guardian maintenance on|off` (لا يُشغَّل ولا يُعاد إنشاء ولا يُحدَّث شيء أثناء تفعيله؛ النسخ الاحتياطية تستمر)، `institflow-guardian check` (فحص الأعطال والإصدار الجديد خلال دقيقة). ويستطيع المالك جعل الإصلاح التلقائي **إبلاغًا فقط** من الإعدادات ← الترخيص ← التحديثات التلقائية.

## نقل تثبيت أُجري قبل 1.7.0

التثبيت الذي أُجري قبل 1.7.0 يعمل بالمحدِّث القديم، وهو لا يستطيع أن يستبدل نفسه ولا أن يثبّت الحارس. نفّذ هذا مرة واحدة، كمستخدم root، على الخادم — ولن يُطلب منك بعده أي شيء آخر أبدًا:

```bash
curl -fsSLo institflow-install.sh https://license.institflow.com/api/v1/install/script
sudo bash institflow-install.sh --reinstall --adopt
```

يتحقّق من أحدث إصدار موقَّع قبل أن يغيّر أي شيء، ويأخذ نسخة احتياطية كاملة، ويثبّت الحارس، ويزيل حاوية `updater` القديمة (تبقى وحدة بياناتها)، ثم يسلّم إلى المحدِّث الجديد، الذي ينقل التثبيت تحت نسخة احتياطية ثانية متحقَّق منها ويعيد كل شيء كما كان إن فشل أي شيء. التثبيت المرتَّب يدويًا في `/opt/institflow/docker` يُنقل أوّلًا إلى `/opt/institflow` بعد نسخته الاحتياطية الخاصة؛ وأي ترتيب آخر يُرفَض مع ذكر ما وُجد. وإلى أن يُنفَّذ، يبقى التثبيت يعمل على إصداره الحالي.

## مع من يتحدّث تثبيتك

ثلاثة عناوين خارجية فقط، ولا شيء غيرها — ولا واحد منها وكيل لحركة زوّارك، ولا شيء يمرّ طلبات مستخدميك النهائيين عبرنا:

1. **خادم الترخيص** (`license.institflow.com`) — نبضة قلب كل ساعة، وبيان التحديث الموقَّع الذي يستطلعه المحدِّث. انقطاع الوصول إليه لا يغيّر شيئًا من تلقاء نفسه؛ انظر دورة حياة الترخيص أدناه.
2. **خدمة ترحيل الرسائل (relay)** — العنوان نفسه لخادم الترخيص، لا عنوان منفصل. عند الإقلاع يولّد تثبيتك زوج مفاتيحه الخاص ويسجّل نفسه بمفتاح الترخيص `LICENSE_KEY`؛ من تلك اللحظة يمكنه أن يطلب من الخدمة إرسال بريدك النظامي (إعادة تعيين كلمة المرور، دعوات الموظفين/البوابة — مجانًا دائمًا، لا يوقفه نفاد الرصيد أبدًا) وكذلك، إن فعّلته، بريد معهدك المدفوع الخاص (الإعدادات ← الإشعارات ← المزوّدون). لا شيء لضبطه هنا — لا يوجد متغيّر بيئة خاص بخدمة الترحيل. أول خادم لترخيصك يُعتمَد فورًا؛ أمّا إعادة التثبيت على خادم آخر فتنتظر اعتماد Institflow، وكذلك الخادم الذي ثُبّت أول مرة بمثبِّت أقدم من 1.6.4 بعد أن يسجّل نفسه من جديد — تعرض الإعدادات ← الإشعارات ← المزوّدون هذه الحالة، ويعود البريد من تلقاء نفسه فور الاعتماد.
3. **سجلّ صور المزوّد** (`registry.institflow.com`) — للسحب فقط، لا يُلامَس إلا بأمر `docker login`/`docker compose pull` عند تثبيت أوّل أو تحديث، لا من النظام أثناء تشغيله أبدًا.

`SMTP_URL` في ملفّ `.env` مطلوب لتشغيل الواجهة الخلفية، لكن اعتبارًا من هذا الإصدار لا يُرسَل عبره أي شيء فعليًا — كل بريد نظامي وكل إشعار بريدي للمعهد يمرّ عبر خدمة الترحيل أعلاه، إلا إن أضفت أنت عمدًا بيانات اعتماد SMTP خاصة بك من داخل التطبيق (الإعدادات ← الإشعارات ← المزوّدون)، وهو مضيف البريد الخارجي الوحيد الذي تختاره أنت بنفسك.

## دورة حياة الترخيص

يضبط مزوّدك التراخيص يدويًا — لا فوترة تلقائية. عند انتهاء الفترة المدفوعة، يكتفي النظام بالتنبيه فقط؛ لا يحجب الوصول أبدًا من تلقاء نفسه.

1. **صالح (Valid)** — وظائف كاملة، لا شيء ظاهر للمستخدمين النهائيين.
2. **منتهٍ (Expired)** — لا تزال كل الوظائف تعمل، إلى أجل غير مسمى. كل استجابة تحمل ترويسة `X-Subscription-Warning: expired; since=<date>`، وتُظهر واجهة الويب شريطًا يظهر فقط للمالك أو المدير ويذكر التاريخ. لا شيء آخر يتغيّر — يحتفظ المعهد المنتهي بوصوله الكامل إلى أن يقفله أحدهم.
3. **مُقفَل (Locked)** — الحالة الوحيدة التي تحجب الوصول، وهي دائمًا نتيجة أمر قفل صريح يحمل سببًا صادرًا عن مزوّدك — الانتهاء وحده لا يُصدر هذا الأمر أبدًا. الطلبات المرتبطة بالمستأجر تُعيد `402 SUBSCRIPTION_SUSPENDED`، باستثناء المصادقة وفحوصات الصحّة وصفحة الفوترة نفسها. **لا تُحذَف البيانات أو تُعدَّل أبدًا** — فك القفل يستعيد الوصول الكامل فورًا، تمامًا من حيث توقّفت.

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

يستمر النظام ذاتي الاستضافة في العمل بلا حد زمني طالما تعذّر عليه الوصول إلى خادم الترخيص — فانقطاع الاتصال لا يغيّر شيئًا من تلقاء نفسه، مهما تكرّرت المحاولات الفاشلة المتتالية. يُبلغ النظام مزوّدك بإشارات العبث (تراجع الساعة، غياب نبضة القلب، وما شابه)، لكنها استشارية فقط: لا يُقفل أي شيء أبدًا تلقائيًا لمجرد إطلاق إشارة، بل فقط عبر الأمر الموقّع الصريح نفسه الذي يصدره إنسان. وإذا كان النظام مقفلاً وتعذّر الوصول إلى خادم الترخيص، يمكن لمزوّدك إصدار رمز فك قفل لمرة واحدة تلصقه في لوحة إدارة الاستضافة الذاتية لاستعادة الوصول دون اتصال.

## ملاحظات أمنية

- غيّر كل سرّ في ملفّ البيئة النموذجي قبل الانطلاق الفعلي — لا شيء من قيمه الفارغة آمن كافتراضي.
- شهادة TLS تلقائية (Caddy) طالما نطاقك قابل للتحليل والمنفذ 80 قابل للوصول لتحدّي ACME الأوّلي.
- المنفذان 80/443 فقط يجب أن يكونا قابلَين للوصول من الإنترنت — Postgres وRedis وGarage والواجهة نفسها لا تُنشَر أبدًا على المضيف.
- `MASTER_KEY` يُشفّر بيانات اعتماد مزوّدي الإشعارات الخاصة بالمستأجر (مفاتيح واتساب/الرسائل النصية/البريد) عند التخزين — تعامل معه كأنّه كلمة مرور قاعدة بيانات، واحتفظ بنسخة احتياطية منه. فقدانه يجعل أي بيانات اعتماد محفوظة غير قابلة لفكّ التشفير نهائيًا.

> **انسخ احتياطيًا أكثر من قاعدة البيانات**
>
> الملفّات المرفوعة تُخزَّن الآن في خدمة `garage` المُرفَقة، لا في MinIO — والنسخ الاحتياطي التلقائي يعكس (mirror) حاوية التخزين تلك ويتحقّق من كل عنصر فيها قبل الأرشفة أصلًا، فاحتفظ بنسخة احتياطية من ملفّ `.env` (الأسرار) بنفس الانضباط الذي تتّبعه مع نسخة Postgres.

## مساحة القرص وعدّاد التخزين

تعرض صفحة الإعدادات ← التخزين كم تستهلك ملفات معهدك من المساحة وكم بقي على قرص الخادم. الملفات المرفوعة (ملفات الدروس وكل رفع آخر) تُحفظ في خدمة `garage` المرفقة على قرص الخادم نفسه.

يحتفظ الخادم بمساحة احتياطية فارغة لقاعدة البيانات والنسخ الاحتياطية والتحديثات: الأكبر بين 10٪ من القرص و5 غيغابايت. تُرفض الرفعات برسالة واضحة قبل أن تستهلك هذا الاحتياطي، ويحمل مخزن الكائنات نفسه حصة صارمة عند الحدّ ذاته، فلا يستطيع حتى رفع مباشر أن يملأ القرص. يتحول العدّاد إلى الأصفر عند 80٪ وإلى الأحمر عند 95٪ مما يمكن تخزينه، وتعرض الصفحة تنبيهاً من 80٪. إذا كان تثبيتك يستخدم مزوّد S3 خارجياً بدل المخزن المرفق فيعرض العدّاد المساحة المستخدمة فقط ولا يوجد حدّ للرفع.

لزيادة المساحة، كبّر قرص الخادم لدى مزوّد الاستضافة، ثم وسّع القسم ونظام الملفات على الخادم ليرى الحجم الأكبر (تختلف الخطوات حسب المزوّد وصورة النظام). لا حاجة لإعادة التثبيت ولا لتغيير الإعدادات: يقرأ الـ API المساحة الجديدة خلال دقيقة ويتبعها العدّاد والحدّ.

## إشعارات جهات خارجية

تُشغِّل هذه الحزمة البرمجيات التالية من جهات خارجية دون تعديل، كلّ منها في حاويته الخاصة — هذه إشعارات، لا استشارة قانونية:

- **Garage v2.4.1** (تخزين الملفّات) — رخصة AGPL-3.0. الشيفرة المصدرية المطابقة كاملة: https://git.deuxfleurs.fr/Deuxfleurs/garage/src/tag/v2.4.1
- **rclone 1.75.1** (نقل النسخ الاحتياطي) — رخصة MIT. https://github.com/rclone/rclone/tree/v1.75.1
- **Caddy v2.11.4** (وكيل عكسي / TLS) — رخصة Apache-2.0. https://github.com/caddyserver/caddy/tree/v2.11.4
- **PostgreSQL 16.15** (قاعدة البيانات) — رخصة PostgreSQL. https://www.postgresql.org/ftp/source/v16.15/
- **Redis 7.4.11** (التخزين المؤقّت والطوابير) — رخصة مزدوجة RSALv2/SSPLv1، وليست BSD (غيّر Redis ترخيصه ابتداءً من الإصدار 7.4). https://github.com/redis/redis/tree/7.4.11
