Monorepo for Aesthetic.Computer aesthetic.computer
core plans session-server-status-page.md
13 kB

Session Server Status Page Plan #

Overview #

Create an HTML dashboard at session-server.aesthetic.computer root path that displays real-time information about all connected clients, including both WebSocket and UDP connections.

Current Stack Analysis #

Server Infrastructure #

  • Framework: Fastify (with HTTPS in dev, HTTP in production)
  • WebSocket: ws library (WebSocketServer)
  • UDP: @geckos.io/server (WebRTC-based UDP)
  • Port: 8889 (dev) / PORT env var (production)
  • File: /workspaces/aesthetic-computer/session-server/session.mjs

Existing Connection Tracking #

WebSocket Connections #

Data Structures:

const connections = {};           // All active WebSocket connections by ID
const worldClients = {};          // Clients in virtual spaces (field, horizon, etc.)
const codeChannels = {};          // Clients subscribed to code channels
const users = {};                 // Map of connection IDs to user subs
let connectionId = 0;             // Auto-incrementing connection ID

Available Data Per Connection:

  • id: Unique connection identifier
  • ip: Client IP address (from req.socket.remoteAddress)
  • ws: WebSocket instance with .isAlive ping/pong tracking
  • codeChannel: Optional code subscription channel
  • User identity (if logged in): users[id] โ†’ user sub ID

worldClients Structure (for pieces like "field", "horizon"):

worldClients[piece][id] = {
  handle: "@username",
  showing: paintingSlug,  // Current painting/piece being shown
  ghost: boolean,         // Ghosted/disconnected state
  ghosted: boolean        // Alternative ghost flag
}

UDP Connections #

Framework: @geckos.io/server Data Structure: Managed by geckos.io library

  • io.onConnection((channel) => {...})
  • channel.id: Unique channel ID
  • channel.webrtcConnection.state: Connection state
  • Channels organized by rooms (broadcast groups)

Available Data:

  • Connection count via geckos internal state
  • Individual channel IDs
  • Connection state ("open", etc.)
  • No built-in IP tracking (WebRTC abstraction)

Comparison: Oven Server Pattern #

The oven/server.mjs provides a good reference implementation:

  • Real-time dashboard with WebSocket updates
  • Express route serving HTML at root (/)
  • WebSocket server on /ws path
  • Dashboard updates via subscribeToUpdates() pattern
  • Clean separation: HTTP endpoints + WebSocket broadcast

Proposed Architecture #

1. HTML Dashboard Route #

Add Fastify route for root path:

// session-server/session.mjs

fastify.get("/", async (request, reply) => {
  reply.type("text/html");
  return generateStatusHTML();
});

2. Status Data API Endpoint #

Create JSON endpoint for programmatic access:

fastify.get("/status", async (request, reply) => {
  return {
    timestamp: Date.now(),
    server: {
      uptime: process.uptime(),
      environment: dev ? "development" : "production",
      port: info.port,
    },
    websocket: {
      total: wss.clients.size,
      connections: getWebSocketStatus(),
    },
    udp: {
      total: io.connectionsCount || 0,
      channels: getUDPStatus(),
    },
  };
});

3. Real-time Updates via Existing WebSocket #

Option A: Extend existing WebSocket protocol

  • Add new message type: server:status
  • Broadcast status updates every N seconds
  • Clients subscribe via special message type

Option B: Separate status WebSocket path

  • New WebSocketServer on /status-stream path
  • Dedicated for dashboard updates only
  • Cleaner separation of concerns

Recommendation: Option B - cleaner and won't interfere with game/piece traffic

4. Data Collection Functions #

function getWebSocketStatus() {
  const connections = [];
  
  wss.clients.forEach((ws) => {
    // Find the connection ID for this ws
    const id = Object.keys(connections).find(key => connections[key] === ws);
    
    const connection = {
      id: id || 'unknown',
      alive: ws.isAlive,
      readyState: ws.readyState,
      user: users[id] || null,
      codeChannel: findCodeChannel(id),
      worlds: getWorldMemberships(id),
    };
    
    connections.push(connection);
  });
  
  return connections;
}

