Why Events?
DeployStack Satellites use a polling-based communication pattern where satellites make outbound HTTP requests to the backend. This is firewall-friendly and NAT-compatible, but creates a timing gap: Problem: Important satellite operations need immediate backend visibility- MCP client connects (user expects instant UI feedback)
- Tool executes (audit trail needs precise timestamps)
- Process crashes (alerts need immediate dispatch)
- Security events (compliance requires real-time logging)
- Events emitted immediately when actions occur
- Batched every 3 seconds for network efficiency
- Sent to backend via existing authentication
- Zero impact on satellite performance
Architecture Overview
Key Components
EventBus Service (src/services/event-bus.ts)
- In-memory queue for event collection
- 3-second batch window (configurable)
- Automatic transmission to backend
- Graceful error handling and retry
src/events/registry.ts)
- Type-safe event definitions
- Event data structures
- Compile-time validation
src/services/backend-client.ts)
sendEvents()method for batch transmission- Uses existing satellite authentication
- Handles partial success responses
Event Types
Naming Convention: All event data fields use snake_case (e.g.,
server_id, team_id, spawn_duration_ms) to match the backend API convention.MCP Server Lifecycle
mcp.server.started
Emitted when MCP server process successfully spawns and completes handshake.
Data Structure:
mcp.server.crashed
Emitted when MCP server process exits unexpectedly with non-zero code.
Data Structure:
mcp.server.restarted
Emitted after successful automatic restart following a crash.
Data Structure:
mcp.server.permanently_failed
Emitted when server exhausts all 3 restart attempts.
Data Structure:
mcp.server.dormant
Emitted when idle stdio process is terminated to save resources.
Data Structure:
- Track resource optimization (memory/CPU savings)
- Monitor process sleep patterns
- Alert on unexpected idle behavior
mcp.server.respawned
Emitted when dormant process is automatically respawned on API call.
Data Structure:
- Track transparent process respawning
- Measure respawn latency (1-3s typical)
- Monitor usage patterns after idle periods
Client Connections
mcp.client.connected
Emitted when MCP client establishes SSE connection to satellite.
Data Structure:
- Parses
User-Agentheader - Detects VS Code, Cursor, Claude Desktop
- Falls back to ‘unknown’ for unrecognized clients
mcp.client.disconnected
Emitted when SSE connection closes (client disconnect, timeout, or error).
Data Structure:
Tool Discovery
mcp.tools.discovered
Emitted after successful tool discovery from HTTP or stdio MCP server with complete tool metadata and token consumption.
Data Structure:
- Store tool metadata in backend database (
mcpToolMetadatatable) - Calculate hierarchical router token savings (traditional vs 2-meta-tool approach)
- Enable frontend tool catalog display with token consumption metrics
- Provide analytics on MCP server complexity and context window usage
- stdio servers: After handshake completion and tool caching
- HTTP/SSE servers: After startup tool discovery and caching
token-counter.ts utility to estimate tokens for each tool based on name, description, and input schema JSON.
mcp.tools.updated
Emitted when tool list changes during configuration refresh.
Data Structure:
Configuration Management
config.refreshed
Emitted after successful configuration fetch from backend.
Data Structure:
config.error
Emitted when configuration fetch fails.
Data Structure:
Event Batching
Batch Window: 3 Seconds
Events are collected in memory for 3 seconds, then sent as a single batch:- Reduces HTTP request overhead
- Efficient network usage
- Near real-time (3s latency acceptable)
- Backend-friendly batching
Max Batch Size: 100 Events
If more than 100 events accumulate, only first 100 are sent:Empty Batch Handling
If no events occur, no HTTP request is made:Memory Management
Queue Limit: 10,000 Events
The in-memory queue holds a maximum of 10,000 events: Normal Operation:- Events queued and sent every 3 seconds
- Queue size typically under 100 events
- Events accumulate in queue
- Queue grows up to 10,000 events
- Oldest events dropped when limit reached
- Dropped count logged for monitoring
- Average event: 1-2KB
- 10,000 events ≈ 10-20MB RAM
- Acceptable footprint for satellite process
Queue is in-memory only. Satellite restarts clear the queue. This is acceptable because events are operational telemetry, not critical data requiring persistence.
Error Handling
Backend Response Codes
400 Bad Request (Invalid event data)- Drops the invalid event immediately
- Logs error with event details
- Continues processing other events
- No retry for malformed data
- Keeps events in queue
- Logs authentication error
- Retries in next batch cycle
- May indicate satellite needs re-registration
- Implements exponential backoff
- Backoff sequence: 3s → 6s → 12s → 24s → 48s (max)
- Keeps all events in queue
- Resumes normal 3s batching after successful send
- Keeps events in queue
- Logs backend error
- Retries in next 3s batch cycle
- Continues normal operations
- Keeps events in queue
- Logs connection failure
- Retries in next 3s batch cycle
- Satellite continues operating normally
Retry Strategy
Natural Retry Pattern:- Failed batches remain in queue
- Next 3-second cycle automatically includes them
- No explicit retry logic needed
- Only applies to 429 rate limit responses
- Temporary increase in batch interval
- Returns to 3s after successful send
- Only drops events for 400 validation errors
- Never drops events for temporary failures
- Queue overflow drops oldest events (logged)
Graceful Shutdown
When satellite receives SIGTERM or SIGINT:Integration Points
ProcessManager
Location:src/process/manager.ts
Events Emitted:
mcp.server.started- After spawn + handshakemcp.server.crashed- On unexpected exitmcp.server.restarted- After auto-restartmcp.server.permanently_failed- After 3 failed restarts
SessionManager
Location:src/core/session-manager.ts
Events Emitted:
mcp.client.connected- On new SSE session creationmcp.client.disconnected- On session cleanup
RemoteToolDiscoveryManager
Location:src/services/remote-tool-discovery-manager.ts
Events Emitted:
mcp.tools.discovered- After initial tool discoverymcp.tools.updated- When tool list changes
DynamicConfigManager
Location:src/services/dynamic-config-manager.ts
Events Emitted:
config.refreshed- After successful config fetchconfig.error- On config fetch failure
Configuration
Environment Variables
Development vs Production
Development:Monitoring Events
Structured Logging
All event operations are logged with structured data:Log Searches
EventBus Statistics
Access runtime statistics programmatically:Type Safety
Compile-Time Validation
The event system uses TypeScript for complete type safety:Event Registry
Location:src/events/registry.ts
Best Practices
DO ✅
Wrap emit() calls in try-catch:DON’T ❌
Never block on event emission:Troubleshooting
Events Not Emitting
Symptom: Noevent_emitted logs in satellite logs
Diagnosis:
src/server.ts:
Events Not Reaching Backend
Symptom: Events emitted but not in backend database Check backend connectivity:High Queue Size
Symptom:event_queue_overflow warnings in logs
Causes:
- Backend unreachable (network issues)
- Backend overloaded (429 responses)
- Very high event volume
Batch Send Failures
Symptom: Repeatedevent_batch_error logs
Check error details:
- Network timeout → Check network connectivity
- 401 Unauthorized → Verify satellite API key
- 500 Server Error → Check backend logs
- Connection refused → Verify backend running
Performance Considerations
Network Efficiency
Batch Size Impact:- 100 events/batch ≈ 100-200KB payload
- Single HTTP request vs 100 individual requests
- Reduced network overhead
- Backend-friendly batching
- 3s default: Near real-time with efficient batching
- 1s interval: More real-time, more requests
- 5s interval: Less real-time, fewer requests
Memory Usage
Queue Memory:- Average event: 1-2KB
- Max queue: 10,000 events
- Total memory: 10-20MB
- Acceptable for satellite process
- Normal: < 100 events
- Backend outage: Grows to 10,000
- Overflow: Oldest events dropped
CPU Impact
Event Emission:- Synchronous queue operation
- No I/O during emit()
- < 1ms overhead per event
- JSON serialization every 3 seconds
- Single HTTP POST request
- Minimal CPU impact
Future Enhancements
Disk-Based Queue (Planned)
Benefits:- Survive satellite restarts
- No event loss during crashes
- Longer backend outage tolerance
- Increased complexity
- Disk I/O overhead
- Not needed for operational telemetry
Event Sampling (Planned)
High-Volume Events:- Sample 10% of tool executions
- 100% sampling for errors
- Configurable sampling rates
- Reduced network traffic
- Lower backend load
- Maintained visibility into patterns
Real-Time Streaming (Future)
WebSocket Event Stream:- Real-time event delivery to frontend
- Sub-second latency
- Live operational dashboards
- WebSocket infrastructure
- Frontend event handling
- Connection management
Related Documentation
- Backend Communication - Satellite-backend communication patterns
- Polling - Command polling system
- Logging - Structured logging configuration
- Process Management - MCP server lifecycle

