التعافي من الكوارث
دليل تشغيل التعافي من الكوارث لكاظمه
Section titled “دليل تشغيل التعافي من الكوارث لكاظمه”الإصدار: 0.6.x / المرحلة 4.5
الجمهور: المشغّلون الذين ينشرون كاظمه على عقدة واحدة أو بنسخ متعددة
ذوو الصلة: kazma_core.backup.restore، kazma_core.backup.restore_drill، SECURITY.md
1. ما الذي يجب حمايته
Section titled “1. ما الذي يجب حمايته”| الأصل | الموقع | الحرجية |
|---|---|---|
| الإعدادات + مؤشرات الأسرار | kazma-data/settings.db (+ الخزنة) | حرج |
| مفتاح تشفير الخزنة | متغير البيئة KAZMA_VAULT_KEY / .env | حرج — بدونه تصبح الأسرار غير قابلة للقراءة |
| سر المشغّل المشترك | KAZMA_SECRET | حرج |
| نقاط حفظ LangGraph | kazma-data/checkpoints.db | عالية — استمرارية المحادثة |
| جلسات الدردشة | kazma-data/chat_sessions.db (أو المسار المُهيأ) | عالية |
| مهام السرب | kazma-data/swarm_tasks.db | متوسطة–عالية |
| الذاكرة / المتجهات / الرسم البياني | kazma-data/vector_memory/، memory.db، vector.db، knowledge_graph.db | متوسطة |
| سجلّ النسيان | kazma-data/memory_forget_journal.db | عالية — كل عملية نسيان، محفوظ خارج قاعدة بيانات الذاكرة كي لا يُلغيها استرداد تلك القاعدة |
| مهام Cron | kazma-data/cron.db | متوسطة |
| البيانات الوصفية لذكاء المستندات | {documents.storage_root}/documents.db على SQLite (الفهرس، وACL، والتدقيق، والمهام وحجوزات الاستقبال حين تكون على SQLite)؛ وجداول Postgres حين يستخدم التثبيت Postgres، ما لم يُضبط KAZMA_DOCUMENTS_METADATA_BACKEND=sqlite | عالية — المكتبة + المهام |
| كتل محتوى المستندات المُعنونة بالمحتوى | {documents.storage_root}/ (quarantine/originals/artifacts + manifests) | حرج — غير قابل للاسترداد دون نسخة احتياطية. تلتقط نسخة المستندات الاحتياطية كل 6 ساعات البيانات الوصفية والكتل نقطةً زمنية واحدة: kazma-data/backups/document-store-* حين تكون البيانات الوصفية والمهام على SQLite (الاسترداد: انسخها إلى جذر تخزين فارغ)، وdocument-pg-* حين يكون الاثنان على Postgres (الاسترداد بـ scripts/document_pg_backup.py؛ وبعد dump Postgres المشترك، مع --replace-document-tables). البيانات الوصفية على SQLite بجانب مهام على Postgres تُلتقط لقطتها كمخزن SQLite، وطابور مهامها في dump Postgres المشترك؛ أما البيانات الوصفية على Postgres بجانب مهام على SQLite (وضع احتياطي) فتُرفض بالاسم. يعيد الإقلاع التالي نشر المستندات المفهرسة إلى مكتبات معرفتها (انظر معالجة المستندات، التي تسجّل تمارين الاسترداد المُقاسة) |
| ذاكرة الرسم البياني | Neo4j (bolt://…) — وحدة تخزين Docker، وليست في مجلد البيانات | متوسطة–عالية |
| إعداد MCP + الموصلات | kazma.yaml في جذر التثبيت، وليس في مجلد البيانات | حرج — الاسترداد بدونه يقلع بلا أدوات |
| جلسات الويب المُعتمة | ConfigStore / Postgres | منخفضة (يعيد المستخدمون تسجيل الدخول) |
خارج النطاق (أبدًا على قرص التطبيق وحده):
KAZMA_SECRETKAZMA_VAULT_KEY- مفاتيح API للمزوّدين إن لم تكن في الخزنة
- سر عميل OIDC
- بيانات اعتماد Postgres (
KAZMA_DATABASE_URL)
2. إجراء النسخ الاحتياطي (SQLite بعقدة واحدة)
Section titled “2. إجراء النسخ الاحتياطي (SQLite بعقدة واحدة)”التكرار
Section titled “التكرار”| البيئة | هدف 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 | عبارة المرور في <install>/.kazma/restic.pass (انظر أدناه) |
| خارج الموقع (موصى به) | s3:https://<account>.r2.cloudflarestorage.com/<bucket> (أو B2) | مفتاح مضيف للإلحاق فقط (PutObject/GetObject/ListBucket + DeleteObject على locks/* فقط) |
| خارج الموقع (قديم) | rclone:<remote>/restic | rclone OAuth — لا تستخدم Google Drive / حساب خدمة. حسابات الخدمة بلا حصة Drive؛ فحوصات الكتابة على rclone: قد تبدو سليمة بينما كل عملية رفع تفشل بخطأ 403. |
يجب أن تسمّي وجهة rclone اتصالًا بعيدًا قائمًا ومضبوطًا، مثل backup:restic.
ولمستودع restic استخدم rclone:backup:restic. تُرفض مواصفات الخلفية المباشرة
مثل :s3:… والأسماء الشبيهة بالخيارات ومحارف التحكم قبل تشغيل rclone.
قد تحوي المسارات مسافات ونصًا عربيًا. يضع الأمر الوجهات بعد --.
فضّل 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.
الاحتفاظ
Section titled “الاحتفاظ”على أساس الزمن، لا العدد: 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.
للنسخة السحابية المضغوطة عددها الخاص. يمكن للإعدادات ← النسخ الاحتياطي
أن ترسل أيضًا كل جيل، مضغوطًا ومن دون .env، إلى مجلد kazma-backups لدى
مزوّد سحابي (Google Drive أو OneDrive أو WebDAV أو FTP أو S3، أو جهة rclone
بعيدة). منذ 2026-11-03 يحتفظ كاظمه هناك بأحدث backups.offsite.keep
نسخة (الإعدادات ← النسخ الاحتياطي ← الاحتفاظ بالنسخ السحابية؛ الافتراضي
7، و0 يبقي كل نسخة) ويحذف الباقي بعد كل رفع ينجح، الأقدم أولًا — نهائيًا،
لا إلى سلة مهملات. لا يُحذف شيء من قائمة فشلت، أو ليست كاملة، أو لا تحمل
النسخة التي رُفعت للتو؛ والنسخة التي تعذّر حذفها تطلق التنبيه
backup.offsite_prune_failed وتُحاوَل مجددًا في النسخة الاحتياطية التالية.
قبل ذلك كانت كل نسخة تبقى: ملف مضغوط بحجم 344 ميغابايت كل ست ساعات على
النسخة المثبتة الحية، إلى أن حذفها مالكها يدويًا.
عبارة المرور
Section titled “عبارة المرور”<install>/.kazma/restic.pass (تفوز ~/.kazma/restic.pass القديمة غير الفارغة
دون غيرها) تفك تشفير كل لقطة، المحلية والخارجية. وهي عمدًا ليست
KAZMA_SECRET — فالعيب الذي كان يُصلَح هو مفتاح الخزنة المسافر مع البيانات
التي يحميها.
احتفظ بنسخة خارج هذه الآلة. التشفير ينقل نقطة الفشل الوحيدة من الأرشيف إلى المفتاح. بدونه، كل نسخة احتياطية غير قابلة للقراءة نهائيًا.
التحقق دون استرجاع
Section titled “التحقق دون استرجاع”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 مع ذكر تلك المنحة، أبدًا
ليس بنسخة احتياطية فاشلة. جهّز حجم الخادم لنسخة ثانية من جداول كاظمه أثناء
تشغيلها.
لرؤيتها تعمل دون انتظار الجولة الأسبوعية، شغّل التمرين العميق الآن (يضم البروفة عندما تكون مفعّلة):
& '.venv\Scripts\python.exe' -m kazma_core.backup.restore_drill --deepالتمرين الذي لم يشغّل قط لا يثبت شيئًا. لا تصف النسخ الاحتياطي بأنها موثقة حتى تظهر نتيجة تمرين في السجل — وجود مجدول في الكود ليس دليلًا. بيان المرنة يعلّم هذه الآلية
proven_in_production=Falseلهذا السبب بالضبط، ويعود إلىTrueعند وجود نتيجة حية.
السكربتات القديمة
Section titled “السكربتات القديمة”ما زال scripts/backup_kazma.py وscripts/restore_kazma.py يعملان وما زالا
ينتجان ملفات zip. لم يعودا المسار الأساسي ولا يشملان تصدير الرسم البياني أو
kazma.yaml.
3. إجراء الاسترداد (عقدة واحدة)
Section titled “3. إجراء الاسترداد (عقدة واحدة)”هدف RTO: أقل من ساعة للعقدة الواحدة مع أسرار معروفة. تمت البروفة: 2026-08-29، من المستودعين المحلي والخارجي.
الخطوة 1 — اعرف ما الذي يمكنك الاسترداد إليه
Section titled “الخطوة 1 — اعرف ما الذي يمكنك الاسترداد إليه”python -m kazma_core.backup.restore --listيسرد كل جيل قابل للاسترداد مع لقطته وملف dump Postgres المرافق له.
لا تستخدم
restic restore latestأبدًا. يختار أحدث لقطة بوقت التقاط اللقطة، وهو ليس أحدث بيانات. الأجيال المُدخلة خارج الترتيب — استيراد جماعي، إعادة رفع — تحمل طوابع زمنية حديثة ومحتوى قديمًا، فقد يناولكlatestنسخة احتياطية تنقصهاkazma.yamlوتصدير الرسم البياني وهي تبدو نجاحًا نظيفًا. الأمر أعلاه يختار حسب الجيل؛ استخدمه بدل restic مباشرة.
الخطوة 2 — استرداد الملفات
Section titled “الخطوة 2 — استرداد الملفات”python -m kazma_core.backup.restore --target D:\fresh-kazmaأضف --generation <epoch> لاختيار نقطة محددة، أو --repo <path> للاسترداد من
المستودع الخارجي حين تكون الآلة مفقودة.
يجب أن يكون الهدف فارغًا — الاسترداد فوق شجرة قائمة يشوّك حالتين في واحدة تبدو معقولة وليست أيًّا منهما.
تحصل على تخطيط تثبيت: .env، وkazma.yaml، وkazma-data/،
وneo4j_graph.jsonl، وملف dump Postgres المقترن تحت pg/. يوضع مجلدا
dbs/ وassets/ في النسخة الاحتياطية بوصفهما دليل البيانات نفسه:
kazma-data/settings.db وkazma-data/vault.db وkazma-data/attachments/
وهكذا، حيث يقرؤها كاظمه. قاعدة البيانات التي كانت في مجلد أصول تؤخذ من
النسخة المتسقة في dbs/، لا من الخام أبدًا، ومن دون -wal/-shm بجانبها.
كل قاعدة بيانات SQLite مستردة تُفحص سلامتها (تُقرأ immutable، فلا يضيف
الفحص ملفًا)، والخطوة الأخيرة تسأل كاظمه نفسه، موجَّهًا إلى kazma-data/
المستردة، أين يعيش كل مخزن: المخزن الذي تذكره النسخة الاحتياطية وليس هناك
يُفشل الاسترداد. حتى 2026-11-02 كان الاسترداد ينسخ dbs/ وassets/
مجلدين، على مستوى أدنى من كل مسار يقرؤه كاظمه، ويبلّغ عن النجاح (FA-268).
الخطوة 3 — تحميل قواعد البيانات
Section titled “الخطوة 3 — تحميل قواعد البيانات”غير تلقائي، وعمدًا: هذه تستبدل بيانات حية. تُطبع الأوامر الدقيقة في نهاية الخطوة 2 مع ملء المسارات.
# Postgrespg_restore --clean --if-exists -d "$env:KAZMA_DATABASE_URL" "D:\fresh-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:\fresh-kazma\neo4j_graph.jsonl'))"لجيل مستعاد يسبق V2، أبقِ بيئة تشغيله متوقفة وخذ نسخة قبل التحويل. استخدم دليل بيانات قائمًا وصريحًا:
python -m kazma_core.memory.backfill_v2 --data-dir D:\fresh-kazma\kazma-data --dry-runpython -m kazma_core.memory.backfill_v2 --data-dir D:\fresh-kazma\kazma-data --applyيحوّل CLI مخزني memory.db وknowledge_graph.db القديمين إلى
memory_state.db دون استدعاءات مزوّد. ويحفظ نص الحلقات ومعرّفات المستأجرين
والطوابع الزمنية والتضمينات المتاحة، ويحوّل كيانات الرسم وحواف الحقائق،
ويتجاوز الحواف الهيكلية. تجعل المعرّفات الثابتة تكرار التشغيل آمنًا من
التكرار؛ وتشمل أعداد الإدراج الصفوف الجديدة فقط. افحص أعداد المقروء والمدرج
والمتجاوز قبل بدء التشغيل المستعاد. تستخدم كل مسارات المخازن هذا الدليل
حتى لو ورثت الصدفة تجاوزات مسارات التثبيت الحي. لا يحوّل ذلك جيل Postgres
ولا يؤهّل الاستخراج الدلالي للمعتقدات.
الخطوة 4 — وجّه كاظمه إليه وابدأ
Section titled “الخطوة 4 — وجّه كاظمه إليه وابدأ”انسخ .env وkazma.yaml وkazma-data/ إلى جذر التثبيت، أو وجّه
KAZMA_DATA_DIR إلى الشجرة المستردة. ثم:
GET /health/ready→ready، كل الفحوص سليمة- ما نُسي بعد النسخة الاحتياطية يبقى منسيًّا. استرداد
memory_state.dbأقدم يعيد الأدوار المنسيّة منذئذ؛ وسجلّ النسيان (memory_forget_journal.db، بجانبها، ولا يُسترد من النسخة الأقدم) يسمّيها، فتطبّقها كاظمه من جديد عند الإقلاع وكل 15 دقيقة (“تقارب النسيان”). احتفظ بالسجلّ الذي لديك حين تسترد قاعدة بيانات الذاكرة؛ ويبلّغ التمرين بعدد عمليات النسيان التي ستعيد نسخةٌ احتياطية تطبيقها. - تلحق الذاكرة بمخزن الدردشة. مؤشر تسوية الأدوار يعيش في
memory_state.db، فتعيد النسخة المستردة قراءة كل دردشة تغيّرت منذ نسختها الاحتياطية وتكتب الأدوار التي تلتها ذكرياتٍ من جديد. الاسترداد من صفحة الذاكرة (POST /api/system/memory/restore) يطبّق سجلّ النسيان ويُجري ذلك اللحاق فورًا؛ والاسترداد اليدوي ينال الاثنين عند الإقلاع التالي. ومع Postgres مخزنًا أساسيًا للذاكرة، يرى مرور الصيانة التالي مرآةً رجعت إلى الوراء فيقارنها كاملةً. RPO وRTO المُقاسان: “Restore drills” في الدليل (Memory and RAG). - تسجيل الدخول يعمل
- تظهر جلسة دردشة سابقة
- الإعدادات ← المزوّدون لا يزالون مُهيئين (الخزنة تُفتح — هذا يثبت أن
.envعاد) - الأدوات مدرجة (هذا يثبت أن
kazma.yamlعاد)
أنماط الفشل
Section titled “أنماط الفشل”| العَرَض | السبب المرجح | الإصلاح |
|---|---|---|
| الإعدادات فارغة / المفاتيح مفقودة | KAZMA_VAULT_KEY خاطئ أو مفقود | استرجع مفتاح الخزنة من مدير كلمات المرور |
| 401 في كل مكان | KAZMA_SECRET خاطئ | استرجع السر؛ امسح الكوكيز القديمة |
| SQLite “database is locked” | العملية لا تزال تعمل | اقتل uvicorn/python؛ أعد المحاولة |
| البحث المتجهي فارغ | وحدة تخزين المتجهات ليست في مسار النسخ الاحتياطي | استرجع مسار vector_memory / Chroma؛ أعد الفهرسة إن لزم |
تدوير كلمة مرور المستودع
Section titled “تدوير كلمة مرور المستودع”kazma_core.backup.restic_repo.rotate_password(repos, old, new, persist=...)
هي واجهة Python المعتمدة للمشغّل. مرّر المستودعات النشطة ودالة لحفظ عبارة
المرور المضبوطة بصورة دائمة. تضيف المفتاح الجديد وتتحقق منه في كل مستودع،
وتحفظه، ثم تزيل المفاتيح القديمة؛ ويرفض إخفاق الإضافة أو التحقق أو الحفظ
الإبطال. اقرأ العبارات عبر مدير كلمات مرور أو getpass، ولا تستخدم وسائط
الأوامر أو المخرجات المطبوعة. انسخ العبارة المحفوظة احتياطيًا، وجدول التدوير
حين تكون النسخ الاحتياطية خاملة، وتحقق من الوصول وتمرين استعادة بعده.
4. Postgres ووحدة الحالة الكاملة
Section titled “4. Postgres ووحدة الحالة الكاملة”عندما يُضبط KAZMA_DATABASE_URL=postgresql://…:
| المكوّن | الخلفية | ملاحظات |
|---|---|---|
| الإعدادات / الجلسات / مخطط مستخدمي المنصة | Postgres (kazma_core.db) | لا تنقل هذه الخلفيات كل مخازن التشغيل إلى Postgres |
| مخازن SQLite وChroma والملفات الدائمة | وحدة حالة كاملة مسيّجة | تُحفظ مع Postgres؛ لا تشارك ملفات SQLite عبر NFS |
| نقاط الحفظ | فضّل منقّط Postgres عند تهيئته | انظر متغيرات البيئة أدناه |
يسمح ملف إعداد HA بمالك تشغيل نشط واحد. استعد Postgres ووحدة الحالة الكاملة معًا؛ فتبقى الإيصالات وقرارات الموافقة على تلك الوحدة. انظر المناطق المتعددة وHA للملكية والتمارين.
بيئة Postgres
Section titled “بيئة Postgres”KAZMA_DATABASE_URL=postgresql://kazma:…@db:5432/kazmaKAZMA_DB_BACKEND=postgresKAZMA_PG_POOL_MIN=1KAZMA_PG_POOL_MAX=10KAZMA_PRODUCTION=1KAZMA_VAULT_KEY=…KAZMA_SECRET=… # or IdP-only with multi-userKAZMA_PUBLIC_URL=https://kazma.example.comحزم اختيارية:
pip install -e ".[postgres]" # psycopg[binary,pool]>=3.3.6, langgraph-checkpoint-postgres>=3.1.3النسخ الاحتياطي لـ Postgres
Section titled “النسخ الاحتياطي لـ Postgres”استخدم معيار منصتك:
- مُدار: فعّل النسخ الاحتياطي المؤتمت + PITR (RDS، Cloud SQL، Azure).
- مُستضاف ذاتيًا:
نافذة طرفيّة pg_dump -Fc "$KAZMA_DATABASE_URL" -f kazma-$(date -u +%Y%m%d).dump - الاسترداد:
نافذة طرفيّة pg_restore -d "$KAZMA_DATABASE_URL" --clean --if-exists kazma-YYYYMMDD.dump
قاعدة: لا تشغّل أبدًا نسخًا متعددة مقابل ملف SQLite مشترك.
يحمل dump قاعدة البيانات الكاملة جداول المستندات أيضًا، لكن ليس كما كانت لحظة
نسخ ملفات محتوى المستندات. استرد مخزن مستندات Postgres من نسخته الاحتياطية المنسّقة
(scripts/document_pg_backup.py، انظر
معالجة المستندات ← النسخ الاحتياطي)، بهذا
الترتيب (مُجرَّب، tests/test_document_pg_restore_order.py): dump قاعدة البيانات
الكاملة أولًا، ثم النسخة المنسّقة مع --replace-document-tables، الذي يُسقط جداول
المستندات الأقدم الآتية من الـdump بعد أن تُتحقق النسخة ويُوجد جذر التخزين فارغًا.
دون هذا الخيار يرفض الاسترداد المنسّق قاعدة البيانات؛ والترتيب الآخر يدع --clean
في الـdump يعيد صفوف مستندات أقدم فوق النسخة المنسّقة.
مثال Compose: docker-compose.postgres.yml.
5. تعدد المستخدمين / IdP (المرحلة 4.4)
Section titled “5. تعدد المستخدمين / IdP (المرحلة 4.4)”| الوضع | الكيفية |
|---|---|
| مشغّل واحد | KAZMA_SECRET + جلسة مُعتمة (الافتراضي) |
| تعدد مستخدمين محلي | أنشئ مستخدمين عبر platform.users / create_local_user()؛ تسجيل الدخول باسم مستخدم+كلمة مرور |
| OIDC | KAZMA_OIDC_ISSUER، CLIENT_ID، CLIENT_SECRET، KAZMA_PUBLIC_URL → /api/auth/oidc/start |
الأدوار: viewer < operator < admin (انظر platform_rbac.py).
بعد استرداد التعافي من الكوارث، أعد الاختبار:
- تسجيل دخول admin
- يستطيع operator الدردشة/الموافقة
- لا يستطيع viewer الوصول إلى
/api/settings
6. قائمة فحص التمرين (تشغَّل فصليًا)
Section titled “6. قائمة فحص التمرين (تشغَّل فصليًا)”النسخ الاحتياطي يشغّل نفسه؛ التمرين موجود لإثبات أن الاسترداد لا يزال يعمل، وهو النصف الذي يتعفن دون ملاحظة.
-
python -m kazma_core.backup.restore --list— النقاط موجودة، والأحدث حديث - استرداد إلى مجلد تجهيز فارغ — توقع كل الخطوات خضراء
- تأكيد أن كل قاعدة بيانات SQLite اجتازت
integrity_check(الاسترداد يبلّغ عنه) - قراءة سطر
memory:forget_journalفي التمرين: عمليات النسيان التي جرت منذ النسخة الاحتياطية، والتي تُطبَّق من جديد عند الإقلاع - تشغيل تمارين استرداد الذاكرة (
pytest tests/test_memory_restore_drill.py، وعلى Postgres يُرمى بعد الاستخدامtests/test_memory_restore_drill_pg.py) ومقارنة سطر RTO فيها بـ “Restore drills” في الدليل - الاسترداد مرة واحدة من المستودع الخارجي، لا المحلي فقط (
--repo rclone:…) - تحميل dump Postgres في قاعدة بيانات تُرمى بعد الاستخدام
- تحميل الرسم البياني في Neo4j فارغ ومقارنة أعداد العقد/العلاقات
- استرداد أحدث نسخة احتياطية للمستندات إلى جذر تخزين فارغ (وعلى Postgres، بعد dump قاعدة البيانات الكاملة:
scripts/document_pg_backup.py verify، ثمrestore ... --replace-document-tables)، والإقلاع عليها وفتح مستند - تأكيد أن الخزنة تُفتح وأن الأدوات مدرجة (يثبت
.env+kazma.yaml) - قيّس الوقت (حدّث ملاحظة RTO أعلاه)
- تأكيد أن عبارة مرور restic لا تزال قابلة للاسترجاع من خارج هذه الآلة
- وثّق أي فجوات في هذا الملف
الأدوات المنقطعة واستئناف الموافقات
Section titled “الأدوات المنقطعة واستئناف الموافقات”بعد التعطل تثبت الموافقة المسجلة قرار الإنسان فقط. يحفظ الإقلاع القرار وصاحبه، ويسمّي الاستئناف المنقطع تنفيذًا غير مؤكد، ويحجب أزرار الموافقة القديمة. أما السؤال الذي لم يُجب عنه فيحتفظ بنقطة حفظه وبطاقة موافقته.
تحجز أدوات الرسم المغيّرة إيصالًا قبل التوزيع في tool_effects.db. تربط
هويته المستأجر والخيط والدور والتكرار ونداء الأداة والأداة نفسها؛ وتربط
بصمة الطلب الوسائط ومساحة العمل أيضًا. توفّر الإيصالات المكتملة نتيجتها
المحفوظة عند إعادة نقطة الحفظ. وتحجز الإيصالات التي بدأت نتيجة مجهولة
وتوقف الدور دون تركيب جواب أو توزيع ثانٍ. ويحجز خطأ محفوظ يحمل
result_uncertain الدور أيضًا: الاكتمال يعني تثبيت الإيصال، لا نجاح الفعل.
يستدعي السجل المحلي الأدوات المغيّرة والمجهولة مرة واحدة؛ ولا تعاد تلقائيًا
إلا القراءات المصنفة. الخطأ بعد الاستدعاء غير يقيني لأن الفعل ربما اكتمل
جزئيًا. أما رفضٌ تصدره الأداة قبل أن تكتب أو ترسل أو تبدأ أي شيء (وسيطٌ، أو
سياسةٌ، أو حسابٌ غير متصل، أو عنوان URL مرفوض، أو متصفح لا يستطيع المضيف
تشغيله) فهو خطأ عادي، لا خطأ غير يقيني: يجوز للنموذج تصحيح الاستدعاء،
ويستمر الدور. وكذلك الإخفاق الذي تسمّي الأداة أثره بدقة ويكون تكراره آمنًا
(فحص generate_pdf أن النص وصل إلى ملف PDF). أما الإرسال أو النشر أو الدفع
فلا يُعامَل هكذا أبدًا. لا تُخزّن القراءات في خبيئة النتائج.
تحمل إخفاقات توزيع أدوات MCP المغيّرة عدم اليقين أيضًا، حتى حين يعيد خطاف
المخرجات صياغة الخطأ. وتكتب نداءات IDE المباشرة وحلقات أدوات السرب داخل
العملية إيصالات أيضًا. توقف أداة السرب غير اليقينية العمل دون نداء تركيب
جواب. لا يستطيع عامل غير ناجح استدعى أداة محلية أو MCP مغيّرة إعادة المحاولة
أو الانتقال لعامل بديل. تبقى آثار العمال المخصصين خارج هذه المنفذات
المشتركة ونقاط الرسم القديمة دون هوية دور غير مغطاة.
تدعم طلبات تغيير IDE ترويسة Idempotency-Key. استخدم مفتاحًا جديدًا لكل
عملية مقصودة واحتفظ به عند فقد الاستجابة. يعيد الطلب المتكرر بالمستخدم
والمستأجر والحمولة ومساحة العمل نفسها الاستجابة المحفوظة؛ وتعيد الطلبات
المتغيرة والآثار غير المحسومة 409 دون توزيع أو موافقة جديدين. تُفحص
المصادقة ودور المشغّل قبل إعادة الاستجابة المحفوظة.
يسبق قبول المعدل قبول الإيصال، ويُطبق مرة لكل طلب، بما فيه قراءة الاستجابة
المحفوظة. لا يقبل HTTP 429 أي أثر؛ ويمكن إعادة إرسال مفتاح العملية نفسه
بعد انقضاء نافذة المحدد. يحتفظ IDE على الويب بمعرّفات العمليات غير المحسومة
عبر إعادة التحميل داخل التبويب نفسه؛ ويحفظ session storage بصمة الحمولة
والمعرّف فقط. يحصل العميل الذي يحذف الترويسة على مفتاح مولّد في الاستجابة،
لا ينقذ استجابة مفقودة لم يتلقها. لا تبدأ عملية بمفتاح جديد لمجرد تجاوز
نتيجة غير يقينية. للنداءات المباشرة غير HTTP إيصالات لكل نداء، لا بروتوكول
إعادة محاولة تلقائي.
افحص البيانات الوصفية من بيئة التثبيت بمعرّف الخيط:
python -m kazma_core.agent.effect_journal --thread THREAD_IDيعرض الأمر هويات الإيصالات وأسماء الأدوات والحالة والطوابع الزمنية. لا يطبع الوسائط أو المحتوى المعاد، ولا يغيّر إيصالًا أو يعيد تشغيل شيء. افحص الهدف الفعلي وسجل تدقيقه الخارجي قبل بدء طلب جديد. لا تحذف إيصالًا مجهولًا ولا تعيد ضبطه لفرض المحاولة. أبقِ الإيصالات ونقاط الحفظ وقرارات الموافقة معًا أثناء النسخ والاستعادة؛ فاستعادة مخزن إيصالات أقدم قد تكرر آثارًا أحدث.
واجهات API الخارجية ليست ذرية مع هذا المخزن المحلي. تظل لأنشطة الوكيل الكامل في Temporal محاولة واحدة، ولم تؤهّل المحاولات التلقائية. يستخدم الإيصال SQLite WAL مع متانة تثبيت كاملة، ويجب أن يبقى على وحدة الحالة الكاملة المسيّجة.
7. جهات الاتصال للحوادث
Section titled “7. جهات الاتصال للحوادث”| الحدث | الإجراء |
|---|---|
| اشتباه اختراق | دوّر KAZMA_SECRET وKAZMA_VAULT_KEY ومفاتيح المزوّدين؛ أبطل الجلسات؛ استرد من آخر نسخة احتياطية معروفة الصلاحية إن لزم |
| تلف البيانات | أوقف الكُتّاب → استرد من آخر zip/dump سليمة → اختبار دخان |
| فقدان مفتاح الخزنة | غير قابل للاسترداد للأسرار المخزنة في الخزنة — استرجع المفتاح من مخزن دون اتصال أو أعد إدخال الأسرار |
حافظ على هذا الدليل مع كل تغيير في بنية الإنتاج.

