تخطَّ إلى المحتوى
kazma.
البحث في كاظمه والتوثيق
EN

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

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

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متوسطة
سجلّ النسيانkazma-data/memory_forget_journal.dbعالية — كل عملية نسيان، محفوظ خارج قاعدة بيانات الذاكرة كي لا يُلغيها استرداد تلك القاعدة
مهام Cronkazma-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_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عبارة المرور في <install>/.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.

يجب أن تسمّي وجهة 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.

على أساس الزمن، لا العدد: 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 ميغابايت كل ست ساعات على النسخة المثبتة الحية، إلى أن حذفها مالكها يدويًا.

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

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

نافذة طرفيّة
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 عند وجود نتيجة حية.

ما زال 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 مع ملء المسارات.

نافذة طرفيّة
# Postgres
pg_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-run
python -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 عاد)
العَرَضالسبب المرجحالإصلاح
الإعدادات فارغة / المفاتيح مفقودة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 للملكية والتمارين.

نافذة طرفيّة
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

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

نافذة طرفيّة
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()؛ تسجيل الدخول باسم مستخدم+كلمة مرور
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 (الاسترداد يبلّغ عنه)
  • قراءة سطر 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 مع متانة تثبيت كاملة، ويجب أن يبقى على وحدة الحالة الكاملة المسيّجة.

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

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