> ## Documentation Index
> Fetch the complete documentation index at: https://hyperscape-ai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# server

> Game server with Fastify, WebSockets, and database

## 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

```
packages/server/
├── src/
│   ├── database/       # Database schema and queries
│   │   ├── repositories/  # Data access layer
│   │   ├── migrations/    # Database migrations
│   │   └── schema.ts      # Drizzle schema definitions
│   ├── infrastructure/ # Docker, CDN configuration
│   ├── scripts/        # Utility scripts
│   ├── shared/         # Shared server utilities
│   ├── startup/        # Server initialization
│   │   └── routes/     # API route handlers
│   ├── systems/        # Server-specific systems
│   │   ├── ServerNetwork/  # Network handlers
│   │   │   └── handlers/   # Event handlers (bank, trade, friends, etc.)
│   │   ├── TradingSystem/  # Player trading system
│   │   ├── DatabaseSystem/ # Database persistence
│   │   └── ActivityLoggerSystem/  # Activity logging
│   ├── types/          # TypeScript types
│   ├── types.ts        # Core type definitions
│   └── index.ts        # Entry point
├── scripts/
│   ├── extract-model-bounds.ts  # Build-time model bounds extraction
│   └── inject-model-collision.ts # GLB collision data injection
├── world/
│   └── assets/         # Game assets (Git LFS)
│       ├── models/     # 3D models (GLB files)
│       └── manifests/  # Generated manifests
│           ├── model-bounds.json  # Auto-generated model bounds
│           └── stations.json      # Station configurations
├── docker-compose.yml  # Docker services
├── turbo.json          # Turbo build configuration
└── .env.example        # Environment template
```

## Entry Point

`src/index.ts` initializes:

1. Server configuration and environment loading
2. Manifest fetching from CDN (production) or local cache (development)
3. Database connection (PostgreSQL via Drizzle ORM)
4. Fastify HTTP server with CORS, rate limiting, and static file serving
5. WebSocket handlers for real-time multiplayer
6. Game world and ECS systems
7. Frontend serving (if client build exists in `public/`)

### Manifest Loading

The server fetches game manifests from CDN at startup:

```typescript theme={"theme":{"light":"github-light","dark":"css-variables"}}
// From src/startup/config.ts
const MANIFEST_FILES = [
  'biomes.json',
  'npcs.json',
  'items/weapons.json',
  'recipes/smithing.json',
  // ... 25+ manifest files
];

// Fetched from PUBLIC_CDN_URL/manifests/
await fetchManifestsFromCDN(cdnUrl, manifestsDir, nodeEnv);
```

**Behavior:**

* **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

This eliminates the need for Git LFS in production deployments.

## Server Systems

### TradingSystem

