Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Zulip Sync

cryo-zulip bridges a cryochamber project with a Zulip stream, enabling remote monitoring and two-way messaging. Stream messages become inbox messages for the agent; outbox messages from the agent are posted back to the stream.

Prerequisites

  • A Zulip server with a bot account
  • A zuliprc file with bot credentials (standard Zulip INI format with [api] section containing email, key, site)
  • A Zulip stream accessible by the bot
  • An initialized cryochamber project (cryo init)

Commands

cryo-zulip init --config ~/.zuliprc --stream my-stream  # Validate credentials, resolve stream, write zulip-sync.json
cryo-zulip init --config ~/.zuliprc --stream my-stream --topic mychannel  # Custom topic (default: "cryochamber")
cryo-zulip sync [--interval N]                           # Start background sync daemon (default from cryo.toml or 5s)
cryo-zulip unsync                                       # Stop the sync daemon
cryo-zulip pull                                         # One-shot: pull new messages → inbox
cryo-zulip push                                         # One-shot: push latest session log → stream
cryo-zulip status                                       # Show sync configuration

How Sync Works

cryo-zulip sync spawns a background daemon (just like cryo start does). It does two things in a loop:

Stream → Inbox (pull direction): Polls the Zulip stream for new messages every --interval seconds (default: zulip_poll_interval in cryo.toml, or 5s). New messages are written to messages/inbox/ where the cryo daemon picks them up on the next session. The bot’s own messages are filtered out to prevent echo loops.

Outbox → Stream (push direction): Watches messages/outbox/ for new files. When the agent sends a message (via cryo-agent send), the sync daemon posts it to the Zulip stream and archives the file to messages/outbox/archive/.

Zulip Stream                      Local filesystem
────────────                      ─────────────────
New message        ──(pull)──→    messages/inbox/       → agent reads on wake
                   ←─(push)──    messages/outbox/      ← agent writes via cryo-agent send

The sync is managed as a system service (launchd on macOS, systemd on Linux) that survives reboots. Logs go to cryo-zulip-sync.log.

1. Initialize the project

cryo init --agent claude
# edit plan.md with your task
cryo-zulip init --config ~/.zuliprc --stream my-stream

This validates the bot credentials, resolves the stream ID, and writes zulip-sync.json. The zuliprc is copied to .cryo/zuliprc for use by the sync daemon.

3. Start the daemon and sync

cryo start
cryo-zulip sync

Both run as background daemons. Monitor with cryo watch.

4. Send messages from Zulip

Post a message in the Zulip stream from the web UI or mobile app. The sync daemon picks it up within 30 seconds and writes it to messages/inbox/. The cryo daemon wakes the agent on the next session (or immediately if watch_inbox = true).

5. Read agent replies on Zulip

When the agent calls cryo-agent send "message", the outbox file is detected immediately by the sync watcher and posted to the Zulip stream.

6. Stop

cryo-zulip unsync   # stop sync daemon
cryo cancel         # stop cryo daemon

One-Shot Usage

For manual or scripted use without the sync daemon:

cryo-zulip pull    # fetch new stream messages into inbox
cryo-zulip push    # post the latest session log to the stream

Example: Chess by Mail over Zulip

Play correspondence chess against an AI agent, sending moves from the Zulip web UI:

cd examples/chess-by-mail
cryo-zulip init --config ~/.zuliprc --stream chess-game
cryo init && cryo start
cryo-zulip sync --interval 30
# Send your moves as messages in the Zulip stream!

See Chess by Mail for the full example.

Files

FilePurpose
zulip-sync.jsonSync state: site, stream, stream ID, bot email, cursor
.cryo/zuliprcBot credentials (copied from user’s zuliprc on init)
cryo-zulip-sync.logSync daemon log output
messages/inbox/Incoming messages (from Zulip stream)
messages/outbox/Outgoing messages (posted to Zulip stream)
messages/outbox/archive/Posted outbox messages (archived after sync)