Chronicle supports bidirectional replication between agent-side and sandbox-side instances.
Architecture
Agent Host Sandbox (E2B / Docker)
┌──────────────┐ ┌──────────────┐
│ Chronicle │ │ Chronicle │
│ (server) │◄─────────────────────►│ (client) │
│ │ Unix socket or │ │
│ Master DB │ WebSocket │ Local DB │
│ CAS Store │ │ Local CAS │
└──────────────┘ └──────────────┘
Roles
Agent Mode (Server)
The agent-side Chronicle is the master. It:
- Listens for sandbox connections on a Unix socket or WebSocket
- Pushes file notifications to connected sandboxes
- Accepts write-back requests from sandboxes
- Serves content via CAS for large files
- Runs with
--replicationflag
Sandbox Mode (Client)
The sandbox-side Chronicle is a replica. It:
- Connects to the agent's replication socket
- Receives file change notifications
- Applies changes to its local filesystem
- Journals local writes and sends them back to the agent
- Runs with
--connector--ws-connectflag
Replication Flow
Agent → Sandbox (Push)
1. Agent writes file → Chronicle creates iteration
2. PushChannelManager detects new iteration
3. Notification sent to connected sandboxes:
- File path, size, permissions, content hash
- Inline content for small files (under 4KB)
4. Sandbox receives notification:
- Applies metadata immediately
- Fetches content via CAS if not inline
5. Sandbox sends ACK (up_to_sequence)
Sandbox → Agent (Write-back)
1. Sandbox writes file → Chronicle creates local iteration
2. WriteJournal records the write:
- filepath, content_hash, size, permissions
- master_rowid_seen (causal dependency)
3. WritebackLoop sends batch to agent:
- WriteBack message with entries
4. Agent reconciles:
- Checks causal dependency (no conflicts)
- Accepted entries → applied to master DB
- Rejected entries → reason sent back
5. Agent sends WriteBackResult
6. Sandbox marks entries as accepted/rejected
Conflict Resolution
Write-back uses causal dependency tracking:
- Each sandbox write records the last master iteration it has seen (
master_rowid_seen) - On reconciliation, if the master has advanced past that point for the same file, the write is rejected
- The sandbox can retry after catching up to the latest state
Catchup and Resync
If a sandbox detects a gap in sequence numbers (via heartbeat comparison):
1. Sandbox sends CatchupRequest { since_sequence } 2. Agent replays missed notifications from that sequence 3. If the gap is too large, agent sends FullResyncRequired 4. Sandbox performs a full reconciliation from scratch
Filtering
Replication supports gitignore-compatible pattern filtering:
let filter = DirectionalFilter::new();
filter.add_pattern(FilterPattern::new("*.pyc"));
filter.add_pattern(FilterPattern::new("node_modules/**"));
filter.add_pattern(FilterPattern::new(".git/**"));
Patterns are evaluated with lazy compilation and fast paths for common cases (exact match, prefix, suffix, contains).