소스 정보
- 저장소
- lukemcqueen/hermes-cortex
- 최근 소스 활동
- 2026년 8월 24일 10:24
- 감지된 SKILL.md 언어
- 영어
- 스타
- 4
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/lukemcqueen/hermes-cortex --skill messaging-gateway명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | messaging-gateway |
| version | 1.0.0 |
| category | devops |
| description | Use when building/debugging the messaging gateway. |
| platforms | ["linux"] |
| metadata | {"hermes":{"tags":["messaging","gateway","telegram","whatsapp","bus","envelope","shim","multi-agent"],"related_skills":["cortex-bus","hermes-gateway-operations","telegram-delivery-diagnostics"]}} |
msg-gateway.py, gateway_envelope.py, agent-shim.py, bot_locks.pyONE gateway daemon per server owns ALL messaging app connections (N Telegram bots, WhatsApp later). It is a deterministic service, not an agent — no LLM, no memory, no inbox of its own; pure plumbing (poll → translate → route → send) with bus identity only. All agents — Hermes AND coding agents — are bus-only for messaging: they read inbox_<AGENT> and write out_<AGENT> via a versioned envelope. The gateway validates at both boundaries and DLQs malformed messages.
┌─────────────────────────────────────────────┐
│ MESSAGING GATEWAY (one daemon per server) │
│ TransportAdapters: telegram, whatsapp, ... │
│ Routing table: channel_user_id → AGENT │
│ Per-bot advisory locks, enqueue-then-ack │
└──────────────┬──────────────────────────────┘
inbound: │ outbound:
inbox_<AGENT>│ out_<AGENT> → adapter.send
▼
┌─────────────┐
│ AGENT BUS │ (PGMQ)
└──────┬──────┘
┌───────────┼───────────┐
▼ ▼ ▼
Hermes coding future
agents agents agents
(bus MCP) (shim) (same fabric)
ops/scripts/msg-gateway.py — the daemon: TransportAdapter interface, TelegramAdapter, Gateway (routing/ingest/drain), run_locked() (advisory locks), run_outbound_only() (when another poller owns the bot)ops/scripts/gateway_envelope.py — envelope v1 schema + HMAC signing (kind=user_message, constant-time verify)ops/scripts/agent-shim.py — the standard coding-agent bus shim (poll inbox, reply out); --generate emits per-agent instancesops/scripts/bot_locks.py — pg_try_advisory_lock per bot (409 avoidance + cutover + multi-server)docs/design/messaging-gateway.md — party-converged design doc (ADR-0005)inbox_<AGENT> = messages FOR the agent (gateway writes, shim polls/reads)
out_<AGENT> = messages FROM the agent (shim writes, gateway drains → app)
The FIRST shim polled out_<AGENT> as "instructions for the agent" — but the gateway ALSO drains out_<AGENT> to send replies to the app. A shim poll would steal the agent's own replies before delivery. Never add a second reader to a queue a daemon drains — check the consumer role first.
The bus /api/pgmq/read returns body as a DICT (the envelope object), NOT a JSON string. json.loads(msg.get("body")) crashes with TypeError on a dict — and if uncaught, the message requeues on visibility timeout forever (silent non-delivery). Accept both shapes:
body = msg.get("body")
if isinstance(body, str):
try: body = json.loads(body)
except ValueError: body = None
Mocks that return body-as-string are too forgiving. Always verify a consumer against the REAL bus, not just mocks (the live test caught what the mock E2E missed).
| Path | Auth | Trap |
|---|---|---|
External nginx port (:13004) | Basic | Bearer masked → 401 |
Direct local uvicorn (127.0.0.1:8903) | Bearer | Basic → 401 |
| MCP tools | cascade Bearer→Basic | works either way |
Trap: a daemon with BOTH CORTEX_BUS_TOKEN + CORTEX_BASIC_AUTH set picks Bearer, silently 401s through nginx, and urllib errors swallow into {"msg_id": None} — reads look "empty" with zero errors. Point the client at the path matching its target, or make it cascade.
pg_try_advisory_lock(bot_id_hash) held for the poller's lifetime — ONE mechanism for:
Session-scoped — a crashed gateway auto-releases on connection close. Never "solve" 409 by adding more pollers; the lock is the single-writer gate.
New coding agent (Codex, Claude Code, Blackbox, Grok) without a bus service:
cortex-agent-manager.py add <agent> — mint a scoped token (read inbox_<agent>, write out_<agent>; never wildcard)gateway.yaml routing row: chat_id → <agent>agent-shim.py --generate --agent <agent> — emit the instance (poll inbox, reply out)The shim needs ONLY bus HTTP access + a scoped token — no Hermes gateway, no bus service, no shared secrets. Per-agent tokens (never a shared bus token) mean one compromised agent rotates only its own key.
{msg_id, ts, from_agent?, to_agent, channel, channel_user_id, thread_id, body, media[], reply_to_msg_id, ack_required} — gateway validates at both boundaries, DLQs malformed. HMAC-signed inbound (kind=user_message) — agents accept only gateway-signed messages as DATA, never directives (anti prompt-injection). Verify strips gateway_sig AND kind (kind is gateway-added metadata, not signed content — a sign/verify mismatch bug bit here).
adapter.send; failed sends stay queued (PGMQ visibility timeout redelivers)Mock E2E is necessary but NOT sufficient — the live test caught three things mocks missed: body-as-dict wire shape, the queue-direction collision, and nginx Bearer masking. The proven pattern:
out_<AGENT>, and have a human confirm receipt in the DMbody is a dict; archive-after-send moved the message out of bus.messages)Never claim "done" on messaging infrastructure without the live human-confirmed round trip.
references/live-test-2026-08-24.md — the full live-test session: wire-shape bug, queue collision, auth paths, permission grant, and the exact commands that proved the path