Located in `src/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

Features:

* 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 in `src/systems/ServerNetwork/handlers/`:

| Handler            | Purpose                                                        |
| ------------------ | -------------------------------------------------------------- |
| `bank/`            | Banking operations (deposit, withdraw, tabs, coins, equipment) |
| `trade/`           | Trading system handlers                                        |
| `friends.ts`       | Friend management and private messaging                        |
| `combat.ts`        | Combat actions and validation                                  |
| `inventory.ts`     | Inventory management                                           |
| `player.ts`        | Player state updates                                           |
| `chat.ts`          | Chat message handling                                          |
| `dialogue.ts`      | NPC dialogue                                                   |
| `quest.ts`         | Quest progression                                              |
| `resources.ts`     | Resource gathering                                             |
| `store.ts`         | Shop transactions                                              |
| `prayer.ts`        | Prayer system                                                  |
| `home-teleport.ts` | Home teleport functionality                                    |

## API Routes

### Core Endpoints

| Route              | Method  | Purpose                        |
| ------------------ | ------- | ------------------------------ |
| `/status`          | GET     | Health check (used by Railway) |
| `/health`          | GET     | Legacy health check            |
| `/ws`              | WS      | Game WebSocket connection      |
| `/api/agent/*`     | Various | ElizaOS agent API endpoints    |
| `/api/character/*` | Various | Character management           |
| `/api/player/*`    | Various | Player data queries            |
| `/api/template/*`  | Various | Character templates            |

## Database

The server supports both PostgreSQL (production via Neon) and SQLite (local development). Schema is defined with Drizzle ORM.

### Key Tables

```typescript theme={"theme":{"light":"github-light","dark":"css-variables"}}
// From packages/server/src/database/schema.ts
export const characters = pgTable("characters", {
  id: text("id").primaryKey(),
  accountId: text("accountId").notNull(),
  name: text("name").notNull(),
  // Combat stats
  combatLevel: integer("combatLevel").default(3),
  attackLevel: integer("attackLevel").default(1),
  strengthLevel: integer("strengthLevel").default(1),
  defenseLevel: integer("defenseLevel").default(1),
  constitutionLevel: integer("constitutionLevel").default(10),
  rangedLevel: integer("rangedLevel").default(1),
  // Gathering skills
  woodcuttingLevel: integer("woodcuttingLevel").default(1),
  fishingLevel: integer("fishingLevel").default(1),
  firemakingLevel: integer("firemakingLevel").default(1),
  cookingLevel: integer("cookingLevel").default(1),
  // XP for all skills...
  health: integer("health").default(100),
  coins: integer("coins").default(0),
  positionX: real("positionX").default(0),
  positionY: real("positionY").default(10),
  positionZ: real("positionZ").default(0),
});
```

| Table             | Purpose                                         |
| ----------------- | ----------------------------------------------- |
| `users`           | Account authentication with Privy/Farcaster IDs |
| `characters`      | Full character data with all skills and XP      |
| `inventory`       | Player items (28 slots with quantities)         |
| `equipment`       | Worn items by slot type                         |
| `items`           | Item definitions and stats                      |
| `worldChunks`     | Persistent world modifications                  |
| `playerSessions`  | Login/logout tracking                           |
| `playerDeaths`    | Death locks to prevent duplication              |
| `npcKills`        | Kill statistics per player                      |
| `friendships`     | Bidirectional friend relationships              |
| `friend_requests` | Pending friend requests                         |
| `ignore_lists`    | Blocked players per account                     |

### Database Commands

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
cd packages/server

# Using Drizzle Kit
bunx drizzle-kit push      # Apply schema to database
bunx drizzle-kit generate  # Generate migrations
bunx drizzle-kit studio    # Open Drizzle Studio GUI
```

## Environment Variables

### Core Configuration

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# Server
PORT=5555                    # HTTP/WebSocket port
NODE_ENV=development         # Environment: development, production, test
WORLD=world                  # World directory path

# Database
DATABASE_URL=postgresql://user:pass@host/db    # PostgreSQL connection string
USE_LOCAL_POSTGRES=true      # Auto-start PostgreSQL in Docker (dev only)

# Security
JWT_SECRET=your-jwt-secret   # Token signing (generate with: openssl rand -base64 32)
ADMIN_CODE=your-admin-code   # In-game admin access code
```

### Authentication

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# Privy (required for persistent accounts)
PUBLIC_PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret
```

<Warning>
  `PUBLIC_PRIVY_APP_ID` must match between client and server.
</Warning>

### Assets & CDN

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# CDN URL for game assets (models, textures, audio, manifests)
PUBLIC_CDN_URL=http://localhost:8080           # Development
# PUBLIC_CDN_URL=https://assets.hyperscape.club  # Production

# WebSocket and API URLs (exposed to client)
PUBLIC_WS_URL=ws://localhost:5555/ws
PUBLIC_API_URL=http://localhost:5555
```

**Manifest Loading:**

* 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

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# ElizaOS AI Integration
ELIZAOS_API_URL=http://localhost:4001

# LiveKit Voice Chat
LIVEKIT_API_KEY=your-livekit-key
LIVEKIT_API_SECRET=your-livekit-secret
LIVEKIT_URL=wss://your-livekit-server

# Advanced
SAVE_INTERVAL=60             # Auto-save interval in seconds
DISABLE_RATE_LIMIT=false     # Disable rate limiting (dev only!)
COMMIT_HASH=abc123           # Git commit hash (auto-set by CI)
```

### CORS Origins

The server automatically allows requests from:

```typescript theme={"theme":{"light":"github-light","dark":"css-variables"}}
// Production domains
'https://hyperscape.club'
'https://www.hyperscape.club'
'https://hyperscape.pages.dev'
'https://hyperscape-production.up.railway.app'

// Dynamic patterns
/^https?:\/\/localhost:\d+$/                    // Any localhost port
/^https?:\/\/.+\.hyperscape\.pages\.dev$/       // Cloudflare preview deployments
/^https:\/\/.+\.up\.railway\.app$/              // Railway preview deployments
```

Additional origins from environment variables:

* `CLIENT_URL`
* `PUBLIC_APP_URL`
* `ELIZAOS_URL` / `ELIZAOS_API_URL`

## Build Scripts

### Model Bounds Extraction

The server includes build-time scripts for automatic collision footprint detection:

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# Extract bounding boxes from GLB models
bun run extract-bounds

# Generates: world/assets/manifests/model-bounds.json
```

**How it works:**

1. Scans `world/assets/models/**/*.glb` files
2. Parses glTF position accessor min/max values
3. Calculates bounding boxes and dimensions
4. Computes tile footprints at scale 1.0
5. Writes manifest for runtime use

**Turbo Integration:**

* Runs automatically before `build` and `dev` commands
* Cached based on GLB file changes
* Only rebuilds when models are added/modified

**Example Output:**

```json theme={"theme":{"light":"github-light","dark":"css-variables"}}
{
  "generatedAt": "2026-01-15T11:25:00.000Z",
  "tileSize": 1.0,
  "models": [
    {
      "id": "furnace",
      "assetPath": "asset://models/furnace/furnace.glb",
      "bounds": {
        "min": { "x": -0.755, "y": 0.0, "z": -0.725 },
        "max": { "x": 0.755, "y": 2.1, "z": 0.725 }
      },
      "dimensions": { "x": 1.51, "y": 2.1, "z": 1.45 },
      "footprint": { "width": 2, "depth": 1 }
    }
  ]
}
```

<Info>
  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.
</Info>

***

## Running

### Development

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
bun run dev:server    # With hot reload and auto-restart
```

