4.6 KiB
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. |
| 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+.
npm install
cp config.example.json config.json
GORI_GATEWAY_CONFIG=./config.json npm run dev
Build and run compiled output:
npm run typecheck
npm run build
GORI_GATEWAY_CONFIG=./config.json npm start
Endpoints:
GET /healthGET /platformsPOST /webhook/feishuPOST /webhook/wecomPOST /webhook/qqPOST /webhook/genericPOST /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 usingchild_process.spawn(command, args)withshell: 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 <name>/new
Feishu setup
- Create a Feishu/Lark custom app and enable bot messaging.
- Configure event subscription for
im.message.receive_v1. - Set the request URL to
https://<host>/webhook/feishu. - Put
appId,appSecret, andverificationTokeninto your config. - Add bot display names to
platforms.feishu.botNamesso group mention detection can also work from flattened text.
The adapter accepts Feishu URL verification challenges and returns { "challenge": "..." }.
Generic webhook
Payload:
{
"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=<hex>or just<hex> - Input: exact JSON request body bytes as sent by the client
- Algorithm: HMAC-SHA256 using
platforms.webhook.secret
Example:
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:
{ "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
allowedUsersandallowedChatsin production. - Keep
requireMentionInGroupenabled for group chats to avoid accidental agent invocation. - Set conservative
timeoutMsandoutputMaxBytesvalues for external-facing agents.