هذا الدليل لمشغّل Hermes Agent مستضاف ذاتيًا يستطيع إدارة حساب Linux ومستودع GitHub الخاص. النتيجة محددة: أرشيف كامل من بيانات المستخدم في Hermes، مشفّر قبل مغادرة الجهاز، ومؤكّد في المستودع البعيد الخاص المقصود، ثم يُجلب ويُستورد على جهاز اختباري منفصل قابل للتخلص منه. الإجراء موثّق لـ Hermes CLI عند commit 86fa9e6، وفُحص في 19 أكتوبر 2026. فحصت BIG CHANGE بنية shell ومسار التحكم ببيانات اختبار محلية خاملة؛ ولم تنفذ نسخ Hermes فعليًا أو تشفير age أو إرسال GitHub أو تشغيلًا مجدولًا أو استعادة. لا تعتبر النسخة مثبتة في تثبيتك إلا بعد إجراء جميع الفحوص التالية بنفسك.

التغيير الكبير

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

1. جهّز الجهاز والمفتاح والمستودع

استخدم Linux مع Bash وأداتي GNU stat -c و du -sb، إضافة إلى Hermes CLI مثبت وGit وGitHub CLI (gh) وage و unzip و sha256sum وSSH وبوابة Hermes نشطة لـ cron. تأكد من مطابقة الأوامر المثبتة hermes backup و hermes import و hermes cron create --help لـ مرجع CLI المثبت عند commit محدد و الأسئلة الشائعة و دليل cron. حدّد مستخدم نظام التشغيل و HERMES_HOME المالكين للبوابة. نفّذ الإعداد كله وأول نسخة يدوية بهذا المستخدم. يفترض المثال نسخة Git مخصصة نظيفة وفرع main.

تشمل نسخة Hermes الكاملة الإعدادات وبيانات الاعتماد والذاكرة والمهارات والجلسات والملفات الشخصية تحت جذر البيانات. وتستثني كود Hermes وتنزيلات التبعيات وبيئة التشغيل والنسخ السابقة ونقاط التحقق وملفات اعتماد المتصفح وملفات SQLite المرافقة وبعض الملفات المحلية. ولا تجمع الملفات غير المرتبطة في أماكن أخرى على الجهاز. لا تستخدم --quick لهذه النتيجة الشاملة. راجع المهم خارج هذا النطاق واحمه بصورة مستقلة. يحتوي ZIP أسرارًا حتى لو كان المستودع الهدف خاصًا.

أنشئ هوية age، أي المفتاح الخاص اللازم لفك تشفير الأرشيف، في موقع محمي خارج HERMES_HOME ودليل التجهيز المؤقت ونسخة Git. احتفظ بنسخة استعادة مستقلة التخزين من الهوية. المستلِم المطبوع age1… معلومة عامة تُوضع في الإعدادات؛ أما الهوية الخاصة فلا. اتبع مرجع استخدام age.

Terminal
umask 077
install -d -m 700 /PRIVATE-KEY-STORE
age-keygen -o /PRIVATE-KEY-STORE/hermes-backup-identity.txt
age-keygen -y /PRIVATE-KEY-STORE/hermes-backup-identity.txt

أنشئ مستودعًا خاصًا لهذه النسخة وحدها، وراجع أصحاب الوصول، وأعد قراءة غير تفاعلية عبر gh وكتابة Git SSH لمستخدم النظام نفسه الذي يشغّل Hermes cron. احتفظ بالرموز والمفاتيح الخاصة لـ SSH خارج بيانات Hermes والتجهيز وGit. دخول تفاعلي بحساب آخر لا يثبت الوصول المجدول. استبدل العناصر النائبة الكبيرة قبل تشغيل الأمثلة. إذا أنشأ المستودع README أو فرع main مسبقًا، فافحص ذلك وتجاوز أوامر التهيئة المتعارضة.

