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:
wslibrary (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 identifierip: Client IP address (fromreq.socket.remoteAddress)ws: WebSocket instance with.isAliveping/pong trackingcodeChannel: 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 IDchannel.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
/wspath - 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-streampath - 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:
-
Server Info (top corner)
- Uptime
- Environment (dev/production)
- Last updated timestamp
-
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
-
UDP Connections (right column)
- Total count
- Channel IDs
- Connection states
- Active rooms/broadcasts
-
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 #
- Add tracking map for UDP channels (manual tracking since geckos.io doesn't expose)
- Create
getWebSocketStatus()function - Create
getUDPStatus()function - Create
getFullStatus()aggregator function - Test with
/statusJSON endpoint
Phase 2: HTML Dashboard #
- Create
generateStatusHTML()function - Add Fastify route for
/(root) - Style based on oven dashboard pattern (black background, monospace, minimalist)
- Static version first (no real-time updates)
- Test rendering with current connection data
Phase 3: Real-time Updates #
- Create separate WebSocketServer on
/status-streampath - Implement broadcast logic (every 2 seconds)
- Add WebSocket client code in HTML dashboard
- Update DOM on incoming status messages
- Add connection status indicator (๐ข connected / ๐ด disconnected)
Phase 4: Enhanced Features #
- Add tab counting (requires client-side tracking via heartbeat)
- Add current command/piece tracking (parse from worldClients)
- Add user authentication status display
- Add ghost/reconnection status visualization
- 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].handlehas "@username" for world participants- No direct "current piece" tracking (only world memberships)
Enhancement Options:
- Track last message type per connection
- Add "current piece" field to connection metadata
- Parse Redis pub/sub
slug:@usernamesubscriptions to track navigation - 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 #
- session-server/session.mjs
- Add root route handler
- Add
/statusJSON endpoint - Add status WebSocketServer
- Add UDP channel tracking
- Add data collection functions
Files to Create #
- session-server/status.html (optional, if extracted from inline)
- Dashboard HTML template
- Could be inlined in
session.mjslike oven example
Testing Strategy #
-
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
- Start session server with
-
Production Deployment
- Deploy to
session-server.aesthetic.computer - Visit
https://session-server.aesthetic.computer - Monitor real production connections
- Verify performance with multiple users
- Deploy to
-
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 #
- Review this plan
- Implement Phase 1 (data collection)
- Test
/statusendpoint with real connections - Proceed to Phase 2 (HTML dashboard)
- Deploy to production
References #
- Existing pattern:
oven/server.mjsdashboard - WebSocket tracking:
session.mjslines 254-730 - UDP server:
session.mjslines 751-800 - worldClients structure:
session.mjslines 509-577