channelkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +829 -0
- package/config.example.yaml +37 -0
- package/dist/api/middleware/auth.d.ts +14 -0
- package/dist/api/middleware/auth.d.ts.map +1 -0
- package/dist/api/middleware/auth.js +130 -0
- package/dist/api/middleware/auth.js.map +1 -0
- package/dist/api/routes/config.d.ts +4 -0
- package/dist/api/routes/config.d.ts.map +1 -0
- package/dist/api/routes/config.js +794 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/dashboard.d.ts +4 -0
- package/dist/api/routes/dashboard.d.ts.map +1 -0
- package/dist/api/routes/dashboard.js +89 -0
- package/dist/api/routes/dashboard.js.map +1 -0
- package/dist/api/routes/inbound.d.ts +4 -0
- package/dist/api/routes/inbound.d.ts.map +1 -0
- package/dist/api/routes/inbound.js +293 -0
- package/dist/api/routes/inbound.js.map +1 -0
- package/dist/api/routes/logs.d.ts +4 -0
- package/dist/api/routes/logs.d.ts.map +1 -0
- package/dist/api/routes/logs.js +49 -0
- package/dist/api/routes/logs.js.map +1 -0
- package/dist/api/routes/mcp.d.ts +4 -0
- package/dist/api/routes/mcp.d.ts.map +1 -0
- package/dist/api/routes/mcp.js +100 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/restart.d.ts +4 -0
- package/dist/api/routes/restart.d.ts.map +1 -0
- package/dist/api/routes/restart.js +11 -0
- package/dist/api/routes/restart.js.map +1 -0
- package/dist/api/routes/send.d.ts +4 -0
- package/dist/api/routes/send.d.ts.map +1 -0
- package/dist/api/routes/send.js +66 -0
- package/dist/api/routes/send.js.map +1 -0
- package/dist/api/routes/settings.d.ts +4 -0
- package/dist/api/routes/settings.d.ts.map +1 -0
- package/dist/api/routes/settings.js +133 -0
- package/dist/api/routes/settings.js.map +1 -0
- package/dist/api/routes/tunnel.d.ts +4 -0
- package/dist/api/routes/tunnel.d.ts.map +1 -0
- package/dist/api/routes/tunnel.js +209 -0
- package/dist/api/routes/tunnel.js.map +1 -0
- package/dist/api/routes/twilio.d.ts +4 -0
- package/dist/api/routes/twilio.d.ts.map +1 -0
- package/dist/api/routes/twilio.js +138 -0
- package/dist/api/routes/twilio.js.map +1 -0
- package/dist/api/routes/update.d.ts +4 -0
- package/dist/api/routes/update.d.ts.map +1 -0
- package/dist/api/routes/update.js +42 -0
- package/dist/api/routes/update.js.map +1 -0
- package/dist/api/server.d.ts +52 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +415 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/types.d.ts +61 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +3 -0
- package/dist/api/types.js.map +1 -0
- package/dist/channels/base.d.ts +15 -0
- package/dist/channels/base.d.ts.map +1 -0
- package/dist/channels/base.js +20 -0
- package/dist/channels/base.js.map +1 -0
- package/dist/channels/email/gmail.d.ts +36 -0
- package/dist/channels/email/gmail.d.ts.map +1 -0
- package/dist/channels/email/gmail.js +351 -0
- package/dist/channels/email/gmail.js.map +1 -0
- package/dist/channels/email/index.d.ts +3 -0
- package/dist/channels/email/index.d.ts.map +1 -0
- package/dist/channels/email/index.js +8 -0
- package/dist/channels/email/index.js.map +1 -0
- package/dist/channels/email/resend.d.ts +29 -0
- package/dist/channels/email/resend.d.ts.map +1 -0
- package/dist/channels/email/resend.js +155 -0
- package/dist/channels/email/resend.js.map +1 -0
- package/dist/channels/endpoint/index.d.ts +21 -0
- package/dist/channels/endpoint/index.d.ts.map +1 -0
- package/dist/channels/endpoint/index.js +80 -0
- package/dist/channels/endpoint/index.js.map +1 -0
- package/dist/channels/sms/index.d.ts +37 -0
- package/dist/channels/sms/index.d.ts.map +1 -0
- package/dist/channels/sms/index.js +163 -0
- package/dist/channels/sms/index.js.map +1 -0
- package/dist/channels/telegram/index.d.ts +24 -0
- package/dist/channels/telegram/index.d.ts.map +1 -0
- package/dist/channels/telegram/index.js +231 -0
- package/dist/channels/telegram/index.js.map +1 -0
- package/dist/channels/voice/index.d.ts +62 -0
- package/dist/channels/voice/index.d.ts.map +1 -0
- package/dist/channels/voice/index.js +286 -0
- package/dist/channels/voice/index.js.map +1 -0
- package/dist/channels/whatsapp/index.d.ts +31 -0
- package/dist/channels/whatsapp/index.d.ts.map +1 -0
- package/dist/channels/whatsapp/index.js +383 -0
- package/dist/channels/whatsapp/index.js.map +1 -0
- package/dist/cli/commands/demo.d.ts +4 -0
- package/dist/cli/commands/demo.d.ts.map +1 -0
- package/dist/cli/commands/demo.js +55 -0
- package/dist/cli/commands/demo.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +254 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/install-skill.d.ts +4 -0
- package/dist/cli/commands/install-skill.d.ts.map +1 -0
- package/dist/cli/commands/install-skill.js +60 -0
- package/dist/cli/commands/install-skill.js.map +1 -0
- package/dist/cli/commands/send.d.ts +5 -0
- package/dist/cli/commands/send.d.ts.map +1 -0
- package/dist/cli/commands/send.js +46 -0
- package/dist/cli/commands/send.js.map +1 -0
- package/dist/cli/commands/start.d.ts +5 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +129 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/helpers.d.ts +26 -0
- package/dist/cli/helpers.d.ts.map +1 -0
- package/dist/cli/helpers.js +120 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +282 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/wizards/channel.d.ts +4 -0
- package/dist/cli/wizards/channel.d.ts.map +1 -0
- package/dist/cli/wizards/channel.js +285 -0
- package/dist/cli/wizards/channel.js.map +1 -0
- package/dist/cli/wizards/provision.d.ts +4 -0
- package/dist/cli/wizards/provision.d.ts.map +1 -0
- package/dist/cli/wizards/provision.js +213 -0
- package/dist/cli/wizards/provision.js.map +1 -0
- package/dist/cli/wizards/service.d.ts +5 -0
- package/dist/cli/wizards/service.d.ts.map +1 -0
- package/dist/cli/wizards/service.js +212 -0
- package/dist/cli/wizards/service.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/parser.d.ts +6 -0
- package/dist/config/parser.d.ts.map +1 -0
- package/dist/config/parser.js +37 -0
- package/dist/config/parser.js.map +1 -0
- package/dist/config/types.d.ts +170 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/core/apiServer.d.ts +2 -0
- package/dist/core/apiServer.d.ts.map +1 -0
- package/dist/core/apiServer.js +7 -0
- package/dist/core/apiServer.js.map +1 -0
- package/dist/core/groupStore.d.ts +19 -0
- package/dist/core/groupStore.d.ts.map +1 -0
- package/dist/core/groupStore.js +48 -0
- package/dist/core/groupStore.js.map +1 -0
- package/dist/core/logger.d.ts +42 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +142 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/messageHandler.d.ts +15 -0
- package/dist/core/messageHandler.d.ts.map +1 -0
- package/dist/core/messageHandler.js +309 -0
- package/dist/core/messageHandler.js.map +1 -0
- package/dist/core/restart.d.ts +3 -0
- package/dist/core/restart.d.ts.map +1 -0
- package/dist/core/restart.js +35 -0
- package/dist/core/restart.js.map +1 -0
- package/dist/core/router.d.ts +56 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +168 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/tunnel.d.ts +16 -0
- package/dist/core/tunnel.d.ts.map +1 -0
- package/dist/core/tunnel.js +99 -0
- package/dist/core/tunnel.js.map +1 -0
- package/dist/core/types.d.ts +54 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/updater.d.ts +44 -0
- package/dist/core/updater.d.ts.map +1 -0
- package/dist/core/updater.js +264 -0
- package/dist/core/updater.js.map +1 -0
- package/dist/core/webhook.d.ts +26 -0
- package/dist/core/webhook.d.ts.map +1 -0
- package/dist/core/webhook.js +224 -0
- package/dist/core/webhook.js.map +1 -0
- package/dist/dashboard/assets/browser-D_-rzKir.js +8 -0
- package/dist/dashboard/assets/index-CNa084vI.js +88 -0
- package/dist/dashboard/assets/index-CRvIEyjF.css +1 -0
- package/dist/dashboard/index.html +17 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +551 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +6 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +45 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +197 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +502 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/media/formatter.d.ts +12 -0
- package/dist/media/formatter.d.ts.map +1 -0
- package/dist/media/formatter.js +147 -0
- package/dist/media/formatter.js.map +1 -0
- package/dist/media/processor.d.ts +33 -0
- package/dist/media/processor.d.ts.map +1 -0
- package/dist/media/processor.js +145 -0
- package/dist/media/processor.js.map +1 -0
- package/dist/media/stt.d.ts +16 -0
- package/dist/media/stt.d.ts.map +1 -0
- package/dist/media/stt.js +298 -0
- package/dist/media/stt.js.map +1 -0
- package/dist/media/tts.d.ts +19 -0
- package/dist/media/tts.d.ts.map +1 -0
- package/dist/media/tts.js +135 -0
- package/dist/media/tts.js.map +1 -0
- package/dist/onboarding/index.d.ts +28 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +144 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +14 -0
- package/dist/paths.js.map +1 -0
- package/dist/provisioning/twilio.d.ts +51 -0
- package/dist/provisioning/twilio.d.ts.map +1 -0
- package/dist/provisioning/twilio.js +175 -0
- package/dist/provisioning/twilio.js.map +1 -0
- package/echo-server.js +163 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
# ChannelKit
|
|
2
|
+
|
|
3
|
+
A self-hosted messaging gateway that connects chat channels (WhatsApp, Telegram, Email) to any application via webhooks.
|
|
4
|
+
|
|
5
|
+
Think **nginx, but for chat.**
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
📱 WhatsApp ──┐
|
|
9
|
+
💬 Telegram ──┤──→ ChannelKit ──→ Your App (webhook)
|
|
10
|
+
📧 Email ──┘ (unified JSON)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Your app receives every message in a **unified JSON format**, regardless of source channel. Respond with text or media, and ChannelKit routes it back through the originating channel.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **WhatsApp** (Baileys, optional) — QR code linking, magic codes + auto-created groups for multi-service
|
|
18
|
+
- **Telegram** (grammY) — bot token setup, slash commands for multi-service
|
|
19
|
+
- **Email** — Gmail (OAuth2 + polling) and Resend (API + polling/webhook)
|
|
20
|
+
- **Services model** — single or multiple services per channel, each with its own webhook
|
|
21
|
+
- **SMS** (Twilio) — inbound/outbound SMS via polling or webhooks
|
|
22
|
+
- **Voice** (Twilio) — inbound voice calls with STT, webhook, and TTS/Say responses
|
|
23
|
+
- **Speech-to-Text** — automatic transcription of voice messages (Google, Whisper, Deepgram)
|
|
24
|
+
- **Text-to-Speech** — voice responses when your webhook returns `voice: true` (Google, ElevenLabs, OpenAI)
|
|
25
|
+
- **Auto language detection** — STT supports multiple languages with automatic detection
|
|
26
|
+
- **AI formatting** — transform incoming messages with AI (OpenAI, Anthropic, Google) before forwarding to your webhook
|
|
27
|
+
- **MCP server** — Model Context Protocol server lets AI assistants manage channels, services, and send messages
|
|
28
|
+
- **Web dashboard** — SQLite-backed logs with real-time WebSocket updates
|
|
29
|
+
- **Async messaging API** — `replyUrl` in every webhook payload for sending messages anytime
|
|
30
|
+
- **Onboarding flow** — magic codes (WhatsApp) and slash commands (Telegram) for user self-service
|
|
31
|
+
- **Echo server** — included test server for quick experimentation
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g @dirbalak/channelkit
|
|
37
|
+
channelkit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **Upgrading?** If you previously installed ChannelKit without the `-g` flag (global), you should uninstall it first and reinstall globally. A local install won't put `channelkit` on your PATH, so the CLI command won't be available system-wide:
|
|
41
|
+
>
|
|
42
|
+
> ```bash
|
|
43
|
+
> npm uninstall @dirbalak/channelkit # remove the local install
|
|
44
|
+
> npm install -g @dirbalak/channelkit # reinstall globally
|
|
45
|
+
> ```
|
|
46
|
+
|
|
47
|
+
On first run, ChannelKit will ask how you'd like to set up:
|
|
48
|
+
|
|
49
|
+
- **Dashboard** — creates a minimal config, starts the server, and opens the dashboard in your browser. You'll get an API secret to log in and configure everything from the UI.
|
|
50
|
+
- **CLI wizard** — step-by-step terminal setup: pick a channel, enter credentials, set a webhook URL, and start.
|
|
51
|
+
|
|
52
|
+
All configuration is stored in `~/.channelkit/` (config, auth sessions, logs).
|
|
53
|
+
|
|
54
|
+
### Running
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
channelkit # start (opens dashboard automatically)
|
|
58
|
+
channelkit start -c /path/to.yaml # use a custom config file
|
|
59
|
+
channelkit start --tunnel # start with a public URL (Cloudflare tunnel)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Public URL (Cloudflare Tunnel)
|
|
63
|
+
|
|
64
|
+
ChannelKit can expose your local instance to the internet using a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/). This is required for features like inbound webhooks (Resend, Twilio) and voice calls, which need a publicly reachable URL.
|
|
65
|
+
|
|
66
|
+
#### Quick tunnel (no setup needed)
|
|
67
|
+
|
|
68
|
+
Just run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
channelkit start --tunnel
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This creates a temporary public URL via [trycloudflare.com](https://trycloudflare.com) — no Cloudflare account or installation required. The URL changes each time you restart.
|
|
75
|
+
|
|
76
|
+
> **Note:** You can also start and stop the tunnel from the dashboard at any time.
|
|
77
|
+
|
|
78
|
+
#### Fixed URL (requires setup)
|
|
79
|
+
|
|
80
|
+
If you need a stable URL that doesn't change between restarts, follow the steps below to set up a named Cloudflare Tunnel.
|
|
81
|
+
|
|
82
|
+
##### 1. Install cloudflared
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# macOS
|
|
86
|
+
brew install cloudflared
|
|
87
|
+
|
|
88
|
+
# Linux (Debian/Ubuntu)
|
|
89
|
+
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
|
|
90
|
+
sudo dpkg -i cloudflared.deb
|
|
91
|
+
|
|
92
|
+
# Windows
|
|
93
|
+
winget install Cloudflare.cloudflared
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
##### 2. Authenticate with Cloudflare
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cloudflared tunnel login
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This opens your browser to authorize `cloudflared` with your Cloudflare account. You need a domain managed by Cloudflare.
|
|
103
|
+
|
|
104
|
+
##### 3. Create a tunnel
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
cloudflared tunnel create channelkit
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This generates a tunnel ID and a credentials file. Note the tunnel ID — you'll need it next.
|
|
111
|
+
|
|
112
|
+
##### 4. Route DNS to the tunnel
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
cloudflared tunnel route dns channelkit ck.yourdomain.com
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Replace `ck.yourdomain.com` with the subdomain you want to use. This creates a CNAME record pointing to the tunnel.
|
|
119
|
+
|
|
120
|
+
##### 5. Configure ChannelKit
|
|
121
|
+
|
|
122
|
+
Add the tunnel section to your `config.yaml`:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
tunnel:
|
|
126
|
+
provider: cloudflared
|
|
127
|
+
token: <your-tunnel-token> # from the credentials file or Cloudflare dashboard
|
|
128
|
+
public_url: https://ck.yourdomain.com
|
|
129
|
+
auto_start: true # start the tunnel automatically with ChannelKit
|
|
130
|
+
expose_dashboard: true # expose the dashboard through the tunnel
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
To get the tunnel token, you can either:
|
|
134
|
+
- Copy it from the Cloudflare Zero Trust dashboard under **Networks → Tunnels → your tunnel → Configure**
|
|
135
|
+
- Or use the credentials JSON file generated by `cloudflared tunnel create`
|
|
136
|
+
|
|
137
|
+
##### 6. Start ChannelKit with the tunnel
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
channelkit start --tunnel
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
ChannelKit will start `cloudflared` automatically and route traffic through your public URL. You can verify it's working by visiting `https://ck.yourdomain.com` in your browser.
|
|
144
|
+
|
|
145
|
+
> **Tip:** If you set `auto_start: true` in the tunnel config, you don't need the `--tunnel` flag — the tunnel starts automatically with `channelkit start`.
|
|
146
|
+
|
|
147
|
+
## CLI Commands
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
channelkit # start (auto-runs init on first run)
|
|
151
|
+
channelkit init # interactive setup wizard
|
|
152
|
+
channelkit start [-c path] # start the gateway
|
|
153
|
+
|
|
154
|
+
channelkit channel add # add a new channel interactively
|
|
155
|
+
channelkit channel list # list configured channels
|
|
156
|
+
channelkit channel remove <name> # remove a channel
|
|
157
|
+
|
|
158
|
+
channelkit service add # add a new service interactively
|
|
159
|
+
channelkit service list # list configured services
|
|
160
|
+
channelkit service remove <name> # remove a service
|
|
161
|
+
|
|
162
|
+
channelkit install-skill # install Claude Code skill
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Services & Multi-Service
|
|
166
|
+
|
|
167
|
+
ChannelKit can route messages from a single channel to multiple backend services.
|
|
168
|
+
|
|
169
|
+
### Add a service
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
channelkit service add
|
|
173
|
+
# → Service name: Expenses
|
|
174
|
+
# → Webhook: http://localhost:3000/expenses
|
|
175
|
+
# → Enable STT? Enable TTS?
|
|
176
|
+
# → ✅ Added!
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### WhatsApp: Magic Codes + Groups
|
|
180
|
+
|
|
181
|
+
When a channel has multiple services, WhatsApp uses magic codes and auto-created groups:
|
|
182
|
+
|
|
183
|
+
1. User sends "EXPENSES" to your WhatsApp number (or clicks a `wa.me` link)
|
|
184
|
+
2. ChannelKit creates a group "Expenses - \<User Name\>"
|
|
185
|
+
3. All messages in that group route to the service's webhook
|
|
186
|
+
|
|
187
|
+
### Telegram: Slash Commands
|
|
188
|
+
|
|
189
|
+
For Telegram multi-service, each service gets a slash command:
|
|
190
|
+
|
|
191
|
+
1. User sends `/expenses` in the bot chat
|
|
192
|
+
2. Subsequent messages route to the Expenses service webhook
|
|
193
|
+
|
|
194
|
+
## Speech-to-Text (STT)
|
|
195
|
+
|
|
196
|
+
Automatically transcribe voice messages before forwarding to your webhook. Configure per-service:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
services:
|
|
200
|
+
myapp:
|
|
201
|
+
channel: whatsapp
|
|
202
|
+
webhook: http://localhost:3000
|
|
203
|
+
stt:
|
|
204
|
+
provider: google # google | whisper | deepgram
|
|
205
|
+
language: he-IL # primary language
|
|
206
|
+
alternative_languages: # auto-detect from these + primary
|
|
207
|
+
- en-US
|
|
208
|
+
- ar-IL
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**API keys via environment variables:**
|
|
212
|
+
|
|
213
|
+
- Google: `GOOGLE_STT_API_KEY` or `GOOGLE_API_KEY`
|
|
214
|
+
- Whisper (OpenAI): `OPENAI_STT_API_KEY` or `OPENAI_API_KEY`
|
|
215
|
+
- Deepgram: `DEEPGRAM_STT_API_KEY` or `DEEPGRAM_API_KEY`
|
|
216
|
+
|
|
217
|
+
## Text-to-Speech (TTS)
|
|
218
|
+
|
|
219
|
+
When your webhook returns `{ "text": "Hello", "voice": true }`, ChannelKit synthesizes audio and sends a voice message. Configure per-service:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
services:
|
|
223
|
+
myapp:
|
|
224
|
+
channel: whatsapp
|
|
225
|
+
webhook: http://localhost:3000
|
|
226
|
+
tts:
|
|
227
|
+
provider: elevenlabs # google | elevenlabs | openai
|
|
228
|
+
voice: 21m00Tcm4TlvDq8ikWAM # optional voice ID
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**API keys via environment variables:**
|
|
232
|
+
|
|
233
|
+
- Google: `GOOGLE_TTS_API_KEY` or `GOOGLE_API_KEY`
|
|
234
|
+
- ElevenLabs: `ELEVENLABS_TTS_API_KEY` or `ELEVENLABS_API_KEY`
|
|
235
|
+
- OpenAI: `OPENAI_TTS_API_KEY` or `OPENAI_API_KEY`
|
|
236
|
+
|
|
237
|
+
## AI Formatting
|
|
238
|
+
|
|
239
|
+
ChannelKit can pass incoming messages through an AI model before forwarding them to your webhook. This enables structured data extraction, translation, classification, and other transformations — all without changing your backend.
|
|
240
|
+
|
|
241
|
+
The processing pipeline is: **STT** (optional) → **AI Format** → **Webhook**
|
|
242
|
+
|
|
243
|
+
### Configuration
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
services:
|
|
247
|
+
myapp:
|
|
248
|
+
channel: whatsapp
|
|
249
|
+
webhook: http://localhost:3000
|
|
250
|
+
format:
|
|
251
|
+
provider: openai # openai | anthropic | google
|
|
252
|
+
model: gpt-4o-mini # optional, each provider has a sensible default
|
|
253
|
+
prompt: "Extract the expense amount and category as JSON"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Supported providers and defaults:**
|
|
257
|
+
|
|
258
|
+
| Provider | Default Model | API Key |
|
|
259
|
+
| --------- | -------------------------- | ------------------- |
|
|
260
|
+
| OpenAI | `gpt-4o-mini` | `OPENAI_API_KEY` |
|
|
261
|
+
| Anthropic | `claude-sonnet-4-20250514` | `ANTHROPIC_API_KEY` |
|
|
262
|
+
| Google | `gemini-2.5-flash` | `GOOGLE_API_KEY` |
|
|
263
|
+
|
|
264
|
+
API keys can be set as environment variables or in `config.yaml`:
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
settings:
|
|
268
|
+
openai_api_key: "sk-..."
|
|
269
|
+
anthropic_api_key: "sk-ant-..."
|
|
270
|
+
google_api_key: "..."
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Example
|
|
274
|
+
|
|
275
|
+
With this prompt:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
Extract: name, amount, category. Return JSON only.
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
A message like `"Paid $45 for lunch with Sarah"` becomes:
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{ "name": "Sarah", "amount": 45, "category": "lunch" }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Your webhook receives the formatted text. The original text is preserved in the dashboard logs.
|
|
288
|
+
|
|
289
|
+
## Voice Channel (Twilio)
|
|
290
|
+
|
|
291
|
+
ChannelKit supports inbound voice calls via Twilio. The flow:
|
|
292
|
+
|
|
293
|
+
1. Caller dials your Twilio number → ChannelKit answers with a greeting
|
|
294
|
+
2. Caller speaks → recording is captured and transcribed (STT)
|
|
295
|
+
3. Transcribed text is sent to your webhook
|
|
296
|
+
4. Your webhook responds with text → ChannelKit speaks it back via TTS or `<Say>`
|
|
297
|
+
5. In **conversational mode**, the loop repeats; otherwise the call ends
|
|
298
|
+
|
|
299
|
+
### Setup
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
channelkit channel add # choose Voice (Twilio)
|
|
303
|
+
channelkit service add # configure webhook + voice settings
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Voice requires a **public URL** — use `--tunnel` or `--public-url` when starting:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
channelkit start --public-url https://your-domain.com
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Voice service config
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
services:
|
|
316
|
+
support:
|
|
317
|
+
channel: voice
|
|
318
|
+
webhook: "http://localhost:3000/support"
|
|
319
|
+
stt:
|
|
320
|
+
provider: google
|
|
321
|
+
language: en-US
|
|
322
|
+
tts:
|
|
323
|
+
provider: elevenlabs
|
|
324
|
+
voice:
|
|
325
|
+
greeting: "Hello! Please speak after the beep."
|
|
326
|
+
hold_message: "One moment please..."
|
|
327
|
+
language: en-US
|
|
328
|
+
voice_name: Polly.Joanna
|
|
329
|
+
conversational: true
|
|
330
|
+
max_record_seconds: 30
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### TTS Audio Serving
|
|
334
|
+
|
|
335
|
+
When your webhook returns `{ "voice": true }` and TTS is configured, ChannelKit synthesizes audio and plays it to the caller via `<Play>`. Audio clips are cached in memory and served via a one-time URL that expires after 60 seconds.
|
|
336
|
+
|
|
337
|
+
## Gmail Channel Setup
|
|
338
|
+
|
|
339
|
+
Setting up a Gmail channel requires creating OAuth2 credentials in Google Cloud Console. Here's a step-by-step guide:
|
|
340
|
+
|
|
341
|
+
### 1. Create a Google Cloud project
|
|
342
|
+
|
|
343
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
344
|
+
2. Click the project dropdown at the top and select **New Project**
|
|
345
|
+
3. Enter a name (e.g. "ChannelKit") and click **Create**
|
|
346
|
+
|
|
347
|
+
### 2. Enable the Gmail API
|
|
348
|
+
|
|
349
|
+
1. In your project, go to **APIs & Services → Library**
|
|
350
|
+
2. Search for **Gmail API**
|
|
351
|
+
3. Click **Gmail API** and then **Enable**
|
|
352
|
+
|
|
353
|
+
### 3. Configure the OAuth consent screen
|
|
354
|
+
|
|
355
|
+
1. Go to **APIs & Services → OAuth consent screen**
|
|
356
|
+
2. Select **External** user type (or **Internal** if using Google Workspace) and click **Create**
|
|
357
|
+
3. Fill in the required fields:
|
|
358
|
+
- **App name**: e.g. "ChannelKit"
|
|
359
|
+
- **User support email**: your email
|
|
360
|
+
- **Developer contact email**: your email
|
|
361
|
+
4. Click **Save and Continue**
|
|
362
|
+
5. On the **Scopes** page, click **Add or Remove Scopes**
|
|
363
|
+
6. Search for `https://www.googleapis.com/auth/gmail.modify` and check it
|
|
364
|
+
7. Click **Update** → **Save and Continue**
|
|
365
|
+
8. On the **Test users** page, click **Add Users** and add the Gmail address you want to connect
|
|
366
|
+
9. Click **Save and Continue** → **Back to Dashboard**
|
|
367
|
+
|
|
368
|
+
> **Note:** While your app is in "Testing" status, only the test users you added can authorize. This is fine for personal use. To remove the test user limitation, you'd need to publish the app and go through Google's verification process.
|
|
369
|
+
|
|
370
|
+
### 4. Create OAuth2 credentials
|
|
371
|
+
|
|
372
|
+
1. Go to **APIs & Services → Credentials**
|
|
373
|
+
2. Click **Create Credentials → OAuth client ID**
|
|
374
|
+
3. Select **Desktop app** as the application type
|
|
375
|
+
4. Enter a name (e.g. "ChannelKit Desktop")
|
|
376
|
+
5. Click **Create**
|
|
377
|
+
6. Copy the **Client ID** and **Client Secret**
|
|
378
|
+
|
|
379
|
+
### 5. Configure the channel
|
|
380
|
+
|
|
381
|
+
Add the Gmail channel via CLI:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
channelkit channel add
|
|
385
|
+
# → Choose Email → Gmail
|
|
386
|
+
# → Paste your Client ID and Client Secret
|
|
387
|
+
# → Set poll interval (default 30 seconds)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Or add it directly to `config.yaml`:
|
|
391
|
+
|
|
392
|
+
```yaml
|
|
393
|
+
channels:
|
|
394
|
+
gmail:
|
|
395
|
+
type: email
|
|
396
|
+
provider: gmail
|
|
397
|
+
client_id: "123456789-abc.apps.googleusercontent.com"
|
|
398
|
+
client_secret: "GOCSPX-..."
|
|
399
|
+
poll_interval: 30
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 6. Authorize
|
|
403
|
+
|
|
404
|
+
When you start ChannelKit, it will automatically open your browser for OAuth authorization. Sign in with the Gmail account you added as a test user, grant access, and the token is saved locally in `~/.channelkit/auth/gmail-<channel-name>.json`.
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
channelkit start
|
|
408
|
+
# → Browser opens → sign in → authorize → done
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
The refresh token is saved automatically. You won't need to re-authorize unless you revoke access or delete the token file.
|
|
412
|
+
|
|
413
|
+
## Config
|
|
414
|
+
|
|
415
|
+
Default location: `~/.channelkit/config.yaml`
|
|
416
|
+
|
|
417
|
+
```yaml
|
|
418
|
+
channels:
|
|
419
|
+
whatsapp:
|
|
420
|
+
type: whatsapp
|
|
421
|
+
number: "+972..."
|
|
422
|
+
telegram:
|
|
423
|
+
type: telegram
|
|
424
|
+
bot_token: "123456:ABC-DEF..."
|
|
425
|
+
gmail:
|
|
426
|
+
type: email
|
|
427
|
+
provider: gmail
|
|
428
|
+
client_id: "..."
|
|
429
|
+
client_secret: "..."
|
|
430
|
+
poll_interval: 30
|
|
431
|
+
|
|
432
|
+
services:
|
|
433
|
+
expenses:
|
|
434
|
+
channel: whatsapp
|
|
435
|
+
webhook: "http://localhost:3000/expenses"
|
|
436
|
+
code: "EXPENSES" # magic code for WhatsApp multi-service
|
|
437
|
+
stt:
|
|
438
|
+
provider: google
|
|
439
|
+
language: he-IL
|
|
440
|
+
tts:
|
|
441
|
+
provider: elevenlabs
|
|
442
|
+
assistant:
|
|
443
|
+
channel: telegram
|
|
444
|
+
webhook: "http://localhost:3000/assistant"
|
|
445
|
+
command: "assistant" # slash command for Telegram multi-service
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Async Messaging API
|
|
449
|
+
|
|
450
|
+
Every webhook payload includes a `replyUrl` — a callback endpoint your service can use to send messages at any time.
|
|
451
|
+
|
|
452
|
+
### Webhook payload
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"id": "msg_abc123",
|
|
457
|
+
"channel": "whatsapp",
|
|
458
|
+
"from": "+44123456789",
|
|
459
|
+
"text": "What's my balance?",
|
|
460
|
+
"replyUrl": "http://localhost:4000/api/send/whatsapp/44123456789%40s.whatsapp.net"
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Sync response (immediate)
|
|
465
|
+
|
|
466
|
+
```json
|
|
467
|
+
{ "text": "Your balance is $42.00" }
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Async message (anytime)
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
curl -X POST "http://localhost:4000/api/send/whatsapp/44123456789%40s.whatsapp.net" \
|
|
474
|
+
-H "Content-Type: application/json" \
|
|
475
|
+
-d '{"text": "Your invoice was approved! ✅"}'
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Health check
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
GET http://localhost:4000/api/health
|
|
482
|
+
→ { "status": "ok", "channels": ["whatsapp"] }
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Web Dashboard
|
|
486
|
+
|
|
487
|
+
ChannelKit includes a built-in web dashboard (enabled by default) that shows:
|
|
488
|
+
|
|
489
|
+
- Real-time message log with WebSocket updates
|
|
490
|
+
- Message details: channel, sender, text, STT transcription, TTS usage
|
|
491
|
+
- Search and filter by channel
|
|
492
|
+
- Stats: total messages, messages by channel, average latency
|
|
493
|
+
|
|
494
|
+
All logs are stored in SQLite (`~/.channelkit/data/logs.db`) with automatic 30-day retention.
|
|
495
|
+
|
|
496
|
+
## MCP Server
|
|
497
|
+
|
|
498
|
+
ChannelKit includes a [Model Context Protocol](https://modelcontextprotocol.io/) server that lets AI assistants (Claude, etc.) manage your messaging gateway programmatically.
|
|
499
|
+
|
|
500
|
+
### Configuration
|
|
501
|
+
|
|
502
|
+
```yaml
|
|
503
|
+
mcp:
|
|
504
|
+
enabled: true
|
|
505
|
+
stdio: true # enable stdio transport (for Claude Desktop)
|
|
506
|
+
secret: "my-token" # optional Bearer token for auth
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Available tools
|
|
510
|
+
|
|
511
|
+
| Tool | Description |
|
|
512
|
+
| ---------------- | --------------------------------------------------------- |
|
|
513
|
+
| `send_message` | Send a message through any channel |
|
|
514
|
+
| `get_messages` | Retrieve message history with search/filtering |
|
|
515
|
+
| `list_channels` | View all channels and their status |
|
|
516
|
+
| `add_channel` | Add a new channel (WhatsApp, Telegram, Email, SMS, Voice) |
|
|
517
|
+
| `remove_channel` | Remove a channel |
|
|
518
|
+
| `list_services` | View all services |
|
|
519
|
+
| `add_service` | Create a service with STT/TTS/format config |
|
|
520
|
+
| `update_service` | Modify service settings |
|
|
521
|
+
| `remove_service` | Remove a service |
|
|
522
|
+
| `get_status` | Get uptime, stats, version info, update availability |
|
|
523
|
+
| `update` | Update ChannelKit to the latest version |
|
|
524
|
+
| `set_config` | Set config values (e.g., `settings.openai_api_key`) |
|
|
525
|
+
|
|
526
|
+
### Transports
|
|
527
|
+
|
|
528
|
+
- **Streamable HTTP** — `http://localhost:4000/mcp` (modern clients)
|
|
529
|
+
- **SSE** — `http://localhost:4000/sse` + `/messages` (legacy clients)
|
|
530
|
+
- **Stdio** — for Claude Desktop and local integrations
|
|
531
|
+
|
|
532
|
+
### Connecting from Claude Desktop
|
|
533
|
+
|
|
534
|
+
Add to your Claude Desktop config (`claude_desktop_config.json`):
|
|
535
|
+
|
|
536
|
+
```json
|
|
537
|
+
{
|
|
538
|
+
"mcpServers": {
|
|
539
|
+
"channelkit": {
|
|
540
|
+
"command": "channelkit",
|
|
541
|
+
"args": ["start", "--mcp-stdio"]
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Or connect to a running instance via HTTP at `http://localhost:4000/mcp`.
|
|
548
|
+
|
|
549
|
+
## Claude Code Skill
|
|
550
|
+
|
|
551
|
+
ChannelKit ships with a skill file that teaches Claude Code how to set up channels, create services, and integrate messaging into your app using the MCP tools.
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
channelkit install-skill # copies skill to ~/.claude/skills/channelkit/SKILL.md
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
The installer will also print a short snippet to add to your `~/.claude/CLAUDE.md`. This ensures Claude Code always knows about ChannelKit's built-in features (TTS/STT, MCP tools, etc.) without relying on the skill trigger to fire. Add it to your `~/.claude/CLAUDE.md` (create the file if it doesn't exist):
|
|
558
|
+
|
|
559
|
+
```markdown
|
|
560
|
+
## ChannelKit
|
|
561
|
+
|
|
562
|
+
When working with WhatsApp, SMS, Voice, Telegram, or Email messaging:
|
|
563
|
+
- **ChannelKit has built-in TTS/STT.** Never install TTS or STT packages in the app. Configure TTS on the ChannelKit service — the app just returns `{ "text": "..." }` and ChannelKit converts it to audio automatically.
|
|
564
|
+
- **Use ChannelKit MCP tools** (get_status, send_message, add_channel, etc.) — not curl. If MCP tools are not available, ask the user to connect: `claude mcp add --transport http channelkit http://localhost:4000/mcp`
|
|
565
|
+
- **Do not install ChannelKit** unless the user explicitly asks — it's likely already running.
|
|
566
|
+
- Run `/channelkit` to load the full ChannelKit skill with detailed integration docs, MCP tool reference, and setup patterns.
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
If ChannelKit runs on a different machine (e.g., a server) than Claude Code (your dev laptop), use `--print` to output the skill and transfer it:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
# On the server
|
|
573
|
+
channelkit install-skill --print > channelkit-skill.md
|
|
574
|
+
|
|
575
|
+
# Copy to your dev machine
|
|
576
|
+
scp server:channelkit-skill.md ~/.claude/skills/channelkit/SKILL.md
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Webhook API
|
|
580
|
+
|
|
581
|
+
### Your app receives
|
|
582
|
+
|
|
583
|
+
```json
|
|
584
|
+
{
|
|
585
|
+
"id": "msg_abc123",
|
|
586
|
+
"channel": "whatsapp",
|
|
587
|
+
"from": "+44123456789",
|
|
588
|
+
"type": "text",
|
|
589
|
+
"text": "What's the temperature?",
|
|
590
|
+
"timestamp": 1708420200,
|
|
591
|
+
"replyUrl": "http://localhost:4000/api/send/whatsapp/..."
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
For voice messages with STT enabled, `type` is `"audio"` and `text` contains the transcription.
|
|
596
|
+
|
|
597
|
+
### Your app responds
|
|
598
|
+
|
|
599
|
+
```json
|
|
600
|
+
{ "text": "Kitchen: 23°C 🌡️" }
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Or with voice:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
{ "text": "Kitchen is 23 degrees", "voice": true }
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Echo Server
|
|
610
|
+
|
|
611
|
+
A test server is included for quick experimentation:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
channelkit demo
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Runs on port 3000 and echoes back any message it receives.
|
|
618
|
+
|
|
619
|
+
## Supported Channels
|
|
620
|
+
|
|
621
|
+
| Channel | Status | Multi-Service |
|
|
622
|
+
| ------------------ | ---------- | -------------------- |
|
|
623
|
+
| WhatsApp (Baileys) | ✅ Working | Magic codes + groups |
|
|
624
|
+
| Telegram (grammY) | ✅ Working | Slash commands |
|
|
625
|
+
| Email — Gmail | ✅ Working | — |
|
|
626
|
+
| Email — Resend | ✅ Working | — |
|
|
627
|
+
| SMS (Twilio) | ✅ Working | — |
|
|
628
|
+
| Voice (Twilio) | ✅ Working | — |
|
|
629
|
+
|
|
630
|
+
## Auto-Start on Reboot
|
|
631
|
+
|
|
632
|
+
To keep ChannelKit running after a system restart, set it up as a system service.
|
|
633
|
+
|
|
634
|
+
### macOS (launchd)
|
|
635
|
+
|
|
636
|
+
First, find the paths you'll need:
|
|
637
|
+
|
|
638
|
+
```bash
|
|
639
|
+
which channelkit # e.g. /Users/you/.nvm/versions/node/v22.0.0/bin/channelkit
|
|
640
|
+
which node # e.g. /Users/you/.nvm/versions/node/v22.0.0/bin/node
|
|
641
|
+
dirname $(which node) # e.g. /Users/you/.nvm/versions/node/v22.0.0/bin
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Then create a plist file at `~/Library/LaunchAgents/com.channelkit.plist`, replacing all three paths below:
|
|
645
|
+
|
|
646
|
+
```xml
|
|
647
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
648
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
649
|
+
<plist version="1.0">
|
|
650
|
+
<dict>
|
|
651
|
+
<key>Label</key>
|
|
652
|
+
<string>com.channelkit</string>
|
|
653
|
+
<key>EnvironmentVariables</key>
|
|
654
|
+
<dict>
|
|
655
|
+
<key>PATH</key>
|
|
656
|
+
<string>REPLACE_WITH_OUTPUT_OF_DIRNAME_WHICH_NODE:/usr/local/bin:/usr/bin:/bin</string>
|
|
657
|
+
</dict>
|
|
658
|
+
<key>ProgramArguments</key>
|
|
659
|
+
<array>
|
|
660
|
+
<string>REPLACE_WITH_OUTPUT_OF_WHICH_NODE</string>
|
|
661
|
+
<string>REPLACE_WITH_OUTPUT_OF_WHICH_CHANNELKIT</string>
|
|
662
|
+
<string>start</string>
|
|
663
|
+
</array>
|
|
664
|
+
<key>RunAtLoad</key>
|
|
665
|
+
<true/>
|
|
666
|
+
<key>KeepAlive</key>
|
|
667
|
+
<true/>
|
|
668
|
+
<key>StandardOutPath</key>
|
|
669
|
+
<string>/tmp/channelkit.log</string>
|
|
670
|
+
<key>StandardErrorPath</key>
|
|
671
|
+
<string>/tmp/channelkit.err</string>
|
|
672
|
+
</dict>
|
|
673
|
+
</plist>
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
> **Why these settings?** launchd doesn't load your shell profile (where `nvm`/`fnm`/Homebrew set up `PATH`), so both `node` and `npm` would be missing. The `EnvironmentVariables` block sets `PATH` so child processes (like auto-update) can find `npm`, and `ProgramArguments` calls `node` directly to avoid the `env: node: No such file or directory` error.
|
|
677
|
+
|
|
678
|
+
Load the service:
|
|
679
|
+
|
|
680
|
+
```bash
|
|
681
|
+
launchctl load ~/Library/LaunchAgents/com.channelkit.plist
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
To stop and remove:
|
|
685
|
+
|
|
686
|
+
```bash
|
|
687
|
+
launchctl unload ~/Library/LaunchAgents/com.channelkit.plist
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Linux (systemd)
|
|
691
|
+
|
|
692
|
+
Find the binary path with `which channelkit`, then create a service file at `/etc/systemd/system/channelkit.service`:
|
|
693
|
+
|
|
694
|
+
```ini
|
|
695
|
+
[Unit]
|
|
696
|
+
Description=ChannelKit Messaging Gateway
|
|
697
|
+
After=network.target
|
|
698
|
+
|
|
699
|
+
[Service]
|
|
700
|
+
Type=simple
|
|
701
|
+
User=your-username
|
|
702
|
+
ExecStart=REPLACE_WITH_OUTPUT_OF_WHICH_CHANNELKIT start
|
|
703
|
+
Restart=always
|
|
704
|
+
RestartSec=10
|
|
705
|
+
Environment=NODE_ENV=production
|
|
706
|
+
|
|
707
|
+
[Install]
|
|
708
|
+
WantedBy=multi-user.target
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Enable and start:
|
|
712
|
+
|
|
713
|
+
```bash
|
|
714
|
+
sudo systemctl daemon-reload
|
|
715
|
+
sudo systemctl enable channelkit # start on boot
|
|
716
|
+
sudo systemctl start channelkit # start now
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Check status and logs:
|
|
720
|
+
|
|
721
|
+
```bash
|
|
722
|
+
sudo systemctl status channelkit
|
|
723
|
+
journalctl -u channelkit -f # follow logs
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## WhatsApp Setup
|
|
727
|
+
|
|
728
|
+
WhatsApp support requires the `@whiskeysockets/baileys` package, which is an **optional peer dependency** — it is not installed automatically with ChannelKit.
|
|
729
|
+
|
|
730
|
+
> **License notice:** `@whiskeysockets/baileys` depends on `libsignal-node`, which is licensed under **GPL-3.0**. By installing it, you accept the GPL-3.0 terms for that dependency. The ChannelKit core remains MIT-licensed.
|
|
731
|
+
|
|
732
|
+
### Install Baileys
|
|
733
|
+
|
|
734
|
+
If ChannelKit is installed globally:
|
|
735
|
+
|
|
736
|
+
```bash
|
|
737
|
+
npm install -g @whiskeysockets/baileys
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
If running from a project directory:
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
npm install @whiskeysockets/baileys
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
Without this package, ChannelKit will skip any WhatsApp channels in your config and print a warning. All other channels (Telegram, Email, SMS, Voice, Endpoint) work without it.
|
|
747
|
+
|
|
748
|
+
### Pair your device
|
|
749
|
+
|
|
750
|
+
Start ChannelKit and scan the QR code with WhatsApp:
|
|
751
|
+
|
|
752
|
+
```bash
|
|
753
|
+
channelkit start
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Open WhatsApp on your phone > Settings > Linked Devices > Link a Device, then scan the QR code shown in the terminal.
|
|
757
|
+
|
|
758
|
+
### Provision a new number
|
|
759
|
+
|
|
760
|
+
To buy a Twilio number and automatically pair it with WhatsApp:
|
|
761
|
+
|
|
762
|
+
```bash
|
|
763
|
+
channelkit channel provision
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
## Development
|
|
767
|
+
|
|
768
|
+
```bash
|
|
769
|
+
git clone https://github.com/dirbalak/channelkit.git
|
|
770
|
+
cd channelkit
|
|
771
|
+
npm install
|
|
772
|
+
npm run dev # starts with auto-reload on code changes
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
## Security
|
|
776
|
+
|
|
777
|
+
ChannelKit is designed to run on a dedicated server. Follow these guidelines to secure your deployment:
|
|
778
|
+
|
|
779
|
+
### Required for production
|
|
780
|
+
|
|
781
|
+
- **Set `api_secret`** in your config — this protects all dashboard and API endpoints with Bearer token authentication. It is auto-generated on first run. Without it, anyone with network access can control your instance.
|
|
782
|
+
- **Run behind a reverse proxy** (nginx, Caddy, Traefik) with TLS termination. ChannelKit itself serves HTTP; the proxy handles HTTPS.
|
|
783
|
+
- **Use firewall rules** to restrict port 4000 to localhost if using a reverse proxy, or to trusted IPs only.
|
|
784
|
+
|
|
785
|
+
### Credentials
|
|
786
|
+
|
|
787
|
+
- **Never commit your config** to version control. Config is stored in `~/.channelkit/` by default, outside your project directory.
|
|
788
|
+
- **Set a strong MCP secret** in Settings if you expose the MCP server externally.
|
|
789
|
+
- **Webhook signature verification** is enabled automatically for Twilio and Resend channels when `auth_token` / `webhook_secret` are configured.
|
|
790
|
+
|
|
791
|
+
### What's protected
|
|
792
|
+
|
|
793
|
+
| Feature | Protection |
|
|
794
|
+
| ----------------------------- | ----------------------------------------- |
|
|
795
|
+
| Dashboard & admin APIs | `api_secret` Bearer token |
|
|
796
|
+
| WebSocket (real-time updates) | Token validated on connection |
|
|
797
|
+
| `/api/send` endpoint | `api_secret` Bearer token |
|
|
798
|
+
| MCP server | `mcp.secret` Bearer token (external only) |
|
|
799
|
+
| Inbound webhooks (Twilio) | Request signature verification |
|
|
800
|
+
| Inbound webhooks (Resend) | Svix signature verification |
|
|
801
|
+
| Endpoint channels | Optional `X-Channel-Secret` header |
|
|
802
|
+
|
|
803
|
+
### Additional hardening
|
|
804
|
+
|
|
805
|
+
- Rate limiting is applied to all endpoints (60/min for send, 120/min for inbound, 300/min for dashboard)
|
|
806
|
+
- Security headers (CSP, X-Frame-Options, etc.) are set via `helmet`
|
|
807
|
+
- Webhook URLs are validated against private IP ranges (SSRF protection) — see [Local webhooks](#local-webhooks) to allow localhost/private IPs
|
|
808
|
+
- Channel/service names are restricted to alphanumeric characters, hyphens, and underscores
|
|
809
|
+
- Sensitive fields (API keys, tokens) are masked in API responses
|
|
810
|
+
- Server log broadcasts redact common API key patterns
|
|
811
|
+
|
|
812
|
+
### Local webhooks
|
|
813
|
+
|
|
814
|
+
By default, ChannelKit blocks webhook requests to `localhost`, `127.0.0.1`, and private IP ranges (`10.x`, `172.16-31.x`, `192.168.x`) as SSRF protection. Cloud metadata endpoints (`169.254.169.254`) are **always** blocked regardless of this setting.
|
|
815
|
+
|
|
816
|
+
If your webhook server runs locally or on a private network, add this to your `config.yaml`:
|
|
817
|
+
|
|
818
|
+
```yaml
|
|
819
|
+
settings:
|
|
820
|
+
allow_local_webhooks: true
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
This is common during development or when ChannelKit and your app run on the same machine or local network.
|
|
824
|
+
|
|
825
|
+
## License
|
|
826
|
+
|
|
827
|
+
MIT — see [LICENSE](LICENSE).
|
|
828
|
+
|
|
829
|
+
WhatsApp integration requires `@whiskeysockets/baileys` (optional peer dependency, GPL-3.0 via `libsignal-node`). Installing it is opt-in and subject to its own license terms.
|