Terminal
gh repo create ACCOUNT/PRIVATE-REPOSITORY --private --clone
gh repo view ACCOUNT/PRIVATE-REPOSITORY --json isPrivate --jq .isPrivate
cd PRIVATE-REPOSITORY
git remote set-url origin git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git
git remote get-url --all origin
git remote get-url --push --all origin
git branch -M main
printf '%s\n' 'Encrypted Hermes backup storage; ciphertext and checksum only.' > README.md
git add README.md
git commit -m 'Initialize private backup repository'
git push -u origin HEAD:main
git ls-remote --heads origin main

gh repo create --clone قد يختار HTTPS من التفضيلات المحلية؛ عنوان SSH الصريح أعلاه هو وجهة هذه الوصفة. لا تتابع إلا عندما يعيد isPrivate القيمة true ويكون المتعاونون مقصودين، ويتطابق عنوانا الجلب والإرسال الفعليان، كل منهما، مع عنوان SSH الوحيد المراجع، وينجح الوصول البعيد بمستخدم الجدول. تسمح قواعد Git للمستودعات البعيدة بإعادة كتابة العنوان ووجهات إضافية و pushurl منفصل؛ يفحص النص القيم الأصلية والفعلية في كل تشغيل.

غادر نسخة Git قبل حفظ كتلتي الكود التاليتين. أنشئ دليل إعداد خاصًا خارج جذر Hermes ونسخة Git ودليل التجهيز ومخزن المفاتيح. احفظ hermes-github-backup.sh و hermes-github-backup.conf فيه؛ تنسخهما أوامر التثبيت إلى ملف المالك الشخصي. يجب أن تبقى نسخة Git المخصصة نظيفة.

Terminal
install -d -m 700 /ABSOLUTE/PRIVATE-SETUP
cd /ABSOLUTE/PRIVATE-SETUP

2. ثبّت نص النسخ المراجع

احفظ كتلة Bash التالية كاملة باسم hermes-github-backup.sh في دليل الإعداد الخاص. واحفظ الكتلة الثانية باسم hermes-github-backup.conf بجانبها بعد استبدال كل عنصر نائب بمسار مطلق أو المستلِم العام. هذه إعدادات shell للمراجعة المحلية؛ افحصها قبل تحميلها واقصر صلاحياتها على المالك. لا تتضمن كلمة مرور أو رمزًا أو هوية age خاصة. النص هو المثال المقترح الكامل؛ فُحصت بنيته ومساره الخامل، لكن يجب تكييفه واختباره في تثبيتك. ملف latest.zip.age الناتج هو الأرشيف المشفّر.

Terminal
#!/usr/bin/env bash
# Documentation-based Linux sample for an operator to review and adapt.
# Place with hermes-github-backup.conf under the owning HERMES_HOME/scripts/.
# Requires GNU stat -c and du -sb. Git Bash was used only for inert fixtures.
set -Eeuo pipefail
umask 077

script_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
owning_home=$(CDPATH= cd -- "$script_dir/.." && pwd -P)
if [[ -n ${HERMES_HOME:-} && $(CDPATH= cd -- "$HERMES_HOME" && pwd -P) != "$owning_home" ]]; then
  printf 'Backup stopped: cron profile and script directory differ.\n' >&2
  exit 1
fi
export HERMES_HOME="$owning_home"

config="$script_dir/hermes-github-backup.conf"
[[ -f "$config" ]] || { printf 'Backup stopped: reviewed configuration missing.\n' >&2; exit 1; }
# This file is local, owner-reviewed shell configuration. It must contain no secret.
# shellcheck source=/dev/null
source "$config"
: "${HERMES_BIN:?set an absolute Hermes executable path}"
: "${REPO_DIR:?set an absolute private clone path}"
: "${STAGING_PARENT:?set an absolute private staging path}"
: "${AGE_RECIPIENT:?set an age public recipient}"
: "${EXPECTED_REMOTE:?set the exact private GitHub SSH remote}"
: "${BRANCH:=main}"