function getUDPStatus() {
  // geckos.io doesn't expose internal channel list easily
  // May need to track manually in io.onConnection
  const channels = [];
  
  // Option: Maintain separate tracking map
  // const udpChannels = {}; // Add at top level
  // io.onConnection((channel) => {
  //   udpChannels[channel.id] = { 
  //     id: channel.id, 
  //     connectedAt: Date.now() 
  //   };
  //   channel.onDisconnect(() => delete udpChannels[channel.id]);
  // });
  
  return channels;
}

function getWorldMemberships(connectionId) {
  const worlds = [];
  Object.keys(worldClients).forEach(piece => {
    if (worldClients[piece][connectionId]) {
      worlds.push({
        piece,
        handle: worldClients[piece][connectionId].handle,
        showing: worldClients[piece][connectionId].showing,
        ghost: worldClients[piece][connectionId].ghost || false,
      });
    }
  });
  return worlds;
}

function findCodeChannel(connectionId) {
  for (const [channel, subscribers] of Object.entries(codeChannels)) {
    if (subscribers.has(connectionId)) return channel;
  }
  return null;
}

5. HTML Dashboard Design #

Inspiration: Oven server dashboard style (minimalist monospace aesthetic)

Sections:

  1. Server Info (top corner)

    • Uptime
    • Environment (dev/production)
    • Last updated timestamp
  2. WebSocket Connections (left column)

    • Total count
    • Table/list of connections:
      • ID
      • User identity (if logged in)
      • World/piece location
      • Current piece/command
      • Connection state (alive/dead)
      • Code channel subscription
  3. UDP Connections (right column)

    • Total count
    • Channel IDs
    • Connection states
    • Active rooms/broadcasts
  4. Tab Tracking Enhancement (future)

    • Currently no direct tab tracking
    • Could add via client heartbeat messages
    • Track multiple tabs per user identity

Color Coding:

  • ๐ŸŸข Active connections (isAlive: true)
  • ๐Ÿ”ด Stale connections (isAlive: false, awaiting timeout)
  • ๐Ÿ‘ป Ghosted users (temporary disconnect, may rejoin)
  • ๐ŸŽจ Users showing paintings
  • ๐Ÿ’ป Code channel subscribers

6. WebSocket Status Stream #

// New WebSocketServer for status updates
const statusWSS = new WebSocketServer({ 
  server, 
  path: '/status-stream' 
});

const statusClients = new Set();

statusWSS.on('connection', (ws) => {
  console.log('๐Ÿ“Š Status dashboard connected');
  statusClients.add(ws);
  
  // Send initial state
  ws.send(JSON.stringify({
    type: 'status',
    data: getFullStatus(),
  }));
  
  ws.on('close', () => {
    statusClients.delete(ws);
  });
});

// Broadcast status updates every 2 seconds
setInterval(() => {
  const status = getFullStatus();
  statusClients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify({ type: 'status', data: status }));
    }
  });
}, 2000);

Implementation Plan #