### Production

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
bun run build:server  # Build to dist/ (runs extract-bounds automatically)
bun start             # Start production server
```

Server runs at `http://localhost:5555` by default.

## Dependencies

| Package                 | Purpose              |
| ----------------------- | -------------------- |
| `fastify`               | HTTP server (v5)     |
| `@fastify/websocket`    | WebSocket support    |
| `@fastify/cors`         | CORS handling        |
| `@fastify/rate-limit`   | Rate limiting        |
| `pg`                    | PostgreSQL client    |
| `@hyperscape/shared`    | Core engine          |
| `@privy-io/server-auth` | Authentication       |
| `livekit-server-sdk`    | Voice chat           |
| `msgpackr`              | Binary serialization |

## Docker Services

The server can use Docker for CDN and PostgreSQL:

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
# Asset CDN
bun run cdn:up       # Start nginx CDN container
bun run cdn:down     # Stop CDN
bun run cdn:logs     # View CDN logs
bun run cdn:verify   # Verify CDN is working

# Asset Management
bun run assets:sync   # Sync assets from Git LFS
bun run assets:deploy # Deploy to R2 (production)
```

## 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)

**Deployment Process:**

1. Push to `main` branch triggers GitHub Actions
2. Workflow calls Railway GraphQL API to trigger deployment
3. Railway builds using Nixpacks configuration
4. Server starts with `cd packages/server && bun dist/index.js`
5. Manifests fetched from Cloudflare R2 CDN at startup

**Environment Variables (Railway):**

```bash theme={"theme":{"light":"github-light","dark":"css-variables"}}
NODE_ENV=production
DATABASE_URL=<auto-set-by-railway-postgres>
JWT_SECRET=<generate-with-openssl>
PRIVY_APP_ID=<from-privy-dashboard>
PRIVY_APP_SECRET=<from-privy-dashboard>
PUBLIC_CDN_URL=https://assets.hyperscape.club
CI=true
SKIP_ASSETS=true
```

**Health Check:**
Railway monitors `/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:

```typescript theme={"theme":{"light":"github-light","dark":"css-variables"}}
// packages/server/src/startup/config.ts
await fetchManifestsFromCDN(CDN_URL, manifestsDir, NODE_ENV);
```

**Fetched Manifests:**

* 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.

**Caching:**

* 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`)

<Info>
  This allows updating game content (items, NPCs, recipes) without redeploying the server—just upload new manifests to R2.
</Info>

## Key Files

| File                 | Purpose                      |
| -------------------- | ---------------------------- |
| `src/index.ts`       | Server entry point           |
| `src/database/`      | Database schema and queries  |
| `src/startup/`       | Initialization logic         |
| `docker-compose.yml` | Docker service configuration |
| `.env.example`       | Environment template         |