fail() { printf 'Backup stopped: %s.\n' "$1" >&2; exit 1; }
for path in "$HERMES_BIN" "$REPO_DIR" "$STAGING_PARENT"; do
  [[ $path == /* ]] || fail 'a configured path is not absolute'
done
[[ -x "$HERMES_BIN" ]] || fail 'Hermes executable unavailable'
[[ $AGE_RECIPIENT == age1* ]] || fail 'age public recipient invalid'
[[ $EXPECTED_REMOTE == git@github.com:*/*.git ]] || fail 'remote is not the reviewed GitHub SSH URL'
[[ $BRANCH =~ ^[a-zA-Z0-9._/-]+$ ]] || fail 'branch name invalid'
[[ -d "$STAGING_PARENT" ]] || fail 'staging directory unavailable'
stage_parent=$(CDPATH= cd -- "$STAGING_PARENT" && pwd -P)
[[ $stage_parent != "$owning_home" && $stage_parent != "$owning_home"/* ]] || fail 'staging is inside Hermes data'

# Atomic, nonblocking whole-flow lock: keep it through push and remote readback.
# A stale directory after a crash requires operator inspection; never steal it.
lockdir="$stage_parent/.hermes-github-backup-flow.lock"
mkdir -- "$lockdir" 2>/dev/null || fail 'another backup flow holds the lock or a stale lock needs review'
work=''
cleanup() {
  if [[ -n $work ]]; then
    rm -f -- "$work/hermes.zip" "$work/latest.zip.age" "$work/latest.sha256" "$work/backup.log"
    rmdir -- "$work" 2>/dev/null || true
  fi
  rmdir -- "$lockdir" 2>/dev/null || true
}
trap cleanup EXIT

[[ -d "$REPO_DIR/.git" ]] || fail 'Git repository unavailable'
repo=$(CDPATH= cd -- "$REPO_DIR" && pwd -P)
[[ $repo != "$owning_home" && $repo != "$owning_home"/* ]] || fail 'repository is inside Hermes data'
[[ $stage_parent != "$repo" && $stage_parent != "$repo"/* ]] || fail 'staging is inside Git repository'
[[ $repo != "$stage_parent"/* ]] || fail 'Git repository is inside staging'

command -v age >/dev/null || fail 'age unavailable'
command -v gh >/dev/null || fail 'GitHub CLI unavailable for private visibility check'
command -v unzip >/dev/null || fail 'zip verifier unavailable'
command -v git >/dev/null || fail 'Git unavailable'
remote_repo=${EXPECTED_REMOTE#git@github.com:}
remote_repo=${remote_repo%.git}
visibility=$(gh repo view "$remote_repo" --json isPrivate --jq .isPrivate 2>/dev/null) || fail 'GitHub repository visibility unreadable'
[[ $visibility == true ]] || fail 'GitHub repository is not confirmed private'
raw_fetch_urls=$(git -C "$repo" config --get-all remote.origin.url) || fail 'Git fetch URL missing'
[[ $raw_fetch_urls == "$EXPECTED_REMOTE" ]] || fail 'Git has multiple or unexpected raw fetch URLs'
raw_push_urls=$(git -C "$repo" config --get-all remote.origin.pushurl || true)
[[ -z $raw_push_urls ]] || fail 'separate Git pushurl requires independent review'
effective_fetch_urls=$(git -C "$repo" remote get-url --all origin) || fail 'effective fetch URL unreadable'
effective_push_urls=$(git -C "$repo" remote get-url --push --all origin) || fail 'effective push URL unreadable'
[[ $effective_fetch_urls == "$EXPECTED_REMOTE" ]] || fail 'Git fetch URL rewrite or extra destination'
[[ $effective_push_urls == "$EXPECTED_REMOTE" ]] || fail 'Git push URL rewrite or extra destination'
[[ $(git -C "$repo" branch --show-current) == "$BRANCH" ]] || fail 'local branch changed'
[[ -z $(git -C "$repo" status --porcelain --untracked-files=all) ]] || fail 'repository has unrelated changes'

# No interactive prompt is possible in an unattended Hermes cron tick.
export GIT_TERMINAL_PROMPT=0
export GIT_SSH_COMMAND='ssh -o BatchMode=yes'
remote_head=$(git -C "$repo" ls-remote --heads origin "$BRANCH" | awk '{print $1}')
[[ -n $remote_head && $remote_head == "$(git -C "$repo" rev-parse HEAD)" ]] || fail 'remote unreadable or branch has changed'

work=$(mktemp -d "$stage_parent/hermes-backup.XXXXXXXX") || fail 'private staging creation failed'

if "$HERMES_BIN" backup -o "$work/hermes.zip" >"$work/backup.log" 2>&1; then
  :
else
  backup_rc=$?
  fail "Hermes full backup returned $backup_rc; temporary zip/log will be removed, so diagnose with a separate controlled manual run"
fi
[[ -s "$work/hermes.zip" ]] || fail 'Hermes archive absent or empty'
unzip -tq "$work/hermes.zip" >"$work/backup.log" 2>&1 || fail 'zip integrity check failed'
age -r "$AGE_RECIPIENT" -o "$work/latest.zip.age" "$work/hermes.zip" >"$work/backup.log" 2>&1 || fail 'archive encryption failed'
[[ -s "$work/latest.zip.age" ]] || fail 'encrypted archive absent or empty'

# Deliberately below GitHub's 50 MiB warning. Git history retains every version.
cipher_bytes=$(stat -c %s "$work/latest.zip.age")
(( cipher_bytes <= 40000000 )) || fail 'encrypted archive exceeds this small-repository recipe'
git_bytes=$(du -sb "$repo/.git" | awk '{print $1}')
(( git_bytes + cipher_bytes + 10000000 <= 500000000 )) || fail 'repository history approaches the review limit'
hash=$(sha256sum "$work/latest.zip.age" | awk '{print $1}')
printf '%s  latest.zip.age\n' "$hash" >"$work/latest.sha256"

cp -- "$work/latest.zip.age" "$repo/latest.zip.age"
cp -- "$work/latest.sha256" "$repo/latest.sha256"
git -C "$repo" add -- latest.zip.age latest.sha256
staged=$(git -C "$repo" diff --cached --name-only)
[[ $staged == $'latest.sha256\nlatest.zip.age' ]] || fail 'staged paths are not the two reviewed files'
git -C "$repo" diff --cached --check >/dev/null || fail 'staged content check failed'
git -C "$repo" commit -m "Encrypted Hermes backup $(date -u +%Y-%m-%dT%H:%M:%SZ)" >/dev/null || fail 'local backup commit failed'
run_head=$(git -C "$repo" rev-parse HEAD)
(( $(du -sb "$repo/.git" | awk '{print $1}') <= 500000000 )) || fail 'Git history exceeds review limit before push'
[[ $(git -C "$repo" rev-parse HEAD) == "$run_head" ]] || fail 'local HEAD changed before push'
git -C "$repo" push origin "$run_head:refs/heads/$BRANCH" >/dev/null || fail 'remote push failed; inspect exact remote before retrying'
remote_head=$(git -C "$repo" ls-remote --heads origin "$BRANCH" | awk '{print $1}')
[[ $remote_head == "$run_head" ]] || fail 'this run commit not confirmed at remote'
printf 'Encrypted backup verified at private remote commit %s.\n' "$run_head"
Terminal
# No passwords, tokens, age identities or private keys belong here.
HERMES_BIN=/absolute/path/to/hermes
REPO_DIR=/absolute/path/to/dedicated-private-github-clone
STAGING_PARENT=/absolute/path/to/private-staging-outside-hermes-and-git
AGE_RECIPIENT=age1replace_with_operator_public_recipient
EXPECTED_REMOTE=git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git
BRANCH=main

أنشئ دليل التجهيز خارج جذر Hermes ونسخة Git. ضع الملفات المراجعة في دليل scripts/ بملف المالك الشخصي. يجب أن تكون نسخة Git نظيفة ومتوافقة مع main البعيد قبل كل تشغيل. يقفل النص مساره كله حتى الإرسال وإعادة القراءة؛ ولا ينتزع قفلًا ممسوكًا أو قديمًا. لا يقبل كمسارات نسخ جديدة مجهّزة لـ Git إلا latest.zip.age و latest.sha256. يتوقف إذا تجاوز النص المشفّر 40,000,000 بايت أو اقترب .git المحلي مع الهامش من 500,000,000 بايت. هذه حدود محافظة للمثال وليست ضمانات تخزين GitHub.

Terminal
export HERMES_HOME=/absolute/path/to/owning-hermes-data-root
install -d -m 700 /ABSOLUTE/PRIVATE-STAGING
install -d -m 700 "$HERMES_HOME/scripts"
install -m 700 hermes-github-backup.sh "$HERMES_HOME/scripts/hermes-github-backup.sh"
install -m 600 hermes-github-backup.conf "$HERMES_HOME/scripts/hermes-github-backup.conf"
"$HERMES_HOME/scripts/hermes-github-backup.sh"

ينبغي أن ينتهي التشغيل اليدوي برمز خروج صفر ويطبع، بعد قراءة بعيدة جديدة، commit الذي أُنشئ في ذلك التشغيل نفسه. تأكد أن gh repo view ما زال يبلغ عن الخصوصية، وافحص المسارات المتتبعة وcommit الدقيق في المستودع البعيد المقصود. الأرشيف أو commit المحلي لا يمثل نتيجة خارج الجهاز. لا تسجّل التشغيل المتوقف كذلك. وفق Hermes يعيد النسخ الكامل 0 فقط عند إضافة كل الملفات المختارة، و 1 للأرشيف الناقص ولو وُجد ZIP، و 2 إذا كان نسخ آخر يعمل. يحذف النص ZIP المؤقت والسجل عند الخروج؛ الحذف العادي ليس محوًا آمنًا. للملفات المتخطاة، استقصِ بنسخة يدوية مستقلة ومضبوطة عبر hermes backup -o /PRIVATE-DIAGNOSTIC/hermes.zip، وافحص حالة الخروج والتقرير وقرر حماية المواد المتروكة.

3. جدوله وافحص أول تشغيل مستحق

بعد النتيجة اليدوية فقط، استخدم وضع Hermes الموثّق --no-agent --script من الملف الشخصي نفسه. يشغّل ملفًا دون استدعاء LLM. اختر التكرار وتسليم الحالة محليًا بلا أسرار عمدًا. افحص هوية نظام البوابة والملف والموعد التالي. وفق Hermes تؤخذ مهلة النص من HERMES_CRON_SCRIPT_TIMEOUT ثم cron.script_timeout_seconds ثم القيمة الافتراضية 3,600 ثانية. اضبط قيمة تناسب تشغيلًا ملاحظًا وتحقق منها؛ انتهاء المهلة فشل. البيئة المنقّاة لا تتلقى تلقائيًا اعتماد المزوّد الذي يديره Hermes؛ تحقق من وصول gh وSSH دون تدخل تحت المستخدم المجدول الفعلي قبل الاعتماد عليه.

Terminal
hermes cron create '0 3 * * *' --no-agent --script hermes-github-backup.sh --deliver local --name 'Encrypted Hermes backup'
hermes cron list
hermes cron status
hermes cron doctor

بعد أول موعد مجدول افحص معرف المهمة وسجل التشغيل الدقيقين، والمخرجات المحلية تحت ~/.hermes/cron/output/ للملف الافتراضي، وأي حادثة، وcommit الرأس في البعيد المقصود. تأكد أن البوابة ظلت متاحة وأن التشغيل أبلغ عن commit البعيد الناتج عن ذلك التفعيل. الاستدعاء اليدوي لا يثبت عمل cron. عند غياب المخرجات افحص المهمة والبوابة بدل إنشاء جدول ثانٍ. لا تضع محتويات ZIP أو الأسرار أو الاعتماد في رسائل الحالة.

4. أثبت الجلب والاستيراد على جهاز جديد

استخدم جهاز Linux تجريبيًا منفصلًا قابلًا للتخلص منه، دون ملف Hermes إنتاجي أو بوابة. ثبّت إصدار Hermes متوافقًا أولًا: الأرشيف بيانات مستخدم لا صورة تطبيق أو بيئة تشغيل. امنح جهاز الاختبار قراءة مستقلة ومصرّحًا بها للمستودع الخاص المقصود وهوية age المحفوظة استقلالًا. تأكد من عنوان البعيد وcommit المختار. لا تشغّل hermes import على الجهاز المصدر أو ملف شخصي قائم. أبقِ كل الجداول والأفعال الخارجية المستعادة معطّلة حتى يراجع إنسان وجهاتها ويقرر التفعيل.

Terminal
umask 077
unset HERMES_HOME
git clone git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git /PRIVATE-RECOVERY/hermes-backup-clone
cd /PRIVATE-RECOVERY/hermes-backup-clone
sha256sum -c latest.sha256
age --decrypt -i /PRIVATE-KEY-STORE/hermes-backup-identity.txt -o /PRIVATE-RECOVERY/hermes.zip latest.zip.age
unzip -tq /PRIVATE-RECOVERY/hermes.zip
hermes import /PRIVATE-RECOVERY/hermes.zip
hermes setup
hermes cron list

قارن الملفات الشخصية والمهارات والذاكرة والجلسات وتعريفات cron المستوردة بما أردت حمايته. hermes setup يفحص إعداد المزوّد، لكن راجع الاعتماد والمسارات الخاصة بالجهاز التي تحتاج تعديلًا. سجّل النتائج الفعلية. تطابق المجموع الاختباري وإمكان فك ZIP وحدهما لا يثبتان استعادة قابلة للاستخدام. أزل ZIP الاختباري غير المشفّر بعد التمرين وفق السياسة المحلية؛ الحذف العادي لا يضمن المحو الآمن.

عندما تتوقف الفحوص

نسخة Hermes ناقصة أو خطأ ZIP أو تشفير أو عنوان Git فعلي غير متوقع أو مستودع غير خاص أو غير مقروء أو قفل ممسوك أو حد حجم أو إرسال فاشل أو commit بعيد مختلف أو استيراد فاشل، كلها تترك التشغيل غير مؤكّد. عالج السبب المحدد وافحص الوضع الحالي قبل المحاولة. إن كان الإرسال غير محسوم فافحص الفرع البعيد وcommit الدقيق أولًا. إذا بقي القفل بعد انهيار، افحص العملية الحية والحالة المحلية والبعيدة؛ لا تحذف قفلًا نشطًا مطلقًا. فقدان هوية age الخاصة يتطلب نسخة مستقلة صالحة لفك التشفير. غيّر الاعتماد المكشوف أولًا إذا دخلت أسرار غير مشفّرة إلى Git؛ حذف ملف لاحق لا يمحو التاريخ القديم أو النسخ الأخرى.

إرشادات GitHub للملفات الكبيرة تحذّر عند 50 MiB وتمنع ملفات Git العادية فوق 100 MiB، وتقول إن Git لم يُصمم أداة نسخ احتياطي. يتوقف المثال عمدًا دون هذه الحدود. تبقى كل نسخة متغيرة من النص المشفّر في تاريخ Git حتى عندما يُستبدل latest.zip.age. لا ترفع حدود 40 MB/500 MB سرًا، ولا تعِد كتابة التاريخ، ولا تعتبر الخصوصية تشفيرًا. أعد تقييم مخزن مخصص للنسخ أو خطة LFS مراجعة استقلالًا إذا فشلت فحوص الحجم أو الاستعادة. تستمر تكاليف الحساب والتخزين والشبكة والجهاز؛ النص بلا وكيل نفسه لا يحتاج استدعاء LLM. لم تُقَس مدة أو نسبة نجاح أو وفورات لهذه الطريقة.

لمهمة Hermes مجدولة أخرى أضيق، راجع إعداد مهمة Hermes متكررة تعتمد على مهارة والتحقق من تشغيلها الأول. فحص تقريرها لا يحل محل أرشيف مشفّر وتمرين استعادة.

المصادر وقراءات إضافية