Communication Bridge (C4)
Central message hub - ALL communication with Claude goes through C4.
Architecture
Web Console ──┐
Telegram ───┼──► C4 Bridge ◄──► Claude
Lark ───┘
Components
| Script | Purpose | Reference |
|---|---|---|
c4-receive.js |
External → Claude (queue incoming messages) | c4-receive |
c4-send.js |
Claude → External (route outgoing messages) | c4-send |
c4-control.js |
System control plane (heartbeat, maintenance) | c4-control |
c4-dispatcher.js |
PM2 daemon: polls pending queue, delivers to tmux | — |
c4-session-init.js |
Hook (session start): context + Memory Sync trigger | hooks |
c4-fetch.js |
Fetch conversations by id range | c4-fetch |
c4-db.js |
Database module and CLI for querying conversations and checkpoints | c4-db |
c4-checkpoint.js |
Create/query checkpoints (sync boundaries) | c4-checkpoint |
Sending Messages
# Send to Telegram DM
cat <<'EOF' | node ~/zylos/.claude/skills/comm-bridge/scripts/c4-send.js telegram 8101553026
Hello! Quotes, $vars, **markdown** — all safe via stdin.
EOF
# Send to Lark group thread
cat <<'EOF' | node ~/zylos/.claude/skills/comm-bridge/scripts/c4-send.js lark "chat_xxx|type:group|root:msg_yyy"
Report ready.
EOF
Always pipe messages via stdin heredoc — never pass as CLI arguments. See c4-send for full reference. Treat the heredoc wrapper as fixed shell syntax: only the message body goes between the start line and the closing terminator line, and the terminator itself must never be copied into the actual outgoing message.
Database
SQLite at ~/zylos/comm-bridge/c4.db:
conversations: All messages (in/out) with priority, status, retry trackingcheckpoints: Recovery points with conversation id rangescontrol_queue: System control messages (heartbeat, maintenance) with priority, ack deadlines, and status lifecycle
Health & Status
The activity monitor writes ~/zylos/activity-monitor/agent-status.json which includes a health field:
| Value | Meaning |
|---|---|
ok |
System healthy, messages accepted normally |
recovering |
Legacy persisted liveness state; normalized to unavailable at runtime |
down |
Legacy persisted liveness state; normalized to unavailable at runtime |
Fail-open semantics: If the status file is missing or malformed, health is assumed ok — intake is never blocked by a read failure.
When health is not ok, c4-receive.js asks the activity monitor MessageRouter how to route the current message. Unhealthy messages are recorded as delivered and receive an immediate status reply when replies are enabled; --no-reply messages are accepted silently. If the MessageRouter IPC is unavailable, c4-receive.js falls back to the status file with the same delivered/current-message behavior.
Keystroke Delivery
The dispatcher supports [KEYSTROKE] control messages for sending raw keystrokes to the tmux session. This is an ops-level capability — no source gating is applied.
When a control message content starts with [KEYSTROKE], the dispatcher:
- Extracts the key name (e.g.,
Enter,Tab,Escape) - Sends it directly via
tmux send-keys(no buffer paste, no "Meanwhile" prefix, no verification) - Auto-acks the control immediately after delivery
Example: the permission auto-approve hook enqueues [KEYSTROKE]Enter at priority 0 with bypass-state to auto-confirm Claude Code's permission prompts.
Any process with access to c4-control.js can enqueue keystroke controls. This mirrors the existing reality that any process can call tmux send-keys directly — the C4 queue adds priority ordering and delivery guarantees, not access control.
Service Management
pm2 status c4-dispatcher
pm2 logs c4-dispatcher
pm2 restart c4-dispatcher