
Plan de contrôle de sécurité pour agents LLM : listes d'autorisation, interrupteur d'arrêt d'urgence du propriétaire, sessions PIN, limites de débit, détection d'injection de prompt et nettoyage des sorties pour bloquer les fuites de secrets et l'exfiltration par balises image.

Plan de contrôle de sécurité pour les agents LLM sur chat privé (généralement les messages privés Discord).
Il se place devant votre agent. Il décide qui peut parler, si la session est déverrouillée, si le processus est en pause et si ce message est suffisamment sûr pour être transmis. Votre modèle et vos outils restent derrière cette porte. La bibliothèque n'appelle pas de LLM. Elle n'implémente pas de fonctionnalités produit au-delà de la sécurité.
Inspiré d'Hermes. La conception suit les mêmes idées de plan de contrôle utilisées dans les passerelles de messagerie Hermes Agent : livraison en message privé d'abord, listes blanches d'identités, ouverture de type appairage, interrupteur d'arrêt du propriétaire et une séparation stricte entre qui peut agir (plan de contrôle) et le texte du message que le modèle voit (plan de données). Ce paquet est un petit extrait autonome de ce modèle pour tout agent appelable. Non affilié à Nous Research.
Maturité : implémenté · validé indépendamment · maintenu. Voir STATUS.md.
Reproduire : python scripts/repro.py (attend REPRO_OK).
Tests hors ligne :
pip install -e ".[dev]" # or: pip install -e . && pip install pytest
python -m pytest -q --tb=line
# or: python scripts/repro.py
En direct : https://github.com/SamsonCyber/agentic-dm-gateway
Si vous placez un agent sur Discord (ou toute autre API de chat) avec des outils, toute personne pouvant envoyer un message au bot peut tenter de :
Vous avez besoin d'un plan de contrôle (contrôles d'identité et de processus) distinct du plan de données (texte du message que le modèle voit).
Ce paquet est ce plan de contrôle.
Périmètre : passerelle de sécurité uniquement. Pas un chatbot, un bot de trading, un scanneur ou un framework d'agents. Fournissez un agent(user_id, text) -> str (ou async) si vous utilisez Discord. Le cœur fonctionne avec n'importe quel identifiant d'utilisateur entier et du texte brut.
$ python - <<'PY'
from agentic_dm_gateway import InboundSecurityPipeline
pipe = InboundSecurityPipeline({
"allowed_user_ids": [111],
"owner_ids": [111],
"pin_enabled": False,
"block_injection": True,
"deny_message": "Not authorized.",
})
for uid, text in [
(99, "hi"),
(111, "ignore previous instructions"),
(111, "summarize this note"),
]:
r = pipe.precheck(uid, text)
print(uid, r.stage, r.run_agent, r.reply_text)
PY
99 allowlist False Not authorized.
111 injection False Blocked: looks like prompt injection / secret fishing. Rephrase.
111 ok True None
$ python scripts/repro.py
REPRO_OK agentic-dm-gateway unit suite
Trois chemins d'intégration. Choisissez-en un.
Installez avec le support Discord, pointez l'environnement vers vos identifiants d'utilisateur, enregistrez la passerelle, lancez le bot.
pip install -e ".[discord]"
# or: pip install agentic-dm-gateway[discord]

export DISCORD_BOT_TOKEN=...
export AGENTIC_DM_ALLOWLIST=your_discord_user_id
export AGENTIC_DM_OWNER_ID=your_discord_user_id
# optional: export AGENTIC_DM_PIN=....