Phase 1: Data Collection #

  1. Add tracking map for UDP channels (manual tracking since geckos.io doesn't expose)
  2. Create getWebSocketStatus() function
  3. Create getUDPStatus() function
  4. Create getFullStatus() aggregator function
  5. Test with /status JSON endpoint

Phase 2: HTML Dashboard #

  1. Create generateStatusHTML() function
  2. Add Fastify route for / (root)
  3. Style based on oven dashboard pattern (black background, monospace, minimalist)
  4. Static version first (no real-time updates)
  5. Test rendering with current connection data

Phase 3: Real-time Updates #

  1. Create separate WebSocketServer on /status-stream path
  2. Implement broadcast logic (every 2 seconds)
  3. Add WebSocket client code in HTML dashboard
  4. Update DOM on incoming status messages
  5. Add connection status indicator (๐ŸŸข connected / ๐Ÿ”ด disconnected)

Phase 4: Enhanced Features #

  1. Add tab counting (requires client-side tracking via heartbeat)
  2. Add current command/piece tracking (parse from worldClients)
  3. Add user authentication status display
  4. Add ghost/reconnection status visualization
  5. Add filtering/search for specific users or connection IDs

User Identity Tracking #

Current State:

  • users[id] maps connection ID โ†’ user sub (from login message)
  • worldClients[piece][id].handle has "@username" for world participants
  • No direct "current piece" tracking (only world memberships)

Enhancement Options:

  1. Track last message type per connection
  2. Add "current piece" field to connection metadata
  3. Parse Redis pub/sub slug:@username subscriptions to track navigation
  4. Client sends periodic "heartbeat" with current location

Technical Considerations #

Performance #

  • Status updates every 2s (configurable)
  • JSON payload size scales with connection count
  • Use efficient data structures (Maps/Sets)
  • Consider pagination if >1000 connections

Security #

  • Add authentication for status page (optional)
  • Filter sensitive data (IPs, user subs)
  • Rate limit status endpoint
  • CORS configuration for dashboard

Compatibility #

  • Works with existing session-server deployment
  • No breaking changes to current WebSocket protocol
  • Separate path for status stream (no interference)

Monitoring #

  • Log dashboard connections separately
  • Track status page access
  • Alert on abnormal connection patterns

Files to Modify #

  1. session-server/session.mjs
    • Add root route handler
    • Add /status JSON endpoint
    • Add status WebSocketServer
    • Add UDP channel tracking
    • Add data collection functions

Files to Create #

  1. session-server/status.html (optional, if extracted from inline)
    • Dashboard HTML template
    • Could be inlined in session.mjs like oven example

Testing Strategy #

  1. Local Development

    • Start session server with ac-session
    • Visit https://aesthetic.local:8889
    • Open multiple tabs/pieces
    • Verify connections appear in dashboard
    • Test WebSocket reconnection
    • Test UDP channel display
  2. Production Deployment

    • Deploy to session-server.aesthetic.computer
    • Visit https://session-server.aesthetic.computer
    • Monitor real production connections
    • Verify performance with multiple users
  3. Load Testing

    • Simulate 100+ connections
    • Check dashboard responsiveness
    • Monitor memory usage
    • Verify WebSocket broadcast performance

Example Dashboard Mockup #

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๐Ÿงฉ session-server                    ๐ŸŸข Connected  โฑ 3h 42m โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                               โ”‚
โ”‚  WebSocket Connections (47)          UDP Channels (12)       โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€    โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€       โ”‚
โ”‚  ๐ŸŸข #1234 @whistlegraph             ๐ŸŸข ch_a8f3d2            โ”‚
โ”‚     World: field                      State: open            โ”‚
โ”‚     Showing: pond                     Room: default          โ”‚
โ”‚     Code: wand                                                โ”‚
โ”‚                                      ๐ŸŸข ch_9b2e1f            โ”‚
โ”‚  ๐ŸŸข #1235 (guest)                     State: open            โ”‚
โ”‚     World: horizon                    Room: default          โ”‚
โ”‚     Code: prompt                                              โ”‚
โ”‚                                      ๐ŸŸข ch_4c7a3d            โ”‚
โ”‚  ๐Ÿ‘ป #1232 @ida                        State: connecting       โ”‚
โ”‚     World: field (ghost)              Room: tv                โ”‚
โ”‚     Last: drawing                                             โ”‚
โ”‚                                      ...                      โ”‚
โ”‚  ๐ŸŸข #1236 @jeffrey                                            โ”‚
โ”‚     Channel: code:wand                                        โ”‚
โ”‚     User: auth_xyz123                                         โ”‚
โ”‚                                                               โ”‚
โ”‚  ...                                                          โ”‚
โ”‚                                                               โ”‚
โ”‚  Last updated: 2025-11-10 14:32:18                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Next Steps #

  1. Review this plan
  2. Implement Phase 1 (data collection)
  3. Test /status endpoint with real connections
  4. Proceed to Phase 2 (HTML dashboard)
  5. Deploy to production

References #

  • Existing pattern: oven/server.mjs dashboard
  • WebSocket tracking: session.mjs lines 254-730
  • UDP server: session.mjs lines 751-800
  • worldClients structure: session.mjs lines 509-577