Documentation Index
Fetch the complete documentation index at: https://cloud-docs.mentra.glass/llms.txt
Use this file to discover all available pages before exploring further.
Overview
User sessions in MentraOS Cloud follow a well-defined lifecycle from initial connection through active use, reconnection scenarios, and eventual cleanup. Understanding this lifecycle is crucial for proper resource management and user experience.
Lifecycle Stages
1. Initial Connection
WebSocket Upgrade
When glasses connect to /glasses-ws:
// In WebSocketService
if (url.pathname === '/glasses-ws') {
// Extract and verify JWT token
const coreToken = request.headers.authorization?.split(' ')[1];
const userData = jwt.verify(coreToken, AUGMENTOS_AUTH_JWT_SECRET);
const userId = userData.email;
// Attach userId to request
(request as any).userId = userId;
// Proceed with WebSocket upgrade
this.glassesWss.handleUpgrade(request, socket, head, ws => {
this.glassesWss.emit('connection', ws, request);
});
}
Authentication Validation
The JWT token must contain:
email: User’s email address (used as userId)
sub: Subject identifier
- Valid signature with
AUGMENTOS_AUTH_JWT_SECRET
2. Session Creation
New Session
When no existing session exists:
// In SessionService
const userSession = new UserSession(userId, ws);
// Initialize with installed apps
const installedApps = await appService.getAllApps(userId);
for (const app of installedApps) {
userSession.installedApps.set(app.packageName, app);
}
return { userSession, reconnection: false };
Reconnection
When user reconnects to existing session:
const existingSession = UserSession.getById(userId);
if (existingSession) {
// Update WebSocket and cancel cleanup
existingSession.updateWebSocket(ws);
existingSession.disconnectedAt = null;
if (existingSession.cleanupTimerId) {
clearTimeout(existingSession.cleanupTimerId);
existingSession.cleanupTimerId = undefined;
}
return { userSession: existingSession, reconnection: true };
}
3. Active Session State
Manager Initialization
Each UserSession initializes specialized managers:
constructor(userId: string, websocket: WebSocket) {
// Core identification
this.userId = userId;
this.websocket = websocket;
// Initialize managers
this.appManager = new AppManager(this);
this.audioManager = new AudioManager(this);
this.displayManager = new DisplayManager(this);
this.transcriptionManager = new TranscriptionManager(this);
// ... other managers
// Start heartbeat
this.setupGlassesHeartbeat();
// Register in storage
SessionStorage.getInstance().set(userId, this);
}
Heartbeat Monitoring
Maintains connection health:
private setupGlassesHeartbeat(): void {
const HEARTBEAT_INTERVAL = 10000; // 10 seconds
this.glassesHeartbeatInterval = setInterval(() => {
if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.ping();
} else {
this.clearGlassesHeartbeat();
}
}, HEARTBEAT_INTERVAL);
}
Connection Acknowledgment
Send initial connection success:
// In GlassesWebSocketService (websocket-glasses.service.ts:558-566)
const connectionAck = {
type: CloudToGlassesMessageType.CONNECTION_ACK,
sessionId: userSession.sessionId,
userSession: await sessionService.transformUserSessionForClient(userSession),
timestamp: new Date()
};
ws.send(JSON.stringify(connectionAck));
4. Disconnection Handling
Grace Period
When connection is lost:
// In GlassesWebSocketService handleDisconnect
userSession.disconnectedAt = new Date();
// Set cleanup timer (5 minutes default)
const CLEANUP_TIMEOUT = 5 * 60 * 1000;
userSession.cleanupTimerId = setTimeout(async () => {
logger.info(`Cleaning up session for user ${userId} after grace period`);
// Stop all running apps
for (const packageName of userSession.runningApps) {
await userSession.appManager.stopApp(packageName);
}
// Dispose session
await userSession.dispose();
}, CLEANUP_TIMEOUT);
Reconnection During Grace Period
If user reconnects within grace period:
- Cancel cleanup timer
- Update WebSocket connection
- Clear disconnection timestamp
- Resume normal operation
- No app restarts needed
5. Session Disposal
Trigger Conditions
Sessions are disposed when:
- Grace period expires after disconnection
- Server shutdown
- Manual cleanup request
- Fatal errors
Disposal Process
async dispose(): Promise<void> {
// Track session metrics
const duration = Date.now() - this.startTime.getTime();
await PosthogService.trackEvent("disconnected", this.userId, {
duration,
disconnectedAt: new Date().toISOString()
});
// Dispose all managers
if (this.appManager) this.appManager.dispose();
if (this.audioManager) this.audioManager.dispose();
if (this.microphoneManager) this.microphoneManager.dispose();
// ... dispose all managers
// Clear heartbeat
this.clearGlassesHeartbeat();
// Clear timers
if (this.cleanupTimerId) {
clearTimeout(this.cleanupTimerId);
}
// Remove from storage
SessionStorage.getInstance().delete(this.userId);
// Clean up subscriptions
subscriptionService.removeAllSubscriptionsForSession(this.userId);
}
6. Resource Cleanup
Manager Disposal
Each manager cleans up its resources:
// Example: AudioManager disposal
dispose(): void {
// Clear intervals
if (this.bufferCleanupInterval) {
clearInterval(this.bufferCleanupInterval);
}
// Clear buffers
this.bufferedAudio = [];
this.recentAudioBuffer = [];
// Remove event listeners
this.removeAllListeners();
}
Subscription Cleanup
Remove all app subscriptions:
// In SubscriptionService
removeAllSubscriptionsForSession(sessionId: string): void {
for (const key of this.subscriptions.keys()) {
if (key.startsWith(`${sessionId}:`)) {
this.subscriptions.delete(key);
}
}
// Clear caches
this.calendarEventsCache.delete(sessionId);
this.lastLocationCache.delete(sessionId);
}
Session State Tracking
Connection States
enum SessionState {
CONNECTING, // Initial connection
ACTIVE, // Normal operation
DISCONNECTED, // Connection lost, grace period
DISPOSING, // Being cleaned up
DISPOSED // Fully removed
}
Metrics Collection
Sessions track:
- Connection duration: Total time connected
- Reconnection count: Number of reconnections
- Active apps: Apps used during session
- Data processed: Audio, transcripts, etc.
Best Practices
- Always handle reconnections gracefully - Preserve state when possible
- Set appropriate grace periods - Balance resource usage vs user experience
- Clean up all resources - Prevent memory leaks
- Track metrics - Monitor session health and patterns
- Log lifecycle events - Aid debugging and monitoring
- Handle edge cases - Concurrent connections, rapid reconnects
Error Handling
Connection Errors
// Send error before closing
userSession.sendError(
"Authentication failed",
GlassesErrorCode.AUTHENTICATION_FAILED
);
// Clean close
ws.close(1008, "Authentication failed");
Disposal Errors
Always attempt full cleanup even if individual components fail:
try {
await manager.dispose();
} catch (error) {
logger.error(`Failed to dispose manager: ${error}`);
// Continue with other disposals
}