نموذج الأمان
يشغّل aipager برنامج Claude Code نيابة عنك ويُدار من Telegram. ومن هنا سؤالان:
- من يستطيع التحكم في البوت؟
- ماذا يستطيع البوت أن يفعل حين يُتحكَّم فيه؟
حدود الثقة#
كل معالج يفتحه البوت (الرسائل، والملفات، والرسائل الصوتية، والأزرار) يمر عبر مرشّح الدردشة في python-telegram-bot، المبني من الدردشات المضبوطة في ~/.config/aipager/aipager.yaml، أو (للأوامر) عبر الفحص نفسه في نظام التخويل الخاص بـ aipager. الاستثناء الوحيد لا يفعل إلا تسجيل المرسلين في المجموعات غير المضبوطة (أدناه). لا يستطيع التفاعل مع البوت إلا الدردشات المضبوطة. لا شيء من أي دردشة أخرى يُعالَج أو يصل إلى جلسة. الدردشة الخاصة غير المضبوطة تتلقى ردًا واحدًا «هذا البوت غير مهيأ للتحدث معك». في مجموعة غير مضبوطة لا يرد البوت أبدًا، لكنه يسجّل من راسله هناك (معرّف Telegram واسم المستخدم والاسم لذلك الشخص، ومعرّف المجموعة وعنوانها ونوعها) في ~/.claude/aipager-pending-users.json، ليتمكن aipager config من إيجاد مجموعة جديدة وأشخاصها أثناء عمل aipager. يستطيع أي أحد إضافة البوت إلى مجموعة، لذا يستطيع أي أحد إضافة سطر إلى ذلك الملف؛ وهذا لا يمنح شيئًا. لا يقرأ الملف أحد غيرك (الصلاحية 0600)، ويُستبدل كاملًا عند كل كتابة (لا يبقى أبدًا نصف مكتوب)، ويحتفظ بأحدث ٢٠٠ شخص على الأكثر، ويُسقط كل من لم يظهر منذ ٣٠ يومًا. في وضع الفريق تُضاف فوق ذلك قائمة سماح لكل مستخدم مع الأدوار، وفي المجموعة لا يُعالَج إلا ما وُجّه إلى البوت (أمر، أو رد على رسالته، أو ذكر له، أو ضغطة على لوحة مفاتيحه)، سواء كان البوت مشرفًا في المجموعة أم لا (يُنظر المجموعات).
وهذا يعني أن ما يواجه «العالم الخارجي» هو:
- توكن البوت (سر).
- معرّف الدردشة (عدد صحيح طويل).
إذا تسرّب الاثنان، يستطيع المهاجم التحكم في aipager لديك. إذا تسرّب التوكن وحده، يستطيع المهاجم قراءة الرسائل المرسلة إلى البوت من دردشتك لكنه لا يستطيع إرسال أوامر ينفّذها aipager. إذا تسرّب معرّف الدردشة وحده، فلا فائدة منه: يحتاج البوت إلى التوكن ليتحدث مع Telegram أصلًا.
الأسرار#
| السر | المكان | الصلاحية |
|---|---|---|
| توكن البوت | ~/.config/aipager/aipager.yaml | 600 افتراضيًا |
| معرّفات الدردشات | الملف نفسه | 600 افتراضيًا |
لا يدخل التوكن سجل aipager: أسطر طلبات HTTP التي تحمله لا تُسجَّل، ورسائل الخطأ وaipager doctor ومعالج الإعداد تُخفي كل ما يشبه توكن بوت. أما معرّفات الدردشات فتظهر في سجل aipager (مثلًا في أسطر حدود السرعة)، لذا يُعامل ناتج aipager logs على أنه خاص. ولا يُودع أي منهما في المستودع: الإعداد في ~/.config لدى المستخدم، لا في المستودع. ومسار إصدار PyPI عبر Trusted Publisher لا يلمس الأسرار أيضًا؛ فالمصادقة يتولاها OIDC.
إذا كان هناك شك في تسرّب التوكن، يُلغى من @BotFather (/revoke)، ويُنشأ توكن جديد، ويُعاد تشغيل aipager config.
بيانات اعتماد Claude: ما الذي يحميها فعلًا#
يشغّل aipager وكيلًا مستقلًا له صلاحية Bash بصفة مستخدم UNIX لديك. لذا فكل سر يستطيع aipager قراءته، يستطيع الوكيل الذي يشغّله قراءته أيضًا. يستحق هذا أن يُقال صراحة، لأن من السهل الاعتماد على نوع خاطئ من الحماية.
~/.config/aipager/ موجود في قائمة المنع في safety.py، التي تمنع الجلسة المدارة من Telegram من قراءة الملفات تحته. هذه حماية تعتمد على المسار، ولا تنفع هنا: بيانات اعتماد Claude (CLAUDE_CODE_OAUTH_TOKEN أو ANTHROPIC_API_KEY) لا يُوصل إليها بمسار، بل تُورَّث إلى بيئة العملية الخاصة بكل جلسة تُشغَّل. داخل الجلسة، يطبعها echo $CLAUDE_CODE_OAUTH_TOKEN، بوجود قائمة المنع أو بدونها. قاعدة مسار الملف هي نوع خاطئ من الحماية لمتغير بيئة.
أين توجد بيانات الاعتماد#
| طريقة التشغيل | المصدر | الصلاحية |
|---|---|---|
| خدمة مستخدم systemd | LoadCredential=claude_oauth:%h/.config/aipager/daemon.env، تُقرأ من $CREDENTIALS_DIRECTORY/claude_oauth | 600 (daemon.env)؛ ويُبقي systemd بيانات الاعتماد خارج بيئة الخدمة نفسها |
| macOS / Docker / دون systemd | ~/.config/aipager/daemon.env، تُقرأ مباشرة | 600 |
يقرأ المسارين الكود نفسه (aipager/daemon_secrets.py) ويسلّمها إلى الجلسة المُشغَّلة عبر جدول env= للعملية الفرعية، ولا يُدرجها أبدًا في نص الأمر bash -c. الملف /proc/PID/cmdline يقرؤه الجميع (0444)؛ والملف /proc/PID/environ صلاحيته 0400 (للمالك فقط).
ما يقدّمه LoadCredential= + 0600 فعلًا: لا يستطيع مستخدمو UNIX الآخرون على الكمبيوتر نفسه قراءة بيانات الاعتماد، ولا تظهر أبدًا في systemctl show، ولا في نسخة احتياطية لملف الخدمة، ولا في لقطة شاشة لمخرجات journalctl يلصقها مالك البوت في تقرير خلل. لا يُنشئ حدًا بين aipager والوكيل الذي يشغّله aipager: هذا الحد غير موجود، ولا يمكن لأي ضبط لصلاحيات الملفات إنشاؤه، لأن عمل الوكيل كله هو التصرف بصفة مالك البوت داخل البيئة نفسها.
الحماية الوحيدة التي تعمل فعلًا#
تقييد بيانات الاعتماد نفسها. تشغيل aipager بحساب Anthropic منفصل، أو بمفتاح API له حد إنفاق، لا بالتوكن نفسه المستخدم لتسجيل دخولك التفاعلي إلى claude. إلغاء بيانات اعتماد aipager يجب ألا يُخرج الإنسان من حسابه أبدًا؛ وإن فعل، فهما بيانات الاعتماد نفسها ولا تقييد لأي منهما. يستطيع aipager doctor --fix إيجاد توكن موجود لملء daemon.env، لكن اختيار أي توكن يُعطى له قرار مالك البوت، وهو القرار الوحيد في هذا المستند الأهم من صلاحيات الملفات المحيطة به.
تشخيص تسجيل الدخول دون تخمين#
يبلّغ aipager doctor (والإشعار ذو السطر الواحد الذي يُرسل مرة عند كل بدء لـ aipager) عن تسجيل الدخول عبر claude auth status من Claude Code نفسه، ولا يعتمد أبدًا على فحص ملفات مصنوع يدويًا. ثلاث حالات تبقى مكتوبة بنصوص مختلفة أينما ظهرت:
-
auth: <method> (<source>): مسجّل الدخول، بتأكيد. -
auth: none (not logged in): غير مسجّل الدخول، بتأكيد. -
auth: unknown (...): فشلت التجربة نفسها (انتهاء المهلة، أو البرنامج غير موجود، أو مخرجات غير مقروءة) أو البرنامج أقدم من الإصدار الذي أضاف مخرجات JSON. هذه الحالة لا تُعرض أبدًا على أنها «غير مسجّل الدخول»: لا يرفض aipager بدء جلسة بسبب فحص تسجيل دخول غير متأكد منه. تسجيل الدخول عبر Keychain في macOS، وبيانات الاعتماد التي ليس فيها إلا refresh token، وتسجيل الدخول في خطة Max دون ملف، كلها غير مرئية لأي فحص ملفات، وكانت ستبدو تمامًا مثل «تم تسجيل الخروج».
من يقرر هل يعمل استدعاء الأداة#
فحصان منفصلان يقرران هل يعمل استدعاء الأداة، ويعملان بهذا الترتيب:
- فحص الأمان في aipager. يشغّل Claude Code هوك
PreToolUseالخاص بـ aipager قبل كل استدعاء أداة. للمهمة التي بدأت من Telegram، يُخضع الاستدعاء لقواعد دور المرسل وللحد الأدنى للأمان (المسارات المحمية، وأنماط الأوامر)، ويرفض ما ترفضه: فيرفض Claude Code الأداة دون سؤال أحد، مهما قالت قواعده. الرسالة المكتوبة في الطرفية لا يقيّدها هذا الفحص. ما يغطيه مشروح أدناه، في تطبيق القواعد في وضع الفريق. - قواعد Claude Code نفسه (
~/.claude/settings.json، ووضع الأذونات للجلسة). إذا قالت «اسأل»، يطلق Claude Code الحدثPermissionRequest، ويعرض aipager الطلب في Telegram (يُنظر الهوكات). تعود ضغطتك إلى Claude Code قرارًا من الهوك، أو تُكتب في مربع حوار Claude Code نفسه إذا انتهت مهلة الانتظار البالغة ٢٠ ثانية. إذا رفضت قواعد Claude Code الاستدعاء، فلا يصل إلى Telegram أبدًا.
لذا يستطيع aipager رفض استدعاء كان Claude Code سيسمح به، وحين يسأل Claude Code لا يجيب aipager «نعم» من تلقاء نفسه أبدًا: السماح دائمًا ضغطة من إنسان. ثلاثة أشياء تجعل الاستدعاءات تعمل دون سؤال أحد:
- وضع الوصول الكامل، للمشرفين (ولأي شخص في دردشته الخاصة). الجلسة في وضع الوصول الكامل تُشغَّل مع
--dangerously-skip-permissions: لا يسأل Claude Code عن شيء، فكل استدعاء لا يرفضه فحص الأمان في aipager يعمل دون ضغطة. لا يستطيع إلا المشرف (الدورانownerوadmin، أو دور فيهcan_manage) بدء جلسة في وضع الوصول الكامل، أو تبديل جلسة إليه (/mode full، بعد تأكيد) أو استئنافها فيه، مع استثناء واحد: في دردشته الخاصة مع البوت، يستطيع الشخص الذي يسمح دوره بإرسال الرسائل والرد على Claude (can_promptوcan_approve، أيuserلاread_only) فعل الشيء نفسه لجلساته (/new !x1، أو ضبط «الجلسات الجديدة» على الوصول الكامل في تلك الدردشة، و/mode full، و/resume)، في Telegram وفي Mini App. أبدًا في مجموعة أو في الدردشة الخاصة لشخص آخر. كل من سواهم يحصل على الوضع التلقائي بدلًا من ذلك، ويذكر الرد ذلك. وضع الوصول الكامل لا يتخطى إلا أسئلة Claude Code نفسه: يظل هوك PreToolUse في aipager يطبّق قواعد دور ذلك الشخص (الحد الأدنى للأمان، وحصر المجلد، وقواعد المنع، وملفات بيانات الاعتماد) على كل استدعاء، تمامًا كما في وضع «اسأل» والوضع التلقائي؛ والوضع الذي يبلّغ عنه Claude Code لا يخففها أبدًا. يظل فحص الأمان في aipager يعمل عند كل استدعاء في وضع الوصول الكامل، لكنه لا يرفض شيئًا في مهام المالك نفسه ولا في رسالة مكتوبة في الطرفية. - الوضع التلقائي، الافتراضي. الجلسة في الوضع التلقائي تُشغَّل مع
--permission-mode auto: فحص الأمان في Claude Code نفسه يراجع كل استدعاء، فيشغّل الآمن ويوقف الخطر، ويصل الخطر إلى Telegram طلبًا كما في وضع «اسأل». تبدأ كل جلسة في الوضع التلقائي ما لم تختر الدردشة غير ذلك في /settings← الجلسات الجديدة. كل من يستطيع التصرف في جلسة يستطيع تبديلها بين «اسأل» والتلقائي (/mode)؛ ويفعل aipager ذلك في مكانه بالضغط على Shift+Tab في الجلسة، ولا يستخدم أبدًا خيارًا يضع الوصول الكامل في دورة Shift+Tab في Claude Code. إذا أبلغ Claude Code يومًا عن وضع الوصول الكامل لجلسة لم يبدأها aipager فيه، يعرض aipager ما أُبلغ عنه، ويكتب تحذيرًا في السجل، ولا يبدأ الجلسة أو يعيد تشغيلها في وضع الوصول الكامل بسبب ذلك أبدًا. حد معروف: الوضع الذي يبلّغ عنه Claude Code يدخل عبر مقبس الهوك، الذي تستطيع أي عملية محلية الكتابة إليه (يُنظر الاتصالات الشبكية). بلاغ مزيّف يستطيع تغيير الوضع الذي تُعرض به الجلسة، وبينما ينتظر تبديل بين «اسأل» والتلقائي تأكيده، يستطيع أن يجعل aipager يضغط Shift+Tab مرة أخرى، فتبقى الجلسة الجارية في الوضع التلقائي بينما المطلوب «اسأل» (الهوك الحقيقي التالي يعرض الوضع الحقيقي مرة أخرى). لا يغيّر أبدًا الخيار الذي تمرّره إعادة التشغيل أو الاستئناف، ولا يستطيع أبدًا بلوغ الوصول الكامل: خيار البدء وحده يضبطه. لا يضغط aipager Shift+Tab في جلسة يعرف أنها قد تدور إلى الوصول الكامل: جلسة بدأت في وضع الوصول الكامل (حتى بعد تبديلها إلى «اسأل» أو التلقائي في الطرفية)، أو جلسة أبلغت aipager الجاري عن الوصول الكامل منذ بدئها. هناك كل تبديل للوضع إعادة تشغيل، تمرّر خيار الوضع الجديد وتُسقط خيار الوصول الكامل، والبلاغ المزيّف عن الوصول الكامل لا يستطيع إلا تحويل التبديل إلى إعادة تشغيل. لا يستطيع aipager معرفة ذلك لجلسة بدأت في الطرفية بالخيار الذي يضيف الوصول الكامل إلى دورة Shift+Tab (--allow- أمام خيار الوصول الكامل)، أو بـ --dangerously-skip-permissionsحين غادرت الوصول الكامل قبل أن يسمع aipager منها (لم يكن aipager يعمل حينها، أو أُعيد تشغيله منذ ذلك). هناك قد يتوقف التبديل من «اسأل» إلى التلقائي عند الوصول الكامل، وتقول البطاقة عندها إن الجلسة في وضع الوصول الكامل. لا تُستخدم تلك الخيارات لجلسات طرفية يستطيع غير المشرفين تبديل وضعها من Telegram. ولكي يعمل وضع الوصول الكامل من Telegram، يضبط aipagerskipDangerousModePermissionPromptفي ~/.claude/settings.jsonعند كل بدء (وإلا لأوقف تحذيرُ Claude Code قبل العمل في هذا الوضع الجلسةَ)؛ وهذا يوقف التحذير أيضًا للجلسات المشغّلة يدويًا. وبالمثل يعلّم المجلد الذي تبدأ فيه كل جلسة بأنه موثوق في ~/.claude.json(ما لم يكن ذلك المجلد أو مجلد أعلى منه موثوقًا من قبل)، حتى لا يوقف سؤال Claude Code «Do you trust this folder?» جلسة بدأت من Telegram: فالرسالة الأولى ستجيب عنه بـ«No, exit». يمكن لإعدادات .claude/ وهوكات .git/ وملف .mcp.jsonالخاصة بمجلد موثوق أن تشغّل أوامر، ولهذا لا يستطيع الأعضاء الذين لا يملكونcan_manageكتابة هذه الملفات أبدًا (ما لم يمنحpolicy.yamlدورهم shell من جديد). يطبّق Claude Code ثقة المجلد على المجلدات التي تحته ولا تقع داخل مستودع git؛ أما المستودع فيبقى له سؤاله الخاص. يُعلَّم المجلد الرئيسي حين تبدأ جلسة فيه، لأن Claude Code يحفظ الموافقة التي تُعطى في المجلد الرئيسي لتلك الجلسة وحدها. بعد ذلك لا يسأل Claude Code شيئًا في أي مجلد تحت مجلدك الرئيسي لا يقع داخل مستودع git، حتى في الجلسات التي بدأتها بنفسك. لا يُعلَّم/ولا المجلدات الأعلى من المجلد الرئيسي أبدًا: فذلك سيشمل كل مجلد من هذا النوع على الجهاز، ولذا تتوقف الجلسة التي تبدأ في أحدها عند السؤال. لا يعلّم aipager شيئًا عند بدئه، بل المجلد الذي توشك جلسة أن تبدأ فيه فقط. تبدأ الجلسة الجديدة في المجلد الذي يعمل فيه aipager (المجلد الرئيسي معaipager service installعلى Linux وmacOS وفي الحاوية)، أو فيAIPAGER_WORK_DIRإذا ضُبط؛ ويحذّر سجل aipager عند البدء إذا كان ذلك المجلد/أو مجلدًا أعلى من المجلد الرئيسي وغير موثوق. كانت الإصدارات السابقة لهذا تعلّم مجلد aipager عند كل بدء، بما في ذلك/لخدمة macOS (التي كانت تعمل في/)؛ ولا يستطيع aipager أن يميّز مثل هذا الإدخال من إدخال أنشأته بنفسك، فيتركه. ويترك aipager الملفين ~/.claude.jsonو~/.claude/settings.jsonدون تغيير إذا تعذّرت عليه قراءتهما كاملين، أو إذا غيّرهما Claude يعمل بينما كان aipager يحدّثهما. - السماح دائمًا، الذي يعيد إلى Claude Code قاعدة «لا تسأل مجددًا» التي عرضها مع الطلب، تمامًا كما يفعل ذلك الخيار في مربع الحوار الخاص به.
لإغلاق أداة أمام الجميع، تُمنع في ~/.claude/settings.json؛ ولإغلاقها أمام بعض الأشخاص، تُستخدم قواعد دورهم في policy.yaml (يُنظر المجموعات).
ما يعرضه الطلب هو ما يعمل. يعرض طلب الإذن الأمر أو المسار الحقيقي تحت وصف Claude له. حروف التحكم في الاتجاه في Unicode (U+202A-U+202E، U+2066-U+2069، U+200E، U+200F، U+061C) تغيّر ترتيب رسم Telegram أو المتصفح أو الهاتف للنص الذي بعدها، بينما تشغّل الطرفية الحروف بترتيبها، فقد تجعل أمرًا يبدو كأمر آخر (حيلة «Trojan Source»، CVE-2021-42574). في كل طلب وسؤال ومعاينة تغييرات وسجل تدقيق، تُعرض كعلامات مرئية (⟨U+202E⟩)، ويحذّر الطلب من أن الأمر قد لا يشغّل ما يبدو عليه. أما الحروف المخفية الأخرى فتُترك كما هي.
تطبيق القواعد في وضع الفريق#
في وضع الفريق يضيف aipager طبقته الخاصة تحت الضغطات: كل رسالة قادمة من Telegram تحمل ملاحظة إذن (من أرسلها وماذا يسمح دوره)، تُكتب في /tmp/claude-notes-<session>/، ملف لكل رسالة. حين يأخذ Claude رسالة، يطابقها البرنامج المساعد aipager-hook مع تلك الملاحظات ويضع قواعد المرسل حيث يقرؤها فحص PreToolUse. كل ما عدا ذلك يعتمد على هذه الخصائص:
- الأشد يغلب. إذا أخذ Claude رسائل من أكثر من شخص في مهمة واحدة (يحتجز aipager الرسائل لمنع ذلك، لكنه يبقى آمنًا إن حدث)، تعمل المهمة بالمزيج الأشد تقييدًا: رسالة المالك لا تعير صلاحياتها لغيره أبدًا.
- المهمة الجارية لا تتسع أبدًا. رسالة من شخص آخر تنتظر حتى تنتهي المهمة الجارية، ثم تعمل مهمةً مستقلة بقواعد مرسلها. وإذا انضمت إحداها إلى مهمة جارية رغم ذلك، تحتفظ المهمة بأشد قواعد كل من فيها؛ ورسالة Telegram التي تنضم إلى مهمة كتبتها في الطرفية تُخضع بقية تلك المهمة لقواعد مرسلها (رسالة المالك لا تغيّر شيئًا).
- الدور المحسوب هو دورك في الدردشة التي يحدث فيها الإجراء. الشخص المذكور في عدة دردشات (مثلًا
userفي مجموعة وownerفي دردشته الخاصة) يحمل دور المجموعة في كل ما يرسله أو يضغطه في المجموعة، ودور الدردشة الخاصة في الدردشة الخاصة، مهما كان ترتيب الدردشات فيaipager.yaml. - المجهول يعني المقيَّد. الرسالة التي لا يمكن نسبتها إلى أحد تعمل بالحد الأدنى المدمج (بلا استثناء، وكل قواعد المنع فعّالة)، ولا تعمل أبدًا كرسالة طرفية غير مقيدة. الرسائل المكتوبة مباشرة في الطرفية تبقى غير مقيدة؛ وكل ما يحمل علامة Telegram في أي سطر يخضع للفحص. كل رسالة Telegram تحمل تلك العلامة، باسم الشخص الذي أرسل تلك الرسالة (لا من أدار الجلسة آخر مرة أبدًا)؛ والمرسل الذي لا يعرفه aipager، مثلًا من حُذف من
aipager.yamlبينما رسالته تنتظر، يحصل على [via Telegram] مجردة وعلى الحد الأدنى. - أوامر الشرطة المائلة تحتاج إلى مشرف. أمر الشرطة المائلة يُكتب في Claude كما هو، دون علامة، والأمر أو المهارة مثل
/deliverيشغّل مهمة كاملة من نصه الخاص. لذا فالعضو الذي لا يتجاوز دوره قواعد الأدوار (userوread_onlyالمدمجان، والأدوار المخصصة دونbypass_role_denies، وكل من لا يعرفه aipager) لا يستطيع إرسال إلا الأوامر الموجودة في لوحة «الأوامر» في aipager (إضافة إلى /compact) وتبديلات النموذج (/model <name>)؛ وأي أمر شرطة مائلة آخر، سواء كُتب، أو أُرسل إلى جلسة بـ /<name>، أو كان تعليقًا على ملف، أو أول رسالة لـ /new، أو قالبًا، يُرفض بـ «ذلك الأمر للمشرفين فقط.» مع 🤷. المالكون والمشرفون يرسلون أي أمر. أمر الشرطة المائلة الذي يصل إلى Claude من Telegram يظل خاضعًا للفحص: يرى الهوك أن المهمة أخذت رسالة Telegram أرسلت هذا الأمر فيُخضعها لقواعد مرسلها. أمر الشرطة المائلة المكتوب في الطرفية يبقى غير مقيد، ما لم يكن الأمر نفسه (بأي وسيطات) قد أُرسل من Telegram في وقت سابق من تلك الجلسة: لا يستطيع aipager التمييز بين نسختك وإعادة Claude Code تسليم نسخة Telegram، لذا تعمل نسختك عندها بقواعد آخر مهمة من Telegram. وينتهي هذا بانتهاء الجلسة. - الرسالة التي لا مرسل جديدًا فيها تُبقي قواعد المهمة. يبلّغ Claude Code أيضًا عن رسائل لا تفسّرها أي رسالة Telegram بينما تجري مهمة من Telegram: رسالة كُتبت في الطرفية ووُضعت في قائمة الانتظار خلف المهمة (يُبلَّغ عنها لحظة وضعها في القائمة)، أو رسالة من جلسة Claude محلية أخرى، أو رسالة Telegram نفسها تُسلَّم مرة أخرى بعد التلخيص. رسالة كهذه لا تضيف مرسلًا من Telegram، لذا تُبقي المهمة القواعد التي تعمل بها، مشددةً بأي رسالة ما زالت تنتظر أن تُؤخذ، ولا تخففها أبدًا. والرسالة التي تحمل نص Telegram لا يستطيع aipager نسبته إلى أحد تعود إلى الحد الأدنى المدمج، والرسالة من مرسل صلاحياته أقل تُنزل المهمة إلى قواعد ذلك المرسل.
ما الذي يحصر المستخدم المقيَّد فعلًا. تعمل جلسة Claude بصفة مستخدم النظام نفسه الذي يملك إعداد aipager (توكن البوت في ~/.config/aipager/aipager.yaml، وبيانات اعتماد Claude في ~/.config/aipager/daemon.env) وملف سياسته لكل مهمة (/tmp/claude-policy-<session>.json). طرفية تعمل بصفة ذلك المستخدم تستطيع الوصول إلى كليهما بطرق كتابة لا يتوقعها أي نمط (sed -i … /tmp/*-policy-*.json، أو حلقة for على glob، أو سطر Python واحد) وإعادة كتابة قواعدها لتصبح قواعد المالك. لذا فأنماط الأوامر مرشّح، لا حد. للمهمة التي تعمل بالأدوار المقيدة المدمجة (user وread_only)، وللمهمة التي لا يستطيع aipager نسبتها إلى أحد، يطبّق aipager هذه القواعد، ولا يعتمد أي منها على أنماط الأوامر (يُنظر الحدود المعروفة أدناه لما لا تغطيه):
- لا طرفية. لا يستطيعون استخدام
Bash، ولا أي أداة أخرى يشغّل بها Claude Code شيفرة (PowerShellوMonitorوREPLوWorkflow، ومجدولا الرسائلCronCreateوRemoteTrigger، …؛ القائمة هيsafety.CODE_EXECUTION_TOOLS). الرسالة المجدولة ستصل دون علامة Telegram، أي غير مقيدة، لذا تُحسب هذه الأدوات أيضًا. وللسبب نفسه لا يستطيعون استخدامSendMessage(قد يسلّم رسالة إلى جلسة Claude محلية أخرى، فتعمل فيها كرسالة طرفية)، ولاEnterWorktree/ExitWorktree(ينقلان مجلد عمل الجلسة، الذي تعتمد عليه القاعدة التالية). - الكتابة تبقى داخل المشروع.
WriteوEditوMultiEditوNotebookEditلا تكتب إلا داخل مجلد الجلسة (مجلد العمل الذي يبلّغ عنه Claude Code في بيانات الهوك، لا مسار من استدعاء الأداة أبدًا) وفي scratchpad الخاص بالجلسة في Claude Code (/tmp/claude-<uid>/<project>/<session id>/، أو /tmp/claude-<uid>/ حين يتعذر تحديد ذلك). كل ما عدا ذلك مرفوض: ملفات بدء الطرفية لديك، و~/.ssh، والمشاريع الأخرى، وملفات aipager في /tmp. داخل المجلد، .claude/ و.git/ و.mcp.jsonمرفوضة أيضًا، لأن كلًا منها يجعل Claude Code أو git يشغّل أمرًا. تُتبع الروابط الرمزية قبل القرار. الجلسة التي مجلدها هو مجلدك الرئيسي، أو/، أو مجلد أعلى من مجلدك الرئيسي، ليس لها مجلد مشروع: هناك لا تكتب المهمة المقيدة إلا في scratchpad الخاص بها، والكتابة في أي مكان آخر تُرفض بـ «تعمل هذه الجلسة في المجلد الرئيسي (أو مجلد أعلى منه)، ولا يكتب هذا الدور إلا داخل مجلد مشروع». وحتى لا يعلق الأعضاء المقيدون هناك، لا يسمح لهم /newوMini App ببدء جلسة في تلك المجلدات (مجلد aipager نفسه، الافتراضي للجلسة الجديدة، هو مجلدك الرئيسي تحت خدمة الخلفية)؛ ويستطيعون إنشاء مجلد جديد داخل مجلدك الرئيسي والبدء فيه (جديد حقًا: لا مجلدًا موجودًا لديك هناك، ولاbinأوnode_modules، اللذين تحمّل منهما برامج أخرى شيفرة). - الأعضاء يبقون في مجلدهم الجذر. الدور الذي ليس فيه
can_manage(userوread_onlyالمدمجان) يعمل داخل المجلد الجذر للدردشة، المضبوط فيaipager config(الافتراضي ~/aipager-users/<label>؛ ويُنشأ ~/aipager-usersوالمجلد بالصلاحية 0700). لا يبدأ /newوالاستئناف والاستبدال وإعادة التشغيل وتبديل الوضع وMini App جلسة عضو كهذا أو يعيد تشغيلها إلا داخله. يفرض هوك الأمان ذلك أيًّا كان طريق وصول الرسالة (Telegram، أو Mini App، أو رسالة من جلسة أخرى): ينتقل المجلد الجذر في لقطة سياسة المهمة، وفي إيصالات الرسائل بين الجلسات، وفي الصلاحيات المثبّتة والمجدولة، وحين يتشارك أعضاء لهم مجلدات جذر مختلفة مهمة واحدة يغلب الأضيق (والمجلدان اللذان لا يتداخلان لا يتركان إلا مجلد الجلسة نفسه). لا تقع الكتابة في مجلد الجلسة إلا حين يكون داخل المجلد الجذر (الجلسة خارجه، مثل جلسة بدأت قبل وجود المجلدات الجذرية، تستمر في العمل، لكن مهامها لا تكتب إلا في scratchpad: «تعمل هذه الجلسة خارج مجلد هذا الدور»)، ولا تصلReadوLSPوGrepوGlobإلى أي شيء في مجلدك الرئيسي خارج المجلد الجذر («في المجلد الرئيسي، لا يقرأ هذا الدور إلا داخل مجلده الخاص»)، باستثناء scratchpad الجلسة وملف الرد الخاص بها. المجلد الجذر لكل دردشة أخرى مسار محمي بالنسبة إلى العضو، أينما كان، وكذلك مجلد الملفات المرفوعة لكل دردشة أخرى تحت /tmp/aipager-files/ (ReadوGrepوGlobوأدوات الكتابة؛ وفي دور أُعطي Bash يُرفض الأمر الذي يذكر أحدها، بذلًا للجهد)، ومعها الملفات التي كانت الدردشات كلها تتشاركها هناك قبل أن يصبح لكل دردشة مجلدها. أما الملفات المرفوعة في دردشة العضو نفسه فتبقى مقروءة. المجلدات خارج مجلدك الرئيسي (/usr، /etc، /srv) تبقى مقروءة. الدور المخصص الذي فيهbypass_role_deniesأوbypass_safetyدونcan_manageيكتب ويبحث في أي مكان داخل مجلده الجذر، وقراءاته في مجلدك الرئيسي تبقى داخله أيضًا: المالكون والمشرفون وحدهم بلا قيود. ومعbypass_safety، في جلسة مجلدها خارج مجلده الجذر، لا يكتب ولا يبحث إلا في scratchpad، كما يفعلuser، وبمجرد رفض أحد استدعاءات أدواته تتوقف بقية المهمة. - البحث يبقى داخل المشروع. لا يُسمح بـ
GrepأوGlobإلا إذا تحقق كل ما يلي: المجلد الذي يبحث فيه (pathالخاص به، أو مجلد الجلسة إن لم يكن له، مع اتباع الروابط الرمزية) داخل مجلد الجلسة (لا حين يكون ذلك مجلدك الرئيسي، أو/، أو أعلى من مجلدك الرئيسي، كما في الكتابة) أو داخل scratchpad؛ وذلك المجلد ليس مسارًا محميًا ولا يحتوي على مسار محمي (الجلسة التي بدأت في مجلدك الرئيسي تحتوي على ~/.config/aipager، فلا تستطيع البحث من أعلاه)؛ وكل glob نمط نسبي بسيط (دون/أو~أو$أو حرف قرص في البداية، ودون..، ودون#(تعليق بالنسبة إلى ripgrep) أو!(نفي)، ودون محرف الهروب\)؛ وليس لديك أي قاعدة مسار محمي لا تبدأ بـ/أو~(**/.envقد يوجد في أي مكان في المشروع، لذا مع وجود قاعدة كهذه يُرفض كل بحث مقيد). وكل ما عدا ذلك مرفوض. هذه قائمة سماح عن قصد: ثلاث جولات مراجعة وجدت كل منها طريقة كتابة glob جديدة أخرجت ripgrep من المجلد الذي توقعه نموذج لطريقة تحليله، وإحداها قرأت توكن البوت. - أدوات الملفات تحترم المسارات المحمية. تتحقق
ReadوLSPوأدوات الكتابة من المكان الذي يؤدي إليه المسار فعلًا، بما في ذلك الروابط الرمزية. ملف التحكم الوحيد الذي يجوز للمهمة قراءته هو /tmp/claude-reply-<session>.txtالخاص بجلستها، الذي يذكره aipager في الرسالة عند الرد على رسالة أقدم؛ وملفات الرد للجلسات الأخرى، وأي كتابة فيه، تبقى مرفوضة. على macOS، /tmpهو /private/tmpوالقرص لا يميّز حالة الأحرف، لذا تُطابَق الكتابتان، وأي حالة أحرف. - الفحص الذي يتعطل يرفض. إذا أثار اتخاذ القرار بشأن استدعاء أداة خطأً (بايت NUL في مسار، أو خلل)، أو نفدت ذاكرة الهوك أثناء القرار، يُرفض الاستدعاء، ما لم تكن قواعد الجلسة مقروءة وتمنح استثناء المالك. كان يُسمح به سابقًا.
المسارات المحمية الخاصة بك. قسم safety: في policy.yaml (deny_paths_no_access وdeny_paths_no_write وdeny_bash_patterns) يضيف إلى المسارات المحمية وأنماط الأوامر المدمجة. وهو جزء من الحد الأدنى الذي تحصل عليه كل مهمة لكل دور ما عدا owner، بما في ذلك المشرفون، ويسري على أدوات الملفات والبحث وأوامر Bash تمامًا مثل القائمة المدمجة. يكتب aipager القائمة المجمّعة في /tmp/claude-policy-.floor-<uid>.json (وهو نفسه مسار محمي) للمسارات الاحتياطية في هوك الأمان؛ وإذا كان ذلك الملف غير موجود، أو غير مقروء، أو يملكه مستخدم نظام آخر، يستخدم الهوك القائمة المدمجة، ولا يستخدم أقل منها أبدًا. التغيير الذي يُجرى بإعادة تحميل دون إعادة تشغيل يسري على الرسائل المرسلة بعد إعادة التحميل، لا على مهمة جارية أو رسالة تنتظر. حتى 0.7.20 كان هذا القسم يُقرأ ويُعرض في aipager doctor --safety-check لكنه لا يُطبَّق.
المهام التي لا يستطيع aipager نسبتها إلى أحد. مهمة Telegram التي لا مرسل يمكن إخضاعها لقواعده تعمل بالحد الأدنى، المطابق للدور user المدمج (لا لأي قيود إضافية مضافة إلى user في policy.yaml) مع قسم safety: الخاص بك: بلا أدوات تشغيل شيفرة، ومع حصر الكتابة والبحث، والمسارات المحمية. يحدث ذلك لرسالة انتهت صلاحية ملاحظة إذنها (بعد ٢٤ ساعة) أو تعذّرت كتابتها، ولرسالة تحمل نص Telegram لا تطابقه أي ملاحظة. يعمل زر إعادة المحاولة بصلاحيات من ضغطه حين يكون هو نفسه من أرسل الرسالة التي تُعاد، لذا فإعادة المالك لرسالته تحتفظ بـ Bash؛ أما إعادة رسالة شخص آخر، أو رسالة لا يعرف aipager مرسلها، فتعمل بالحد الأدنى، حتى لا يستعير نص أحد صلاحيات من يضغط.
القراءة خارج المسارات المحمية مسموحة لدور لا يُحصر في مجلد جذر (دور مخصص فيه can_manage دون أي استثناء): يستطيع قراءة المشاريع الأخرى لمستخدم النظام. أما user وread_only المدمجان فلا يقرآن شيئًا في مجلدك الرئيسي خارج مجلدهما الجذر (أعلاه). ملفات بيانات اعتمادك مسارات محمية لكل دور ليس فيه bypass_role_denies (user وread_only المدمجان، والأدوار المخصصة، والحد الأدنى): ~/.ssh و~/.gnupg و~/.aws و~/.config/gh و~/.git-credentials و~/.netrc و~/.docker/config.json و~/.kube و~/.config/gcloud و~/.azure و~/.password-store و~/.pgpass و~/.npmrc و~/.pypirc لا يمكن قراءتها ولا الكتابة فيها ولا البحث فيها. إنها قيمة افتراضية للدور، لذا فـ deny_paths_no_access الخاص بدور في policy.yaml يحل محلها لذلك الدور، إلا حين تنضم إلى مهمة جارية رسالة أخرى أو رسالة مكتوبة في الطرفية: من تلك اللحظة حتى نهاية المهمة، تخضع لها من جديد (الأشد يغلب). ملف بيانات اعتماد في مكان آخر (.env في مشروع، أو توكن في مجلد آخر) غير مشمول: يُضاف إلى deny_paths_no_access الخاص بالدور. المالكون لا يخضعون لأي من هذا. كتابة المشرفين غير محصورة. ويستطيع Grep/Glob لديهم البحث في أي مجلد لا يصل إلى مسار محمي: يُرفض البحث حين يكون المجلد الذي يبحث فيه (path الخاص به، أو مجلد الجلسة إن لم يكن له؛ ولـ Glob أيضًا المجلد الذي يذكره نمطه، كما في /home/you/.config/aipager/*، أو المجلد الذي فوق نمط بلا محارف بدل، حيث يبحث Claude Code عن ذلك الاسم على أي عمق)، بعد حل الروابط الرمزية، مجلدًا محميًا، أو داخل مجلد محمي، أو يحتوي على مجلد محمي. لذا يُرفض Grep على ~، أو /، أو المجلد الذي فوق مجلدك الرئيسي، أو /tmp (تحتوي على ~/.config/aipager أو ~/.claude أو ملفات /tmp/claude-* الخاصة بـ aipager)، ولا يُرفض Grep على مشروع أو على /tmp/myproj. قاعدة المسار المحمي التي لا تبدأ بـ / أو ~ (مثل **/.env) قد تطابق في أي مجلد، لذا مع وجودها في قواعد مشرف (أو في قسم safety:، الذي يحصل عليه كل دور ما عدا owner) يُرفض كل بحث للمشرف، كما للأدوار المقيدة؛ ويحذّر aipager doctor من قاعدة كهذه في قسم safety:. المشرفون لديهم Bash، لذا تبقى قواعدهم بذلًا للجهد (أدناه).
الحدود المعروفة. هذه الأمور غير مشمولة:
- إذا تعطّل الهوك نفسه قبل أن يبدأ القرار (مثلًا لا يستطيع قراءة ملف حالته، أو تنفد ذاكرته أثناء البدء)، يعامل Claude Code ذلك على أنه «سماح». لا يستطيع المستخدم المقيَّد التسبب في هذا؛ وأي خطأ أثناء القرار يرفض.
- الأدوات التي ليست أدوات ملفات لكنها تستطيع إرسال بيانات إلى الخارج لا يُفحص مسارها:
WebFetch(كل ما تستطيع المهمة قراءته تستطيع إرساله إلى عنوان URL)، وأدوات الإرسال الأخرى في Claude Code. يمكن منعها لدور ما فيpolicy.yamlإن كان ذلك مهمًا. أماSendUserFile، الذي يرسل الملفات إلى الدردشة، فيُفحص (انظر الملفات التي يرسلها Claude). - الوكيل في الخلفية الذي بدأته مهمة مقيدة يستمر في العمل بعد انتهاء المهمة، وتُحكم استدعاءات أدواته اللاحقة بأحدث رسالة في الجلسة: رسالة لاحقة من المالك أو من الطرفية ترفع قيوده.
- الدور المخصص الذي فيه
can_manageدون أي من الاستثناءين لا يُحصر في مجلد جذر: يستطيع البدء في أي مجلد مشروع تعمل فيه دردشته من قبل (مجلد استخدمته جلسة سابقة)، بما في ذلك مجلد لك، وقراءة مشاريعك الأخرى. - المهمة التي لا يمكن إخضاعها لأي عضو (مرسل مجهول، أو عضو محذوف، أو رسالة من جلسة أخرى يتعذر التحقق من مرسلها) تعمل بالحد الأدنى المقيد، وليس له مجلد جذر. في القراءة، هذا الحد أضعف من
userالمدمج: تستطيع مهمة كهذه قراءة أي ملف في مجلدك الرئيسي ليس مسارًا محميًا (مشاريعك الأخرى مثلًا)، بينما لا تقرأ مهمةuserإلا داخل مجلدها الجذر. وتستطيع مهمة كهذه أيضًا قراءة الملفات المرفوعة من كل الدردشات في /tmp/aipager-files/، بينما لا تقرأ مهمةuserمنها إلا ملفات دردشتها. وتبقى كتابتها محصورة في مجلد الجلسة وscratchpad. والرسالة المتحقَّق منها من جلسة عضو تحمل المجلد الجذر لذلك العضو. - المجلد الجذر لا يقيّد إلا أدوات الملفات. الدور المخصص الذي يُعطى
Bashيستطيع القراءة خارجه عبر الطرفية (userوread_onlyالمدمجان ليس لديهماBash). والدور المخصص الذي فيهbypass_safetyدونcan_manageيحتفظ باستثنائه في كل شيء ما عدا مسارات أدوات الملفات: لا أداة ولا نمط أمر ممنوع عليه. - سبب الرفض الذي يذكر المجلد الجذر لعضو آخر يعرض مسار ذلك المجلد لـ Claude وفي الدردشة.
- مهمة العضو التي كانت جارية حين ظهرت المجلدات الجذرية (بتحديث) ليس لها مجلد جذر حتى رسالة ذلك العضو التالية.
- داخل المشروع، يستطيع المستخدم المقيَّد تغيير ما سيُشغَّل أو يُقرأ من جهتك لاحقًا (الشيفرة المصدرية، و
Makefile، وسكربتاتpackage.json، وconftest.py، وملفCLAUDE.mdتحمّله مهامك).
الدور الذي لديه Bash (admin، الذي يتجاوز قواعد منع الأدوار، أو أي دور تعيده إليه policy.yaml: allow_tools تذكر Bash، أو deny_tools مستبدلة) لا يخضع إلا لقواعد هي بذل للجهد: أنماط الأوامر أدناه، التي يستطيع شخص مصمم الالتفاف عليها. يحذّر aipager doctor مرة لكل دور كهذا يحمله عضو. لا يُعطى Bash إلا لمن يمكن ائتمانه على طرفية في هذا الكمبيوتر.
قواعد الأوامر المدمجة. من بين أنماط Bash التي تخضع لها كل مهمة Telegram لديها Bash وليست للمالك، نمط يمنع الكلمة claude: يوقف claude متداخلًا (بأي مسار أو غلاف أو خيار) وأي وصول إلى ~/.claude بقاعدة واحدة، ويلتقط أيضًا الأسماء الصريحة لملفات التحكم /tmp/claude-* الخاصة بـ aipager، التي تشترك في البادئة نفسها (النمط ليس حماية حقيقية لتلك الملفات: كتابة بـ glob تتجنبه). الاستثناء الوحيد هو مجلد scratchpad الذي ينشئه Claude Code، /tmp/claude-<uid>/…: ذلك المجلد بالضبط، كجزء كامل من المسار، مستثنى، لذا لا تتوقف الأوامر المشغّلة من scratchpad الجلسة. يملكه مستخدم النظام، لا جلسة واحدة، لذا تستطيع المهمة التي لديها Bash قراءة scratchpad الجلسات الأخرى ومخرجات مهامها هناك، كما تستطيع أداة Read. أما glob له أو اسم يشبهه (/tmp/claude-1*، claude-1000x)، أو مجلد مؤقت آخر، أو أي شيء يُوصل إليه منه (…/../../home/you/.claude) فيبقى ممنوعًا. والمواضع الأخرى لهذه الكلمة (git log --grep=claude، أو ملف اسمه claude-notes.md، أو pip show claude-agent-sdk) تبقى ممنوعة أيضًا، عن قصد: الكلمة نفسها تسمّي البرنامج، وحزم SDK الخاصة به، وملفات تحكم aipager، والنمط لا يستطيع التمييز بينها. القواعد المضافة في policy.yaml ترى الأمر كما كُتب بالضبط، بما فيه scratchpad.
البحث عبر Bash. بعض إصدارات Claude Code لا تقدّم أداة Grep أو Glob (2.1.289 أحدها)، فيبحث Claude عندها بـ grep أو rg أو find عبر Bash، حيث لا تسري قاعدة Grep/Glob أعلاه. لكل دور ما عدا owner، تمنع أنماط الأوامر أيضًا البحث أو السرد العودي للمجلد الرئيسي كله أو لـ /: grep -r (-R، --recursive، -d recurse، egrep، fgrep، rgrep)، وrg (وrg --files)، وag، وack (وack -f)، وfind، وfd، وtree، وdu وls -R حين يكون مجلدها ~، أو ~user، أو $HOME، أو ${HOME} أو "$HOME"، أو /home، أو /home/<user> (أو /home/$USER)، أو /Users، أو /Users/<user>، أو /root أو / (مع / أو /. أو /* في النهاية أو دونها)، أو ~/.config أو ~/.local، اللذين يحتويان مجلدات aipager. يبقى هذا صحيحًا بعد cd x &&، وفي أنبوب، وداخل bash -c "..."، وبعد xargs أو env أو timeout، وبعد إعدادات على طريقة LC_ALL=C. ويقترح الرد البحث داخل مجلد مشروع بدلًا من ذلك. البحث في مجلد مشروع (grep -rn x ~/proj، rg x src/، find . -name y)، والبحث داخل مشروع عن مسار المجلد الرئيسي (grep -rn "$HOME" src، فأول كلمة بعد الخيارات هي النص المطلوب، لا مجلد)، والبحث غير العودي (grep x ~/.bashrc، ls ~) لا تتأثر. هذا بذل للجهد، ككل نمط أوامر: النمط يرى الكلمات، لا مكان تشغيل الطرفية لها، فلا يرى بحثًا بلا مجلد، أو مع . أو ..، يُشغَّل من المجلد الرئيسي (أو ما تحته) (cd ~ && grep -r x .)، ولا مجلدًا رئيسيًا يُوصل إليه عبر متغير آخر أو glob، ولا مجلدًا رئيسيًا مكتوبًا كاملًا خارج /home و/Users و/root، ولا \grep، ولا مجلدًا يأتي مباشرة بعد كلمة تنتهي بـ -e أو -f (يُعدّ النص الذي تعطيه -e/-f لـ grep وrg، لذا يمر ag x -f ~). ولا يعرف أيضًا أي الخيارات تأخذ قيمة منفصلة، لذا فإن rg -t py "$HOME" . أو grep -rn -A 3 "/home/you" src يقرأ py أو 3 على أنه النص ومسار المجلد الرئيسي على أنه مجلد، فيُرفض (الحل كتابة -A3، أو --include=*.py، أو وضع الخيار بعد النص)، وكذلك مسار المجلد الرئيسي المعطى لـ -e داخل مجموعة خيارات (grep -rne "/home" src؛ أما -r -n -e فلا بأس به)؛ والأمر المقتبس داخل نص أمر آخر (echo "a; rg x ~") يُرفض أيضًا، وكذلك du -sh ~ (الأحجام فقط).
لماذا يوقف منع واحد المهمة كلها. بعد منع استدعاء أداة، يُرفض كل استدعاء لاحق في المهمة نفسها أيضًا، حتى الرسالة التالية. دون ذلك، يعيد الوكيل ببساطة محاولة الإجراء نفسه بصياغة أخرى (glob، أو أمر مختلف، أو مسار مُرمَّز) حتى تفلت إحدى الكتابات من الأنماط. إيقاف المهمة يجعل المحاولة الأولى مكلفة؛ ولا يجعل النمط حدًا، لأن أول كتابة تُجرَّب قد تكون هي التي تفلت. ثمن المنع الخاطئ مهمة واحدة متوقفة، ولهذا تُبقى القواعد أعلاه ضيقة.
يُنظر المجموعات ← كيف تعمل القواعد للقواعد التي تحملها هذه الملاحظات، كما تظهر لك.
الملفات التي يرسلها Claude#
في كل جلسة يبدؤها aipager أداة SendUserFile الخاصة بـ Claude Code (CLAUDE_CODE_ATTACHMENTS_BY_PATH=1)، وهي تقرأ الملفات وتوصلها إلى دردشة الجلسة (الأوامر). يفحص هوك PreToolUse كل ملف في الاستدعاء قبل أن يقرأه Claude Code:
- الحد الأدنى للإرسال، لكل مهمة، بما فيها مهام المالك والرسالة المكتوبة في الطرفية: إعدادات aipager وحالته (
~/.config/aipager/** و~/.local/share/aipager/** و~/.local/state/aipager/** وملفات التحكم الخاصة به في /tmp)، وكل ~/.claude/** و~/.claude.json* (بيانات اعتماد Claude وحسابه)، وملفات بيانات الاعتماد التي لا يصل إليها أي دور مقيَّد (~/.ssh/** و~/.gnupg/** و~/.aws/** و~/.git-credentialsو~/.netrcوبقية تلك القائمة)، و/proc/** و/sys/** و/dev/**، والأسماء التي تبدو أسرارًا أينما كانت (.envو.env.* و*.pemو*.keyوid_rsa* وid_ed25519* وأسماء المفاتيح الأخرى و*.p12و*.pfxو*.kdbxو*.jksو*.keystoreو.pgpassوcredentialsو*credentials*.json). يُطابَق المسار كما كُتب وكما يقود فعلًا، فيُرفض أيضًا الرابط الرمزي إلى أي منها. وبعض الملفات غير الضارة تحمل هذه الأسماء (ملف *.keyمن Keynote، أو .env.example) فتُرفض كذلك. - الملف نفسه: ملف عادي بعد حلّ الروابط الرمزية، له رابط صلب واحد، غير فارغ، وحجمه ٥٠ ميغابايت كحد أقصى.
- قواعد المهمة نفسها: ما لا يحق للمهمة قراءته (Read)، وللعضو (دور بلا
can_manageمقيَّد بـ مجلد جذر) القاعدة الأشد: يجب أن يكون كل ملف، كما كُتب وكما يقود فعلًا، داخل المجلد الجذر. وعلى خلاف ما يقرؤه العضو، لا استثناء للملفات خارج المجلد الرئيسي (/tmpو/etc) ولا لمسودة الجلسة (scratchpad).
الرفض رفض ليّن: يقرأ Claude السبب («SendUserFile refused, nothing was sent: ... Tell the person which file could not be sent and why.») وتستمر المهمة؛ ولا تتوقف الجلسة كما يحدث حين تُحجب أداة. والفحص الذي يفشل يرفض الملف، حتى للمالك. ولكل ملف مرفوض سجل FileRefused في سجل التدقيق.
لا يصدّق aipager مقبس التحكم على كلامه حين يتعلق الأمر بملف: يستطيع أي برنامج محلي الكتابة فيه وتسمية أي جلسة أو أي استدعاء أو أي سجل محادثة، بل إن hook استدعاء أداة وحده يجعل الجلسة الخاملة تبدو كأنها تعمل. لذلك لا يُحتسب الاستدعاء إلا خلال مهمة رأى aipager أنها بدأت بموجّه (موجّه كتبه هو من Telegram، أو UserPromptSubmit) وخلال العمل في الخلفية لتلك المهمة، إلى أن تصبح الجلسة خاملة دون أي عمل جارٍ؛ وعند تلك البداية يسجّل aipager الوقت وسجل المحادثة الذي يتابعه للجلسة (وUserPromptSubmit لاحق من المهمة نفسها يسمّي سجل المحادثة من جديد). أمر الشرطة المائلة الذي يكتبه aipager لا يبدأ أي مهمة (والأمر الذي يشغّل مهمة يرسل UserPromptSubmit الخاص به)، والمهمة التي لا تبدأ أبدًا (تبقى الجلسة خاملة دون عمل بعد ١٥ ثانية) تُغلق من جديد. يبدأ Claude Code بعض المهام دون UserPromptSubmit: رسالة أُرسلت أثناء تشغيل مهمة فيأخذها مهمته التالية، والمهمة التي يُبقيها hook من نوع Stop مستمرة (/goal). تُحتسب مهمة كهذه حين يكون aipager نفسه قد كتب تلك الرسالة، أو بمجرد أن يُظهر سجل المحادثة الذي سجّله بدايتها بعد آخر Stop (سطر طابور dequeue، أو سطر Stop hook feedback: الذي يكتبه Claude Code)؛ وحتى ذلك الحين لا يُقرأ شيء من الاستدعاء، لمدة تصل إلى ٢٠ ثانية. ويأخذ قائمة الملفات من PreToolUse الذي فحصه، أو من PostToolUse حين يفوته الأول (بعد إعادة تشغيل)؛ ولا يأخذها من نتيجة Claude Code أبدًا. وينسخ كل استدعاء مرة واحدة فقط: معرّف الاستدعاء الذي نسخه من قبل لا يُنسخ ولا يُرسل مرة أخرى أبدًا (يحتفظ بآخر ٢٥٦ معرّفًا لكل جلسة، حتى بعد إعادة التشغيل). يحدد المسار الحقيقي لكل ملف مرة واحدة فقط ويفحص الملف من جديد عليه (الحد الأدنى وفحوص الملف والمجلد الجذر للجلسة)، ويفتح هذا المسار بعينه دون أن يتبع أي رابط، ويتأكد أن الملف المفتوح هو نفسه الذي فحصه وأنه ما زال في هذا المسار، ثم ينسخه إلى ~/.local/state/aipager/outbox/<chat>/ (المجلد 0700 والملف 0600)، وهو مكان لا تصل إليه أي مهمة مقيَّدة أصلًا. وقبل الإرسال يبحث عن الاستدعاء في سجل المحادثة الذي سجّله للمهمة (وفي حالة استدعاء وكيل فرعي، في سجل محادثة ذلك الوكيل الفرعي نفسه بجانبه)، ولا يبحث أبدًا في سجل تسمّيه رسالة بيانات لاستدعاء أداة: سطر من المساعد فيه tool_use من SendUserFile بهذا المعرّف وبهذه الملفات تمامًا، كُتب من المجلد نفسه وليس قبل بداية المهمة، في ملف .jsonl تحت ~/.claude/projects (أو $CLAUDE_CONFIG_DIR/projects كما هو معرّف لـ aipager نفسه) يملكه مستخدم النظام الذي يعمل به aipager نفسه. يكتب Claude Code سجل المحادثة متأخرًا، لذلك ينتظر aipager الاستدعاء حتى ٢٠ ثانية، وما دام عمل الجلسة جاريًا (وكيل في الخلفية، استدعاء أداة) يحتفظ بالملفات ويبحث من جديد لاحقًا. والاستدعاء الذي لا يجده حتى بعد أن يتوقف كل شيء لا يُرسل وتُحذف نسخه؛ أما الاستدعاء الذي وصل ولا مهمة كهذه جارية، أو وصل مرة ثانية، فلا يُنسخ أصلًا. ويحصل كل ملف من ملفاتها على سجل FileFailed في سجل التدقيق بالسبب unverified. ولا تحصل الدردشة على سطر عنها (⚠️ تعذّر إرسال report.pdf: لم يتمكن aipager من التأكد من أن Claude أرسله ضمن هذه المهمة.) إلا حين يُظهر سجل محادثة Claude نفسه ذلك الاستدعاء بعينه خلال المهمة، ويسمّي السطر الملفات التي يذكرها ذلك السطر من السجل، فلا يستطيع استدعاء مزيّف أن يضع في الدردشة سطرًا ولا اسمًا؛ وفيما عدا ذلك لا شيء في الدردشة. لا يُرسل إلا النسخة، وإلى دردشة الجلسة نفسها فقط، بعد إجابة المهمة وبعد ملفات الجلسة السابقة ودون أن يؤخر المهمة التالية؛ وتُحذف فور إرسالها، أو بعد يوم.
ما لا تغطيه هذه الفحوص: البرنامج الذي يعمل بمستخدم النظام الذي يعمل به aipager نفسه يستطيع كتابة سجلات محادثة Claude كما يكتب في المقبس (فهو يستطيع أصلًا قراءة تلك الملفات بنفسه). والبرنامج التابع لمستخدم آخر ويصل إلى المقبس يستطيع أيضًا أن يزيّف UserPromptSubmit، وهو ما يبدأ مهمة في نظر هذه الفحوص ويسمّي سجل محادثتها؛ ثم يستطيع أن يجعل aipager يرسل إلى دردشة تلك الجلسة ملفًا سمّاه استدعاء حقيقي لـ SendUserFile، إذا كُتب ذلك الاستدعاء في ذلك السجل من المجلد نفسه بعد تلك اللحظة (كاستدعاء تجريه جلسة أخرى في تلك اللحظة نفسها مثلًا)، مرة واحدة لكل استدعاء. ولفعل ذلك يجب أن يرسل أيضًا معرّف ذلك الاستدعاء الجاري نفسه (tool_use_id)، ولا يستطيع مستخدم آخر قراءته: فهو في سجلات المحادثة تحت ~/.claude لمستخدم aipager، ويستلمه الـ hook عبر مدخله لا عبر سطر أوامره. وStop المزيّف ينهي المهمة في نظر هذه الفحوص، فلا تُرسل الملفات التي ترسلها تلك المهمة بعده. والجلسة التي بدأت بـ $CLAUDE_CONFIG_DIR غير الذي يستخدمه aipager، أو المهمة التي فات aipager UserPromptSubmit الخاص بها (كُتبت في الطرفية بينما كان aipager يعيد التشغيل)، لا ترسل أي ملف. ولا ترسل ملفات أيضًا مهمة يبدؤها Claude Code دون hook بطريقة أخرى (المعروف هو الطريقتان أعلاه فقط)؛ والملف الذي يُظهر سجل محادثة Claude أنه طلبه هناك يحصل على السطر أعلاه. يوجد المقبس في $XDG_RUNTIME_DIR، الذي لا يصل إليه المستخدمون الآخرون، حين يكون معرّفًا؛ وإلا فهو في /tmp.
سجل التدقيق#
كل ضغطة سماح / رفض، وكل إجابة عن AskUserQuestion، تضيف سطر JSON واحدًا إلى ~/.claude/aipager-audit.jsonl:
{
"ts": "2026-05-18T15:42:11+00:00",
"session": "claude-jim",
"label": "jim",
"action": "Allowed",
"tool": "Bash",
"summary": "ls -la /tmp",
"user_id": 12345,
"username": "alice",
"denied": false
}
الحقول (يُنظر aipager/audit.py للمجموعة الكاملة):
-
ts: طابع زمني UTC بصيغة ISO 8601، بدقة الثانية. -
session/label: الاسم الداخلي والاسم المقروء. -
action: ما حدث (Allowed،Denied،Allowed always، أو إجابةAskUserQuestion، أو رفض تلقائي، …). -
tool/summary: اسم الأداة ومدخل مختصر أو نص السؤال. -
user_id/username: من ضغط، لمعرفة من فعل ماذا في وضع الفريق؛ وscope_label/scope_chat_idحين تكون هناك عدة دردشات مضبوطة. -
denied: true لأي رفض، بضغطة أو بقاعدة.
الكتابة بذل للجهد. إذا امتلأ القرص أو أصبح ~/.claude/ غير قابل للكتابة، يكتب aipager تحذيرًا WARNING في السجل ويستمر في العمل: لا ضياع صامت، ولا تعطّل. يُنظر aipager/audit.py.
سجل التدقيق على القرص للإضافة فقط. ومع رد التدقيق في الدردشة (رسالة Telegram لكل قرار، تحت بطاقة العمل) يصبح لديك سجلان مستقلان.
حدود الصلاحيات#
aipager لا يرفع صلاحياته أبدًا. لا sudo، ولا setuid، ولا doas. كل ملف يكتبه موجود في مجلدك الرئيسي، أو في /tmp (ملفات التحكم لكل جلسة في aipager، والتنزيلات، وسجل إعادة التشغيل) أو في مجلد مقبس التحكم الخاص به ($XDG_RUNTIME_DIR)؛ والقائمة الكاملة في البنية. وكل عملية فرعية (claude، وdtach، والمثبّتات، وclaude update) تعمل بصفة مستخدم aipager.
تثبيت ميزة الرسائل الصوتية من Telegram ([📦 تثبيت ميزة الرسائل الصوتية]) يمر عبر المثبّت الذي يملك aipager (uv tool install --reinstall aipager[voice]، pipx install --force aipager[voice])، وإلا يثبّت حزم الميزة بـ Python الخاص بـ aipager نفسه (<python> -m pip install): دائمًا في بيئة aipager نفسه، لا في Python النظام أبدًا.
الزر [🔄 إعادة تشغيل aipager]، تحت خدمة systemd، يجدول systemctl --user restart aipager.service في خدمة مستخدم مؤقتة منفصلة؛ وعلى macOS يشغّل launchctl kickstart -k؛ أما aipager الذي بدأته بنفسك فينشئ نسخة منفصلة من نفسه تنتظر خروج القديمة. بعد التحديث، لا تعيد تشغيل نفسها إلا خدمة systemd (يُنظر الأوامر ← التحديث). وكلها تعمل بصفة المستخدم نفسه؛ دون أي رفع للصلاحيات.
الاتصالات الشبكية#
لا يستمع aipager على أي منفذ TCP غير loopback. يرتبط خادم Mini App بـ 127.0.0.1 فقط (مكتوب في الشيفرة: لا خيار للمضيف عن قصد) ولا يمكن الوصول إليه من الخارج إلا عبر النفق المُدار الموصوف أدناه. الاتصالات الصادرة:
- استطلاع HTTPS طويل إلى
api.telegram.org(استقبال تحديثات بوت Telegram). - HTTPS إلى
telegram.orgلسكربت SDK الخاص بـ Mini App (telegram-web-app.js)، يُجلب مرة عند بدء خادم Mini App ويُحدَّث مرة في اليوم على الأكثر؛ ويُخزَّن مؤقتًا في ~/.local/share/aipager/webapp-sdk/. يقدّم aipager تلك البايتات للصفحة من مصدره الخاص ولا ينفّذها أبدًا. ولا يعيد aipager توزيع السكربت: لا شيء منه مضمّن في الحزمة، وحين لا تكون لدى aipager نسخة، تحمّله الصفحة منtelegram.orgمباشرة، كما كان من قبل. - نفق Mini App إلى Cloudflare، ما دام مفعّلًا (وهو مفعّل افتراضيًا). برنامج
cloudflaredالذي يشغّله يُنزَّل مرة واحدة من إصداراتgithub.com/cloudflare/cloudflared، ويُطابَق مع SHA-256 مثبّت، ويُخزَّن مؤقتًا في ~/.local/share/aipager/cloudflared/؛ ولا يُستخدم أبدًاcloudflaredالموجود فيPATHلديك. لا يحدث شيء من هذا مع عنوانك الخاص لـ Mini App (يُنظر أدناه). - فحص الإصدارات، فقط حين يضغط أحد على التحقق من التحديثات أو تحديث (
/update، وقسم التحديثات في Mini App)، أو حين يفتح شخص يحق له التحديث تبويب الإعدادات في Mini App (مرة كل عشر دقائق على الأكثر لكل تطبيق مفتوح):pypi.orgلـ aipager، وdownloads.claude.aiأوregistry.npmjs.orgلـ Claude Code، بحسب طريقة تثبيته. ثم يشغّل التحديث المثبّت (الذي يتصل بـ PyPI أو Homebrew) وclaude update. أماaipager updateفي سطر الأوامر فلا يشغّل إلا المثبّت. - تثبيت ميزة الرسائل الصوتية، عند الضغط على زرها: يجلب المثبّت الحزم من PyPI، ويُنزَّل نموذج التعرف على الكلام من Hugging Face (
huggingface.co) عند أول استخدام. - HTTPS إلى
raw.githubusercontent.com(ملف مفتاح التقارير) وإلى خادم استقبال البيانات في Sentry، فقط عند تأكيد إرسال تقرير مشكلة: التقرير الذي عاينته وملخص قصير مأخوذ منه، لا شيء غير ذلك. يُنظر تقارير المشكلات.
الاتصالات الواردة:
- مقبس datagram من نوع Unix في
$XDG_RUNTIME_DIR/aipager.sock(ويعود إلى /tmp/aipager.sockحين لا يكون $XDG_RUNTIME_DIRمضبوطًا). يربطه aipager ويضبط صلاحياته بـ chmod عند البدء (aipager/dtach/hook_receiver.py). الصلاحية 0o666حتى تستطيع أي عملية محلية إرسال أحداث الهوك إليه: الثقة نفسها التي في ~/.claude/settings.json، الذي يتحكم أصلًا في ما يشغّل الهوكات.
وهذا يعني: aipager ليس سطح هجوم عن بُعد. لا يستطيع مهاجم على الشبكة الوصول إليه دون موطئ قدم على الكمبيوتر نفسه.
نص Claude والتنزيلات التي يجريها Telegram بنفسه. تنزّل خوادم Telegram بنفسها بعض العناوين الموجودة في الرسالة: كتلة صورة أو فيديو أو صوت في رسالة منسّقة، والصفحة التي خلف معاينة الرابط. إجابات Claude، وملاحظاته على بطاقة العمل، وأوصاف الوكلاء، ووسيطات الأدوات، تصل إلى Telegram كما كتبها Claude، لذا قد يجعل حقنُ التعليمات (prompt injection) Claude يكتب عنوانًا يحمل سرًا إلى الخارج، دون استدعاء أداة ودون طلب إذن. يغلق aipager الطريق الأول، والثاني حيث يوفّر Telegram خيارًا لذلك:
- في كل رسالة منسّقة، تُرسل الصورة المكتوبة بالشكل
 رابطًا عاديًا مع نصها البديل، وتُعرض نصًا أي صيغة صورة أخرى والوسوم الخام <img> و<video> و<audio> و<source> و<picture> و<figure> و<iframe> و<embed> و<object> و<tg-document> و<tg-collage> و<tg-slideshow> و<tg-button> و<tg-button-row>(aipager/bot/media_guard.py). وداخل كتلة شيفرة، مثل مقتطف HTML، يظهر بسبب ذلك\أو < زائد. - معاينات الروابط معطّلة في كل رسالة نصية عادية أو HTML يرسلها aipager أو يعدّلها (بما في ذلك النسخ النصية العادية الاحتياطية للإجابات والبطاقات)، ورسائله الخاصة أيضًا. روابط Mini App الخاصة به أزرار، ولا يجلب Telegram الأزرار.
لا يوثّق Telegram أي خيار للمعاينة في الرسائل المنسّقة، ولم يُتحقق على الخدمة الفعلية مما إذا كان يبني معاينة رابط لرسالة كهذه.
نفق Mini App#
Mini App مفعّل افتراضيًا، لذا في التثبيت العادي يفتح aipager هذا النفق دون أن يُطلب منه. هذه مقايضة مقصودة (خيار تفعيل يدوي لم يكتشفه أحد كان ميزة غير موجودة فعليًا)، لكنها تعني أن التثبيت الجديد يتصل بالإنترنت. لإيقافه: aipager miniapp disable، أو ضبط enabled: false تحت miniapp: في aipager.yaml؛ والقيمة false الصريحة تُحترم دائمًا. ويُلاحظ أن كتلة miniapp: المفقودة أو التالفة تُعامل الآن على أنها مفعّلة لا معطّلة.
يشغّل Mini App (ما لم يُعطَ --url بديلًا) نفق Cloudflare السريع مُدارًا، موجّهًا إلى خادم loopback الخاص بـ aipager، وينشر عنوان https://*.trycloudflare.com العام الذي يُعطى له زرًا لقائمة Telegram وإجابةً لـ /app.
ذلك العنوان ليس سرًا، ولا يُفترض أن يكون: كل طلب يقدّمه Mini App يُتحقق منه بتوقيع initData من Telegram (يُنظر حدود الثقة أعلاه)، وهو الفحص نفسه الذي يحميه أيًّا كانت طريقة الحصول على العنوان. معرفة اسم المضيف وحدها لا تعطي المهاجم شيئًا لا يستطيع الحصول عليه بتخمين نطاق فرعي عشوائي في trycloudflare.com: فحص التوقيع هو البوابة الحقيقية. يُعامل مثل أي عنوان غير مدرج لكنه ليس سرًا: لا بأس في تركه يعمل، لكن لا داعي للصقه في قناة عامة دون سبب.
لا يُحفظ اسم المضيف إلا في الذاكرة: لا يُكتب أبدًا في aipager.yaml ولا في أي مكان تحت ~/.config/aipager/. إعادة التشغيل المخطط لها (إعادة التشغيل بعد التحديث، وإعادة التشغيل الآن، وإعادة التشغيل في المكان دون خدمة) تترك النفق يعمل، فيبقى العنوان وزر القائمة وMini App المفتوح تعمل: لا يتولاه aipager التالي إلا بعد أن يتحقق من أنه الإقلاع نفسه، والمنفذ نفسه، وأن العملية هي cloudflared ذاك (من سطر أوامرها)، وأنه ما زال يعطي العنوان نفسه (يحفظ ~/.claude/aipager-tunnel.json بصمة له، لا العنوان أبدًا)، وأن العنوان يجيب. وفي أي حالة أخرى يوقفه ويشغّل نفقًا جديدًا، والنفق المحفوظ الذي لا يتولاه أي aipager خلال ٣ دقائق توقفه عملية حارسة صغيرة. أي توقف آخر (Ctrl+C، وdocker stop، وsystemctl stop) يوقف النفق، وإعادة تشغيل الجهاز أو الحاوية تعطي عنوانًا جديدًا. يوقف MINIAPP_KEEP_TUNNEL=0 هذا الحفظ. إذا قُتل aipager فجأة (نفاد الذاكرة، kill -9)، يبقى النفق يعمل حتى يبدأ aipager من جديد والنفق المُدار مفعّل (Mini App مفعّل، بلا MINIAPP_PUBLIC_URL) فيتولاه أو يوقفه، أو حتى يُعاد تشغيل الجهاز؛ ويوقفه aipager uninstall أيضًا. إذا تعطّل النفق أثناء العمل، يعيد aipager تشغيله بفواصل متزايدة وينشر العنوان الجديد؛ وإذا لم يستطع إعادته بعد عدة محاولات، يُزال الزر بدل أن يبقى مشيرًا إلى منفذ ميت.
ضبط MINIAPP_PUBLIC_URL (أو aipager miniapp enable --url https://…) يوقف النفق المُدار تمامًا (لا يُنزَّل برنامج cloudflared ولا يُشغَّل أبدًا)، ويُستخدم العنوان المعطى كما هو، مع فحص initData نفسه تحته. ويبقى الاكتشاف التلقائي لـ Tailscale (tailscale status --json) بديلًا أسهل لمن يشغّل Tailscale أصلًا ويفضّل عدم الاعتماد على Cloudflare إطلاقًا.
تحويل الرسائل الصوتية إلى نص#
يعمل faster-whisper داخل العملية. يُنزَّل الصوت بصيغة .ogg إلى مجلد الدردشة نفسها تحت /tmp/aipager-files/ (حيث تذهب الملفات المرفوعة؛ 0700، والملف 0600)، ويُحوَّل إلى نص محليًا على المعالج، ويبقى الملف تحت تحكمك. لا يغادر أي صوت الكمبيوتر. لا واجهة برمجية لطرف ثالث. ولا حاجة إلى مفتاح غير توكن البوت اللازم أصلًا للتحدث مع Telegram.
إذا حُذف ملف .ogg بعد التحويل، فالسجل النصي الوحيد للرسالة هو النص الناتج عن التحويل، الذي يُرسل إلى جلسة claude (حيث يخضع لسياسة الخصوصية في Claude Code نفسه).
عزل الجلسات#
تعمل كل جلسة Claude Code في dtach الخاص بها. مقبس التحكم في /tmp/claude-dtach-<name>.sock يملكه مستخدم aipager؛ ويرفض dtach الاتصال من مستخدمين آخرين. تبدأ كل جلسة في مجلدها الخاص، المختار عبر /new، أو Mini App، أو المجلد الذي شُغّل فيه aipager session.
لا يعزل aipager الجلسات بعضها عن بعض على مستوى نظام الملفات: الجلسة المرتبطة بـ ~/projects/foo تستطيع من حيث المبدأ قراءة ~/projects/bar إذا سمحت أذونات claude code بذلك. لعزل أقوى، يمكن استخدام ~/.claude/settings.json خاص بكل مشروع أو حاوية (صورة Docker).
الرسائل بين جلسات Claude#
يسمح Claude Code لجلسة بإرسال رسالة إلى جلسة أخرى (أداة SendMessage الخاصة به). في الجلسة المستقبِلة تكون تلك الرسالة رسالة كأي رسالة أخرى، لذا يقرر aipager صلاحياتها بالطريقة التي يقرر بها صلاحيات رسالة Telegram: بحسب من أرسلها فعلًا، لا بحسب ما تقوله أبدًا.
- الرسالة تحمل صلاحيات مرسلها، لا أكثر أبدًا. حين يسمح aipager لجلسة بإرسال رسالة، يكتب إيصالًا يُستخدم مرة واحدة: بصمة للنص بالضبط، وعملية Claude المرسلة (رقم عمليتها، ووقت بدئها على Linux) والصلاحيات التي كانت لتلك المهمة. يسجّل Claude Code الرسالة المستقبَلة مع رقم عملية المرسل، الذي تحقق منه النواة، ولا يثق أبدًا بالاسم الذي يطلقه المرسل على نفسه. قبل أن تشغّل المهمة المستقبِلة أي أداة، يطابق aipager الاثنين ويعطي المهمة صلاحيات المهمة المرسلة بالضبط: رسالة المالك تعمل بصلاحيات المالك، ورسالة العضو بصلاحيات ذلك العضو، والرسالة المكتوبة في طرفية الجلسة المرسلة بلا قيود.
- دون إيصال، صلاحيات مقيدة. الرسالة التي لا يستطيع aipager مطابقتها (أرسلتها جلسة أو عملية Claude لا يشغّلها aipager، أو إيصال انتهت صلاحيته بعد ساعة، أو مرسل على كمبيوتر آخر) تعمل بأشد القواعد، دون أي تغيير للملفات ودون وصول إلى الويب (قراءة وتفكير فقط). صلاحيات الجلسة المستقبِلة السابقة لا تسري عليها أبدًا.
- وكذلك أي مدخل آخر من خارج الجلسة. كل ما يسجّله Claude Code على أنه لم يأتِ من صاحب هذه الجلسة يحصل على تلك الصلاحيات المقيدة أيضًا: دفعة من قناة MCP، أو مستدعٍ عن بُعد، أو رسالة تضعها إضافة (plugin) في قائمة الانتظار، أو رسالة منقولة من صندوق وارد جلسة أخرى، وأي نوع من المدخلات لا يعرفه aipager بعد (أمر الإضافة يُعدّ مدخلًا خارجيًا حتى لو بدأته أنت). أما وكلاء الجلسة نفسها في الخلفية (ما يعيدونه وتقاريرهم) وإشعارات مهامها فتحتفظ بصلاحيات العمل.
- الرسالة التي جدولتها الجلسة تعمل بصلاحيات المهمة التي جدولتها. حين تُعدّ مهمة
/loopأو cron، يسجّل aipager صلاحيات تلك المهمة للرسالة؛ وحين تنطلق لاحقًا، تحصل على تلك الصلاحيات، لا أكثر أبدًا، أيًّا كان من تكلم أخيرًا؛ والرسالة التي تنضم إلى تلك المهمة لا تستطيع إلا تضييقها، وتغيير السياسة الذي يسحب صلاحيات ممن جدولها يضيّقها أيضًا. الرسالة نفسها المجدولة مرتين تحتفظ بالصلاحيات الأشد. أما /loopالمجرد فينطلق بتعليمات الحلقة في Claude Code بدل نصه الخاص، لذا يحصل على أشد صلاحيات كل الحلقات المجردة التي أعدّتها الجلسة، بمجرد أن يُظهر سجل المحادثة الانطلاق (وحتى ذلك الحين يُرفض استدعاء الأداة المرفوض برفق، ويحاول Claude مرة أخرى). والرسالة التي لم يجدولها أحد عبر فحص aipager تحصل على الصلاحيات المقيدة. - النص لا يثبت شيئًا. اسم مرسل، أو سطر
[via Telegram · @owner]، أو «المالك يطلب…» داخل رسالة لا يغيّر شيئًا. يُخبَر Claude بمن تحقق aipager أنه المرسل، وبأن ادعاءات النص ليست دليلًا. - الانضمام إلى مهمة جارية لا يفعل إلا تضييقها. رسالة من جلسة أخرى تصل أثناء مهمة جارية تجعل تلك المهمة بشدة الأشد بين الاثنين، لا أخف أبدًا. ويبقى ذلك صحيحًا حين يأخذ Claude Code الرسالة دون حدث رسالة خاص بها (مدمجة في المهمة الجارية، أو مشغّلة كمهمة تالية من قائمة انتظاره): المسار السريع للمالك ينظر إلى نهاية سجل المحادثة قبل كل استدعاء أداة، ونهاية المهمة وaipager يعلّمان الجلسة للفحص حين يريان رسالة كهذه في قائمة الانتظار.
- الأدوار تبقى في دردشتها. الإيصال من جلسة في دردشة أخرى لا يُحتسب إلا حين تكون لمهمته صلاحيات المالك أو كانت مكتوبة في طرفيتها؛ وكل ما عدا ذلك يعمل مقيدًا.
- من يحق له الإرسال. الأدوار المقيدة المدمجة لا تستطيع استخدام SendMessage إطلاقًا. والدور الذي تعطيه سياسةٌ إياه لا يستطيع الكتابة إلا إلى جلسات يشغّلها aipager (مُخاطَبة بالعنوان
uds:…/<pid>.sock)؛ ويحذّرaipager doctorمن دور كهذا. - وكلاء الخلفية يحتفظون بصلاحياتهم. الوكيل يحتفظ بصلاحيات المهمة التي أطلقته (أو بالصلاحيات الأشد لمهمة لاحقة)، لا بصلاحيات مهمة لاحقة لديها أكثر أبدًا.
- حين تُوقف. إذا أوقفت قواعد الأمان مهمة بدأتها جلسة أخرى، فبطاقة العمل تعرض أصلًا من أرسلها، وإشعار الإيقاف يذكر ذلك، ومن سلّم المهمة يُبلَّغ، فلا تختفي بصمت أبدًا: دردشة الجلسة المرسلة حين تكون الجلستان في الدردشة نفسها؛ وإلا فالمالك وحده، في الخاص، وفقط حين يكون المالك (أو الطرفية) قد أدار المهمة المرسلة.
الحدود المعروفة:
- المهمة التي لديها طرفية (للمالك أو لمشرف) تعمل بصفة مستخدم النظام نفسه الذي يعمل به aipager وتستطيع تزوير إيصال، كما تستطيع أصلًا إعادة كتابة ملفات التحكم الأخرى في aipager. هذا نموذج الثقة القائم: لا تُعطى الطرفية إلا لمن يمكن ائتمانه على الكمبيوتر.
- جلسات Claude التي لا يشغّلها aipager لا يحكمها aipager إطلاقًا، أيًّا كان من يكتب إليها.
- خارج Linux، يُطابَق المرسل برقم عمليته وحده (لا يسجّل Claude Code وقت البدء هناك). على Linux، سجل الاستقبال الذي لا وقت بدء فيه لا يطابق شيئًا.
- قد يكتب Claude Code الرسالة في سجل المحادثة بعد أول استدعاء أداة في المهمة بلحظة. وحتى يفعل، يُرفض برفق الاستدعاء الذي سترفضه الصلاحيات المقيدة (يُطلب من Claude المحاولة مجددًا؛ ولا تتوقف المهمة). وبعد ٨ استدعاءات دونها، تعمل المهمة مقيدة.
- يقرأ المسار السريع للمالك آخر ١ ميبيبايت من سجل المحادثة، لا أكثر أبدًا. الرسالة من جلسة أخرى الأطول من ذلك تنتهي بسطر يعدّه المسار السريع رسالة كهذه (وكذلك أي سطر أطول من ٢٥٦ كيبيبايت تقطعه النافذة). أما الرسالة الواقعة كلها أبعد إلى الخلف (أُضيف بعدها أكثر من ١ ميبيبايت من السجل قبل استدعاء الأداة التالي) فيلتقطها aipager أثناء عمل المهمة، أو نهاية المهمة ما دامت في قائمة الانتظار، لا المسار السريع.
- ما دامت لدى جلسة
/loopمجردة مُعدّة، فأي مهمة لا تبدأها رسالة Telegram (بما فيها رسالة مكتوبة في الطرفية) قد تُرفض أولى استدعاءات أدواتها برفق، حتى يكتب Claude Code رسالة تلك المهمة ويستطيع aipager معرفة أنها ليست انطلاق الحلقة؛ ثم يحاول Claude مرة أخرى. - يقرأ aipager مهمة الرسالة المجدولة مما أُضيف إلى سجل المحادثة منذ بدئها؛ وما بعد ٨ ميبيبايت، من أول ٤ ميبيبايت وآخرها (ما بينهما فُحص حين كان الجزء الأحدث).
- الصلاحيات المسجّلة لرسالة مجدولة تبقى حتى إنهاء الجلسة (
/kill): الجلسة المنتهية أو المستأنفة تحتفظ بها، كما يحتفظ Claude Code بمهام cron الخاصة به وبإيقاظ /loopالمنتظر. يُحفظ ٢٥٦ على الأكثر لكل جلسة، وجدولة واحدة أخرى بعد ذلك تُرفض لكل من في الجلسة، لذا يستطيع كل من يحق له الجدولة استنفادها (عندها يمكن بدء جلسة باسم آخر، أو /killلهذه). الرسالة التي تنطلق دون سجل تحصل على الصلاحيات المقيدة، أو في جلسة فيها /loopمجرد على صلاحيات تلك الحلقة، بمجرد أن يكتبها Claude Code في سجل المحادثة؛ وحتى ذلك الحين يقرأ aipager المهمة بحسب التي قبلها. يحدث ذلك لرسالة أُعدّت قبل هذا الإصدار؛ وبعد إعادة تشغيل الكمبيوتر (السجلات في المجلد المؤقت للنظام، الذي تمسحه إعادة التشغيل، بينما يستطيع Claude Code إعادة إيقاظ جلسة مستأنفة خلال الساعة)؛ ولمهمة cron دائمة، يشغّلها Claude Code في جلسة أخرى من المجلد نفسه بعد انتهاء جلستها. والجلسة الأقل صلاحيات التي تجدول النص نفسه تستطيع خفض صلاحيات رسالة (لا رفعها أبدًا). - الجلسة التي تبدأ من جديد باسم جلسة سابقة (استبدال) تحتفظ بسجلات تلك الجلسة، بما فيها صلاحيات
/loopالمجرد الخاص بها، حتى /kill. - الرسالة المجدولة التي يشغّلها Claude Code مباشرة بعد مهمة أخرى، دون حدث رسالة خاص بها (من قائمة انتظاره)، تحصل على الأشد بين صلاحياتها وصلاحيات المهمة التي تليها (بعد مهمة مكتوبة في الطرفية، صلاحياتها هي بالضبط).
- أولى استدعاءات الأدوات لمهمة مكتوبة في الطرفية قد تُرفض حتى يكتب Claude Code رسالتها في سجل المحادثة (حتى ذلك الحين يقرأ aipager المهمة التي قبلها).
- الوكيل في الخلفية الذي أُطلق بصلاحيات أضيق يستطيع إعادة نص إلى مهمة لاحقة من العمل نفسه، تقرؤه بصلاحيات العمل، كما مع إشعارات المهام الأخرى في Claude Code.
- قد يحتجز Claude Code رسالة بانتظار الموافقة في الطرفية المستقبِلة (إعداده
crossSessionInbound، أو حين تعمل الجلستان في فئتي أذونات مختلفتين، مثل الوصول الكامل والتلقائي). لا يستطيع aipager تحرير رسالة كهذه من Telegram؛ وينتظر الإيصال حتى ساعة.
ملخص التهديدات#
| التهديد | الحماية |
|---|---|
| غريب يرسل أمرًا إلى البوت | مرشّح معرّف الدردشة يرفض |
| سرقة توكن البوت | /revoke في @BotFather، ثم إعادة الإعداد |
| استدعاء أداة claude مخترَق | فحص الأمان في aipager يرفض ما تمنعه قواعد مهمة Telegram؛ في وضع «اسأل» تقرر قواعد Claude Code نفسه في settings.json ما يسأل عنه، وضغطة إنسان هي السماح الوحيد؛ في الوضع التلقائي (الافتراضي لكل جلسة جديدة) يشغّل فحص الأمان في Claude Code نفسه الاستدعاءات التي يراها آمنة دون سؤال ويوقف الخطرة ليقرر إنسان؛ وجلسة الوصول الكامل (للمشرفين، ولأي شخص في دردشته الخاصة) لا تسأل عن شيء، فلا يقف في الطريق إلا فحص الأمان في aipager، ولا شيء في مهام المالك نفسه ورسائل الطرفية (من يقرر) |
| مستخدم Telegram مقيَّد يرتقي إلى صلاحيات المالك | لا طرفية، والجلسات لا تبدأ إلا داخل المجلد الجذر للعضو نفسه، والكتابة والبحث محصوران في المشروع داخله (لا المجلد الرئيسي أبدًا)، ولا قراءة في المجلد الرئيسي خارج المجلد الجذر، والمجلدات الجذرية لأعضاء آخرين والملفات المرفوعة في دردشات أخرى غير قابلة للوصول، وملفات بيانات الاعتماد غير مقروءة، ومسارات محمية لأدوات الملفات، والفحوص المتعطلة ترفض (تطبيق القواعد في وضع الفريق)؛ بذل للجهد فقط لدور أُعطي Bash، وغير مشمول للحالات في «الحدود المعروفة» |
| رسالة جلسة تدّعي سلطة المالك | تعمل الرسالة بصلاحيات المهمة المرسلة المثبتة، لا بصلاحيات الجلسة المستقبِلة السابقة أبدًا؛ ودون دليل، قراءة وتفكير فقط (الرسائل بين الجلسات) |
| عضو في مجموعة يستعير صلاحيات غيره | كل رسالة تعمل بدور مرسلها في الدردشة التي جاءت منها؛ ورسالة مرسل آخر تنتظر المهمة الجارية؛ وإن انضمت رغم ذلك، يغلب الأشد؛ وأوامر الشرطة المائلة من الأدوار المقيدة محصورة في أوامر لوحة المفاتيح (تطبيق القواعد في وضع الفريق) |
| أحاديث المجموعة تصل إلى جلسة | في المجموعة لا يُعالج إلا الأوامر، والردود على البوت، وذكره، وضغطات لوحة المفاتيح المعلّمة (المجموعات) |
| عضو محذوف يحتفظ بالوصول | يعيد aipager config تحميل aipager دون إعادة تشغيل؛ وتُحذف رسائله المحتجزة، وتفقد مهمته الجارية صلاحياته القديمة؛ وإخراجه من مجموعة Telegram يمنعه من قراءتها (المجموعات ← إعادة التحميل دون إعادة تشغيل) |
| العبث بسجل التدقيق | للإضافة فقط؛ منعه خارج النطاق دون برنامج توقيع منفصل |
| حقن التعليمات (prompt injection) يجعل Claude يكتب عنوانًا يحمل بيانات إلى الخارج | الصور ووسوم الوسائط في نص Claude تُعرض روابط أو نصًا، لا وسائط أبدًا، ومعاينات الروابط معطّلة في الرسائل النصية العادية ورسائل HTML (الاتصالات الشبكية) |
| مهاجم على الشبكة | لا منفذ وارد، ولا يمكن الوصول إليه مباشرة |
| رفع الصلاحيات محليًا | لا sudo / setuid؛ يبقى aipager في مساحة المستخدم |
| تسرّب صوت الرسائل الصوتية إلى السحابة | التحويل إلى نص محلي |
| الوكيل يقرأ بيانات اعتماد Claude الخاصة بـ aipager نفسه | متوقع، ليس خللًا: يُنظر بيانات اعتماد Claude. تُقيَّد بيانات الاعتماد نفسها، لا الملف الذي تُحفظ فيه |
صفحات ذات صلة#
- البنية: بنية العملية.
- أحداث الهوك: ما يراه aipager فعلًا من claude.
- أوامر البوت: الجانب الذي يديره الإنسان.