iLink Hub
iLink-compatible multiplexer hub for WeChat ClawBot — connect one WeChat account to multiple AI agent backends running on different machines or workspaces, with zero client-side code changes.
The Problem
WeChat ClawBot's iLink API enforces an exclusive lock: only one process can poll getupdates at a time. If you run Recursive on your Mac, Recursive on a server, and OpenClaw on your laptop — they all fight for the same connection, and only one wins.
The Solution
iLink Hub is a transparent iLink proxy:
[WeChat User]
↕ real iLink protocol
[iLink Hub] ← the sole connection holder
↕ emulated iLink API (same HTTP endpoints, same protocol)
┌───────────────┐ ┌────────────────────┐ ┌────────────────┐
│Recursive (Mac)│ │Recursive (Server) │ │OpenClaw (etc.) │
│base_url=hub │ │base_url=hub │ │base_url=hub │
│token=vhub_abc │ │token=vhub_def │ │token=vhub_xyz │
└───────────────┘ └────────────────────┘ └────────────────┘
Clients don't need any code changes — just point WEIXIN_BASE_URL at the Hub and use a virtual token. The Hub handles multiplexing, routing, and token mapping transparently.
Features
- iLink-compatible API — any existing iLink client works out-of-the-box
- Multi-backend routing — route messages to different backends via WeChat commands
- Context-token mapping — real context tokens never leak to clients; persisted across restarts
- QR code login — scan once, token saved to DB
- Multi-database — SQLite (default), PostgreSQL, MySQL via
DATABASE_URL - Full persistence — client registrations, routing state, and context mappings survive restarts
- Web admin panel — manage clients and copy config at
/hub/ui - Admin auth — protect
/hub/endpoints withILINK_ADMIN_TOKENenv var - Bounded queues — per-client message buffer capped at 200 to prevent OOM
- Prometheus metrics — counters and gauges at
/metrics - Friendly fallback — when all backends are offline, WeChat users get an instant reply
- Pre-built binaries — download from GitHub Releases (Linux/macOS/Windows), no Rust required
- Health checks — auto-marks offline clients after 90s idle
- CLI bridge (
ilink-hub-bridge) — connect as a Hub backend and run a local CLI per message (docs/bridge/README.md) - Docker support — single-command deployment, multi-arch image (amd64 + arm64)
Desktop app (Tauri)
A Tauri 2 desktop shell lives under desktop/ilink-hub-desktop/: it embeds the same run_serve runtime as ilink-hub serve (default listen 127.0.0.1:8765, SQLite under the OS app data dir). The root crate stays out of any workspace with this app, so cargo build / cargo test at the repo root are unchanged. See desktop/ilink-hub-desktop/README.md for npm run tauri dev / tauri build. Longer-term notes: docs/desktop-tauri-roadmap.md.
Quick Start
Option A: Pre-built Binary (fastest)
# Linux x86_64
&&
# macOS Apple Silicon
&&
# macOS Intel
&&
Windows: download
ilink-hub-windows-x86_64.exefrom Releases.
Option B: Cargo (requires Rust)
# Start Hub (QR login runs inline on first start if no token in DB)
# Open web admin panel
# Visit http://your-hub.example.com:8765/hub/ui
# Register each backend (CLI or via the web UI)
# Outputs:
# WEIXIN_BASE_URL=http://your-hub.example.com:8765
# WEIXIN_TOKEN=vhub_xxxxxxxxxxxxxxxx
Option C: Docker Compose
# docker-compose.yml
services:
ilink-hub:
image: ghcr.io/jeffkit/ilink-hub:latest
restart: unless-stopped
ports:
- "8765:8765"
volumes:
- ilink-hub-data:/data
environment:
DATABASE_URL: sqlite:/data/ilink-hub.db
# ILINK_TOKEN: your-token # Optional: skip QR login if you have a token
volumes:
ilink-hub-data:
# First-time iLink bind: follow logs for the QR code
Option D: PostgreSQL backend
DATABASE_URL=postgres://user:pass@localhost/ilink_hub
WeChat Commands
Send these from WeChat to control the Hub:
| Command | Effect |
|---|---|
/list |
List all registered backends and their status |
/use <name> |
Switch active backend (e.g. /use mac-home) |
/broadcast <text> |
Send a message to all online backends |
/status |
Show Hub status (online/total clients) |
Configuring Clients
Recursive
# ~/.recursive/config.toml
[]
= "http://your-hub.example.com"
= "vhub_xxxxxxxxxxxxxxxx"
Or via environment:
WEIXIN_BASE_URL=http://your-hub.example.com
WEIXIN_TOKEN=vhub_xxxxxxxxxxxxxxxx
Any wechatbot-based Rust SDK
let bot = new;
OpenClaw
# ~/.openclaw/openclaw.json
ilink-hub-bridge (local CLI)
Run a local command (Claude Code, Cursor Agent, Codex, etc.) for each routed WeChat text message — same iLink virtual-token flow as other backends. Usage guide (Chinese): bridge/USAGE. Quick echo path: 5-minute try. Full options: docs/bridge/README.md and docs/bridge/examples/.
# Default — no WEIXIN_TOKEN and no cred file yet: POST /hub/register, saves ~/.ilink-hub/bridge-credentials.json (ILINK_ADMIN_TOKEN if Hub requires it). If the file exists but is corrupt/empty, bridge errors instead of overwriting — use --force-register or delete the file.
WEIXIN_BASE_URL=http://127.0.0.1:8765
# Optional — Hub client QR pairing instead: add --pair
# Optional — explicit vtoken: WEIXIN_TOKEN=vhub_xxx …
Hub API Reference
The Hub exposes the full iLink API surface plus Hub-specific management endpoints:
iLink-compatible endpoints (same as ilinkai.weixin.qq.com)
| Method | Path | Description |
|---|---|---|
POST |
/ilink/bot/getupdates |
Long-poll for messages (30s timeout) |
POST |
/ilink/bot/sendmessage |
Send reply (context_token auto-translated) |
POST |
/ilink/bot/sendtyping |
Send typing indicator |
POST |
/ilink/bot/getconfig |
Get typing ticket |
POST |
/ilink/bot/getuploadurl |
Get CDN upload URL |
Authentication: Same as real iLink — Authorization: Bearer <vtoken> header.
Hub management endpoints
| Method | Path | Description |
|---|---|---|
POST |
/hub/register |
Register a new backend client |
GET |
/hub/clients |
List all registered clients (includes vtoken) |
GET |
/hub/ui |
Web admin panel (browser UI) |
GET |
/metrics |
Prometheus-format metrics |
GET |
/health |
Health check |
Admin auth: Set ILINK_ADMIN_TOKEN=<secret> on the Hub. Then pass Authorization: Bearer <secret>
when calling /hub/register or /hub/clients. If the env var is unset, these endpoints are open (suitable
for local dev / private networks).
Architecture
ilink-hub/
├── src/
│ ├── ilink/
│ │ ├── types.rs — Complete iLink protocol types (mirrors ilinkai.weixin.qq.com)
│ │ ├── upstream.rs — Real iLink poller (exponential backoff, auto-reconnect)
│ │ └── login.rs — QR login flow (terminal QR rendering)
│ ├── hub/
│ │ ├── registry.rs — Client registry (vtoken management)
│ │ ├── router.rs — Message routing + WeChat command parser
│ │ ├── queue.rs — Per-client queues + context_token mapping
│ │ └── health.rs — Background health checker
│ ├── server/
│ │ └── routes.rs — iLink-compatible HTTP handlers
│ ├── store/
│ │ └── mod.rs — sqlx database layer (SQLite/PostgreSQL/MySQL)
│ └── main.rs — CLI: serve / login / register / clients
├── Dockerfile — Multi-stage build
└── .github/workflows/ci.yml
Message flow
WeChat sends message
↓
Hub polls real iLink getupdates → receives InboundMessage
↓
Router: parse WeChat command or determine target client
↓
Map real context_token → virtual context_token (stored in DB)
↓
Push to target client's queue (notify waiting getupdates long-poll)
↓
Client's getupdates returns the message
↓
Client processes, sends sendmessage with virtual context_token
↓
Hub resolves virtual → real context_token
↓
Hub forwards sendmessage to real iLink
↓
WeChat receives reply ✓
Security Recommendations
- Deploy behind HTTPS — use a reverse proxy (Nginx, Caddy) with TLS
- Restrict
/hub/admin endpoints — add IP allowlist or Bearer token auth to admin routes - Use PostgreSQL for production — SQLite works but isn't suited for high-concurrency deployments
- Rotate virtual tokens periodically — re-register clients with a new name to get fresh vtokens
- Keep Hub on private network — only expose port 8765 if needed; ideally put Nginx in front
Nginx example
server {
listen 443 ssl;
server_name hub.example.com;
# Only allow your backend IPs to access admin endpoints
location /hub/ {
allow 192.168.1.0/24;
deny all;
proxy_pass http://localhost:8765;
}
# iLink API open to registered clients
location /ilink/ {
proxy_pass http://localhost:8765;
proxy_set_header Host $host;
}
location /health {
proxy_pass http://localhost:8765;
}
}
Comparison with Similar Projects
| Project | Protocol for clients | Multi-machine | Standalone |
|---|---|---|---|
| iLink Hub (this) | ✅ iLink-compatible | ✅ Yes | ✅ Yes |
| OpeniLink Hub | ❌ Custom WebSocket/SDK | ✅ Yes | ✅ Yes |
| HermesClaw | ❌ Local proxy only | ❌ No | ✅ Yes |
| wechat-clawbot | HTTP webhook | ✅ Yes | ✅ Yes |
| OpenClaw bindings | ❌ OpenClaw-specific | ❌ Same machine | ✅ Yes |
License
MIT © 2026 jeffkit