channelkit
v1.3.1
Published
A self-hosted messaging gateway that connects chat channels to any application via webhooks
Maintainers
Readme
ChannelKit
A self-hosted messaging gateway that connects chat channels (WhatsApp, Telegram, Email) to any application via webhooks.
Think nginx, but for chat.
📱 WhatsApp ──┐
💬 Telegram ──┤──→ ChannelKit ──→ Your App (webhook)
📧 Email ──┘ (unified JSON)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.
Features
- WhatsApp (Baileys, optional) — QR code linking, magic codes + auto-created groups for multi-service
- Telegram (grammY) — bot token setup, slash commands for multi-service
- Email — Gmail (OAuth2 + polling) and Resend (API + polling/webhook)
- Services model — single or multiple services per channel, each with its own webhook
- SMS (Twilio) — inbound/outbound SMS via polling or webhooks
- Voice (Twilio) — inbound voice calls with STT, webhook, and TTS/Say responses
- Speech-to-Text — automatic transcription of voice messages (Google, Whisper, Deepgram)
- Text-to-Speech — voice responses when your webhook returns
voice: true(Google, ElevenLabs, OpenAI) - Auto language detection — STT supports multiple languages with automatic detection
- AI formatting — transform incoming messages with AI (OpenAI, Anthropic, Google) before forwarding to your webhook
- MCP server — Model Context Protocol server lets AI assistants manage channels, services, and send messages
- Web dashboard — SQLite-backed logs with real-time WebSocket updates
- Async messaging API —
replyUrlin every webhook payload for sending messages anytime - Onboarding flow — magic codes (WhatsApp) and slash commands (Telegram) for user self-service
- Echo server — included test server for quick experimentation
Quick Start
npx channelkitThat's it — no install needed. ChannelKit will download and run.
Install options
| Method | Command | When to use |
|--------|---------|-------------|
| npx (no install) | npx channelkit | Try it out, quick start |
| Global install | npm install -g channelkit | Daily use, shorter commands |
With npx, prefix all commands with npx (e.g. npx channelkit demo).
With a global install, just use channelkit directly.
Permission error on global install? Use nvm to manage Node — it installs to your home directory, no sudo needed. Already have Node without nvm? See npm docs on fixing permissions.
On first run, ChannelKit will ask how you'd like to set up:
- 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.
- CLI wizard — step-by-step terminal setup: pick a channel, enter credentials, set a webhook URL, and start.
All configuration is stored in ~/.channelkit/ (config, auth sessions, logs).
Running
channelkit # start (opens dashboard automatically)
channelkit start --tunnel # start with a public URL (Cloudflare tunnel)
channelkit demo # run the built-in echo server for testing
channelkit daemon install # install as a system service (starts on boot)Using npx? Just prefix:
npx channelkit demo,npx channelkit daemon install, etc.
Public URL (Cloudflare Tunnel)
ChannelKit can expose your local instance to the internet using a Cloudflare Tunnel. This is required for features like inbound webhooks (Resend, Twilio) and voice calls, which need a publicly reachable URL.
Quick tunnel (no setup needed)
Just run:
channelkit start --tunnelThis creates a temporary public URL via trycloudflare.com — no Cloudflare account or installation required. The URL changes each time you restart.
Note: You can also start and stop the tunnel from the dashboard at any time.
Fixed URL (requires setup)
If you need a stable URL that doesn't change between restarts, follow the steps below to set up a named Cloudflare Tunnel.
1. Install cloudflared
# macOS
brew install cloudflared
# Linux (Debian/Ubuntu)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb
# Windows
winget install Cloudflare.cloudflared2. Authenticate with Cloudflare
cloudflared tunnel loginThis opens your browser to authorize cloudflared with your Cloudflare account. You need a domain managed by Cloudflare.
3. Create a tunnel
cloudflared tunnel create channelkitThis generates a tunnel ID and a credentials file. Note the tunnel ID — you'll need it next.
4. Route DNS to the tunnel
cloudflared tunnel route dns channelkit ck.yourdomain.comReplace ck.yourdomain.com with the subdomain you want to use. This creates a CNAME record pointing to the tunnel.
5. Configure ChannelKit
Add the tunnel section to your config.yaml:
tunnel:
provider: cloudflared
token: <your-tunnel-token> # from the credentials file or Cloudflare dashboard
public_url: https://ck.yourdomain.com
auto_start: true # start the tunnel automatically with ChannelKit
expose_dashboard: true # expose the dashboard through the tunnelTo get the tunnel token, you can either:
- Copy it from the Cloudflare Zero Trust dashboard under Networks → Tunnels → your tunnel → Configure
- Or use the credentials JSON file generated by
cloudflared tunnel create
6. Start ChannelKit with the tunnel
channelkit start --tunnelChannelKit 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.
Tip: If you set
auto_start: truein the tunnel config, you don't need the--tunnelflag — the tunnel starts automatically withchannelkit start.
Remote Data Store
Store all ChannelKit data (config, auth sessions, group mappings) on a remote server instead of locally. This lets you run ChannelKit on ephemeral infrastructure, share config across machines, or back up everything to your own database.
Quick Start
# Terminal 1: start the reference remote store server
channelkit remote-store
# Terminal 2: start ChannelKit in remote mode
channelkit start --remote http://localhost:4500On startup, ChannelKit fetches config and auth from the remote endpoint. Any changes you make (via dashboard, CLI, or MCP) are synced back automatically.
CLI Flags & Environment Variables
| Flag | Env Var | Description |
|------|---------|-------------|
| --remote <url> | CHANNELKIT_REMOTE | Remote store endpoint URL |
| --remote-auth <header> | CHANNELKIT_REMOTE_AUTH | Authorization header (e.g. Bearer mysecret) |
Example with env vars (useful for Docker / systemd):
CHANNELKIT_REMOTE=https://store.example.com CHANNELKIT_REMOTE_AUTH="Bearer mysecret" channelkit startRemote Store Server
The reference server (remote-store-server.js) stores data as flat files. Use it as-is for testing, or as a template for your own implementation backed by a database, S3, etc.
channelkit remote-store # default: port 4500, data in ./ck-remote-data
channelkit remote-store -p 8080 # custom port
channelkit remote-store --auth-token mysecret # require Bearer authAPI Contract
Implement these 6 endpoints to build your own remote store:
| Method | Endpoint | Content-Type | Description |
|--------|----------|-------------|-------------|
| GET | /config | text/plain | Fetch YAML config (return 404 if none) |
| PUT | /config | text/plain | Store YAML config |
| GET | /auth | application/zip | Fetch auth files as ZIP (return 404 if none) |
| PUT | /auth | application/zip | Store auth files as ZIP |
| GET | /groups | application/json | Fetch group mappings |
| PUT | /groups | application/json | Store group mappings |
What Gets Synced
| Data | Fetch | Push | |------|-------|------| | Config (channels, services, settings) | On startup | On every config save | | Auth (WhatsApp sessions, Gmail tokens) | On startup | Every 60 seconds | | Groups (user-to-service mappings) | On startup | On change (debounced) |
CLI Commands
channelkit # start (auto-runs init on first run)
channelkit init # interactive setup wizard
channelkit start [-c path] # start the gateway
channelkit channel add # add a new channel interactively
channelkit channel list # list configured channels
channelkit channel remove <name> # remove a channel
channelkit service add # add a new service interactively
channelkit service list # list configured services
channelkit service remove <name> # remove a service
channelkit install-skill # install Claude Code skill
channelkit remote-store # run the reference remote store server
channelkit demo # run the built-in echo/demo serverServices & Multi-Service
ChannelKit can route messages from a single channel to multiple backend services.
Add a service
channelkit service add
# → Service name: Expenses
# → Webhook: http://localhost:3000/expenses
# → Enable STT? Enable TTS?
# → ✅ Added!WhatsApp: Magic Codes + Groups
When a channel has multiple services, WhatsApp uses magic codes and auto-created groups:
- User sends "EXPENSES" to your WhatsApp number (or clicks a
wa.melink) - ChannelKit creates a group "Expenses - <User Name>"
- All messages in that group route to the service's webhook
Telegram: Slash Commands
For Telegram multi-service, each service gets a slash command:
- User sends
/expensesin the bot chat - Subsequent messages route to the Expenses service webhook
Speech-to-Text (STT)
Automatically transcribe voice messages before forwarding to your webhook. Configure per-service:
services:
myapp:
channel: whatsapp
webhook: http://localhost:3000
stt:
provider: google # google | whisper | deepgram
language: he-IL # primary language
alternative_languages: # auto-detect from these + primary
- en-US
- ar-ILAPI keys via environment variables:
- Google:
GOOGLE_STT_API_KEYorGOOGLE_API_KEY - Whisper (OpenAI):
OPENAI_STT_API_KEYorOPENAI_API_KEY - Deepgram:
DEEPGRAM_STT_API_KEYorDEEPGRAM_API_KEY
Text-to-Speech (TTS)
When your webhook returns { "text": "Hello", "voice": true }, ChannelKit synthesizes audio and sends a voice message. Configure per-service:
services:
myapp:
channel: whatsapp
webhook: http://localhost:3000
tts:
provider: elevenlabs # google | elevenlabs | openai
voice: 21m00Tcm4TlvDq8ikWAM # optional voice IDAPI keys via environment variables:
- Google:
GOOGLE_TTS_API_KEYorGOOGLE_API_KEY - ElevenLabs:
ELEVENLABS_TTS_API_KEYorELEVENLABS_API_KEY - OpenAI:
OPENAI_TTS_API_KEYorOPENAI_API_KEY
AI Formatting
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.
The processing pipeline is: STT (optional) → AI Format → Webhook
Configuration
services:
myapp:
channel: whatsapp
webhook: http://localhost:3000
format:
provider: openai # openai | anthropic | google
model: gpt-4o-mini # optional, each provider has a sensible default
prompt: "Extract the expense amount and category as JSON"Supported providers and defaults:
| Provider | Default Model | API Key |
| --------- | -------------------------- | ------------------- |
| OpenAI | gpt-4o-mini | OPENAI_API_KEY |
| Anthropic | claude-sonnet-4-20250514 | ANTHROPIC_API_KEY |
| Google | gemini-2.5-flash | GOOGLE_API_KEY |
API keys can be set as environment variables or in config.yaml:
settings:
openai_api_key: "sk-..."
anthropic_api_key: "sk-ant-..."
google_api_key: "..."Example
With this prompt:
Extract: name, amount, category. Return JSON only.A message like "Paid $45 for lunch with Sarah" becomes:
{ "name": "Sarah", "amount": 45, "category": "lunch" }Your webhook receives the formatted text. The original text is preserved in the dashboard logs.
Voice Channel (Twilio)
ChannelKit supports inbound voice calls via Twilio. The flow:
- Caller dials your Twilio number → ChannelKit answers with a greeting
- Caller speaks → recording is captured and transcribed (STT)
- Transcribed text is sent to your webhook
- Your webhook responds with text → ChannelKit speaks it back via TTS or
<Say> - In conversational mode, the loop repeats; otherwise the call ends
Setup
channelkit channel add # choose Voice (Twilio)
channelkit service add # configure webhook + voice settingsVoice requires a public URL — use --tunnel or --public-url when starting:
channelkit start --public-url https://your-domain.comVoice service config
services:
support:
channel: voice
webhook: "http://localhost:3000/support"
stt:
provider: google
language: en-US
tts:
provider: elevenlabs
voice:
greeting: "Hello! Please speak after the beep."
hold_message: "One moment please..."
language: en-US
voice_name: Polly.Joanna
conversational: true
max_record_seconds: 30TTS Audio Serving
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.
Gmail Channel Setup
Setting up a Gmail channel requires creating OAuth2 credentials in Google Cloud Console. Here's a step-by-step guide:
1. Create a Google Cloud project
- Go to Google Cloud Console
- Click the project dropdown at the top and select New Project
- Enter a name (e.g. "ChannelKit") and click Create
2. Enable the Gmail API
- In your project, go to APIs & Services → Library
- Search for Gmail API
- Click Gmail API and then Enable
3. Configure the OAuth consent screen
- Go to APIs & Services → OAuth consent screen
- Select External user type (or Internal if using Google Workspace) and click Create
- Fill in the required fields:
- App name: e.g. "ChannelKit"
- User support email: your email
- Developer contact email: your email
- Click Save and Continue
- On the Scopes page, click Add or Remove Scopes
- Search for
https://www.googleapis.com/auth/gmail.modifyand check it - Click Update → Save and Continue
- On the Test users page, click Add Users and add the Gmail address you want to connect
- Click Save and Continue → Back to Dashboard
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.
4. Create OAuth2 credentials
- Go to APIs & Services → Credentials
- Click Create Credentials → OAuth client ID
- Select Desktop app as the application type
- Enter a name (e.g. "ChannelKit Desktop")
- Click Create
- Copy the Client ID and Client Secret
5. Configure the channel
Add the Gmail channel via CLI:
channelkit channel add
# → Choose Email → Gmail
# → Paste your Client ID and Client Secret
# → Set poll interval (default 30 seconds)Or add it directly to config.yaml:
channels:
gmail:
type: email
provider: gmail
client_id: "123456789-abc.apps.googleusercontent.com"
client_secret: "GOCSPX-..."
poll_interval: 306. Authorize
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.
channelkit start
# → Browser opens → sign in → authorize → doneThe refresh token is saved automatically. You won't need to re-authorize unless you revoke access or delete the token file.
Config
Default location: ~/.channelkit/config.yaml
channels:
whatsapp:
type: whatsapp
number: "+972..."
telegram:
type: telegram
bot_token: "123456:ABC-DEF..."
gmail:
type: email
provider: gmail
client_id: "..."
client_secret: "..."
poll_interval: 30
services:
expenses:
channel: whatsapp
webhook: "http://localhost:3000/expenses"
code: "EXPENSES" # magic code for WhatsApp multi-service
stt:
provider: google
language: he-IL
tts:
provider: elevenlabs
assistant:
channel: telegram
webhook: "http://localhost:3000/assistant"
command: "assistant" # slash command for Telegram multi-serviceAsync Messaging API
Every webhook payload includes a replyUrl — a callback endpoint your service can use to send messages at any time.
Webhook payload
{
"id": "msg_abc123",
"channel": "whatsapp",
"from": "+44123456789",
"text": "What's my balance?",
"replyUrl": "http://localhost:4000/api/send/whatsapp/44123456789%40s.whatsapp.net"
}Sync response (immediate)
{ "text": "Your balance is $42.00" }Async message (anytime)
curl -X POST "http://localhost:4000/api/send/whatsapp/44123456789%40s.whatsapp.net" \
-H "Content-Type: application/json" \
-d '{"text": "Your invoice was approved! ✅"}'Health check
GET http://localhost:4000/api/health
→ { "status": "ok", "channels": ["whatsapp"] }Web Dashboard
ChannelKit includes a built-in web dashboard (enabled by default) that shows:
- Real-time message log with WebSocket updates
- Message details: channel, sender, text, STT transcription, TTS usage
- Search and filter by channel
- Stats: total messages, messages by channel, average latency
All logs are stored in SQLite (~/.channelkit/data/logs.db) with automatic 30-day retention.
MCP Server
ChannelKit includes a Model Context Protocol server that lets AI assistants (Claude, etc.) manage your messaging gateway programmatically.
Configuration
mcp:
enabled: true
stdio: true # enable stdio transport (for Claude Desktop)
secret: "my-token" # optional Bearer token for authAvailable tools
| Tool | Description |
| ---------------- | --------------------------------------------------------- |
| send_message | Send a message through any channel |
| get_messages | Retrieve message history with search/filtering |
| list_channels | View all channels and their status |
| add_channel | Add a new channel (WhatsApp, Telegram, Email, SMS, Voice) |
| remove_channel | Remove a channel |
| list_services | View all services |
| add_service | Create a service with STT/TTS/format config |
| update_service | Modify service settings |
| remove_service | Remove a service |
| get_status | Get uptime, stats, version info, update availability |
| update | Update ChannelKit to the latest version |
| set_config | Set config values (e.g., settings.openai_api_key) |
Transports
- Streamable HTTP —
http://localhost:4000/mcp(modern clients) - SSE —
http://localhost:4000/sse+/messages(legacy clients) - Stdio — for Claude Desktop and local integrations
Connecting from Claude Desktop
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"channelkit": {
"command": "channelkit",
"args": ["start", "--mcp-stdio"]
}
}
}Or connect to a running instance via HTTP at http://localhost:4000/mcp.
Claude Code Skill
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.
channelkit install-skill # copies skill to ~/.claude/skills/channelkit/SKILL.mdThe 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):
## ChannelKit
When working with WhatsApp, SMS, Voice, Telegram, or Email messaging:
- **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.
- **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`
- **Do not install ChannelKit** unless the user explicitly asks — it's likely already running.
- Run `/channelkit` to load the full ChannelKit skill with detailed integration docs, MCP tool reference, and setup patterns.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:
# On the server
channelkit install-skill --print > channelkit-skill.md
# Copy to your dev machine
scp server:channelkit-skill.md ~/.claude/skills/channelkit/SKILL.mdWebhook API
Your app receives
{
"id": "msg_abc123",
"channel": "whatsapp",
"from": "+44123456789",
"type": "text",
"text": "What's the temperature?",
"timestamp": 1708420200,
"replyUrl": "http://localhost:4000/api/send/whatsapp/..."
}For voice messages with STT enabled, type is "audio" and text contains the transcription.
Your app responds
{ "text": "Kitchen: 23°C 🌡️" }Or with voice:
{ "text": "Kitchen is 23 degrees", "voice": true }Echo Server
A test server is included for quick experimentation:
channelkit demoRuns on port 3000 and echoes back any message it receives.
Supported Channels
| Channel | Status | Multi-Service | | ------------------ | ---------- | -------------------- | | WhatsApp (Baileys) | ✅ Working | Magic codes + groups | | Telegram (grammY) | ✅ Working | Slash commands | | Email — Gmail | ✅ Working | — | | Email — Resend | ✅ Working | — | | SMS (Twilio) | ✅ Working | — | | Voice (Twilio) | ✅ Working | — |
Auto-Start on Reboot
ChannelKit can install itself as a system service that starts automatically on boot.
Quick setup
channelkit daemon installThat's it! ChannelKit will start on boot and restart if it crashes.
Tip: ChannelKit also asks about this on first run — just answer "y" when prompted.
Manage the service
channelkit daemon status # check if running
channelkit daemon stop # stop the service
channelkit daemon start # start the service
channelkit daemon uninstall # remove the serviceHow it works
- macOS — creates a LaunchAgent at
~/Library/LaunchAgents/com.channelkit.server.plistwith KeepAlive + RunAtLoad - Linux — creates a systemd user service at
~/.config/systemd/user/channelkit.servicewith Restart=on-failure
Logs:
- macOS:
~/.channelkit/channelkit.log - Linux:
journalctl --user -u channelkit -f
macOS (launchd)
Find paths:
which channelkit
which nodeCreate ~/Library/LaunchAgents/com.channelkit.server.plist with ProgramArguments pointing to node + channelkit, RunAtLoad + KeepAlive enabled, and PATH set to include your node binary directory.
Linux (systemd)
Create ~/.config/systemd/user/channelkit.service:
[Unit]
Description=ChannelKit Messaging Gateway
After=network.target
[Service]
Type=simple
ExecStart=/path/to/node /path/to/channelkit start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetsystemctl --user daemon-reload
systemctl --user enable channelkit
systemctl --user start channelkit
loginctl enable-lingerWhatsApp Setup
WhatsApp support requires the @whiskeysockets/baileys package, which is an optional peer dependency — it is not installed automatically with ChannelKit.
License notice:
@whiskeysockets/baileysdepends onlibsignal-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.
Install Baileys
If ChannelKit is installed globally:
npm install -g @whiskeysockets/baileysIf running from a project directory:
npm install @whiskeysockets/baileysWithout 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.
Pair your device
Start ChannelKit and scan the QR code with WhatsApp:
channelkit startOpen WhatsApp on your phone > Settings > Linked Devices > Link a Device, then scan the QR code shown in the terminal.
Provision a new number
To buy a Twilio number and automatically pair it with WhatsApp:
channelkit channel provisionDevelopment
git clone https://github.com/valery380/channelkit.git
cd channelkit
npm install
npm run dev # starts with auto-reload on code changesSecurity
ChannelKit is designed to run on a dedicated server. Follow these guidelines to secure your deployment:
Required for production
- Set
api_secretin 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. - Run behind a reverse proxy (nginx, Caddy, Traefik) with TLS termination. ChannelKit itself serves HTTP; the proxy handles HTTPS.
- Use firewall rules to restrict port 4000 to localhost if using a reverse proxy, or to trusted IPs only.
Credentials
- Never commit your config to version control. Config is stored in
~/.channelkit/by default, outside your project directory. - Set a strong MCP secret in Settings if you expose the MCP server externally.
- Webhook signature verification is enabled automatically for Twilio and Resend channels when
auth_token/webhook_secretare configured.
What's protected
| Feature | Protection |
| ----------------------------- | ----------------------------------------- |
| Dashboard & admin APIs | api_secret Bearer token |
| WebSocket (real-time updates) | Token validated on connection |
| /api/send endpoint | api_secret Bearer token |
| MCP server | mcp.secret Bearer token (external only) |
| Inbound webhooks (Twilio) | Request signature verification |
| Inbound webhooks (Resend) | Svix signature verification |
| Endpoint channels | Optional X-Channel-Secret header |
Additional hardening
- Rate limiting is applied to all endpoints (60/min for send, 120/min for inbound, 300/min for dashboard)
- Security headers (CSP, X-Frame-Options, etc.) are set via
helmet - Webhook URLs are validated against private IP ranges (SSRF protection) — see Local webhooks to allow localhost/private IPs
- Channel/service names are restricted to alphanumeric characters, hyphens, and underscores
- Sensitive fields (API keys, tokens) are masked in API responses
- Server log broadcasts redact common API key patterns
Local webhooks
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.
If your webhook server runs locally or on a private network, add this to your config.yaml:
settings:
allow_local_webhooks: trueThis is common during development or when ChannelKit and your app run on the same machine or local network.
License
MIT — see LICENSE.
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.
