Overview
The@hyperscape/server package runs the authoritative game server:
- Fastify 5 HTTP API with rate limiting
- WebSocket real-time game state
- PostgreSQL database with Drizzle ORM
- LiveKit voice chat integration
- CDN manifest loading at startup
- Integrated frontend serving (production mode)
Package Location
Entry Point
src/index.ts initializes:
- Server configuration and environment loading
- Manifest fetching from CDN (production) or local cache (development)
- Database connection (PostgreSQL via Drizzle ORM)
- Fastify HTTP server with CORS, rate limiting, and static file serving
- WebSocket handlers for real-time multiplayer
- Game world and ECS systems
- Frontend serving (if client build exists in
public/)
Manifest Loading
The server fetches game manifests from CDN at startup:- Production/CI: Always fetches from CDN, caches locally
- Development: Skips fetch if local manifests exist
- Caching: Manifests cached in
world/assets/manifests/with 5-minute TTL
Server Systems
TradingSystem
Located insrc/systems/TradingSystem/:
Modular player-to-player trading system with:
- acceptance.ts: Accept/decline logic
- items.ts: Add/remove items from offers
- request.ts: Trade initiation with proximity checks
- swap.ts: Screen transitions (Screen 1 ↔ Screen 2)
- helpers.ts: Shared utilities and validation
- types.ts: Type definitions
- Two-screen confirmation flow (OSRS-accurate)
- Wealth transfer indicators
- Proximity validation (2-tile range)
- Interface blocking (prevents exploits)
- Audit logging for all trades
ServerNetwork Handlers
Located insrc/systems/ServerNetwork/handlers/:
API Routes
Core Endpoints
Database
The server supports both PostgreSQL (production via Neon) and SQLite (local development). Schema is defined with Drizzle ORM.Key Tables
Database Commands
Environment Variables
Core Configuration
Authentication
Assets & CDN
- Server fetches manifests from
PUBLIC_CDN_URL/manifests/at startup - Cached locally in
world/assets/manifests/ - In development, skips fetch if local manifests exist
- In production/CI, always fetches fresh manifests
Optional Services
CORS Origins
The server automatically allows requests from:CLIENT_URLPUBLIC_APP_URLELIZAOS_URL/ELIZAOS_API_URL
Build Scripts
Model Bounds Extraction
The server includes build-time scripts for automatic collision footprint detection:- Scans
world/assets/models/**/*.glbfiles - Parses glTF position accessor min/max values
- Calculates bounding boxes and dimensions
- Computes tile footprints at scale 1.0
- Writes manifest for runtime use
- Runs automatically before
buildanddevcommands - Cached based on GLB file changes
- Only rebuilds when models are added/modified
Footprints are calculated from model dimensions ×
modelScale from stations.json. A furnace with raw dimensions 1.51×1.45 and scale 1.5 becomes 2.27×2.18 meters → 2×2 tiles.Running
Development
Production
http://localhost:5555 by default.
Dependencies
Docker Services
The server can use Docker for CDN and PostgreSQL:Deployment
Railway (Production)
The server deploys to Railway using Nixpacks for automatic builds: Configuration Files:nixpacks.toml- Build configuration (Bun provider, build commands)railway.server.json- Service configuration (health checks, restart policy)Dockerfile.server- Multi-stage Docker build (alternative to Nixpacks)
- Push to
mainbranch triggers GitHub Actions - Workflow calls Railway GraphQL API to trigger deployment
- Railway builds using Nixpacks configuration
- Server starts with
cd packages/server && bun dist/index.js - Manifests fetched from Cloudflare R2 CDN at startup
/status endpoint every 30 seconds. Server must respond within 3 seconds or it will be restarted.
Manifest Fetching at Startup
In production, the server fetches manifests from the CDN instead of bundling them:- Root:
npcs.json,stores.json,world-areas.json, etc. - Items:
items/weapons.json,items/tools.json, etc. - Gathering:
gathering/woodcutting.json,gathering/mining.json, etc. - Recipes:
recipes/cooking.json,recipes/smithing.json, etc.
- Manifests cached locally in
packages/server/world/assets/manifests/ - Only re-fetched if content changes (HTTP ETag validation)
- 5-minute cache headers (
Cache-Control: public, max-age=300, must-revalidate)
This allows updating game content (items, NPCs, recipes) without redeploying the server—just upload new manifests to R2.