تخطَّ إلى المحتوى
kazma.
EN نجمة 6 ابدأ الآن

التعافي من الكوارث

دليل تشغيل التعافي من الكوارث لكاظمه

Section titled “دليل تشغيل التعافي من الكوارث لكاظمه”

الإصدار: 0.6.x / المرحلة 4.5
الجمهور: المشغّلون الذين ينشرون كاظمه على عقدة واحدة أو بنسخ متعددة
ذوو الصلة: kazma_core.backup.restore، kazma_core.backup.restore_drill، SECURITY.md


الأصلالموقعالحرجية
الإعدادات + مؤشرات الأسرارkazma-data/settings.db (+ الخزنة)حرج
مفتاح تشفير الخزنةمتغير البيئة KAZMA_VAULT_KEY / .envحرج — بدونه تصبح الأسرار غير قابلة للقراءة
سر المشغّل المشتركKAZMA_SECRETحرج
نقاط حفظ LangGraphkazma-data/checkpoints.dbعالية — استمرارية المحادثة
جلسات الدردشةkazma-data/chat_sessions.db (أو المسار المُهيأ)عالية
مهام السربkazma-data/swarm_tasks.dbمتوسطة–عالية
الذاكرة / المتجهات / الرسم البيانيkazma-data/vector_memory/، memory.db، vector.db، knowledge_graph.dbمتوسطة
مهام Cronkazma-data/cron.dbمتوسطة
البيانات الوصفية لذكاء المستندات{documents.storage_root}/documents.db (أو Postgres عندما تكون خلفية البيانات الوصفية PG)عالية — المكتبة + المهام
كتل محتوى المستندات المُعنونة بالمحتوى{documents.storage_root}/ (quarantine/originals/artifacts + manifests)حرج — غير قابل للاسترداد دون نسخة احتياطية
ذاكرة الرسم البيانيNeo4j (bolt://…) — وحدة تخزين Docker، وليست في مجلد البياناتمتوسطة–عالية
إعداد MCP + الموصلاتkazma.yaml في جذر التثبيت، وليس في مجلد البياناتحرج — الاسترداد بدونه يقلع بلا أدوات
جلسات الويب المُعتمةConfigStore / Postgresمنخفضة (يعيد المستخدمون تسجيل الدخول)

خارج النطاق (أبدًا على قرص التطبيق وحده):

  • KAZMA_SECRET
  • KAZMA_VAULT_KEY
  • مفاتيح API للمزوّدين إن لم تكن في الخزنة
  • سر عميل OIDC
  • بيانات اعتماد Postgres (KAZMA_DATABASE_URL)

2. إجراء النسخ الاحتياطي (SQLite بعقدة واحدة)

Section titled “2. إجراء النسخ الاحتياطي (SQLite بعقدة واحدة)”
البيئةهدف RPOالإجراء
مختبر / شخصيعلى أفضل جهديوميًا أو قبل الترقيات
إنتاج بعقدة واحدة≤ 24hنسخة يومية مؤتمتة + نسخ خارج الموقع
إنتاج بنسخ متعددة≤ 1hنسخ احتياطي Postgres مستمر + لقطة التطبيق كل 6 ساعات

كيف يعمل الآن (restic، منذ 2026-08-29)

Section titled “كيف يعمل الآن (restic، منذ 2026-08-29)”

النسخ الاحتياطي تلقائي. لا شيء يحتاج إلى تشغيل يدوي.

تنتج كل دورة جيل تجهيز تحت kazma-data/backups/universal/<epoch>/ يحتوي كل قاعدة بيانات SQLite (بأمان WAL عبر Online Backup API)، والأصول، و.env، وkazma.yaml، وresearch/، وتصدير JSONL لرسم Neo4j البياني. ويُفرَّغ Postgres منفصلًا إلى kazma-data/backups/pg/ بـ pg_dump -Fc.

ثم تُؤخذ لقطة لذلك الجيل في مستودعي restic مستقلين:

المستودعالموقعبيانات الاعتماد
محليkazma-data/backups/resticعبارة المرور في ~/.kazma/restic.pass
خارج الموقع (موصى به)s3:https://<account>.r2.cloudflarestorage.com/<bucket> (أو B2)مفتاح مضيف للإلحاق فقط (PutObject/GetObject/ListBucket + DeleteObject على locks/* فقط)
خارج الموقع (قديم)rclone:<remote>/resticrclone OAuth — لا تستخدم Google Drive / حساب خدمة. حسابات الخدمة بلا حصة Drive؛ فحوصات الكتابة على rclone: قد تبدو سليمة بينما كل عملية رفع تفشل بخطأ 403.

فضّل restic الأصلي لـ S3 (‏Cloudflare R2 أو Backblaze B2). يجب ألا يستطيع مفتاح المضيف تنفيذ restic forget --prune. احتفظ بمفتاح تقليم بوصول كامل خارج هذه الآلة. تفحص remote_writable() المسار s3: بعملية PUT+DELETE حقيقية بتوقيع SigV4 تحت locks/ — لم يعد يُعامل Remote من نوع rclone: يستطيع السرد دون الكتابة على أنه سليم.

يستخدم المستودعان بيانات اعتماد مختلفة عمدًا: يجب ألا يفشل مسار خارج الموقع عند إبطال رمز موصل. فعل Drive+rclone ذلك بالضبط في 2026-08-27، فذهبت 29 نسخة احتياطية متتالية إلى المحلي فقط دون أن يلاحظ أحد.

ما يتبقى على المشغّل (ليس كودًا): أنشئ حاوية R2/B2 ومفتاح المضيف للإلحاق فقط، واضبط AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY، ووجّه backups.restic.remote إلى s3:…، ونفّذ restic init، ثم شغّل python -m kazma_core.backup.restore_drill ضد جيل مسترد قبل التقاعد عن Drive. أودع عبارة مرور restic خارج هذا المضيف.

لماذا restic بدلًا من أرشيفات zip التي كان هذا الدليل يصفها: إزالة التكرار، والتشفير، واسترداد يختبره المشروع الأصلي بصرامة أكثر مما نستطيع. على هذا التثبيت كلّفت أربعة أجيال 131 MB حيث كلّفت خطة zip مساحة 3.8 GB، وكل جيل إضافي يضيف نحو 2 MB.

على أساس الزمن، لا العدد: 24 ساعية، و30 يومية، و8 أسبوعية، و12 شهرية، تطبقها مهمة restic_maintenance، التي تمسح أيضًا الأقفال القديمة وتشغّل restic check. عبارة “احتفظ بآخر 30” تعني ثلاثين يومًا أو ثلاثين ساعة حسب عدد مرات تشغيل الحلقة — وليست ضمانة استرداد.

تُطبَّق السياسة لكل نوع نسخة احتياطية (forget --group-by host,tags: ‏universal وpg وlegacy وprobe). التجميع الافتراضي في restic حسب المضيف + المسارات، وكل نسخة universal مجلد backups/universal/<epoch> جديد — فكانت كل لقطة مجموعتها الخاصة المفردة، وحتى 2026-09-23 لم يحذف الاحتفاظ شيئًا قط (199 لقطة، 199 مجموعة). ثلاثون يومية لا سبعة هو خيار المشغّل من اليوم نفسه: ‏69 ذاكرة دردشة أُفرغت بين 08-30 و09-21 لم تكن قابلة للاسترداد إلا من لقطة عمرها ثلاثة أسابيع.

التشغيل النظيف يسجّل [restic] maintenance ok: <local|remote> (forget --prune, check)؛ قبل وجود هذا السطر، كانت الصيانة التي لا تعمل قط لا تُميَّز عن الصيانة التي تنجح دائمًا.

التجهيز على القرص يحتفظ بـ 2 جيلين فقط؛ التاريخ يعيش في restic.

<install>/.kazma/restic.pass (تفوز ~/.kazma/restic.pass القديمة غير الفارغة دون غيرها) تفك تشفير كل لقطة، المحلية والخارجية. وهي عمدًا ليست KAZMA_SECRET — فالعيب الذي كان يُصلَح هو مفتاح الخزنة المسافر مع البيانات التي يحميها.

احتفظ بنسخة خارج هذه الآلة. التشفير ينقل نقطة الفشل الوحيدة من الأرشيف إلى المفتاح. بدونه، كل نسخة احتياطية غير قابلة للقراءة نهائيًا.

Terminal window
python -m kazma_core.backup.restore_drill

رموز الخروج تميّز الدليل: 0 / PASS تعني نجاح كل فحص منطبق؛ 1 / FAIL تعني أن فحصًا وجد فشلًا؛ 2 / UNVERIFIED تعني أن الفحوصات المطلوبة لم تكتمل، دون دليل على تلف. غياب pg_restore، وقراءة الإعادة غير المتاحة، ومستودع مقفل بنسخة احتياطية حية، لا يمكنها إثبات صحة الاسترداد. تصدر التشغيلات غير الموثقة تحذيرًا عبر قناة العمليات القائمة؛ وتصدر الفحوصات الفاشلة إنذارًا حرجًا. ثبّت أدوات العميل المفقودة أو اعالج التبعية المُبلَّغ عنها وأعد تشغيل التمرين للقراءة فقط.

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

ما تفحصه الجولة اليومية. السلامة وحدها تثبت أن البايتات غير تالفة؛ ولا تثبت أنك تستطيع استرجاع بياناتك. لذا، بترتيب العواقب:

الفحصما الذي سيكشفه
vault:decrypt — مفتاح .env الخاص بالنسخة الاحتياطية نفسها يفتح خزنة النسخة نفسها‏KAZMA_VAULT_KEY قديم أو مُدوَّر. الملف موجود وبالحجم الصحيح تمامًا في الحالتين، وكل سر مشفر خلف مفتاح خاطئ مفقود
الاكتمال مقابل databases.items الخاصة بالبيان التعريفي نفسهقاعدة بيانات زعم البيان أنه أخذها ولم يأخذها
PRAGMA integrity_check على كل ملف SQLite في نسخة scratchتلف في الصفحات المنسوخة
صفر جداول يُعد فشلًاملف فارغ يجتاز integrity_check بكل سرور
pg_restore --list على أرشيف Postgresترويسة dump مبتورة أو غير قابلة للقراءة

ما تضيفه الجولة الأسبوعية، بقراءة البايتات لا الترويسات:

الفحصما الذي سيكشفه
تمرير dump‏ Postgres عبر pg_restore --file=-‏dump تالفة أقسام بياناتها خلف TOC سليم. كل كتلة تُقرأ — لكن لا شيء يُحمَّل، فهذا يثبت أن الأرشيف يُقرأ، لا أنه يُسترد
استرداد الـdump في قاعدة scratch وفحصها ثم إسقاطها‏dump تُقرأ لكن لا تُسترد: مخطط لا يُعاد إنشاؤه، صفوف لا تُحمَّل، جداول كاظمه مفقودة، kazma_settings فارغة. مفعّلة افتراضيًا مع Postgres — انظر أدناه
restic check --read-data-subset=5%تعفّن البتات داخل حزم المستودع
قراءة راجعة لـ HEAD للكائن الخارجي، بمقارنة الحجم المخزن بالمرفوعرفع مبتور. الكائن الناقص والكامل يبدوان متطابقين من جانب الإرسال، الذي يعيد 200 لكليهما

بروفة الاسترداد (kazma_core.backup.restore_rehearsal) هي فحص النسخ الاحتياطي الوحيد الذي يكتب إلى خادم قاعدة البيانات. وهي مفعّلة افتراضيًا مع Postgres (منذ 2026-09-27؛ كانت اختيارية، لذا لم يكن “الـdump يُسترد” مستنتجًا قط إلا من “الـdump تُقرأ”). اضبط backups.pg.restore_rehearsal على false، أو KAZMA_PG_RESTORE_REHEARSAL=0، لتعطيلها (=1 تفعّلها رغم الإعداد). تنشئ الجولة الأسبوعية kazma_restore_rehearsal_<epoch> على الخادم نفسه، وتسترد أحدث dump فيها بـ pg_restore، وتفحص أن جداول كاظمه عادت وأن kazma_settings ليست فارغة، ثم تُسقطها. كل CREATE/DROP تعيد فحص الاسم مقابل ذلك النمط بالضبط وترفض اسم قاعدة البيانات الحية نفسها؛ الانهيار بين الاثنين يترك قاعدة scratch تزيلها الجولة التالية (الأقدم من يوم، النمط نفسه — لا شيء غير ذلك). يحتاج مستخدم قاعدة البيانات إلى CREATEDB‏ (ALTER ROLE <user> CREATEDB)؛ بدونه يبلّغ الفحص UNVERIFIED مع ذكر تلك المنحة، أبدًا ليس بنسخة احتياطية فاشلة. جهّز حجم الخادم لنسخة ثانية من جداول كاظمه أثناء تشغيلها.

لرؤيتها تعمل دون انتظار الجولة الأسبوعية، شغّل التمرين العميق الآن (يضم البروفة عندما تكون مفعّلة):

Terminal window
& '.venv\Scripts\python.exe' -m kazma_core.backup.restore_drill --deep

التمرين الذي لم يشغّل قط لا يثبت شيئًا. لا تصف النسخ الاحتياطي بأنها موثقة حتى تظهر نتيجة تمرين في السجل — وجود مجدول في الكود ليس دليلًا. بيان المرنة يعلّم هذه الآلية proven_in_production=False لهذا السبب بالضبط، ويعود إلى True عند وجود نتيجة حية.

ما زال scripts/backup_kazma.py وscripts/restore_kazma.py يعملان وما زالا ينتجان ملفات zip. لم يعودا المسار الأساسي ولا يشملان تصدير الرسم البياني أو kazma.yaml.


3. إجراء الاسترداد (عقدة واحدة)

Section titled “3. إجراء الاسترداد (عقدة واحدة)”

هدف RTO: أقل من ساعة للعقدة الواحدة مع أسرار معروفة. تمت البروفة: 2026-08-29، من المستودعين المحلي والخارجي.

الخطوة 1 — اعرف ما الذي يمكنك الاسترداد إليه

Section titled “الخطوة 1 — اعرف ما الذي يمكنك الاسترداد إليه”
Terminal window
python -m kazma_core.backup.restore --list

يسرد كل جيل قابل للاسترداد مع لقطته وملف dump‏ Postgres المرافق له.

لا تستخدم restic restore latest أبدًا. يختار أحدث لقطة بوقت التقاط اللقطة، وهو ليس أحدث بيانات. الأجيال المُدخلة خارج الترتيب — استيراد جماعي، إعادة رفع — تحمل طوابع زمنية حديثة ومحتوى قديمًا، فقد يناولك latest نسخة احتياطية تنقصها kazma.yaml وتصدير الرسم البياني وهي تبدو نجاحًا نظيفًا. الأمر أعلاه يختار حسب الجيل؛ استخدمه بدل restic مباشرة.

الخطوة 2 — استرداد الملفات

Section titled “الخطوة 2 — استرداد الملفات”
Terminal window
python -m kazma_core.backup.restore --target D: resh-kazma

أضف --generation <epoch> لاختيار نقطة محددة، أو --repo <path> للاسترداد من المستودع الخارجي حين تكون الآلة مفقودة.

يجب أن يكون الهدف فارغًا — الاسترداد فوق شجرة قائمة يشوّك حالتين في واحدة تبدو معقولة وليست أيًّا منهما.

تحصل على تخطيط تثبيت: .env، وkazma.yaml، وkazma-data/، وneo4j_graph.jsonl، وملف dump‏ Postgres المقترن تحت pg/. كل قاعدة بيانات SQLite مستردة تُفحص سلامتها قبل أن يبلّغ عن النجاح.

الخطوة 3 — تحميل قواعد البيانات

Section titled “الخطوة 3 — تحميل قواعد البيانات”

غير تلقائي، وعمدًا: هذه تستبدل بيانات حية. تُطبع الأوامر الدقيقة في نهاية الخطوة 2 مع ملء المسارات.

Terminal window
# Postgres
pg_restore --clean --if-exists -d "$env:KAZMA_DATABASE_URL" "D: resh-kazma\pg\…\pg_shared_….dump"
# Graph memory, into an EMPTY Neo4j (it refuses a populated one)
python -c "from kazma_core.backup.neo4j_backup import restore_graph; print(restore_graph(r'D: resh-kazma
eo4j_graph.jsonl'))"

الخطوة 4 — وجّه كاظمه إليه وابدأ

Section titled “الخطوة 4 — وجّه كاظمه إليه وابدأ”

انسخ .env وkazma.yaml وkazma-data/ إلى جذر التثبيت، أو وجّه KAZMA_DATA_DIR إلى الشجرة المستردة. ثم:

  • GET /health/ready → ready، كل الفحوص سليمة
  • تسجيل الدخول يعمل
  • تظهر جلسة دردشة سابقة
  • الإعدادات ← المزوّدون لا يزالون مُهيئين (الخزنة تُفتح — هذا يثبت أن .env عاد)
  • الأدوات مدرجة (هذا يثبت أن kazma.yaml عاد)
العَرَضالسبب المرجحالإصلاح
الإعدادات فارغة / المفاتيح مفقودةKAZMA_VAULT_KEY خاطئ أو مفقوداسترجع مفتاح الخزنة من مدير كلمات المرور
401 في كل مكانKAZMA_SECRET خاطئاسترجع السر؛ امسح الكوكيز القديمة
SQLite “database is locked”العملية لا تزال تعملاقتل uvicorn/python؛ أعد المحاولة
البحث المتجهي فارغوحدة تخزين المتجهات ليست في مسار النسخ الاحتياطياسترجع مسار vector_memory / ‏Chroma؛ أعد الفهرسة إن لزم

4. النسخ المتعددة / Postgres (المرحلة 4.3)

Section titled “4. النسخ المتعددة / Postgres (المرحلة 4.3)”

عندما يُضبط KAZMA_DATABASE_URL=postgresql://…:

المكوّنالخلفيةملاحظات
الإعدادات / الجلسات / مخطط مستخدمي المنصة المشتركPostgres (kazma_core.db)مطلوب لاتساق النسخ المتعددة
ذاكرات التخزين المؤقت المحلية، Chroma، مؤقت كل عقدةالقرص المحليلا تشارك ملفات SQLite عبر NFS
نقاط الحفظفضّل منقّط Postgres عند تهيئتهانظر متغيرات البيئة أدناه

متغيرات البيئة للنسخ المتعددة

Section titled “متغيرات البيئة للنسخ المتعددة”
Terminal window
KAZMA_DATABASE_URL=postgresql://kazma:…@db:5432/kazma
KAZMA_DB_BACKEND=postgres
KAZMA_PG_POOL_MIN=1
KAZMA_PG_POOL_MAX=10
KAZMA_PRODUCTION=1
KAZMA_VAULT_KEY=…
KAZMA_SECRET=… # or IdP-only with multi-user
KAZMA_PUBLIC_URL=https://kazma.example.com

حزم اختيارية:

Terminal window
pip install 'psycopg[binary,pool]>=3.1' 'langgraph-checkpoint-postgres>=2.0'
# or: pip install -e ".[postgres]"

النسخ الاحتياطي لـ Postgres

Section titled “النسخ الاحتياطي لـ Postgres”

استخدم معيار منصتك:

  • مُدار: فعّل النسخ الاحتياطي المؤتمت + ‏PITR‏ (RDS، Cloud SQL، Azure).
  • مُستضاف ذاتيًا:
    Terminal window
    pg_dump -Fc "$KAZMA_DATABASE_URL" -f kazma-$(date -u +%Y%m%d).dump
  • الاسترداد:
    Terminal window
    pg_restore -d "$KAZMA_DATABASE_URL" --clean --if-exists kazma-YYYYMMDD.dump

قاعدة: لا تشغّل أبدًا نسخًا متعددة مقابل ملف SQLite مشترك.

مثال Compose: docker-compose.postgres.yml.


5. تعدد المستخدمين / IdP (المرحلة 4.4)

Section titled “5. تعدد المستخدمين / IdP (المرحلة 4.4)”
الوضعالكيفية
مشغّل واحدKAZMA_SECRET + جلسة مُعتمة (الافتراضي)
تعدد مستخدمين محليأنشئ مستخدمين عبر platform.users / create_local_user()؛ تسجيل الدخول باسم مستخدم+كلمة مرور
OIDCKAZMA_OIDC_ISSUER، CLIENT_ID، CLIENT_SECRET، KAZMA_PUBLIC_URL → /api/auth/oidc/start

الأدوار: viewer < operator < admin (انظر platform_rbac.py).

بعد استرداد التعافي من الكوارث، أعد الاختبار:

  1. تسجيل دخول admin
  2. يستطيع operator الدردشة/الموافقة
  3. لا يستطيع viewer الوصول إلى /api/settings

6. قائمة فحص التمرين (تشغَّل فصليًا)

Section titled “6. قائمة فحص التمرين (تشغَّل فصليًا)”

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

  • python -m kazma_core.backup.restore --list — النقاط موجودة، والأحدث حديث
  • استرداد إلى مجلد تجهيز فارغ — توقع كل الخطوات خضراء
  • تأكيد أن كل قاعدة بيانات SQLite اجتازت integrity_check (الاسترداد يبلّغ عنه)
  • الاسترداد مرة واحدة من المستودع الخارجي، لا المحلي فقط (--repo rclone:…)
  • تحميل dump‏ Postgres في قاعدة بيانات تُرمى بعد الاستخدام
  • تحميل الرسم البياني في Neo4j فارغ ومقارنة أعداد العقد/العلاقات
  • تأكيد أن الخزنة تُفتح وأن الأدوات مدرجة (يثبت .env + kazma.yaml)
  • قيّس الوقت (حدّث ملاحظة RTO أعلاه)
  • تأكيد أن عبارة مرور restic لا تزال قابلة للاسترجاع من خارج هذه الآلة
  • وثّق أي فجوات في هذا الملف

الحدثالإجراء
اشتباه اختراقدوّر KAZMA_SECRET وKAZMA_VAULT_KEY ومفاتيح المزوّدين؛ أبطل الجلسات؛ استرد من آخر نسخة احتياطية معروفة الصلاحية إن لزم
تلف البياناتأوقف الكُتّاب → استرد من آخر zip/dump سليمة → اختبار دخان
فقدان مفتاح الخزنةغير قابل للاسترداد للأسرار المخزنة في الخزنة — استرجع المفتاح من مخزن دون اتصال أو أعد إدخال الأسرار

حافظ على هذا الدليل مع كل تغيير في بنية الإنتاج.