Monorepo for Aesthetic.Computer aesthetic.computer

Session Server Architecture Plan#

Current State (As of 2025-10-28)#

Overview#

Aesthetic Computer has transitioned away from Jamsocket (paid service discontinued) to a self-hosted session server architecture. This document outlines the current setup and what needs to be documented/updated.

Current Production Architecture#

Session Server#

  • Production URL: https://session-server.aesthetic.computer
  • Purpose: Handles both WebSocket connections AND UDP connections
  • Location: Same server that previously only handled UDP (157.245.134.225)
  • Previous UDP-only URL: https://udp.aesthetic.computer (now deprecated/redundant)

Connection Flow#

  1. Client requests session info from Netlify function /session/{slug}
  2. Function returns URLs for WebSocket and UDP connections
  3. Both URLs now point to the same unified session server

Code Locations#

Frontend (disk.mjs)#

File: system/public/aesthetic.computer/lib/disk.mjs

Key Functions:

  • session(slug, forceProduction, service) (line ~3855)

    • Fetches session connection info from /session/ endpoint
    • Parses WebSocket and UDP URLs
    • Currently still references Jamsocket EventSource for backend status (NEEDS UPDATE)
  • startSocket() (line ~6320)

    • Establishes WebSocket connection via socket.connect()
    • Sets up UDP connection via send({ type: "udp:connect", ... })

Backend Session Function#

File: system/netlify/functions/session.js

Current Behavior:

  • Development mode (dev && !forceProd):
    • Returns http://localhost:8889 for both WebSocket and UDP
  • Production monolith mode (service === "monolith"):
    • Returns https://session-server.aesthetic.computer for WebSocket
    • Returns https://udp.aesthetic.computer for UDP (REDUNDANT NOW)
  • Jamsocket mode (old, still in code):
    • Attempts to spawn/check Jamsocket backends (NO LONGER FUNCTIONAL)

Issues to Fix#

1. ac-session Fish Function#

Problem: The Emacs detection in ac-session may be interfering with proper session server startup.

Current Code (.devcontainer/config.fish line ~1117):

if test -n "$INSIDE_EMACS"
    echo "📋 Starting session server (non-blocking mode for Emacs)..."
    # ... background startup logic
    return
end

Analysis:

  • The Emacs mode starts nodemon in background and immediately returns
  • This may prevent proper monitoring/interaction with the session server
  • The session server might not be getting proper stdio/logging visibility

Proposed Fix:

  • Remove or simplify Emacs special handling
  • Let nodemon run normally in all environments
  • Use standard terminal output for monitoring

2. Jamsocket References (Obsolete)#

Problem: Code still references Jamsocket API and EventSource streams that no longer work.

Locations:

  1. disk.mjs line ~3891: EventSource connection to api.jamsocket.com
  2. disk.mjs line ~3913-3927: State handling for Jamsocket backend states (Loading, Starting, Ready)
  3. session.js line ~52-130: Jamsocket backend spawning/checking logic

Proposed Fix:

  • Remove EventSource polling for backend status (session server is always ready in production)
  • Simplify session() function to just return URLs without state checking
  • Keep monolith mode as primary production path
  • Remove all Jamsocket API calls and unused Redis backend tracking code

3. Redundant UDP URL#

Problem: The UDP connection URL still points to https://udp.aesthetic.computer but the session server now handles UDP on the same host.

Current (session.js line ~23):

const udpUrl = `https://udp.aesthetic.computer`;

Proposed Fix:

  • In monolith mode, return the same base URL for both WebSocket and UDP
  • Update to: udp: "https://session-server.aesthetic.computer"
  • Or better: just return a single URL and have client use same host for both protocols

Proposed Changes#

Priority 1: Fix ac-session Fish Function#

function ac-session
    echo "🎮 Starting session server..."
    ac
    cd session-server
    
    echo "🔍 Cleaning up any stuck processes..."
    pkill -f "nodemon.*session.mjs" 2>/dev/null
    sleep 1
    npx kill-port 8889 2>/dev/null
    
    echo "🚀 Starting session server on port 8889..."
    PORT=8889 NODE_ENV=development npx nodemon -I --watch session.mjs session.mjs
end

Priority 2: Simplify session.js Netlify Function#

async function fun(event, context) {
  const forceProd = parseInt(event.queryStringParameters.forceProduction) === 1;

  if (dev && !forceProd) {
    // Local development
    let host = event.headers.host.split(":")[0];
    if (host === "local.aesthetic.computer") {
      return {
        statusCode: 200,
        body: JSON.stringify({ 
          url: `https://session.${host}`, 
          udp: `https://session.${host}`,
          state: "Ready"
        }),
        headers: { "Access-Control-Allow-Origin": "*" },
      };
    } else {
      return {
        statusCode: 200,
        body: JSON.stringify({ 
          url: `http://${host}:8889`, 
          udp: `http://${host}:8889`,
          state: "Ready"
        }),
        headers: { "Access-Control-Allow-Origin": "*" },
      };
    }
  } else {
    // Production - unified session server
    return {
      statusCode: 200,
      body: JSON.stringify({
        url: `https://session-server.aesthetic.computer`,
        udp: `https://session-server.aesthetic.computer`,
        state: "Ready"
      }),
      headers: { "Access-Control-Allow-Origin": "*" },
    };
  }
}

Priority 3: Simplify disk.mjs session() Function#

Remove EventSource logic and Jamsocket state polling since session server is always ready:

async function session(slug, forceProduction = false, service) {
  let endPoint = "/session/" + slug;
  const params = { service };
  if (forceProduction) params.forceProduction = 1;
  endPoint += "?" + new URLSearchParams(params);

  const req = await fetch(endPoint);
  
  if (req.status === 200 || req.status === 304) {
    const session = await req.json();
    return session; // Already includes url, udp, and state: "Ready"
  } else {
    const error = await req.text();
    console.error("Session fetch error:", error);
    return error;
  }
}

Testing Plan#

  1. Test ac-session locally:

    • Run ac-session in terminal
    • Verify nodemon starts properly
    • Check logs appear in terminal
    • Test hot reload by editing session.mjs
    • Try Ctrl+C to stop
  2. Test development WebSocket/UDP:

    • Start local session server with ac-session
    • Load aesthetic.computer locally
    • Verify WebSocket connects to localhost:8889
    • Verify UDP connects to localhost:8889
    • Test piece with socket() API
  3. Test production connection:

    • Deploy simplified session.js function
    • Load aesthetic.computer in production
    • Verify WebSocket connects to session-server.aesthetic.computer
    • Verify UDP connects to same host
    • Test multiplayer piece functionality

Migration Notes#

Breaking Changes: None - this is a cleanup/documentation update

Rollback Plan:

  • Keep old session.js code commented out
  • Can revert to Jamsocket mode if needed (though service is discontinued)

Post-Migration:

  • Remove all Jamsocket-related code
  • Remove Redis backend tracking code (unused)
  • Update documentation to reflect unified session server architecture

Open Questions#

  1. Should we unify the WebSocket and UDP URLs into a single connection string?
  2. Do we need per-slug session isolation in production? (Current code doesn't use slug parameter)
  3. Should session-server.aesthetic.computer handle multiple pieces on different paths?

Status: Draft - Ready for review and implementation Created: 2025-10-28 Last Updated: 2025-10-28