Modèle de sécurité
aipager fait tourner Claude Code pour vous et se pilote depuis Telegram. Deux questions en découlent :
- Qui peut piloter le bot ?
- Que peut faire le bot une fois piloté ?
Limite de confiance#
Chaque gestionnaire que le bot expose (messages, fichiers, messages vocaux, boutons) passe par le filtre de discussions de python-telegram-bot, construit à partir des discussions configurées dans ~/.config/aipager/aipager.yaml, ou (pour les commandes) par la même vérification dans les autorisations d'aipager lui-même. La seule exception se contente de noter les expéditeurs dans les groupes qui ne sont pas configurés (plus bas). Seules les discussions configurées peuvent interagir avec le bot. Rien de ce qui vient d'une autre discussion n'est traité ni envoyé à une session. Une discussion privée qui n'est pas configurée reçoit une seule réponse « Ce bot n'est pas configuré pour vous parler ». Dans un groupe qui n'est pas configuré, le bot ne répond jamais, mais il note qui lui a envoyé un message là-bas (l'identifiant Telegram de la personne, son nom d'utilisateur et son nom, et l'identifiant, le titre et le type du groupe) dans ~/.claude/aipager-pending-users.json, pour qu'aipager config puisse trouver un nouveau groupe et ses membres pendant qu'aipager tourne. N'importe qui peut ajouter le bot à un groupe, donc n'importe qui peut ajouter une ligne à ce fichier ; cela ne donne aucun droit. Le fichier n'est lisible que par vous (droits 0600), est réécrit en entier à chaque écriture (jamais laissé à moitié écrit), et garde au plus les 200 personnes les plus récentes, en retirant celles qu'il n'a pas vues depuis 30 jours. En usage en équipe, une liste d'autorisations par personne avec des rôles s'ajoute par-dessus, et dans un groupe seul un message adressé au bot (une commande, une réponse à l'un de ses messages, une mention, ou un appui sur son clavier) est traité, que le bot soit admin du groupe ou non (voir groupes).
Cela veut dire que ce qui est ouvert au monde extérieur, c'est :
- Le token du bot (un secret).
- L'identifiant de la discussion (un grand nombre entier).
Si les deux fuient, un attaquant peut piloter votre aipager. Si seul le token fuit, un attaquant peut lire les messages envoyés au bot depuis votre discussion, mais ne peut pas envoyer de commandes qu'aipager exécuterait. Si seul l'identifiant de la discussion fuit, rien d'utile : le bot a besoin du token pour parler à Telegram.
Secrets#
| Secret | Emplacement | Droits |
|---|---|---|
| Token du bot | ~/.config/aipager/aipager.yaml | 600 par défaut |
| Identifiant(s) de discussion | même fichier | 600 par défaut |
Le token reste en dehors du journal d'aipager : les lignes de requêtes HTTP qui le portent ne sont pas écrites dans le journal, et les messages d'erreur, aipager doctor et l'assistant masquent tout ce qui a la forme d'un token de bot. Les identifiants de discussion apparaissent bien dans le journal d'aipager (par exemple dans les lignes de limites de débit), alors traitez la sortie d'aipager logs comme privée. Aucun des deux n'est publié dans le dépôt : la configuration vit dans le ~/.config de l'utilisateur, pas dans le dépôt. La publication des versions sur PyPI par Trusted Publisher ne touche jamais de secrets non plus ; OIDC s'occupe de l'authentification.
Si vous pensez que le token est compromis, révoquez-le auprès de @BotFather (/revoke), générez-en un nouveau, et relancez aipager config.
L'identifiant Claude : ce qui le protège vraiment#
aipager fait tourner un agent autonome qui a accès à Bash, avec votre utilisateur UNIX. Donc tout secret qu'aipager peut lire, l'agent qu'il lance peut le lire aussi. Cela vaut la peine d'être dit clairement, parce qu'il est facile de se tromper de type de protection.
~/.config/aipager/ est dans la liste de refus de safety.py, ce qui empêche une session pilotée depuis Telegram de lire les fichiers qu'il contient. C'est une protection par chemin, et elle ne sert à rien ici : l'identifiant Claude (CLAUDE_CODE_OAUTH_TOKEN ou ANTHROPIC_API_KEY) n'est pas atteint par un chemin, il est hérité dans l'environnement du processus de chaque session lancée. Dans une session, echo $CLAUDE_CODE_OAUTH_TOKEN l'affiche, liste de refus ou pas. Une règle de chemin de fichier n'est pas le bon type de protection pour une variable d'environnement.
Où se trouve l'identifiant#
| Installation | Source | Droits |
|---|---|---|
| Service utilisateur systemd | LoadCredential=claude_oauth:%h/.config/aipager/daemon.env, lu dans $CREDENTIALS_DIRECTORY/claude_oauth | 600 (daemon.env) ; systemd garde l'identifiant en dehors de l'environnement du service lui-même |
| macOS / Docker / sans systemd | ~/.config/aipager/daemon.env, lu directement | 600 |
Les deux chemins sont lus par le même code (aipager/daemon_secrets.py) et passés à la session lancée par la table env= du sous-processus, jamais insérés dans la chaîne de commande bash -c. /proc/PID/cmdline est lisible par tout le monde (0444) ; /proc/PID/environ est en 0400 (le propriétaire seulement).
Ce que LoadCredential= + 0600 apporte vraiment : les autres utilisateurs UNIX du même ordinateur ne peuvent pas lire l'identifiant, et il n'apparaît jamais dans systemctl show, dans une copie du fichier du service, ni dans une capture d'écran de journalctl qu'un opérateur colle dans un rapport de bug. Cela ne crée pas de barrière entre aipager et l'agent qu'aipager lance : cette barrière n'existe pas, et aucun réglage de droits de fichiers ne peut la créer, parce que tout le travail de l'agent est d'agir comme l'opérateur dans ce même environnement.
La seule protection qui marche vraiment#
Limitez l'identifiant lui-même. Faites tourner aipager sur un compte Anthropic à part, ou avec une clé d'API qui a une limite de dépense, pas avec le même token que celui de votre propre connexion interactive à claude. Révoquer l'identifiant d'aipager ne doit jamais déconnecter l'humain ; si c'était le cas, les deux seraient le même identifiant et aucun ne serait limité. aipager doctor --fix peut trouver un token existant pour remplir daemon.env, mais choisir quel token lui donner revient à l'opérateur, et c'est la seule décision de ce document qui compte plus que les droits de fichiers autour.
Vérifier la connexion sans deviner#
aipager doctor (et la notification d'une ligne envoyée une fois par démarrage d'aipager) indiquent l'état de connexion grâce au claude auth status de Claude Code lui-même, jamais par une vérification de fichier faite à la main. Trois états restent écrits différemment partout où ils apparaissent :
auth: <method> (<source>): connexion confirmée.auth: none (not logged in): absence de connexion confirmée.auth: unknown (...): l'essai lui-même a échoué (délai dépassé, exécutable manquant, sortie illisible) ou l'exécutable est plus ancien que la version qui a ajouté la sortie JSON. Ce n'est jamais présenté comme « pas connecté » : aipager ne refuse pas de lancer une session sur une vérification de connexion dont il n'est pas sûr. La connexion par le Trousseau de macOS, les identifiants qui n'ont qu'un token de renouvellement et la connexion du forfait Max sans fichier sont tous invisibles pour une vérification de fichier et ressembleraient sinon exactement à « déconnecté ».
Qui décide si un appel d'outil s'exécute#
Deux vérifications distinctes décident si un appel d'outil s'exécute, et elles passent dans cet ordre :
- La vérification de sécurité d'aipager. Claude Code lance le hook
PreToolUsed'aipager avant chaque appel d'outil. Pour une tâche lancée depuis Telegram, il tient l'appel aux règles du rôle de la personne qui l'a envoyée et au socle de sécurité (chemins protégés, motifs de commandes), et refuse ce qu'ils refusent : Claude Code refuse alors l'outil sans demander à personne, quoi que disent ses propres règles. Un message tapé dans le terminal n'est pas limité par cette vérification. Ce qu'elle couvre est expliqué plus bas, dans comment les règles s'appliquent en équipe. - Les règles de Claude Code lui-même (
~/.claude/settings.json, le mode d'autorisation de la session). Si elles disent de demander, Claude Code déclenchePermissionRequest, et aipager affiche la demande dans Telegram (voir hooks). Votre appui revient à Claude Code comme décision du hook, ou est tapé dans la propre boîte de dialogue de Claude Code si l'attente de 20 secondes est dépassée. Si les règles de Claude Code refusent l'appel, il n'arrive jamais jusqu'à Telegram.
Donc aipager peut refuser un appel que Claude Code autoriserait, et quand Claude Code demande, aipager ne répond jamais oui de lui-même : une autorisation est toujours l'appui d'une personne. Trois choses laissent des appels s'exécuter sans rien demander à une personne :
- Le Mode Accès complet, pour les admins (et pour une personne dans sa propre discussion privée). Une session en Mode Accès complet est lancée avec
--dangerously-skip-permissions: Claude Code ne demande rien, donc chaque appel que la vérification de sécurité d'aipager ne refuse pas s'exécute sans appui. Seul un admin (les rôlesowneretadmin, ou un rôle aveccan_manage) peut lancer une session en Mode Accès complet, y faire passer une session (/mode full, après une confirmation) ou en reprendre une dans ce mode, avec une exception : dans sa propre discussion privée avec le bot, une personne dont le rôle peut envoyer des messages et répondre à Claude (can_promptetcan_approve, donc unusermais pas unread_only) peut faire de même pour ses propres sessions (/new !x1, ou « Nouvelles sessions » réglé sur le Mode Accès complet dans cette discussion,/mode full,/resume), dans Telegram et dans la Mini App. Jamais dans un groupe ni dans la discussion privée d'une autre personne. Toute autre personne reçoit le Mode Automatique à la place, et la réponse le dit. Le Mode Accès complet ne saute que les demandes de Claude Code lui-même : le hook PreToolUse d'aipager applique toujours les règles du rôle de cette personne (le socle de sécurité, l'enfermement dans son dossier, les règles de refus, les fichiers d'identifiants) à chaque appel, exactement comme en Mode Demander et en Mode Automatique ; le mode que signale Claude Code ne les assouplit jamais. La vérification de sécurité d'aipager tourne toujours sur chaque appel en Mode Accès complet, mais elle ne refuse rien dans les tâches du propriétaire lui-même ni dans un message tapé dans le terminal. - Le Mode Automatique, le mode par défaut. Une session en Mode Automatique est lancée avec
--permission-mode auto: la vérification de sécurité de Claude Code lui-même examine chaque appel, exécute ceux qui sont sûrs et arrête ceux qui sont risqués, qui arrivent alors dans Telegram comme une demande, comme en Mode Demander. Chaque session démarre en Mode Automatique sauf si la discussion a choisi autre chose dans/settings→ Nouvelles sessions. Quiconque peut agir sur une session peut la faire passer du Mode Demander au Mode Automatique, et inversement (/mode) ; aipager le fait sans redémarrage en appuyant sur Shift+Tab dans la session, jamais avec une option qui mettrait le Mode Accès complet dans le cycle de Shift+Tab de Claude Code. Si Claude Code signale un jour le Mode Accès complet pour une session qu'aipager n'a pas lancée dans ce mode, aipager affiche ce qui a été signalé, écrit un avertissement dans le journal et ne lance ni ne redémarre jamais la session en Mode Accès complet à cause de cela. Limite connue : le mode que signale Claude Code arrive par le socket des hooks, sur lequel n'importe quel processus local peut écrire (voir connexions réseau). Un faux signalement peut changer le mode affiché pour une session et, pendant qu'un passage entre Mode Demander et Mode Automatique attend sa confirmation, il peut faire appuyer aipager une fois de plus sur Shift+Tab, ce qui peut laisser la session en Mode Automatique alors que le Mode Demander était demandé (le prochain vrai hook affiche de nouveau le vrai mode). Il ne change jamais l'option que passe un redémarrage ou une reprise, et il ne peut jamais atteindre le Mode Accès complet : seule l'option de lancement le met en place. aipager n'appuie pas sur Shift+Tab dans une session dont il sait que le cycle des modes peut l'amener en Mode Accès complet : une session lancée en Mode Accès complet (même après qu'on l'a passée en Mode Demander ou en Mode Automatique dans le terminal), ou une session qui, depuis son lancement, a signalé le Mode Accès complet à l'aipager qui tourne en ce moment. Là, chaque changement de mode est un redémarrage, qui lance l'option du nouveau mode et abandonne celle du Mode Accès complet, et un faux signalement du Mode Accès complet ne peut que transformer un changement en redémarrage. aipager ne peut pas le savoir pour une session lancée dans un terminal avec l'option qui ajoute le Mode Accès complet au cycle de Shift+Tab (--allow-devant l'option du Mode Accès complet), ou avec--dangerously-skip-permissionsquand elle a quitté le Mode Accès complet avant qu'aipager ait eu de ses nouvelles (aipager ne tournait pas alors, ou a redémarré depuis). Là, passer du Mode Demander au Mode Automatique peut s'arrêter en Mode Accès complet, et la carte dit alors que la session est en Mode Accès complet. N'utilisez pas ces options pour des sessions de terminal dont des personnes qui ne sont pas admins peuvent changer le mode depuis Telegram. Pour que le Mode Accès complet marche depuis Telegram, aipager règleskipDangerousModePermissionPromptdans~/.claude/settings.jsonà chaque démarrage (sinon l'avertissement de Claude Code avant de tourner dans ce mode bloquerait la session) ; cela désactive aussi l'avertissement pour les sessions que vous lancez vous-même. De la même façon, il marque comme fiable dans~/.claude.jsonle dossier où démarre chaque session (sauf si ce dossier ou un dossier au-dessus l'est déjà), pour que la question « Do you trust this folder? » de Claude Code ne bloque pas une session lancée depuis Telegram : le premier message y répondrait par « No, exit ». Les réglages.claude/, les hooks.git/et le.mcp.jsonpropres à un dossier fiable peuvent lancer des commandes ; c'est pourquoi les membres sanscan_managene peuvent jamais écrire ces fichiers (sauf sipolicy.yamlrend un shell à leur rôle). Claude Code étend la confiance d'un dossier aux dossiers en dessous qui ne sont pas dans un dépôt git ; un dépôt reçoit quand même sa propre question. Le dossier personnel est marqué quand une session y démarre, car Claude Code ne garde un oui donné dans le dossier personnel que pour cette session. Ensuite, Claude Code ne demande plus rien dans un dossier sous votre dossier personnel qui n'est pas dans un dépôt git, y compris dans les sessions que vous lancez vous-même./et les dossiers au-dessus du dossier personnel ne sont jamais marqués : cela toucherait tous ces dossiers de l'ordinateur, donc une session lancée dans l'un d'eux reste bloquée sur la question. Au démarrage, aipager ne marque rien, seulement le dossier où une session est sur le point de démarrer. Une nouvelle session démarre dans le dossier où tourne aipager (le dossier personnel avecaipager service installsur Linux et macOS, et dans le conteneur) ou dansAIPAGER_WORK_DIRs'il est défini ; le journal d'aipager avertit au démarrage quand ce dossier est/ou un dossier au-dessus du dossier personnel et n'est pas fiable. Les versions précédentes marquaient le dossier d'aipager à chaque démarrage,/compris pour un service macOS (qui tournait dans/) ; aipager ne peut pas distinguer une telle entrée d'une entrée que vous avez créée, donc il la laisse. aipager ne touche pas à~/.claude.jsonni à~/.claude/settings.jsonquand il ne peut pas les lire en entier, ou quand un Claude en cours d'exécution les a modifiés pendant qu'aipager les mettait à jour. - Toujours autoriser, qui rend à Claude Code la règle « ne plus demander » qu'il a proposée avec la demande, exactement comme le ferait cette option dans sa propre boîte de dialogue.
Pour bloquer un outil pour tout le monde, refusez-le dans ~/.claude/settings.json ; pour le bloquer pour certaines personnes, utilisez les règles de leur rôle dans policy.yaml (voir groupes).
Ce que montre une demande est ce qui s'exécute. Une demande d'autorisation montre la vraie commande ou le vrai chemin sous la description qu'en donne Claude. Les caractères de contrôle de direction d'Unicode (U+202A-U+202E, U+2066-U+2069, U+200E, U+200F, U+061C) changent l'ordre dans lequel Telegram, un navigateur ou un téléphone dessinent le texte qui les suit, alors que le shell exécute les caractères dans l'ordre, donc ils pourraient faire lire une commande comme une autre (l'astuce « Trojan Source », CVE-2021-42574). Dans chaque demande, chaque question, chaque aperçu de diff et chaque ligne d'audit, ils sont affichés comme des marques visibles (⟨U+202E⟩), et la demande prévient que la commande peut exécuter autre chose que ce qu'elle a l'air de faire. Les autres caractères invisibles sont laissés tels quels.
Comment les règles s'appliquent en équipe#
En usage en équipe, aipager ajoute sa propre couche sous les appuis : chaque message venu de Telegram porte une note d'autorisation (qui l'a envoyé et ce que son rôle permet) écrite dans /tmp/claude-notes-<session>/, un fichier par message. Quand Claude prend un message, le programme aipager-hook le rapproche de ces notes et installe les règles de son expéditeur là où la vérification PreToolUse les lit. Ces propriétés portent tout le reste :
- Le plus strict l'emporte. Si Claude prend en une seule tâche des messages de plus d'une personne (aipager retient les messages pour l'éviter, mais reste sûr si cela arrive quand même), la tâche tourne avec la combinaison la plus restrictive : le message d'un propriétaire ne peut jamais prêter ses droits à quelqu'un d'autre.
- Une tâche en cours ne s'élargit jamais. Un message d'une autre personne attend la fin de la tâche en cours, puis tourne comme sa propre tâche avec les règles de son expéditeur. Si un message rejoint quand même une tâche en cours, la tâche garde les règles les plus strictes de tous ceux qui en font partie ; un message Telegram qui rejoint une tâche que vous avez tapée dans le terminal tient le reste de cette tâche aux règles de son expéditeur (celui d'un propriétaire ne change rien).
- Votre rôle est celui de la discussion où vous agissez. Une personne inscrite dans plusieurs discussions (disons
userdans un groupe etownerde sa propre discussion privée) a le rôle du groupe pour tout ce qu'elle envoie ou touche dans le groupe et le rôle de la discussion privée dans la discussion privée, quel que soit l'ordre des discussions dansaipager.yaml. - Inconnu veut dire restreint. Un message qui ne peut être attribué à personne tourne avec le socle intégré (aucune exemption, toutes les règles de refus actives), jamais comme un message de terminal sans restriction. Les messages que vous tapez directement dans le terminal restent sans restriction ; tout ce qui porte la marque Telegram sur n'importe quelle ligne est contrôlé. Chaque message Telegram porte cette marque, avec le nom de la personne qui a envoyé ce message (jamais celui de la dernière personne qui a piloté la session) ; un expéditeur qu'aipager ne connaît pas, par exemple quelqu'un retiré d'
aipager.yamlpendant que son message attendait, reçoit un simple[via Telegram]et le socle. - Les commandes slash demandent un admin. Une commande slash est tapée telle quelle dans Claude, sans marque, et une commande ou un skill comme
/deliverfait tourner toute une tâche à partir de son propre texte. Donc un membre dont le rôle n'est pas exempté des règles de rôle (les rôles intégrésuseretread_only, les rôles que vous définissez sansbypass_role_denies, et quiconque qu'aipager ne connaît pas) ne peut envoyer que les commandes du clavier Commandes d'aipager (plus/compact) et les changements de modèle (/model <name>) ; toute autre commande slash, tapée, envoyée à une session avec/<name>, en légende d'un fichier, comme premier message de/newou comme modèle, est refusée avec « Cette commande est réservée aux admins. » et un 🤷. Les propriétaires et les admins envoient n'importe laquelle. Une commande slash qui arrive vraiment à Claude depuis Telegram reste contrôlée : le hook voit que la tâche a pris un message Telegram qui a envoyé cette commande et la tient aux règles de cet expéditeur. Une commande slash que vous tapez dans le terminal reste sans restriction, sauf si la même commande (avec n'importe quels arguments) a été envoyée depuis Telegram plus tôt dans cette session : aipager ne peut pas distinguer votre copie de Claude Code qui relivre celle de Telegram, donc la vôtre tourne alors avec les règles de la dernière tâche Telegram. Cela s'arrête quand la session se termine. - Un message sans nouvel expéditeur garde les règles de la tâche. Claude Code signale aussi des messages qu'aucun message Telegram n'explique pendant qu'une tâche Telegram tourne : un message tapé dans le terminal et mis en file derrière la tâche (signalé au moment où il est mis en file), un message d'une autre session Claude locale, ou le même message Telegram relivré après un compactage. Un tel message n'ajoute aucun expéditeur Telegram, donc la tâche garde les règles avec lesquelles elle tournait, rendues plus strictes par tout message qui attend encore d'être pris, et jamais plus souples. Un message qui porte du texte Telegram qu'aipager ne peut attribuer à personne tombe toujours sur le socle intégré, et un message d'un expéditeur qui a moins de droits abaisse toujours la tâche aux règles de cet expéditeur.
Ce qui limite vraiment une personne restreinte. La session Claude tourne avec le même utilisateur du système que celui à qui appartient la configuration d'aipager (le token du bot dans ~/.config/aipager/aipager.yaml, l'identifiant Claude dans ~/.config/aipager/daemon.env) et son fichier de règles par tâche (/tmp/claude-policy-<session>.json). Un shell qui tourne avec cet utilisateur peut atteindre les deux par des écritures qu'aucun motif ne prévoit (sed -i … /tmp/*-policy-*.json, une boucle for sur un glob, une ligne de Python) et réécrire ses propres règles en celles du propriétaire. Les motifs de commandes sont donc un filtre, pas une barrière. Pour une tâche qui tourne avec les rôles restreints intégrés (user, read_only), et pour une tâche qu'aipager ne peut attribuer à personne, aipager applique ces règles, dont aucune ne dépend des motifs de commandes (voir les limites connues plus bas pour ce qu'elles ne couvrent pas) :
- Pas de shell. Ils ne peuvent pas utiliser
Bash, ni aucun autre outil avec lequel Claude Code exécute du code (PowerShell,Monitor,REPL,Workflow, les programmateurs de messagesCronCreateetRemoteTrigger, … ; la liste estsafety.CODE_EXECUTION_TOOLS). Un message programmé arriverait sans la marque Telegram, c'est-à-dire sans restriction, donc ceux-là comptent aussi. Pour la même raison, ils ne peuvent pas utiliserSendMessage(qui peut passer un message à une autre session Claude locale, qui l'exécuterait comme un message de terminal), niEnterWorktree/ExitWorktree(qui déplacent le dossier de travail de la session, sur lequel compte la règle suivante). - Les écritures restent dans le projet.
Write,Edit,MultiEditetNotebookEditn'écrivent que dans le dossier de la session (le dossier de travail que Claude Code indique dans le contenu du hook, jamais un chemin tiré de l'appel d'outil) et dans le scratchpad Claude Code de la session (/tmp/claude-<uid>/<project>/<session id>/, ou/tmp/claude-<uid>/quand on ne peut pas le déterminer). Tout le reste est refusé : les fichiers de démarrage de votre shell,~/.ssh, les autres projets, les fichiers/tmpd'aipager. Dans le dossier,.claude/,.git/et.mcp.jsonsont refusés aussi, parce que chacun d'eux fait exécuter une commande à Claude Code ou à git. Les liens symboliques sont suivis avant de décider. Une session dont le dossier est votre dossier personnel,/ou un dossier au-dessus de votre dossier personnel n'a pas de dossier de projet : là, une tâche restreinte n'écrit que dans son scratchpad, et une écriture ailleurs est refusée avec « cette session tourne dans le dossier personnel (ou au-dessus), et ce rôle ne peut écrire que dans un dossier de projet ». Pour que les membres restreints n'y restent pas coincés,/newet la Mini App ne les laissent pas lancer une session dans ces dossiers (le dossier d'aipager lui-même, le dossier par défaut d'une nouvelle session, est votre dossier personnel sous le service en arrière-plan) ; ils peuvent créer un nouveau dossier dans votre dossier personnel et y démarrer (un nouveau : pas un dossier que vous avez déjà là, et pasbinninode_modules, dans lesquels d'autres programmes chargent du code). - Les membres restent dans leur dossier racine. Un rôle sans
can_manage(les rôles intégrésuseretread_only) travaille dans le dossier racine de la discussion, que vous définissez dansaipager config(par défaut~/aipager-users/<label>;~/aipager-userset le dossier sont créés avec les droits 0700)./new, Reprendre, Remplacer, Redémarrer, le changement de mode et la Mini App ne lancent ou ne relancent une session de ce membre que dedans. Le hook de sécurité l'impose quel que soit le chemin d'un message (Telegram, la Mini App, le message d'une autre session) : le dossier racine voyage dans l'instantané des règles de la tâche, dans les reçus des messages entre sessions et dans les droits figés et programmés, et quand des membres avec des dossiers racines différents partagent une tâche, le plus étroit l'emporte (deux qui ne sont pas l'un dans l'autre ne laissent que le dossier de la session elle-même). Les écritures ne vont dans le dossier de la session que s'il est dans le dossier racine (une session en dehors, comme une session lancée avant l'arrivée des dossiers racines, continue de tourner, mais ses tâches n'écrivent que dans le scratchpad : « cette session tourne hors du dossier de ce rôle »), etRead,LSP,GrepetGlobn'atteignent rien de votre dossier personnel en dehors du dossier racine (« dans le dossier personnel, ce rôle ne lit que dans son propre dossier »), à part le scratchpad de la session et son propre fichier de réponse. Le dossier racine de chaque autre discussion est un chemin protégé pour le membre, où qu'il soit, tout comme le dossier des fichiers envoyés de chaque autre discussion sous/tmp/aipager-files/(pourRead,Grep,Globet les outils d'écriture ; pour un rôle à qui on donne Bash, une commande qui en nomme un est refusée, au mieux), ainsi que les fichiers que toutes les discussions y partageaient avant les dossiers par discussion. Les fichiers envoyés dans la propre discussion du membre restent lisibles. Les dossiers en dehors de votre dossier personnel (/usr,/etc,/srv) restent lisibles. Un rôle personnalisé avecbypass_role_deniesoubypass_safetymais sanscan_manageécrit et cherche partout dans son dossier racine, et ses lectures dans votre dossier personnel restent aussi dedans : seuls les propriétaires et les admins n'ont pas de restriction. Avecbypass_safety, dans une session dont le dossier est en dehors de son dossier racine, il n'écrit et ne cherche que dans le scratchpad, comme unuser, et dès qu'un de ses appels d'outil est refusé, le reste de la tâche est arrêté. - Les recherches restent dans le projet. Un
Grepou unGlobn'est autorisé que si tout ceci est vrai : le dossier dans lequel il cherche (sonpath, ou le dossier de la session quand il n'en a pas, liens symboliques suivis) est dans le dossier de la session (pas quand celui-ci est votre dossier personnel,/ou au-dessus de votre dossier personnel, comme pour les écritures) ou dans le scratchpad ; ce dossier n'est pas un chemin protégé et n'en contient aucun (une session lancée dans votre dossier personnel contient~/.config/aipager, donc elle ne peut pas chercher depuis son sommet) ; chaque glob est un simple motif relatif (pas de/,~,$ou lettre de lecteur au début, pas de.., pas de#(un commentaire pour ripgrep) ni de!(une négation), pas d'échappement\) ; et vous n'avez aucune règle de chemin protégé sans/ou~au début (**/.envpeut se trouver n'importe où dans le projet, donc avec une telle règle, chaque recherche restreinte est refusée). Tout le reste est refusé. C'est une liste d'autorisation, exprès : trois tours de relecture ont chacun trouvé une nouvelle écriture de glob qui faisait sortir ripgrep du dossier que prévoyait un modèle de sa lecture, et l'une d'elles a lu le token du bot. - Les outils de fichiers respectent les chemins protégés.
Read,LSPet les outils d'écriture vérifient où un chemin mène vraiment, liens symboliques compris. Le seul fichier de contrôle qu'une tâche peut lire est le/tmp/claude-reply-<session>.txtde sa propre session, qu'aipager nomme dans le message quand vous répondez à un message plus ancien ; les fichiers de réponse des autres sessions, et toute écriture dans celui-ci, restent refusés. Sur macOS,/tmpest/private/tmpet le disque ne distingue pas les majuscules, donc les deux écritures, et toutes les variantes de majuscules, sont reconnues. - Une vérification qui échoue refuse. Si la décision sur un appel d'outil lève une erreur (un octet NUL dans un chemin, un bug), ou si le hook manque de mémoire pendant qu'il décide, l'appel est refusé, sauf si les règles de la session ont pu être lues et donnent l'exemption du propriétaire. Autrefois il passait.
Vos propres chemins protégés. La section safety: de policy.yaml (deny_paths_no_access, deny_paths_no_write, deny_bash_patterns) s'ajoute aux chemins protégés et aux motifs de commandes intégrés. Elle fait partie du socle que reçoit chaque tâche de chaque rôle sauf owner, admins compris, et elle s'applique aux outils de fichiers, aux recherches et aux commandes Bash exactement comme la liste intégrée. aipager écrit la liste combinée dans /tmp/claude-policy-.floor-<uid>.json (lui-même un chemin protégé) pour les solutions de repli du hook de sécurité ; si ce fichier est absent, illisible ou appartient à un autre utilisateur du système, le hook utilise la liste intégrée, jamais moins. Un changement fait par un rechargement sans redémarrage s'applique aux messages envoyés après le rechargement, pas à une tâche déjà en cours ni à un message qui attend déjà. Jusqu'à la 0.7.20, la section était lue et affichée par aipager doctor --safety-check mais pas appliquée.
Les tâches qu'aipager ne peut attribuer à personne. Une tâche Telegram sans expéditeur à qui la rattacher tourne avec le socle, qui correspond au rôle intégré user (pas aux restrictions en plus que vous ajoutez à user dans policy.yaml) plus votre section safety: : pas d'outils qui exécutent du code, écritures et recherches enfermées, les chemins protégés. Cela arrive pour un message dont la note d'autorisation a expiré (après 24 heures) ou n'a pas pu être écrite, et pour un message qui porte du texte Telegram qu'aucune note ne reconnaît. Le bouton Réessayer tourne avec les droits de la personne qui l'a touché quand c'est elle aussi qui a envoyé le message réessayé, donc le Réessayer d'un propriétaire sur son propre message garde Bash ; un Réessayer sur le message de quelqu'un d'autre, ou sur un message dont aipager ne connaît pas l'expéditeur, tourne avec le socle, pour que le texte de quelqu'un n'emprunte jamais les droits de la personne qui touche le bouton.
Les lectures en dehors des chemins protégés sont permises pour un rôle qui n'est pas tenu à un dossier racine (un rôle personnalisé avec can_manage mais sans exemption) : il peut lire les autres projets de l'utilisateur du système. Les rôles intégrés user et read_only ne lisent rien de votre dossier personnel en dehors de leur dossier racine (plus haut). Vos fichiers d'identifiants sont des chemins protégés pour chaque rôle sans bypass_role_denies (les rôles intégrés user et read_only, les rôles que vous définissez, et le socle) : ~/.ssh, ~/.gnupg, ~/.aws, ~/.config/gh, ~/.git-credentials, ~/.netrc, ~/.docker/config.json, ~/.kube, ~/.config/gcloud, ~/.azure, ~/.password-store, ~/.pgpass, ~/.npmrc et ~/.pypirc ne peuvent être ni lus, ni écrits, ni fouillés. C'est une valeur par défaut du rôle, donc le propre deny_paths_no_access d'un rôle dans policy.yaml les remplace pour ce rôle, sauf quand une tâche en cours est rejointe par un autre message ou par un message tapé dans le terminal : de ce moment jusqu'à la fin de la tâche, elle y est de nouveau tenue (les règles les plus strictes l'emportent). Un fichier d'identifiants ailleurs (un .env dans un projet, un token dans un autre dossier) n'est pas couvert : ajoutez-le au deny_paths_no_access du rôle. Les propriétaires ne sont tenus à rien de tout cela. Les écritures des admins ne sont pas enfermées. Leurs Grep/Glob peuvent chercher dans n'importe quel dossier qui n'atteint pas un chemin protégé : une recherche est refusée quand le dossier où elle cherche (son path, ou le dossier de la session quand elle n'en a pas ; pour un Glob, aussi le dossier que nomme son motif, comme dans /home/you/.config/aipager/*, ou le dossier au-dessus d'un motif sans joker, que Claude Code fouille à toute profondeur pour ce nom), liens symboliques résolus, est un dossier protégé, se trouve dans un dossier protégé ou en contient un. Donc un Grep de ~, de /, du dossier au-dessus de votre dossier personnel ou de /tmp est refusé (ils contiennent ~/.config/aipager, ~/.claude ou les fichiers /tmp/claude-* d'aipager), alors qu'un Grep d'un projet ou de /tmp/myproj ne l'est pas. Une règle de chemin protégé sans / ou ~ au début (comme **/.env) peut correspondre dans n'importe quel dossier, donc avec elle dans les règles d'un admin (ou dans la section safety:, que reçoit chaque rôle sauf owner), chaque recherche d'admin est refusée, comme pour les rôles restreints ; aipager doctor prévient quand il y en a une dans la section safety:. Les admins ont Bash, donc leurs règles ne s'appliquent qu'au mieux (plus bas).
Limites connues. Ceci n'est pas couvert :
- Si le hook lui-même échoue avant de commencer à décider (par exemple s'il ne peut pas lire son propre fichier d'état, ou s'il manque de mémoire en démarrant), Claude Code le traite comme « autoriser ». Une personne restreinte ne peut pas provoquer cela ; toute erreur pendant la décision refuse.
- Les outils qui ne sont pas des outils de fichiers mais peuvent envoyer des données dehors ne sont pas vérifiés par chemin :
WebFetch(tout ce que la tâche peut lire, elle peut l'envoyer à une URL) et les autres outils d'envoi de Claude Code. Refusez-les pour un rôle danspolicy.yamlsi cela compte pour vous.SendUserFile, qui envoie des fichiers dans le chat, est vérifié (voir fichiers envoyés par Claude). - Un agent en arrière-plan qu'une tâche restreinte a lancé continue de tourner après la fin de la tâche, et ses appels d'outil suivants sont jugés d'après le message le plus récent de la session : un message plus tard du propriétaire ou du terminal lève ses restrictions.
- Un rôle que vous définissez avec
can_managemais sans aucune des deux exemptions n'est pas tenu à un dossier racine : il peut toujours démarrer dans n'importe quel dossier de projet où sa discussion travaille déjà (un dossier qu'une session précédente a utilisé), y compris l'un des vôtres, et lire vos autres projets. - Une tâche qu'on ne peut rattacher à aucun membre (un expéditeur inconnu, un membre retiré, un message d'une autre session dont l'expéditeur ne peut pas être vérifié) tourne avec le socle restreint, qui n'a pas de dossier racine. Pour les lectures, ce socle est plus faible qu'un
userintégré : une telle tâche peut lire n'importe quel fichier de votre dossier personnel qui n'est pas un chemin protégé (vos autres projets, par exemple), là où la tâche d'unuserne lit que dans son dossier racine. Une telle tâche peut aussi lire les fichiers envoyés de toutes les discussions dans/tmp/aipager-files/, alors que la tâche d'unuserne les lit que pour sa propre discussion. Ses écritures restent enfermées dans le dossier de la session et le scratchpad. Un message vérifié venant de la session d'un membre porte le dossier racine de ce membre. - Le dossier racine ne limite que les outils de fichiers. Un rôle personnalisé à qui vous donnez
Bashpeut lire en dehors par le shell (les rôles intégrésuseretread_onlyn'ont pasBash). Un rôle personnalisé avecbypass_safetymais sanscan_managegarde son exemption pour tout sauf les chemins des outils de fichiers : aucun outil ni motif de commande ne lui est refusé. - Une raison de refus qui nomme le dossier racine d'un autre membre montre le chemin de ce dossier à Claude et dans la discussion.
- La tâche d'un membre déjà en cours au moment où les dossiers racines arrivent (une mise à jour) n'en a pas jusqu'au prochain message de ce membre.
- Dans le projet, une personne restreinte peut modifier ce que vous lancerez ou lirez vous-même plus tard (les sources, un
Makefile, les scripts depackage.json,conftest.py, unCLAUDE.mdque chargent vos propres tâches).
Un rôle qui a Bash (admin, exempté des règles de refus de rôle, ou tout rôle à qui policy.yaml le redonne : allow_tools qui nomme Bash, ou un deny_tools remplacé) n'est tenu qu'à des règles appliquées au mieux : les motifs de commandes ci-dessous, qu'une personne déterminée peut contourner. aipager doctor avertit une fois pour chaque rôle de ce genre qu'a un membre. Ne donnez Bash qu'à quelqu'un à qui vous donneriez un shell sur l'ordinateur.
Les règles de commandes intégrées. Parmi les motifs Bash auxquels est tenue chaque tâche Telegram qui n'est pas celle d'un propriétaire et qui a Bash, l'un bloque le mot claude : il arrête un claude imbriqué (avec n'importe quel chemin, enveloppe ou option) et tout accès à ~/.claude en une seule règle, et il attrape aussi les noms simples des fichiers de contrôle /tmp/claude-* d'aipager, dont les noms partagent ce préfixe (un motif n'est pas une vraie protection pour ces fichiers : une écriture en glob l'évite). La seule exception est le dossier scratchpad que crée Claude Code, /tmp/claude-<uid>/… : ce dossier exact, comme composant entier du chemin, est exempté, donc les commandes lancées depuis le scratchpad d'une session ne sont pas arrêtées. Il appartient à l'utilisateur du système, pas à une session, donc une tâche avec Bash peut y lire les scratchpads et les sorties de tâches des autres sessions, comme le peut l'outil Read. Un glob ou un sosie de ce dossier (/tmp/claude-1*, claude-1000x), un autre dossier temporaire, et tout ce qu'on atteint depuis lui (…/../../home/you/.claude) restent bloqués. Les autres mentions du mot (git log --grep=claude, un fichier nommé claude-notes.md, pip show claude-agent-sdk) restent aussi bloquées, exprès : le même mot nomme l'exécutable, ses SDK et les fichiers de contrôle d'aipager, et un motif ne peut pas les distinguer. Les règles que vous ajoutez dans policy.yaml voient la commande exactement comme elle a été tapée, scratchpad compris.
Les recherches par Bash. Certaines versions de Claude Code n'ont pas d'outil Grep ni Glob (la 2.1.289 en est une), et Claude cherche alors avec grep, rg ou find par Bash, où la règle Grep/Glob ci-dessus ne s'applique pas. Pour chaque rôle sauf owner, les motifs de commandes refusent aussi une recherche ou un listage récursif de tout le dossier personnel ou de / : grep -r (-R, --recursive, -d recurse, egrep, fgrep, rgrep), rg (et rg --files), ag, ack (et ack -f), find, fd, tree, du et ls -R dont le dossier est ~, ~user, $HOME, ${HOME} ou "$HOME", /home, /home/<user> (ou /home/$USER), /Users, /Users/<user>, /root ou / (avec ou sans /, /. ou /* à la fin), ou ~/.config ou ~/.local, qui contiennent les dossiers d'aipager. Cela vaut après cd x &&, dans un pipe, dans bash -c "...", après xargs, env ou timeout et après des réglages du genre LC_ALL=C. La réponse dit de chercher plutôt dans un dossier de projet. Les recherches dans un dossier de projet (grep -rn x ~/proj, rg x src/, find . -name y), les recherches, dans un projet, d'un chemin personnel comme texte à trouver (grep -rn "$HOME" src : le premier mot après les options est le texte à trouver, pas un dossier) et les recherches qui ne sont pas récursives (grep x ~/.bashrc, ls ~) ne sont pas touchées. C'est au mieux, comme tout motif de commande : un motif voit les mots, pas l'endroit où le shell les exécute, donc il ne peut pas voir une recherche sans dossier, ou avec . ou .., lancée depuis le dossier personnel (ou en dessous) (cd ~ && grep -r x .), un dossier personnel atteint par une autre variable ou un glob, ni un dossier personnel écrit en entier en dehors de /home, /Users et /root, ni \grep, ni un dossier juste après un mot qui finit par -e ou -f (pris pour le texte que -e/-f donne à grep et rg, donc ag x -f ~ passe). Il ne sait pas non plus quelles options prennent une valeur séparée, donc rg -t py "$HOME" . ou grep -rn -A 3 "/home/you" src lit py ou 3 comme le texte et le chemin personnel comme un dossier, et est refusé (écrivez -A3, --include=*.py, ou mettez l'option après le texte), tout comme un chemin personnel donné à -e dans un groupe d'options (grep -rne "/home" src ; -r -n -e ne pose pas de problème) ; une commande citée dans le texte d'une autre commande (echo "a; rg x ~") est refusée aussi, tout comme du -sh ~ (seulement des tailles).
Pourquoi un seul blocage arrête toute la tâche. Après le blocage d'un appel d'outil, chaque appel suivant dans la même tâche est refusé aussi, jusqu'au message suivant. Sans cela, l'agent réessaie simplement la même action autrement formulée (un glob, une autre commande, un chemin encodé) jusqu'à ce qu'une écriture passe entre les motifs. Arrêter la tâche rend une première tentative coûteuse ; cela ne fait pas d'un motif une barrière, puisque la première écriture essayée peut être celle qui passe. Le prix d'une fausse alerte est une tâche arrêtée, et c'est pour cela que les règles ci-dessus restent étroites.
Voir groupes → comment les règles fonctionnent pour les règles que portent ces notes, telles que vous les voyez.
Fichiers envoyés par Claude#
Chaque session lancée par aipager a l'outil SendUserFile de Claude Code (CLAUDE_CODE_ATTACHMENTS_BY_PATH=1), qui lit des fichiers et les envoie dans le chat de la session (commandes). Le hook PreToolUse vérifie chaque fichier de l'appel avant que Claude Code ne le lise :
- Le plancher d'envoi, pour chaque tâche, celle du propriétaire et un message tapé dans le terminal compris : la configuration et l'état d'aipager (
~/.config/aipager/**,~/.local/share/aipager/**,~/.local/state/aipager/**et ses fichiers de contrôle dans/tmp), tout~/.claude/**et~/.claude.json*(les identifiants et le compte de Claude), les fichiers d'identifiants dont chaque rôle restreint est tenu à l'écart (~/.ssh/**,~/.gnupg/**,~/.aws/**,~/.git-credentials,~/.netrcet le reste de cette liste),/proc/**,/sys/**,/dev/**, et les noms qui ressemblent à des secrets, où qu'ils soient (.env,.env.*,*.pem,*.key,id_rsa*,id_ed25519*et les autres noms de clés,*.p12,*.pfx,*.kdbx,*.jks,*.keystore,.pgpass,credentials,*credentials*.json). La vérification porte sur le chemin tel qu'il est écrit et tel qu'il mène vraiment, donc un lien symbolique vers l'un d'eux est refusé aussi. Certains fichiers inoffensifs portent ces noms (un*.keyde Keynote, un.env.example) et sont refusés eux aussi. - Le fichier lui-même : un fichier ordinaire une fois les liens symboliques résolus, avec un seul lien physique, non vide, de 50 Mo au plus.
- Les règles de la tâche : ce que la tâche n'a pas le droit de lire (Read), et pour un membre (un rôle sans
can_manage, tenu à un dossier racine) la règle plus stricte selon laquelle chaque fichier, tel qu'écrit et tel qu'il mène vraiment, est dans le dossier racine. Contrairement aux lectures d'un membre, il n'y a pas d'exception pour les fichiers hors du dossier personnel (/tmp,/etc), ni pour le scratchpad de la session.
Un refus est un refus doux : Claude lit la raison (« SendUserFile refused, nothing was sent: ... Tell the person which file could not be sent and why. ») et la tâche continue ; la session n'est pas arrêtée comme pour un outil bloqué. Une vérification qui échoue refuse le fichier, pour le propriétaire aussi. Chaque fichier refusé a une entrée FileRefused dans le journal d'audit.
aipager ne croit pas le socket de contrôle sur parole pour un fichier : n'importe quel processus local peut y écrire, en nommant n'importe quelle session, n'importe quel appel ou historique de conversation, et le hook d'un appel d'outil suffit à lui seul à faire passer une session inactive pour occupée. Un appel ne compte donc que pendant une tâche qu'aipager a vue commencer par un prompt (un qu'il a tapé depuis Telegram, ou un UserPromptSubmit) et pendant le travail en arrière-plan de cette tâche, jusqu'à ce que la session soit inactive sans aucun travail en cours ; à ce début, aipager note l'heure et l'historique qu'il suit pour la session (un UserPromptSubmit ultérieur de la même tâche nomme de nouveau l'historique). Une commande slash qu'aipager tape ne commence aucune tâche (une commande qui lance une tâche envoie son propre UserPromptSubmit), et une tâche qui ne commence jamais (la session encore inactive sans travail 15 secondes plus tard) est refermée. Claude Code commence certaines tâches sans UserPromptSubmit : un message envoyé pendant qu'une tâche tourne et qu'il prend comme tâche suivante, et la tâche qu'un hook Stop fait continuer (/goal). Une telle tâche compte quand aipager a tapé ce message lui-même, ou dès que l'historique qu'il a noté montre son début après le dernier Stop (une ligne de file dequeue, ou la ligne Stop hook feedback: qu'écrit Claude Code) ; d'ici là, rien de l'appel n'est lu, pendant 20 secondes au plus. Il prend la liste des fichiers du PreToolUse qu'il a vérifié, ou du PostToolUse quand le premier a été manqué (après un redémarrage) ; jamais du résultat de Claude Code. Il ne copie chaque appel qu'une fois : un identifiant d'appel déjà copié n'est plus jamais copié ni envoyé (il garde les 256 derniers par session, même après un redémarrage). Il détermine une seule fois le vrai chemin de chaque fichier et l'y vérifie à nouveau (le plancher, les vérifications de fichier et le dossier racine de la session), ouvre exactement ce chemin sans suivre de lien, s'assure que le fichier ouvert est bien celui qu'il a vérifié et qu'il est toujours à ce chemin, et le copie dans ~/.local/state/aipager/outbox/<chat>/ (dossier 0700, fichier 0600), un endroit dont chaque tâche restreinte est déjà tenue à l'écart. Avant l'envoi, il cherche l'appel dans l'historique qu'il a noté pour la tâche (pour l'appel d'un sous-agent, dans l'historique propre de ce sous-agent, à côté), jamais dans un historique que nomme le datagramme d'un appel d'outil : une ligne de l'assistant avec un tool_use de SendUserFile qui a cet identifiant et exactement ces fichiers, écrite depuis le même dossier et pas avant le début de la tâche, dans un fichier .jsonl sous ~/.claude/projects (ou $CLAUDE_CONFIG_DIR/projects, tel qu'il est défini pour aipager lui-même) qui appartient au propre utilisateur système d'aipager. Claude Code écrit son historique en retard, donc aipager attend l'appel jusqu'à 20 secondes, et tant que le travail de la session continue (un agent en arrière-plan, un appel d'outil), il garde les fichiers et cherche de nouveau plus tard. Un appel toujours introuvable quand plus rien ne tourne n'est pas envoyé et ses copies sont supprimées ; un appel arrivé alors qu'aucune tâche de ce genre ne tournait, ou arrivé une deuxième fois, n'est même pas copié. Chacun de leurs fichiers reçoit une entrée FileFailed dans le journal d'audit avec la raison unverified. Le chat reçoit une ligne pour eux (⚠️ Impossible d'envoyer report.pdf : aipager n'a pas pu confirmer que Claude l'a envoyé pendant cette tâche.) seulement quand l'historique de Claude lui-même montre cet appel précis pendant la tâche, et la ligne nomme les fichiers que cette ligne de l'historique liste, si bien qu'un appel simulé ne peut mettre ni ligne ni nom dans le chat ; sinon, rien dans le chat. Seule la copie est envoyée, seulement dans le chat de la session, après la réponse de la tâche, après les fichiers précédents de la session et sans retenir la tâche suivante ; elle est supprimée une fois envoyée, ou après un jour.
Ce que ces vérifications ne couvrent pas : un processus qui tourne sous le propre utilisateur système d'aipager peut écrire les historiques de Claude aussi bien que le socket (il peut déjà lire ces fichiers lui-même). Un processus d'un autre utilisateur qui atteint le socket peut aussi simuler un UserPromptSubmit, qui pour ces vérifications commence une tâche et nomme son historique ; il peut ensuite faire envoyer par aipager, dans le chat de cette session, un fichier qu'un vrai appel SendUserFile a nommé, si cet appel est écrit dans cet historique depuis le même dossier après ce moment (par exemple un appel qu'une autre session fait à cet instant précis), une fois par appel. Pour cela, il doit aussi envoyer l'id propre de cet appel en cours (tool_use_id), qu'un autre utilisateur ne peut pas lire : il se trouve dans les historiques sous le ~/.claude de l'utilisateur d'aipager, et le hook le reçoit par son entrée, pas par sa ligne de commande. Un Stop simulé termine la tâche pour ces vérifications, si bien que les fichiers que cette tâche envoie ensuite ne sont pas envoyés. Une session lancée avec un autre $CLAUDE_CONFIG_DIR que celui d'aipager, ou une tâche dont aipager a manqué le UserPromptSubmit (tapée dans le terminal pendant qu'aipager redémarrait), n'envoie aucun fichier. Une tâche que Claude Code commence sans hook d'une autre façon (les deux ci-dessus sont les seules connues) n'envoie pas de fichiers non plus ; un fichier que l'historique de Claude montre qu'il a demandé là reçoit la ligne ci-dessus. Le socket se trouve dans $XDG_RUNTIME_DIR, que les autres utilisateurs ne peuvent pas atteindre, quand il est défini ; sinon, il se trouve dans /tmp.
Journal d'audit#
Chaque appui sur Autoriser / Refuser, plus chaque réponse à un AskUserQuestion, ajoute une ligne 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
}
Champs (voir aipager/audit.py pour la liste complète) :
ts: l'heure UTC au format ISO 8601, à la seconde près.session/label: le nom interne et le nom lisible.action: ce qui s'est passé (Allowed,Denied,Allowed always, une réponse à unAskUserQuestion, un refus automatique, …).tool/summary: le nom de l'outil et une entrée ou un texte de question raccourcis.user_id/username: qui a appuyé, pour savoir qui a fait quoi en équipe ;scope_label/scope_chat_idquand plusieurs discussions sont configurées.denied: true pour tout refus, qu'il vienne d'un appui ou d'une règle.
L'écriture est faite au mieux. Si le disque se remplit ou si ~/.claude/ n'est plus accessible en écriture, aipager écrit un WARNING dans son journal et continue de tourner : pas de perte silencieuse, pas de plantage. Voir aipager/audit.py.
Le journal d'audit ne fait qu'ajouter sur le disque. Associez-le à la réponse d'audit dans la discussion (un message Telegram par décision, sous la carte d'activité) pour avoir deux traces indépendantes.
Limite des droits#
aipager ne demande jamais plus de droits. Pas de sudo, pas de setuid, pas de doas. Chaque fichier qu'il écrit se trouve dans votre dossier personnel, sous /tmp (les fichiers de contrôle de chaque session d'aipager, les téléchargements et le journal de redémarrage) ou dans le dossier de son socket de contrôle ($XDG_RUNTIME_DIR) ; la liste complète est dans architecture. Chaque sous-processus (claude, dtach, les installateurs, claude update) tourne avec l'utilisateur d'aipager.
Installer la fonction vocale depuis Telegram ([📦 Installer la fonction vocale]) passe par l'installateur qui possède aipager (uv tool install --reinstall aipager[voice], pipx install --force aipager[voice]), et sinon installe les paquets de la fonction avec le Python d'aipager lui-même (<python> -m pip install) : toujours dans l'environnement d'aipager, jamais dans le Python du système.
Le bouton [🔄 Redémarrer aipager], sous le service systemd, programme systemctl --user restart aipager.service dans un service utilisateur temporaire à part ; sur macOS, il lance launchctl kickstart -k ; un aipager que vous avez lancé vous-même lance une copie détachée de lui-même qui attend que l'ancien se termine. Après une mise à jour, seul le service systemd redémarre tout seul (voir commandes → mise à jour). Tout cela tourne avec le même utilisateur ; aucune élévation.
Connexions réseau#
aipager n'écoute sur aucun port TCP autre que le bouclage local. Le serveur de la Mini App se lie seulement à 127.0.0.1 (codé en dur : il n'y a exprès aucune option d'hôte) et n'est joignable de l'extérieur que par le tunnel géré décrit plus bas. Connexions sortantes :
- Interrogation longue en HTTPS vers
api.telegram.org(le polling du bot Telegram). - HTTPS vers
telegram.orgpour le script du SDK de la Mini App (telegram-web-app.js), récupéré une fois au démarrage du serveur de la Mini App et rafraîchi au plus une fois par jour ; gardé dans~/.local/share/aipager/webapp-sdk/. aipager sert ces octets à la page depuis sa propre origine et ne les exécute jamais. aipager ne redistribue pas le script : rien n'est inclus dans le paquet, et quand aipager n'en a pas de copie, la page le charge directement depuistelegram.org, comme avant. - Le tunnel de la Mini App vers Cloudflare, tant qu'il est activé (c'est le cas par défaut). L'exécutable
cloudflaredqu'il lance est téléchargé une fois depuis les versions publiées surgithub.com/cloudflare/cloudflared, vérifié contre un SHA-256 figé, et gardé dans~/.local/share/aipager/cloudflared/; uncloudflaredde votrePATHn'est jamais utilisé. Rien de tout cela n'arrive avec votre propre URL de Mini App (voir plus bas). - La vérification des versions, seulement quand quelqu'un touche Rechercher des mises à jour ou Mettre à jour (
/update, les Mises à jour de la Mini App), ou quand quelqu'un qui peut mettre à jour ouvre l'onglet Paramètres de la Mini App (au plus une fois toutes les dix minutes par app ouverte) :pypi.orgpour aipager, etdownloads.claude.aiouregistry.npmjs.orgpour Claude Code, selon la façon dont il a été installé. Une mise à jour lance ensuite l'installateur (qui parle à PyPI ou à Homebrew) etclaude update.aipager updateen ligne de commande ne lance que l'installateur. - L'installation de la fonction vocale, quand vous touchez son bouton : l'installateur récupère les paquets sur PyPI, et le modèle vocal est téléchargé depuis Hugging Face (
huggingface.co) à la première utilisation. - HTTPS vers
raw.githubusercontent.com(le fichier de la clé des rapports) et vers l'hôte de réception de Sentry, seulement quand vous confirmez l'envoi d'un rapport de problème : le rapport que vous avez vu et un court résumé tiré de lui, rien d'autre. Voir Rapports de problèmes.
Connexions entrantes :
- Un socket datagram Unix à
$XDG_RUNTIME_DIR/aipager.sock(avec un repli sur/tmp/aipager.sockquand$XDG_RUNTIME_DIRn'est pas défini). Lié et réglé parchmodpar aipager au démarrage (aipager/dtach/hook_receiver.py). Droits0o666pour que n'importe quel processus local puisse lui envoyer des événements de hook : la même confiance que~/.claude/settings.json, qui contrôle déjà ce qui lance les hooks.
Cela veut dire qu'aipager n'est pas une surface d'attaque à distance. Un attaquant sur le réseau ne peut pas l'atteindre sans avoir déjà pris pied sur l'ordinateur.
Le texte de Claude et ce que Telegram télécharge de lui-même. Les serveurs de Telegram téléchargent d'eux-mêmes certaines adresses d'un message : un bloc d'image, de vidéo ou d'audio dans un message enrichi, et la page derrière un aperçu de lien. Les réponses de Claude, ses notes sur la carte d'activité, les descriptions des agents et les arguments des outils arrivent dans Telegram tels que Claude les a écrits, donc une injection de prompt pourrait faire écrire à Claude une adresse qui fait sortir un secret, sans appel d'outil et sans demande d'autorisation. aipager ferme la première voie, et la seconde là où Telegram a un réglage pour cela :
- Dans chaque message enrichi, une image écrite
est envoyée comme un simple lien avec son texte alternatif, et toute autre façon d'écrire une image, ainsi que les balises brutes<img>,<video>,<audio>,<source>,<picture>,<figure>,<iframe>,<embed>,<object>,<tg-document>,<tg-collage>,<tg-slideshow>,<tg-button>et<tg-button-row>, sont affichées comme du texte (aipager/bot/media_guard.py). Dans un bloc de code, comme un extrait de HTML, cela fait apparaître un\ou un<en trop. - Les aperçus de liens sont désactivés sur chaque message en texte brut ou en HTML qu'aipager envoie ou modifie (y compris les versions en texte brut des réponses et des cartes, utilisées en secours), ses propres messages compris. Les liens de sa Mini App sont des boutons, que Telegram ne va pas chercher.
Telegram ne documente aucune option d'aperçu pour les messages enrichis ; on n'a pas vérifié sur le vrai service si Telegram construit un aperçu de lien pour l'un d'eux.
Le tunnel de la Mini App#
La Mini App est activée par défaut, donc sur une installation standard aipager ouvre ce tunnel sans qu'on le lui demande. C'est un choix voulu (une option à activer que personne ne découvrait était une fonction qui n'existait pas), mais cela veut dire qu'une installation neuve va sur internet. Désactivez-la avec aipager miniapp disable, ou mettez enabled: false sous miniapp: dans aipager.yaml ; un false explicite est toujours respecté. Notez qu'un bloc miniapp: absent ou abîmé revient maintenant à ACTIVÉ plutôt qu'à DÉSACTIVÉ.
La Mini App (sauf si une URL --url la remplace) lance un tunnel rapide Cloudflare géré, qui pointe vers le serveur local d'aipager lui-même, et publie l'adresse publique https://*.trycloudflare.com qu'il reçoit comme bouton de menu de Telegram et comme réponse de /app.
Cette adresse n'est pas secrète, et n'a pas à l'être : chaque demande que sert la Mini App est vérifiée avec la signature initData de Telegram (voir Limite de confiance plus haut), la même vérification qui la protège quelle que soit la façon dont l'URL a été obtenue. Connaître seulement le nom d'hôte ne donne rien à un attaquant qu'il ne pourrait pas déjà obtenir en devinant au hasard un sous-domaine de trycloudflare.com : la vérification de signature est la vraie porte. Traitez-la comme n'importe quelle autre URL non publiée mais pas secrète : rien de mal à la laisser tourner, mais pas question de la coller dans un canal public sans raison.
Le nom d'hôte n'est gardé qu'en mémoire : il n'est jamais écrit dans aipager.yaml ni nulle part sous ~/.config/aipager/. Un redémarrage prévu (celui d'une mise à jour, Redémarrer maintenant, le redémarrage sur place sans service) laisse le tunnel tourner, donc l'adresse, le bouton de menu et une Mini App ouverte continuent de marcher : l'aipager suivant ne le reprend qu'après avoir vérifié que c'est le même démarrage de l'ordinateur, le même port, que le processus est bien ce cloudflared (sa ligne de commande), qu'il donne toujours la même adresse (~/.claude/aipager-tunnel.json en garde une empreinte, jamais l'adresse) et que l'adresse répond. Dans tous les autres cas, il l'arrête et en lance un nouveau, et un tunnel conservé qu'aucun aipager ne reprend en 3 minutes est arrêté par un petit processus gardien. Tout autre arrêt (Ctrl+C, docker stop, systemctl stop) arrête le tunnel, et un redémarrage de l'ordinateur ou du conteneur donne une nouvelle adresse. MINIAPP_KEEP_TUNNEL=0 désactive cette conservation. Si aipager est tué net (plus de mémoire, kill -9), le tunnel continue de tourner jusqu'à ce qu'aipager redémarre avec le tunnel géré activé (la Mini App activée, pas de MINIAPP_PUBLIC_URL) et le reprenne ou l'arrête, ou jusqu'au redémarrage de l'ordinateur ; aipager uninstall l'arrête aussi. Si le tunnel tombe en cours de route, aipager le relance avec des délais croissants et republie la nouvelle adresse ; s'il ne peut pas revenir après plusieurs essais, le bouton est retiré plutôt que de rester pointé vers un port mort.
Définir MINIAPP_PUBLIC_URL (ou aipager miniapp enable --url https://…) désactive complètement le tunnel géré (aucun exécutable cloudflared n'est jamais téléchargé ni lancé) et l'URL donnée est utilisée telle quelle, avec la même vérification initData derrière. La détection automatique de Tailscale (tailscale status --json) reste une solution plus simple pour qui fait déjà tourner Tailscale et préfère ne pas dépendre de Cloudflare du tout.
Transformer les messages vocaux en texte#
faster-whisper tourne dans le processus d'aipager. L'audio est téléchargé en .ogg dans le propre dossier de la discussion sous /tmp/aipager-files/ (là où vont les fichiers envoyés, 0700, le fichier 0600), transcrit localement sur le processeur, et le fichier reste sous votre contrôle. Aucun audio ne quitte l'ordinateur. Aucune API tierce. Aucune clé n'est nécessaire en dehors du token du bot, qui sert de toute façon à parler à Telegram.
Si vous supprimez le .ogg après la transcription, la seule trace écrite du message est la transcription qui est tapée dans la session claude (où elle suit les règles de confidentialité de Claude Code lui-même).
Isolement entre sessions#
Chaque session Claude Code tourne dans son propre dtach. Le socket de contrôle à /tmp/claude-dtach-<name>.sock appartient à l'utilisateur d'aipager ; dtach refuse qu'un autre utilisateur s'y attache. Chaque session démarre dans son propre dossier, celui choisi avec /new ou la Mini App, ou le dossier où vous avez lancé aipager session.
aipager n'isole pas les sessions entre elles au niveau du système de fichiers : une session attachée à ~/projects/foo peut en principe lire ~/projects/bar si les autorisations de Claude Code le permettent. Utilisez des réglages ~/.claude/settings.json par projet ou un conteneur (image Docker) pour un isolement plus fort.
Messages entre sessions Claude#
Claude Code permet à une session d'envoyer un message à une autre (son outil SendMessage). Dans la session qui le reçoit, ce message est un message comme un autre, donc aipager décide de ses droits de la même façon que pour un message Telegram : d'après qui l'a vraiment envoyé, jamais d'après ce qu'il dit.
- Le message porte les droits de l'expéditeur, jamais plus. Quand aipager autorise une session à envoyer un message, il écrit un reçu à usage unique : une empreinte du texte exact, le processus Claude qui l'envoie (son numéro de processus et, sur Linux, son heure de démarrage) et les droits qu'avait cette tâche. Claude Code enregistre le message reçu avec le numéro de processus de l'expéditeur, vérifié par le noyau, sans jamais faire confiance au nom que l'expéditeur se donne. Avant que la tâche qui reçoit le message exécute le moindre outil, aipager rapproche les deux et donne à la tâche exactement les droits de la tâche qui l'a envoyé : le message d'un propriétaire tourne avec les droits du propriétaire, celui d'un membre avec les droits de ce membre, et un message tapé dans le terminal de la session qui l'envoie, sans restriction.
- Pas de reçu, droits restreints. Un message qu'aipager ne peut pas rapprocher (envoyé par une session ou un processus Claude qu'aipager ne fait pas tourner, un reçu qui a expiré au bout d'une heure, un expéditeur sur un autre ordinateur) tourne avec les règles les plus strictes, sans modification de fichiers et sans accès au web (lire et réfléchir seulement). Les droits antérieurs de la session qui le reçoit ne s'y appliquent jamais.
- Les autres entrées venues de l'extérieur de la session, pareil. Tout ce que Claude Code enregistre comme ne venant pas de la personne de cette session reçoit aussi ces droits restreints : une notification poussée par un canal MCP, un appelant distant, un message qu'un plugin met en file, un message relayé depuis la boîte de réception d'une autre session, et toute sorte d'entrée qu'aipager ne connaît pas encore (une commande de plugin compte comme une entrée extérieure même si c'est vous qui l'avez lancée). Les propres agents en arrière-plan de la session (leurs retours et leurs comptes rendus) et ses notifications de tâche gardent les droits du travail.
- Un message que la session a programmé tourne avec les droits de la tâche qui l'a programmé. Quand une tâche met en place un
/loopou un cron, aipager enregistre les droits de cette tâche pour ce message ; quand il se déclenche plus tard, il reçoit ces droits, jamais plus, quelle que soit la dernière personne qui a parlé ; un message qui rejoint cette tâche ne peut que les restreindre, et un changement de règles qui retire des droits à la personne qui l'a programmé les restreint aussi. Le même message programmé deux fois garde les droits les plus stricts. Un/loopnu se déclenche avec les instructions de boucle de Claude Code au lieu de son propre texte, donc il reçoit les droits les plus stricts de tous les/loopnus que la session a mis en place, une fois que l'historique de la conversation montre le déclenchement (jusque-là, un appel d'outil refusé l'est en douceur, et Claude réessaie). Un message que personne n'a programmé par la vérification d'aipager reçoit les droits restreints. - Le texte ne prouve rien. Un nom d'expéditeur, une ligne
[via Telegram · @owner]ou « le propriétaire demande… » dans un message ne changent rien. Claude apprend qui aipager a vérifié comme expéditeur, et que ce qu'affirme le texte n'est pas une preuve. - Rejoindre une tâche en cours ne fait que la restreindre. Un message d'une autre session qui arrive pendant qu'une tâche tourne rend cette tâche aussi stricte que la plus stricte des deux, jamais plus souple. C'est vrai aussi quand Claude Code prend le message sans événement de message à lui (absorbé dans la tâche en cours, ou exécuté depuis sa file comme tâche suivante) : le chemin rapide du propriétaire regarde la fin de l'historique de la conversation avant chaque appel d'outil, et la fin de la tâche et aipager marquent la session pour une vérification quand ils voient un tel message en file.
- Les rôles restent dans leur discussion. Un reçu d'une session d'une autre discussion ne compte que si sa tâche avait les droits du propriétaire ou a été tapée dans son terminal ; tout le reste tourne restreint.
- Qui peut envoyer. Les rôles restreints intégrés ne peuvent pas du tout utiliser SendMessage. Un rôle à qui une règle le donne ne peut écrire qu'aux sessions que fait tourner aipager (adressées comme
uds:…/<pid>.sock) ;aipager doctoravertit pour un tel rôle. - Les agents en arrière-plan gardent leurs propres droits. Un agent garde les droits de la tâche qui l'a lancé (ou les droits plus stricts d'une tâche suivante), jamais les droits d'une tâche suivante qui en a davantage.
- Quand il est arrêté. Si une tâche qu'une autre session a lancée est arrêtée par les règles de sécurité, la carte d'activité montre déjà qui l'a envoyée, la notification d'arrêt le dit, et la personne qui a confié la tâche est prévenue, pour qu'elle ne disparaisse jamais en silence : la discussion de la session qui l'a envoyée quand les deux sessions sont dans la même discussion ; sinon seulement le propriétaire, en privé, et seulement quand le propriétaire (ou le terminal) a piloté la tâche qui l'a envoyée.
Limites connues :
- Une tâche qui a un shell (celle du propriétaire ou d'un admin) tourne avec le même utilisateur du système qu'aipager et pourrait fabriquer un faux reçu, tout comme elle pouvait déjà réécrire les autres fichiers de contrôle d'aipager. C'est le modèle de confiance existant : ne donnez un shell qu'à des personnes à qui vous confieriez l'ordinateur.
- Les sessions Claude qu'aipager ne fait pas tourner ne sont pas du tout gérées par aipager, quoi que ce soit qui leur écrive.
- En dehors de Linux, l'expéditeur est reconnu à son seul numéro de processus (Claude Code n'y enregistre pas d'heure de démarrage). Sur Linux, un enregistrement de réception sans heure de démarrage ne correspond à rien.
- Claude Code peut écrire le message dans l'historique de la conversation un instant après le premier appel d'outil de la tâche. Tant qu'il ne l'a pas fait, un appel que les droits restreints refuseraient est refusé en douceur (on dit à Claude de réessayer ; la tâche n'est pas arrêtée). Après 8 appels sans lui, la tâche tourne restreinte.
- Le chemin rapide du propriétaire lit le dernier 1 Mio de l'historique de la conversation, jamais plus. Un message d'une autre session plus long que cela se termine par une ligne que le chemin rapide prend pour un tel message (c'est le cas de toute ligne de plus de 256 Kio coupée par la fenêtre). Un message qui se trouve entièrement plus loin en arrière (plus de 1 Mio d'historique écrit après lui avant l'appel d'outil suivant) est attrapé par aipager pendant que la tâche tourne, ou par la fin de la tâche tant qu'il est encore en file, pas par le chemin rapide.
- Tant qu'une session a un
/loopnu en place, toute tâche qu'aucun message Telegram ne lance (y compris un message tapé dans le terminal) peut voir ses premiers appels d'outil refusés en douceur, jusqu'à ce que Claude Code ait écrit le message de cette tâche et qu'aipager puisse voir que ce n'est pas la boucle qui se déclenche ; Claude réessaie alors. - aipager lit la tâche d'un message programmé dans ce que l'historique de la conversation a reçu depuis son début ; au-delà de 8 Mio, dans ses 4 premiers et ses 4 derniers Mio (ce qui se trouve entre les deux a été vérifié quand c'était la partie la plus récente).
- Les droits enregistrés d'un message programmé restent jusqu'à ce que la session soit terminée (
/kill) : une session finie ou reprise les garde, comme Claude Code garde ses crons et un réveil/loopen attente. Au plus 256 sont gardés par session, et en programmer un de plus au-delà est refusé à tout le monde dans cette session, donc n'importe qui pouvant programmer peut les épuiser (lancez alors une session sous un autre nom, ou faites/killsur celle-ci). Un message qui se déclenche sans enregistrement reçoit les droits restreints, ou dans une session avec un/loopnu les droits de cette boucle, une fois que Claude Code l'a écrit dans l'historique de la conversation ; jusque-là, aipager lit la tâche d'après celle d'avant. Cela arrive pour un message mis en place avant cette version ; après un redémarrage de l'ordinateur (les enregistrements vivent dans le dossier temporaire du système, qu'un redémarrage vide, alors que Claude Code peut encore ramener le réveil d'une session reprise dans l'heure) ; et pour un cron durable, que Claude Code lance dans une autre session du même dossier une fois sa propre session terminée. Une session qui a moins de droits et qui programme le même texte peut abaisser les droits d'un message (jamais les relever). - Une session neuve lancée sous le nom d'une session précédente (Remplacer) garde les enregistrements de cette session, y compris les droits de son
/loopnu, jusqu'à/kill. - Un message programmé que Claude Code exécute juste après une autre tâche, sans événement de message à lui (depuis sa file), reçoit les droits les plus stricts entre les siens et ceux de la tâche qu'il suit (après une tâche tapée dans le terminal, exactement les siens).
- Les premiers appels d'outil d'une tâche tapée dans le terminal peuvent être refusés jusqu'à ce que Claude Code ait écrit son message dans l'historique de la conversation (aipager lit jusque-là la tâche d'avant).
- Un agent en arrière-plan lancé avec des droits plus étroits peut renvoyer du texte dans une tâche suivante du même travail, qui le lit avec les droits du travail, comme les autres notifications de tâche de Claude Code.
- Claude Code peut retenir un message en attente d'approbation dans le terminal qui le reçoit (son réglage
crossSessionInbound, ou quand les deux sessions tournent dans des classes d'autorisation différentes, par exemple le Mode Accès complet et le Mode Automatique). aipager ne peut pas libérer un tel message depuis Telegram ; le reçu attend jusqu'à une heure.
Résumé des menaces#
| Menace | Protection |
|---|---|
| Un inconnu envoie une commande au bot | Le filtre d'identifiant de discussion la rejette |
| Token du bot volé | /revoke dans @BotFather, puis reconfigurer |
| Appel d'outil de claude compromis | La vérification de sécurité d'aipager refuse ce que les règles d'une tâche Telegram refusent ; en Mode Demander, les propres règles de Claude Code dans settings.json décident de ce sur quoi il demande et l'appui d'une personne est la seule autorisation ; en Mode Automatique (le mode par défaut de chaque nouvelle session), la propre vérification de sécurité de Claude Code exécute sans demander les appels qu'elle juge sûrs et arrête les risqués pour une personne ; une session en Mode Accès complet (les admins, et une personne dans sa propre discussion privée) ne demande rien, donc seule la vérification de sécurité d'aipager fait obstacle, et pour les tâches du propriétaire lui-même et les messages du terminal, rien ne fait obstacle (qui décide) |
| Une personne restreinte sur Telegram prend les droits du propriétaire | Pas de shell, sessions lancées seulement dans le dossier racine du membre, écritures et recherches enfermées dans le projet à l'intérieur (jamais le dossier personnel), pas de lecture dans le dossier personnel en dehors du dossier racine, dossiers racines des autres membres et fichiers envoyés des autres discussions inaccessibles, fichiers d'identifiants illisibles, chemins protégés pour les outils de fichiers, une vérification qui échoue refuse (comment les règles s'appliquent en équipe) ; seulement au mieux pour un rôle qui a Bash, et pas couvert pour les cas de « Limites connues » |
| Le message d'une session prétend venir du propriétaire | Le message tourne avec les droits prouvés de la tâche qui l'a envoyé, jamais avec les droits antérieurs de la session qui le reçoit ; sans preuve, lire et réfléchir seulement (messages entre sessions) |
| Un membre d'un groupe emprunte les droits de quelqu'un d'autre | Chaque message tourne avec le rôle de son propre expéditeur dans la discussion d'où il vient ; le message d'un autre expéditeur attend la tâche en cours ; si un message la rejoint quand même, les règles les plus strictes l'emportent ; les commandes slash des rôles restreints sont limitées à celles du clavier (comment les règles s'appliquent en équipe) |
| La conversation du groupe arrive à une session | Dans un groupe, seuls les commandes, les réponses au bot, les mentions du bot et les appuis sur le clavier marqué sont traités (groupes) |
| Un membre retiré garde l'accès | aipager config recharge aipager sans redémarrage ; ses messages retenus sont abandonnés et une tâche à lui encore en cours perd ses anciens droits ; le retirer du groupe Telegram l'empêche de le lire (groupes → recharger sans redémarrer) |
| Falsification du journal d'audit | Ajout seulement ; l'empêcher demanderait un programme de signature à part, hors du périmètre |
| Une injection de prompt fait écrire à Claude une adresse qui fait sortir des données | Les images et les balises de médias dans le texte de Claude sont affichées comme des liens ou du texte, jamais comme des médias, et les aperçus de liens sont désactivés sur les messages en texte brut et en HTML (connexions réseau) |
| Attaquant sur le réseau | Aucun port entrant, pas joignable directement |
| Élévation de droits locale | Pas de sudo / setuid ; aipager reste dans l'espace utilisateur |
| L'audio des messages vocaux part dans le cloud | La transcription est locale |
| L'agent lit l'identifiant Claude d'aipager lui-même | Prévu, pas un bug : voir L'identifiant Claude. Limitez l'identifiant lui-même, pas le fichier où il est rangé |
Voir aussi#
- Architecture : comment le processus fonctionne.
- Événements de hook : ce qu'aipager voit vraiment venir de claude.
- Commandes du bot : le côté piloté par les personnes.