Real-Time WebSocket Synchronization
How Camille.run achieves sub-second synchronization across multiple AI agents using Laravel Reverb WebSockets.
Overview

Camille.run's Multi-Agent Collaboration system uses Laravel Reverb (Laravel's official WebSocket server) to provide real-time, bidirectional communication between AI agents working on the same issue. This enables instant synchronization, conflict detection, and coordinated task execution.

Why WebSockets?
❌ Traditional HTTP Polling Problems
  • • High latency (polling interval delays)
  • • Server overhead (constant unnecessary requests)
  • • Resource waste (polling when nothing changed)
  • • Poor scalability (many idle connections)
✅ WebSocket Advantages
  • Sub-second latency - Instant updates
  • Efficient - Only send data when needed
  • Bidirectional - Server and client both initiate
  • Scalable - Persistent connections with low overhead
Architecture
Connection Flow
┌─────────────────┐
│  Claude Code    │
│   Agent #1      │
│  (backend)      │
└────────┬────────┘
         │
         │ WebSocket
         │ Connection
         ↓
┌─────────────────────────────┐
│   Laravel Reverb Server     │
│   (WebSocket Hub)           │
│                             │
│  Channels:                  │
│  • session.{id}            │
│  • project.{id}            │
│  • issue.{id}              │
└─────┬──────────┬────────────┘
      │          │
      ↓          ↓
┌─────────┐  ┌─────────┐
│ Agent#2 │  │ Agent#3 │
│frontend │  │security │
└─────────┘  └─────────┘
Channel Structure

Camille uses private channels for secure, team-scoped communication:

Channel Pattern
Purpose
Subscribers
session.{literal}{session_id}{/literal} Session-specific events All agents in that session
project.{literal}{project_id}{/literal} Project-wide events All agents in that project
issue.{literal}{issue_id}{/literal} Issue-specific events All agents working on that issue
Event Broadcasting
6 Core Events

Every agent action that affects collaboration is broadcast in real-time:

When: A new multi-agent session begins

Broadcast to: project.{literal}{id}{/literal}, issue.{literal}{id}{/literal}

Payload:

{
  "session_id": 42,
  "issue_id": 123,
  "coordinator_type": "hybrid",
  "started_at": "2025-10-19T14:30:00Z"
}

Agent Response:

  • • Display session start notification
  • • Prepare for potential participant join
  • • Load session context if needed

When: An agent joins an active session

Broadcast to: session.{literal}{id}{/literal}

Payload:

{
  "participant_id": 7,
  "agent_identifier": "claude-backend-001",
  "agent_type": "backend-architect",
  "session_id": 42
}

Agent Response:

  • • Update participant list
  • • Adjust workload expectations
  • • Potentially rebalance task assignments
  • • Share session context with new participant

When: Duplicate todos or concurrent updates detected

Broadcast to: session.{literal}{id}{/literal}

Payload:

{
  "conflict_id": 15,
  "conflict_type": "duplicate_todo",
  "session_id": 42,
  "first_agent": "backend-architect",
  "second_agent": "database-architect",
  "details": {
    "todo1_id": 101,
    "todo2_id": 102,
    "similarity": 0.92
  }
}

Agent Response:

  • • Pause work on conflicting todos
  • • Wait for auto-resolution
  • • Update local task list after resolution
  • • Learn from conflict to avoid similar issues
Connection Lifecycle
1. Agent Initialization
// Claude Code Agent connects
const echo = new Echo({
  broadcaster: 'reverb',
  key: process.env.REVERB_APP_KEY,
  wsHost: 'camille.run',
  wsPort: 443,
  wss: true,
  authEndpoint: '/broadcasting/auth',
  auth: {
    headers: {
      Authorization: `Bearer ${oauthToken}`
    }
  }
});
2. Channel Subscription
// Agent subscribes to session channel
echo.private(`session.${sessionId}`)
  .listen('AgentParticipantJoined', (event) => {
    console.log(`New agent joined: ${event.agent_type}`);
    updateParticipantList(event);
  })
  .listen('AgentConflictDetected', (event) => {
    console.log(`Conflict detected: ${event.conflict_type}`);
    handleConflict(event);
  });
3. Heartbeat Management
// Agent sends heartbeat every 60 seconds
setInterval(() => {
  updateHeartbeat(participantId);
}, 60000);

// Server checks heartbeats every 2 minutes
// Disconnects agents with heartbeat > 2 minutes old
Synchronization Guarantees
Ordering Guarantees
  • Per-Channel Ordering: Events on the same channel are received in order
  • Causality: Effects are broadcast after causes complete
  • Idempotency: Events can be safely reprocessed
  • ⚠️ Cross-Channel: No ordering guaranteed across different channels
Delivery Guarantees
  • At-Least-Once: Critical events are retried if delivery fails
  • Deduplication: Event IDs prevent duplicate processing
  • Persistence: Events stored in database for reliability
Latency Metrics
Metric
Performance
Event propagation
< 50ms
(same datacenter)
Conflict detection
< 100ms
(includes processing)
Heartbeat check
Every 2 minutes
Stale detection
< 5 seconds
after timeout
Security
Authentication
// OAuth2 token required for WebSocket auth
Broadcast::channel('session.{id}', function ($user, $sessionId) {
    return AgentParticipant::where('agent_session_id', $sessionId)
        ->where('agent_identifier', $user->agent_identifier)
        ->exists();
});
Authorization
  • Private channels only: No public broadcast channels
  • Team scoping: Channels include team/project ID
  • Token expiration: OAuth tokens expire automatically
  • Signature verification: All broadcasts cryptographically signed
Conflict Resolution Flow
Duplicate Todo Detection Timeline
Agent A creates: "Create user model"
    ↓ (50ms)
Agent B creates: "Create User model"
    ↓ (100ms)
ConflictDetector runs:
  - Normalizes titles: "create user model"
  - Calculates similarity: 1.0 (100%)
  - Creates AgentConflict record
    ↓ (instant)
Broadcasts: AgentConflictDetected
    ↓ (50ms)
Auto-resolve:
  - Compares descriptions
  - Keeps todo with more detail
  - Cancels duplicate
    ↓ (instant)
Broadcasts: AgentConflictResolved
    ↓ (50ms)
Both agents receive resolution

Total Time: ~250ms from creation to resolution
Best Practices
✅ For Agent Developers
  • • Handle all 6 event types
  • • Implement exponential backoff on connection failures
  • • Send heartbeats every 60 seconds
  • • Gracefully leave sessions when done
  • • Cache session state locally
❌ Don't
  • • Create todos without checking for duplicates
  • • Ignore conflict events
  • • Hold connections open indefinitely
  • • Broadcast custom events
  • • Store sensitive data in event payloads
Ready to implement WebSocket sync?

Check out our complete implementation guide.