
LLM 에이전트를 위한 보안 컨트롤 플레인: 허용 목록(allowlist), 소유자 킬 스위치, PIN 세션, 속도 제한, 프롬프트 인젝션 탐지, 그리고 비밀 유출과 이미지 비콘 외부 유출을 차단하는 출력 스크러빙.

비공개 채팅(일반적으로 Discord DM)을 통한 LLM 에이전트용 보안 컨트롤 플레인입니다.
이 라이브러리는 에이전트 앞에 위치합니다. 누가 대화할 수 있는지, 세션이 잠금 해제되었는지, 프로세스가 일시 중지되었는지, 이 메시지를 전달하기에 충분히 안전한지 결정합니다. 모델과 도구는 그 게이트 뒤에 남습니다. 이 라이브러리는 LLM을 호출하지 않습니다. 보안 외의 제품 기능은 구현하지 않습니다.
Hermes 영감. 설계는 Hermes Agent 메시징 게이트웨이에 사용된 동일한 컨트롤 플레인 아이디어를 따릅니다: DM 우선 전달, 신원 허용 목록, 페어링 방식 개방, 소유자 킬 스위치, 그리고 행동할 수 있는 대상(컨트롤 플레인)과 모델이 보는 메시지 텍스트(데이터 플레인)의 엄격한 분리. 이 패키지는 모든 호출 가능한 에이전트를 위한 해당 패턴의 작은 독립형 추출물입니다. Nous Research와 관련이 없습니다.
성숙도: 구현됨 · 독립 검증 완료 · 유지 관리 중. STATUS.md 참조.
재현: python scripts/repro.py (REPRO_OK 필요).
오프라인 테스트:
pip install -e ".[dev]" # or: pip install -e . && pip install pytest
python -m pytest -q --tb=line
# or: python scripts/repro.py
라이브: https://github.com/SamsonCyber/agentic-dm-gateway
도구를 가진 에이전트를 Discord(또는 모든 채팅 API)에 올리면, 봇에게 메시지를 보낼 수 있는 사람은 누구든 다음을 시도할 수 있습니다:
데이터 플레인(모델이 보는 메시지 텍스트)과 분리된 컨트롤 플레인(신원 및 프로세스 제어)이 필요합니다.
이 패키지가 바로 그 컨트롤 플레인입니다.
범위: 보안 게이트 전용입니다. 챗봇, 트레이딩 봇, 스캐너 또는 에이전트 프레임워크가 아닙니다. Discord를 사용한다면 agent(user_id, text) -> str(또는 비동기) 함수를 전달하세요. 코어는 모든 정수 사용자 ID와 일반 텍스트에서 작동합니다.
$ 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
세 가지 연동 경로가 있습니다. 하나를 선택하세요.
Discord 지원으로 설치하고, 환경 변수에 사용자 ID를 지정하고, 게이트웨이를 등록하고, 봇을 실행하세요.
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
자체 봇에서:
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)
register_dm_gateway가 수행하는 작업:
discord.Client / 봇에 on_message 핸들러를 설치합니다.InboundSecurityPipeline.precheck를 실행합니다.agent(user_id, sanitized_text, is_owner=...)를 호출합니다.서버 메시지는 에이전트에 도달하지 않습니다. 허용 목록에 있는 사용자의 DM만 도달합니다.
on_message를 이미 보유한 경우)register_dm_gateway를 사용할 수 없다면(기존 핸들러 체인), 파이프라인을 직접 호출하세요:
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])
Discord import가 필요 없습니다. 모든 에이전트 턴에 동일한 사전 검사를 사용하세요:
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
PrecheckResult 필드:
run_agent: true일 때만 모델로 전달sanitized_text: 정리된 입력reply_text: 거부 / 제어 명령 응답stage: allowlist | kill | pin | rate | injection | ok | ...연동 체크리스트:
InboundSecurityPipeline을 한 번 생성합니다(설정 + 환경 변수).pre = pipe.precheck(user_id, text).pre.run_agent인 경우: pre.sanitized_text만으로 에이전트를 호출합니다.sanitize_agent_output에 통과시킵니다.run_agent가 false일 때 제어 응답(/auth, /kill, …)은 처리된 것으로 간주합니다.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
컨트롤 플레인: 사용자가 누구인지(허용 목록 / 소유자). 데이터 플레인: 메시지 본문(검사를 통과하기 전까지 항상 신뢰할 수 없음).
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
| 모듈 | 역할 |
|---|---|
SecurityGateway | 단일 check_message(user_id, text) -> SecurityVerdict |
InboundSecurityPipeline | 허용 목록 + 슬래시 명령 + 게이트웨이를 한 번의 호출로 처리 |
DiscordDMGateway | DM 전용 어댑터; 에이전트 함수를 주입합니다 |
필수 런타임 종속성이 없습니다. Discord는 선택 사항입니다: 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
기본 상태 디렉터리: ./data/agentic_dm/.
이 명령들은 모델을 호출하지 않습니다.
MIT. LICENSE 참조.
| 제어 | 동작 |
|---|
| 허용 목록 | 구성된 사용자 ID만 진행할 수 있습니다. 그 외 모든 사람은 차단됩니다(조용히 또는 짧은 거부 메시지와 함께). |
| 소유자 vs 친구 | 소유자는 PIN을 건너뛰고 전체 에이전트를 일시 중지할 수 있습니다. 친구는 시간 제한 개방을 위해 공유 PIN이 필요할 수 있습니다(Hermes 스타일 페어링 아이디어의 단순화 버전). |
| 킬 스위치 | 전역 일시 중지 파일 또는 환경 변수 플래그. 활성화되면 에이전트가 실행되지 않습니다. |
| 속도 제한 | 사용자별 슬라이딩 윈도우(분당 및 시간당). |
| 입력 검사 | 최대 길이, 이상한 제어 문자 제거, 일반적인 주입/비밀 유출 문구에 대한 정규식 휴리스틱. |
| 출력 정리 | 비밀 형식의 토큰(API 키, JWT, Bearer 헤더)을 삭제하고 자동 가져오기를 통해 외부 유출될 수 있는 마크다운/HTML 이미지 비콘을 제거합니다. |
| 감사 로그 | 추후 검토를 위한 허용/거부/인증/킬 이벤트의 추가 전용(append-only) JSONL. |
| 로컬 명령 | /auth, /lock, /kill, /unkill, /status를 모델 호출 없이 처리합니다. |
| 키 | 기본값 | 의미 |
|---|
allowed_user_ids | [] | 채팅이 허용된 사용자 ID |
owner_ids | [] | PIN 생략 가능; /kill 가능 |
pin_enabled | True | 비소유자용 PIN 게이트 |
pin_ttl_hours | 72 | 개방 시간 |
rate_limit_per_minute | 8 | 슬라이딩 윈도우 |
rate_limit_per_hour | 60 | 슬라이딩 윈도우 |
max_input_chars | 2000 | 최대 입력 길이 |
block_injection | True | 휴리스틱 차단 목록 |
deny_message | False | 무음, True 또는 사용자 지정 문자열 |
audit_log | True | 감사 JSONL 작성 |
enabled | True | 전체 스위치 |
| 변수 | 용도 |
|---|
AGENTIC_DM_ALLOWLIST | 쉼표로 구분된 사용자 ID |
AGENTIC_DM_OWNER_ID | 소유자 ID |
AGENTIC_DM_PIN | PIN 평문 |
AGENTIC_DM_PIN_REQUIRED | 1 = 설정되지 않아도 PIN 요구 |
AGENTIC_DM_KILLED | 1 = 킬 스위치 켜짐 |
AGENTIC_DM_DATA_DIR | 킬 파일, 개방, 감사 로그용 디렉터리 |
AGENTIC_DM_SECRETS_DIR | dm_pin.txt / dm_allowlist.txt용 디렉터리 |
| 명령 | 대상 | 효과 |
|---|
/kill /pause | 소유자 | 모든 사용자에 대해 에이전트 일시 중지 |
/unkill /resume | 소유자 | 일시 중지 해제 |
/status | 소유자 | 킬 / PIN / 허용 목록 스냅샷 |
/auth <pin> | 허용 목록 사용자 | TTL 동안 세션 개방 |
/lock | 허용 목록 사용자 | 개방 상태 해제 |