Cookbook
All IM platforms use one paired owner and explicit chat bindings, including single-workspace deployments. Run chord-gateway --pair, then send /bind "<directory>" <pairing_code> in a WeChat/Feishu/Telegram/WeCom/DingTalk/Discord private bot chat. See pairing and local reset and the upgrade notes. Slack uses /chord bind "<directory>" <pairing_code> in the App DM; see the Slack guide.
This page collects copyable recipes for installing, configuring, and running chord-gateway in common real deployments.
- Chinese version: cookbook_CN.md
Choose an installation method
Section titled “Choose an installation method”Option A: install from GitHub Releases
Section titled “Option A: install from GitHub Releases”Use this when you want a prebuilt binary and do not need to build from source.
-
Pick a release tag and target from the GitHub Releases page.
Current release assets are named like:
chord-gateway-${VERSION}-darwin-amd64.tar.gzchord-gateway-${VERSION}-darwin-arm64.tar.gzchord-gateway-${VERSION}-linux-amd64.tar.gzchord-gateway-${VERSION}-linux-arm64.tar.gzchord-gateway-${VERSION}-windows-amd64.zipSHA256SUMS -
Download and install one asset. Example for Linux amd64:
Terminal window VERSION=v0.3.1 # replace with the release you wantOS=linuxARCH=amd64ASSET="chord-gateway-${VERSION}-${OS}-${ARCH}.tar.gz"curl -LO "https://github.com/keakon/chord-gateway/releases/download/${VERSION}/${ASSET}"curl -LO "https://github.com/keakon/chord-gateway/releases/download/${VERSION}/SHA256SUMS"grep " ${ASSET}$" SHA256SUMS | sha256sum -c -tar -xzf "${ASSET}"sudo install -m 0755 "chord-gateway-${VERSION}-${OS}-${ARCH}/chord-gateway" /usr/local/bin/chord-gatewaychord-gateway --versionIf you run the command on macOS, use
shasum -a 256 -c -instead ofsha256sum -c -. ReplaceVERSION,OS, andARCHfor your platform. On Windows, download the.zipasset and putchord-gateway.exesomewhere onPATH.
Option B: install with Go
Section titled “Option B: install with Go”Use this when you already have the target Go toolchain locally:
go install github.com/keakon/chord-gateway@latestThe binary is usually installed to $(go env GOPATH)/bin. Make sure that directory is on PATH, or call the binary by absolute path.
Option C: build from a checkout
Section titled “Option C: build from a checkout”git clone https://github.com/keakon/chord-gateway.gitcd chord-gatewaygo build -o chord-gateway ../chord-gateway --versionPrepare config and state directories
Section titled “Prepare config and state directories”A predictable layout makes service files and troubleshooting easier:
mkdir -p ~/.config/chord-gateway ~/.local/state/chord-gateway# If you are in a source checkout, you can start from the example file:# cp config.example.yaml ~/.config/chord-gateway/config.yaml# Otherwise create config.yaml from one of the recipes below.touch ~/.config/chord-gateway/config.yamlchmod 700 ~/.config/chord-gateway ~/.local/state/chord-gatewaychmod 600 ~/.config/chord-gateway/config.yamlEdit ~/.config/chord-gateway/config.yaml before starting the gateway.
Important notes:
- Set
chord_pathto an absolute path if you run under systemd or launchd. Services do not load shell aliases and may have a minimalPATH. - Keep Discord
bot_token, Feishuapp_secret, WeChat token files, and the whole state directory out of version control. - Gateway state defaults to
${XDG_STATE_HOME:-~/.local/state}/chord-gateway.
Recipe: WeChat only, one workspace
Section titled “Recipe: WeChat only, one workspace”Use this when a single WeChat login should control one workspace.
ims: wechat: base_url: https://ilinkai.weixin.qq.comworkspaces: default: path: ~/work/project-achord_path: /usr/local/bin/chordidle_timeout: 30mRun it:
chord-gateway -f ~/.config/chord-gateway/config.yaml --pairOn first run, scan the QR code URL printed by the gateway. The reusable WeChat token is stored under <state_dir>/wechat/token.json unless ims.wechat.token_path is set.
Complete private pairing with /bind "~/work/project-a" <pairing_code> before sending tasks.
Recipe: Feishu only, one workspace
Section titled “Recipe: Feishu only, one workspace”Use this when explicitly bound Feishu chats should route to one workspace. Authorization alone does not bind a chat.
ims: feishu: app_id: cli_xxx app_secret: your-app-secretworkspaces: default: path: ~/work/project-achord_path: /usr/local/bin/chordidle_timeout: 30mFeishu app setup checklist:
- In the Feishu developer console, use long connection event delivery.
- Subscribe to
im.message.receive_v1. - Add
card.action.triggerin callback configuration for interactive cards. - Publish a new app version after changing capabilities, permissions, or event subscriptions.
- Add the bot to the target chat.
- Start with
--pairand send/bind "~/work/project-a" <pairing_code>in a private bot chat, then/status.
For initial setup, omit owner and bindings, run chord-gateway --pair, and complete private-chat pairing as described in pairing. The configured identities shown below represent the saved result.
Recipe: Feishu multiple groups, multiple workspaces
Section titled “Recipe: Feishu multiple groups, multiple workspaces”Use this when different Feishu groups should control different repositories.
Recommended rollout:
- Complete private pairing with the Feishu setup above.
- Create each target Feishu group and add the bot.
- Send a text message (
textorpost) in the group. - In that same chat, run
/bind "<path>". - Re-open
config.yamland verify thatims.feishu.chat_bindingswas written as expected.
Example final config:
ims: feishu: app_id: cli_xxx app_secret: your-app-secret owner: {account_id: cli_xxx, user_id: ou_owner_xxx} chat_bindings: oc_project_a: project-a oc_project_b: project-bworkspaces: project-a: path: ~/work/project-a project-b: path: ~/work/project-bchord_path: /usr/local/bin/chordidle_timeout: 30m/bind only updates Feishu chat bindings and workspace definitions. Manual edits to other config fields still require a gateway restart.
Recipe: Discord DMs, channels, and threads
Section titled “Recipe: Discord DMs, channels, and threads”ims: discord: bot_token: your-bot-token application_id: "123456789012345678"chord_path: chordInstall the bot with View Channel, Send Messages, and Send Messages in Threads; keep privileged intents disabled. Run chord-gateway -f config.yaml --pair, then privately send /bind "~/work/project-a" <pairing_code>. The owner binds each server channel or existing accessible thread with @Bot /bind "~/work/project-a"; later commands also use @Bot. Each thread has its own route and session. See the Discord guide for private-thread access, archived threads, and deployment checks.
Recipe: WeChat plus Feishu
Section titled “Recipe: WeChat plus Feishu”Use this when WeChat should stay on one fixed workspace while Feishu groups route independently.
ims: wechat: base_url: https://ilinkai.weixin.qq.com owner: {account_id: bot_example, user_id: user_example} chat_bindings: user_example: project-a feishu: app_id: cli_xxx app_secret: your-app-secret owner: {account_id: cli_xxx, user_id: ou_owner_xxx} chat_bindings: oc_project_a: project-a oc_project_b: project-bworkspaces: project-a: path: ~/work/project-a project-b: path: ~/work/project-bchord_path: /usr/local/bin/chordidle_timeout: 30mRouting behavior:
- The bound WeChat chat goes to
project-a. - Feishu chat
oc_project_agoes toproject-a. - Feishu chat
oc_project_bgoes toproject-b.
Run as a systemd user service
Section titled “Run as a systemd user service”Use this on Linux when you want the gateway to restart automatically after failures.
-
Make sure config and state directories exist:
Terminal window mkdir -p ~/.config/chord-gateway ~/.local/state/chord-gateway ~/.config/systemd/user -
Create
~/.config/systemd/user/chord-gateway.service:[Unit]Description=chord-gatewayAfter=default.target[Service]Type=simpleExecStart=/usr/local/bin/chord-gateway -f %h/.config/chord-gateway/config.yamlRestart=on-failureRestartSec=5sEnvironment=CHORD_GATEWAY_STATE_DIR=%h/.local/state/chord-gateway[Install]WantedBy=default.targetIf you installed with
go install, changeExecStartto%h/go/bin/chord-gateway ...or another absolute path. -
Enable and start it:
Terminal window systemctl --user daemon-reloadsystemctl --user enable --now chord-gatewayjournalctl --user -u chord-gateway -f -
To keep the user service running after logout, enable lingering if your system requires it:
Terminal window sudo loginctl enable-linger "$USER"
Gateway logs are still written to <state_dir>/gateway.log; journalctl is useful for service lifecycle logs.
Run with launchd on macOS
Section titled “Run with launchd on macOS”Use this when you want the gateway to start at login.
-
Prepare directories:
Terminal window mkdir -p ~/.config/chord-gateway ~/.local/state/chord-gateway ~/Library/LaunchAgents -
Create
~/Library/LaunchAgents/io.github.keakon.chord-gateway.plist.Replace
/Users/YOUR_USERand binary paths with absolute paths.launchddoes not expand~in plist strings.<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>io.github.keakon.chord-gateway</string><key>ProgramArguments</key><array><string>/usr/local/bin/chord-gateway</string><string>-f</string><string>/Users/YOUR_USER/.config/chord-gateway/config.yaml</string></array><key>EnvironmentVariables</key><dict><key>CHORD_GATEWAY_STATE_DIR</key><string>/Users/YOUR_USER/.local/state/chord-gateway</string></dict><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>/Users/YOUR_USER/.local/state/chord-gateway/launchd.out.log</string><key>StandardErrorPath</key><string>/Users/YOUR_USER/.local/state/chord-gateway/launchd.err.log</string></dict></plist> -
Load it:
Terminal window launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/io.github.keakon.chord-gateway.plistlaunchctl enable "gui/$(id -u)/io.github.keakon.chord-gateway"launchctl kickstart -k "gui/$(id -u)/io.github.keakon.chord-gateway" -
Inspect logs:
Terminal window tail -f ~/.local/state/chord-gateway/gateway.logtail -f ~/.local/state/chord-gateway/launchd.err.log
Operational checklist before unattended use
Section titled “Operational checklist before unattended use”Before you leave the gateway running unattended:
- Run
/statusfrom every IM chat you expect to use. - Confirm
chord_pathresolves to the intended Chord binary under the service environment. - Confirm the gateway log path and state directory are writable by the service user.
- Pair an owner on each platform and explicitly bind each chat; localhost does not prevent external users from messaging the bot.
- Keep backups of
config.yamland any state directory that contains WeChat token files if you want login state to survive host replacement. - Know how to stop the service:
- systemd user service:
systemctl --user stop chord-gateway - launchd:
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/io.github.keakon.chord-gateway.plist
- systemd user service: