# gori-agent-gateway Hermes-gateway-style multi-IM Agent Gateway for routing instant-message webhooks to configurable CLI agents. It is intentionally standalone and not tied to Pi. ## Hermes reference This project follows the Hermes gateway pattern: platform adapters normalize inbound messages, a central gateway applies policy/session/concurrency handling, then replies are sent through the originating adapter. The Feishu adapter ports the key Hermes behavior for tenant token caching, URL challenge handling, token verification, `im.message.receive_v1` parsing, mention cleanup, and message replies. ## Platform status | Platform | Inbound | Outbound | Notes | | --- | --- | --- | --- | | Feishu/Lark | Implemented | Implemented | Replies to `im.message.receive_v1` via `/im/v1/messages/{message_id}/reply`. | | WeCom | 501 scaffold | Implemented | Uses `gettoken` and `message/send`; inbound callback verification/encryption is not in v1. | | personal WeChat | External webhook scaffold | Synchronous webhook response | Native iLink/personal WeChat integration is not included in v1. | | QQ | 501 scaffold | Implemented | Uses QQ Bot access token and user/group text-message heuristics. | | Generic webhook | Implemented | Synchronous JSON | HMAC-SHA256 signed JSON endpoint for local bridges and tests. | ## Install and run Requires Node.js 20+. ```bash npm install cp config.example.json config.json GORI_GATEWAY_CONFIG=./config.json npm run dev ``` Build and run compiled output: ```bash npm run typecheck npm run build GORI_GATEWAY_CONFIG=./config.json npm start ``` Endpoints: - `GET /health` - `GET /platforms` - `POST /webhook/feishu` - `POST /webhook/wecom` - `POST /webhook/qq` - `POST /webhook/generic` - `POST /webhook/weixin` ## Configuration Config is loaded from `GORI_GATEWAY_CONFIG`. If the variable is not set, the gateway loads `config.example.json` from the current working directory. Important sections: - `policy.allowedUsers`: allow only listed normalized user IDs when non-empty. - `policy.allowedChats`: allow only listed normalized chat IDs when non-empty. - `policy.requireMentionInGroup`: if true, group messages are ignored unless the adapter reports a bot mention. - `defaultAgent`: selected when a chat has not chosen an agent. - `agents[]`: named CLI agents using `child_process.spawn(command, args)` with `shell: false`. CLI agent options: - `inputMode: "stdin"`: sends the message text to stdin. - `inputMode: "arg"`: appends the message text as the final argv item. - `timeoutMs`: kills slow processes. - `outputMaxBytes`: caps captured stdout/stderr. - `cwd`: optional working directory for the agent process. ## Commands The gateway handles these commands per chat before invoking an agent: - `/help` - `/status` - `/agents` - `/agent ` - `/new` ## Feishu setup 1. Create a Feishu/Lark custom app and enable bot messaging. 2. Configure event subscription for `im.message.receive_v1`. 3. Set the request URL to `https:///webhook/feishu`. 4. Put `appId`, `appSecret`, and `verificationToken` into your config. 5. Add bot display names to `platforms.feishu.botNames` so group mention detection can also work from flattened text. The adapter accepts Feishu URL verification challenges and returns `{ "challenge": "..." }`. ## Generic webhook Payload: ```json { "chat_id": "demo-chat", "user_id": "demo-user", "text": "hello", "message_id": "optional-message-id", "is_group": false, "mentions_bot": true } ``` Signature header: - Header name: `X-Gori-Signature` - Format: `sha256=` or just `` - Input: exact JSON request body bytes as sent by the client - Algorithm: HMAC-SHA256 using `platforms.webhook.secret` Example: ```bash body='{"chat_id":"demo-chat","user_id":"demo-user","text":"/status"}' sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac 'replace-me' -hex | awk '{print $2}') curl -sS http://localhost:3000/webhook/generic \ -H 'Content-Type: application/json' \ -H "X-Gori-Signature: sha256=$sig" \ -d "$body" ``` Response shape: ```json { "ok": true, "output": "...agent or command output..." } ``` ## Security notes - Do not expose the gateway publicly without HTTPS and upstream authentication/rate limits. - Keep platform secrets outside source control; use a copied config file or secret manager. - CLI agents are spawned without a shell, but they can still execute arbitrary local code. Only configure trusted commands. - Use `allowedUsers` and `allowedChats` in production. - Keep `requireMentionInGroup` enabled for group chats to avoid accidental agent invocation. - Set conservative `timeoutMs` and `outputMaxBytes` values for external-facing agents.