Durable Objects

2 Durable Object classes for real-time WebSocket communication. Each society gets its own DO instance. Stateful, single-threaded per instance.

1. GateSyncDO — Real-Time Guard Terminal Sync

File: src/durable_objects/GateSyncDO.ts (368 lines)

WebSocket hub connecting all guard terminals in a society. Handles real-time events for visitor entry/exit, ANPR readings, boom barrier signals, and overstay alerts.

WebSocket Protocol

EventDirectionPayload
visitor.entryGuard → Hub → All{ visitorId, name, flatNumber, gate, timestamp }
visitor.exitGuard → Hub → All{ visitorId, gate, duration, timestamp }
anpr.readingCamera → Hub → All{ plateNumber, cameraId, match: boolean, vehicle? }
boom.signalHub → Barrier{ barrierId, action: 'open'|'close' }
overstay.alertHub → All Guards{ visitorId, name, flatNumber, duration, threshold }
sos.triggerGuard → Hub → All{ message, location, severity }

State Management

// In-memory state per society DO instance:
- activeConnections: Map<WebSocket, { guardId, gate }>
- activeVisitors: Map<visitorId, { entryTime, flat, gate }>
- barrierStatus: Map<barrierId, { isOpen, lastSignal }>
- overstayThresholds: { default: 4h, delivery: 1h, guest: 24h }

2. EmergencySosDO — Emergency SOS Broadcast

File: src/durable_objects/EmergencySosDO.ts (377 lines)

Emergency SOS hub. When any resident or guard triggers an SOS, it broadcasts to all connected guards and administrators in real-time.

WebSocket Protocol

EventDirectionPayload
sos.triggeredTrigger → Hub → All{ sosId, type, triggeredBy, flat, location, timestamp }
sos.acknowledgedGuard → Hub → All{ sosId, acknowledgedBy, eta }
sos.resolvedGuard → Hub → All{ sosId, resolvedBy, resolutionNotes }
sos.false_alarmGuard → Hub → All{ sosId, reportedBy }
lockdown.broadcastAdmin → Hub → All{ reason, gates: ['entry', 'exit'] }
active.alarmsNew Client → HubReturns all currently active SOS alarms

State Management

// Per-society DO instance:
- activeAlarms: Map<sosId, { type, status, triggeredBy, timestamp }>
- connectedClients: Map<WebSocket, { role, userId }>
- lockdownState: { isLocked, gates, reason, timestamp }
- escalationTimers: Map<sosId, Timeout>  // auto-escalate after 5min

DO Usage Pattern

// From any route handler:
const gateHub = c.env.GATE_SYNC_HUB.get(
  c.env.GATE_SYNC_HUB.idFromName(societyId)
);
const stub = gateHub.get(societyId);
await stub.fetch(new Request('https://do/visitor.entry', {
  method: 'POST',
  body: JSON.stringify({ visitorId, flatNumber, gate })
}));