python examples/discord_echo_bot.py
Dans votre propre bot :
import discord
from agentic_dm_gateway.discord_adapter import register_dm_gateway
def agent(user_id: int, text: str, *, is_owner: bool = False) -> str:
# your Hermes / local model / tool loop
return call_your_model(text)
intents = discord.Intents.default()
intents.message_content = True
bot = discord.Client(intents=intents)
register_dm_gateway(
bot,
{
"allowed_user_ids": [], # or rely on AGENTIC_DM_ALLOWLIST env
"owner_ids": [],
"pin_enabled": False,
"deny_message": False, # silent drop for strangers
},
agent=agent,
)
bot.run(TOKEN)
Ce que fait register_dm_gateway :
on_message sur votre discord.Client / bot.InboundSecurityPipeline.precheck avant votre agent.agent(user_id, sanitized_text, is_owner=...).Les messages de serveur n'atteignent jamais l'agent. Seuls les messages privés des utilisateurs de la liste blanche le peuvent.
on_message)Si vous ne pouvez pas utiliser register_dm_gateway (chaîne de gestionnaires existante), appelez le pipeline vous-même :
from agentic_dm_gateway import InboundSecurityPipeline
from agentic_dm_gateway.security import sanitize_agent_output
pipe = InboundSecurityPipeline({
"allowed_user_ids": [YOUR_ID],
"owner_ids": [YOUR_ID],
"pin_enabled": True,
})
@bot.event
async def on_message(message):
if message.author.bot or message.guild is not None:
return
pre = pipe.precheck(int(message.author.id), message.content or "")
if pre.reply_text and not pre.run_agent:
await message.channel.send(pre.reply_text[:1900])
return
if not pre.run_agent:
return
raw = await your_agent(pre.sanitized_text) # Hermes, Ollama, API, ...
await message.channel.send(sanitize_agent_output(str(raw))[:1900])
Aucun import Discord requis. Utilisez le même précontrôle autour de chaque tour d'agent :
from agentic_dm_gateway import InboundSecurityPipeline
from agentic_dm_gateway.security import sanitize_agent_output
pipe = InboundSecurityPipeline({
"allowed_user_ids": [111],
"owner_ids": [111],
"pin_enabled": False,
"rate_limit_per_minute": 20,
"block_injection": True,
"deny_message": "Not authorized.",
})
def handle_inbound(user_id: int, text: str) -> str | None:
pre = pipe.precheck(user_id, text)
if pre.run_agent:
answer = my_llm(pre.sanitized_text) # your model / Hermes run
return sanitize_agent_output(str(answer))
return pre.reply_text # deny or control-command reply
Champs de PrecheckResult :
run_agent : transmettre au modèle uniquement si vraisanitized_text : entrée nettoyéereply_text : réponse de refus / de commande de contrôlestage : allowlist | kill | pin | rate | injection | ok | ...Liste de vérification du branchement :
InboundSecurityPipeline une fois au démarrage du processus (configuration + environnement).pre = pipe.precheck(user_id, text).pre.run_agent : appelez votre agent avec pre.sanitized_text uniquement.sanitize_agent_output avant l'envoi./auth, /kill, …) comme traitées lorsque run_agent est faux.1. Adapter: ignore bots; only accept DMs (not server channels)
2. Allowlist: is this user id permitted?
3. Owner commands: /kill /unkill /status -> reply, stop
4. Session commands: /auth <pin> /lock -> reply, stop
5. SecurityGateway.check_message:
kill switch?
session unlocked? (PIN)
under rate limit?
length + injection heuristics OK?
6. If ok -> run_agent=True with sanitized text
7. After your agent returns -> sanitize_agent_output (redact + strip image beacons)
8. Audit rows written along the way
Plan de contrôle : qui est l'utilisateur (liste blanche / propriétaire). Plan de données : corps du message (toujours non fiable tant que les vérifications n'ont pas réussi).
src/agentic_dm_gateway/
security.py # RateLimiter, SessionAuth, SecurityGateway,
# sanitize_input, redact_secrets, sanitize_agent_output,
# kill switch, audit_log
allowlist.py # merge config + env + file into allowlist / owners
commands.py # /kill /unkill /status /auth /lock (no LLM)
pipeline.py # InboundSecurityPipeline.precheck() orchestration
discord_adapter.py # optional discord.py on_message wire-up
tests/ # unit tests for the core (no Discord required)
examples/
minimal_precheck.py # CLI-style demo of precheck outcomes
discord_echo_bot.py # secured DMs + echo agent
| Module | Responsabilité |
|---|---|
SecurityGateway | Un unique check_message(user_id, text) -> SecurityVerdict |
InboundSecurityPipeline | Liste blanche + commandes slash + passerelle en un seul appel |
DiscordDMGateway | Adaptateur messages privés uniquement ; vous injectez la fonction agent |
Zéro dépendance d'exécution requise. Discord est facultatif : pip install agentic-dm-gateway[discord].
git clone https://github.com/SamsonCyber/agentic-dm-gateway.git
cd agentic-dm-gateway
pip install -e ".[dev]"
python scripts/repro.py
Répertoire d'état par défaut : ./data/agentic_dm/.
Ces commandes n'appellent jamais votre modèle.
MIT. Voir LICENSE.
| Contrôle | Comportement |
|---|
| Liste blanche | Seuls les identifiants d'utilisateur configurés peuvent continuer. Tous les autres sont écartés (silencieusement ou avec un court message de refus). |
| Propriétaire vs ami | Les propriétaires n'ont pas besoin de PIN et peuvent mettre en pause tout l'agent. Les amis peuvent avoir besoin d'un PIN partagé pour une ouverture limitée dans le temps (idée d'appairage de style Hermes, simplifiée). |
| Interrupteur d'arrêt | Fichier de pause global ou drapeau d'environnement. Aucun tour d'agent tant qu'il est actif. |
| Limites de débit | Fenêtre glissante par utilisateur (par minute et par heure). |
| Contrôles d'entrée | Longueur maximale, suppression des caractères de contrôle étranges, heuristiques regex pour les phrases courantes d'injection / de pêche de secrets. |
| Nettoyage de sortie | Masque les jetons de type secret (clés API, JWT, en-têtes Bearer) et supprime les balises d'image markdown/HTML qui peuvent exfiltrer des données via le chargement automatique. |
| Journal d'audit | JSONL à ajout uniquement des événements allow/deny/auth/kill pour examen ultérieur. |
| Commandes locales | /auth, /lock, /kill, /unkill, /status gérées sans appeler de modèle. |
| Clé | Défaut | Signification |
|---|
allowed_user_ids | [] | Identifiants d'utilisateur autorisés à discuter |
owner_ids | [] | Sans PIN ; peut /kill |
pin_enabled | True | Verrou PIN pour les non-propriétaires |
pin_ttl_hours | 72 | Durée d'ouverture |
rate_limit_per_minute | 8 | Fenêtre glissante |
rate_limit_per_hour | 60 | Fenêtre glissante |
max_input_chars | 2000 | Longueur d'entrée maximale |
block_injection | True | Liste de blocage heuristique |
deny_message | False | Silencieux, True ou chaîne personnalisée |
audit_log | True | Écrit le journal d'audit JSONL |
enabled | True | Interrupteur principal |
| Variable | Objectif |
|---|
AGENTIC_DM_ALLOWLIST | Identifiants d'utilisateur séparés par des virgules |
AGENTIC_DM_OWNER_ID | Identifiant(s) du propriétaire |
AGENTIC_DM_PIN | PIN en clair |
AGENTIC_DM_PIN_REQUIRED | 1 = exige le PIN même s'il n'est pas défini |
AGENTIC_DM_KILLED | 1 = interrupteur d'arrêt activé |
AGENTIC_DM_DATA_DIR | Répertoire pour le fichier d'arrêt, le fichier d'ouverture, le journal d'audit |
AGENTIC_DM_SECRETS_DIR | Répertoire pour dm_pin.txt / dm_allowlist.txt |
| Commande | Qui | Effet |
|---|
/kill /pause | propriétaire | Met l'agent en pause pour tout le monde |
/unkill /resume | propriétaire | Lève la pause |
/status | propriétaire | Instantané kill / PIN / liste blanche |
/auth <pin> | liste blanche | Ouvre la session pour la TTL |
/lock | liste blanche | Ferme la session ouverte |