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

# Configuration

> Environment variables and port allocation

## Environment Files

Each package has its own `.env` file:

| Package    | File                              | Purpose              |
| ---------- | --------------------------------- | -------------------- |
| Server     | `packages/server/.env`            | Server configuration |
| Client     | `packages/client/.env`            | Client configuration |
| Plugin     | `packages/plugin-hyperscape/.env` | AI agent config      |
| AssetForge | `packages/asset-forge/.env`       | Asset tools config   |

Copy from examples:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cp packages/client/.env.example packages/client/.env
cp packages/server/.env.example packages/server/.env
```

## Server Environment

### Required

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Database (production)
DATABASE_URL=postgresql://user:pass@host:5432/db

# Security (REQUIRED in production/staging as of February 2026)
JWT_SECRET=your-random-secret-key  # Generate with: openssl rand -base64 32
                                   # Throws error if not set in production/staging
                                   # Warns in development if not set

# Admin Access (REQUIRED for production security)
ADMIN_CODE=your-admin-code         # Prevents unauthorized admin access
                                   # Without this, only GRANT_DEV_ADMIN provides admin

# Authentication
PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret

# Database Migrations (CI/Testing)
SKIP_MIGRATIONS=true              # Skip server migration execution, table validation, AND recovery loop when schema created externally via drizzle-kit push (commits eb8652a, 6a5f4ee)
```

### Optional

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Server
PORT=5555

# Assets
PUBLIC_CDN_URL=http://localhost:8080

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

# RTMP Streaming (Multi-Platform)
# Twitch
TWITCH_STREAM_KEY=live_123456789_abcdefghij
TWITCH_RTMP_URL=rtmp://live.twitch.tv/app  # Optional override

# Kick (uses RTMPS)
KICK_STREAM_KEY=your-kick-stream-key
KICK_RTMP_URL=rtmps://fa723fc1b171.global-contribute.live-video.net/app

# X/Twitter
X_STREAM_KEY=your-x-stream-key
X_RTMP_URL=rtmp://sg.pscp.tv:80/x

# YouTube (optional)
YOUTUBE_STREAM_KEY=xxxx-xxxx-xxxx-xxxx-xxxx
YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2

# Streaming Timing
STREAMING_CANONICAL_PLATFORM=twitch  # youtube | twitch | hls (default: youtube)
STREAMING_PUBLIC_DELAY_MS=0          # Override platform default delay (ms)

# Streaming Quality Settings (commit 4c630f12, Feb 26 2026)
# Low latency mode (zerolatency tune, 2x buffer) vs balanced mode (film tune, 4x buffer)
STREAM_LOW_LATENCY=false  # Set to true for ultra-low latency (may cause viewer buffering)
                          # Default: false (uses 'film' tune with B-frames for smoother playback)

# See packages/server/.env.example for full RTMP configuration
```

### Solana Configuration

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Solana RPC Endpoints
SOLANA_RPC_URL=https://api.devnet.solana.com
SOLANA_WS_URL=wss://api.devnet.solana.com/

# Solana Keypairs (base58 encoded private keys)
SOLANA_DEPLOYER_PRIVATE_KEY=<base58-private-key>  # Used for all roles if others not set
SOLANA_ARENA_AUTHORITY_SECRET=<base58-private-key>  # Falls back to DEPLOYER
SOLANA_ARENA_REPORTER_SECRET=<base58-private-key>  # Falls back to DEPLOYER
SOLANA_ARENA_KEEPER_SECRET=<base58-private-key>    # Falls back to DEPLOYER

# Market Configuration (commit 34255ee, Feb 26 2026)
# Markets now use native token (WSOL on Solana) instead of GOLD token
# MARKET_MINT defaults to WSOL (So11111111111111111111111111111111111112)
# GOLD_MINT is deprecated - use MARKET_MINT instead
MARKET_MINT=So11111111111111111111111111111111111112  # WSOL (native SOL wrapped)

# Perps Oracle (disabled by default on devnet, commit 34255ee)
# Perps program not deployed on devnet - enable when ready
ENABLE_PERPS_ORACLE=false  # Set to true when perps program deployed
```

**Keypair Setup:**

The `SOLANA_DEPLOYER_PRIVATE_KEY` is automatically decoded and written to:

* `~/.config/solana/id.json` (Solana CLI default location)
* `deployer-keypair.json` (legacy location)

This happens during deployment via `scripts/decode-key.ts`.

## Client Environment

### Required

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
PUBLIC_PRIVY_APP_ID=your-privy-app-id
```

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

### Production

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
PUBLIC_API_URL=https://api.hyperscape.lol
PUBLIC_WS_URL=wss://api.hyperscape.lol
PUBLIC_CDN_URL=https://cdn.hyperscape.lol
```

## AI Agent Environment

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ElizaOS
ELIZAOS_PORT=4001

# LLM Providers (at least one required)
OPENAI_API_KEY=your-openai-key
ANTHROPIC_API_KEY=your-anthropic-key

# Agent Spawning Control
SPAWN_MODEL_AGENTS=false                        # Enable heavyweight model-agent spawner (default: false)
MAX_MODEL_AGENTS=0                              # Maximum number of model agents to spawn (default: 0)
MEMORY_RESTART_THRESHOLD_MB=12288               # Memory threshold for auto-restart in MB (default: 12288)

# Duel Stack Defaults (scripts/duel-stack.mjs)
AUTO_START_AGENTS=true                          # Auto-load embedded agents (default: true for duel stack)
AUTO_START_AGENTS_MAX=10                        # Max embedded agents to start (default: 10)
EMBEDDED_AGENT_AUTONOMY_ENABLED=false           # Enable background questing/pathing (default: false for streaming)

# Streaming Duel Configuration
STREAMING_DUEL_ENABLED=true                     # Enable streaming duel scheduler (default: true)
STREAMING_DUEL_COMBAT_AI_ENABLED=false          # Enable DuelCombatAI for agents (default: false for stability)
STREAMING_DUEL_LLM_TACTICS_ENABLED=false        # Enable LLM-based combat strategy (default: false)
STREAMING_COMBAT_STALL_NUDGE_MS=15000           # Fallback damage nudge delay (ms)

# Duel Arena Visuals System
DUEL_ARENA_VISUALS_ENABLED=true                 # Enable duel arena visuals system (default: true)
                                                # Registers flat zones for arena floors to prevent player sinking

# Trash Talk System
TRASH_TALK_COOLDOWN_MS=8000                     # Cooldown between trash talk messages (default: 8s)
TRASH_TALK_LLM_TIMEOUT_MS=3000                  # LLM generation timeout (default: 3s)
TRASH_TALK_ENABLED=true                         # Enable trash talk system (default: true)

# Streaming Timing Configuration
STREAMING_ANNOUNCEMENT_MS=30000                 # Announcement phase duration (default: 30s)
STREAMING_FIGHTING_MS=150000                    # Fighting phase duration (default: 150s)
STREAMING_END_WARNING_MS=10000                  # End warning duration (default: 10s)
STREAMING_RESOLUTION_MS=5000                    # Resolution phase duration (default: 5s)
```

## PM2 Production Deployment

The production duel stack uses PM2 for process management with automatic restarts:

### PM2 Configuration

```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From ecosystem.config.cjs
module.exports = {
  apps: [{
    name: "hyperscape-duel",
    script: "scripts/duel-stack.mjs",
    interpreter: "bun",
    args: "--skip-betting --skip-bots",
    
    // Restart policy
    autorestart: true,
    max_restarts: 999999,
    min_uptime: "10s",
    restart_delay: 5000,
    max_memory_restart: "4G",
    
    // Logging
    error_file: "logs/duel-error.log",
    out_file: "logs/duel-out.log",
    
    env: {
      NODE_ENV: "production",
      USE_LOCAL_POSTGRES: "false",
      DATABASE_URL: process.env.DATABASE_URL || "postgresql://...",
      PUBLIC_CDN_URL: "https://assets.hyperscape.club",
      
      // Streaming configuration
      STREAM_CAPTURE_MODE: "cdp",
      STREAM_CAPTURE_HEADLESS: "false",  // Headful with Xvfb
      STREAM_CAPTURE_CHANNEL: "chrome-dev",
      STREAM_CAPTURE_ANGLE: "vulkan",
      STREAMING_CANONICAL_PLATFORM: "twitch",
      STREAMING_PUBLIC_DELAY_MS: "0",
      
      // Stream keys (from environment)
      TWITCH_STREAM_KEY: process.env.TWITCH_STREAM_KEY || "",
      KICK_STREAM_KEY: process.env.KICK_STREAM_KEY || "",
      KICK_RTMP_URL: process.env.KICK_RTMP_URL || "rtmps://fa723fc1b171.global-contribute.live-video.net/app",
      X_STREAM_KEY: process.env.X_STREAM_KEY || "",
      X_RTMP_URL: process.env.X_RTMP_URL || "rtmp://sg.pscp.tv:80/x"
    }
  }]
};
```

### PM2 Commands

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Start duel stack
bunx pm2 start ecosystem.config.cjs

# Monitor
bunx pm2 logs hyperscape-duel
bunx pm2 status
bunx pm2 monit

# Control
bunx pm2 restart hyperscape-duel
bunx pm2 stop hyperscape-duel
bunx pm2 delete hyperscape-duel

# Persist across reboots
bunx pm2 save
bunx pm2 startup
```

### Stream Key Management

Stream keys must be explicitly unset and re-exported before PM2 start to avoid stale environment variables:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# From scripts/deploy-vast.sh
unset TWITCH_STREAM_KEY X_STREAM_KEY X_RTMP_URL KICK_STREAM_KEY KICK_RTMP_URL
source /root/hyperscape/packages/server/.env
bunx pm2 start ecosystem.config.cjs
```

This ensures PM2 picks up the correct stream keys from the .env file, not stale values from the shell environment.

**Why This Matters:**

* Vast.ai servers can have stale stream keys in their environment from previous deployments
* These stale values override the .env file values
* Explicitly unsetting ensures clean environment before sourcing .env
* Prevents streams from going to wrong channels/accounts

## Maintenance Mode

The server provides a maintenance mode API for graceful deployments without data loss.

### Environment Variables

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Maintenance mode is controlled via API, not environment variables
# The scheduler checks this internal flag:
STREAMING_DUEL_MAINTENANCE_MODE=true  # Set by enterMaintenanceMode()
```

### API Endpoints

**Enter Maintenance Mode:**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
POST /admin/maintenance/enter
Headers:
  x-admin-code: <ADMIN_CODE>
  Content-Type: application/json
Body:
  {
    "reason": "deployment",
    "timeoutMs": 300000
  }
```

**Exit Maintenance Mode:**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
POST /admin/maintenance/exit
Headers:
  x-admin-code: <ADMIN_CODE>
```

**Check Status:**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
GET /admin/maintenance/status
Headers:
  x-admin-code: <ADMIN_CODE>
```

### What Maintenance Mode Does

When maintenance mode is entered:

1. **Pauses new duel cycles** - Current cycle completes, no new cycles start
2. **Locks betting markets** - No new bets accepted
3. **Waits for resolution** - Current market resolves before reporting safe to deploy
4. **Reports status** - API returns `safeToDeploy: true` when ready

**Safe to Deploy Conditions:**

* Not in active duel phase (FIGHTING, COUNTDOWN, ANNOUNCEMENT)
* No pending betting markets (or all markets resolved)
* Maintenance mode active

### Helper Scripts

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Enter maintenance mode
bash scripts/pre-deploy-maintenance.sh

# Exit maintenance mode
bash scripts/post-deploy-resume.sh
```

### CI/CD Integration

The Vast.ai deployment workflow automatically uses maintenance mode:

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .github/workflows/deploy-vast.yml
- name: Enter Maintenance Mode
  run: |
    curl -X POST "$VAST_SERVER_URL/admin/maintenance/enter" \
      -H "x-admin-code: $ADMIN_CODE" \
      -d '{"reason": "deployment", "timeoutMs": 300000}'

# ... deploy steps ...

- name: Exit Maintenance Mode
  run: |
    curl -X POST "$VAST_SERVER_URL/admin/maintenance/exit" \
      -H "x-admin-code: $ADMIN_CODE"
```

**Benefits:**

* Prevents data loss during deployments
* Avoids market inconsistency
* Ensures clean duel cycle boundaries
* No interrupted streams or broken markets

## AssetForge Environment

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
OPENAI_API_KEY=your-openai-key
MESHY_API_KEY=your-meshy-key
ASSET_FORGE_PORT=3400
ASSET_FORGE_API_PORT=3401
```

## Website Environment

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# External URLs
NEXT_PUBLIC_DOCS_URL=https://hyperscape-ai.mintlify.app/
NEXT_PUBLIC_GAME_URL=https://play.hyperscape.club
NEXT_PUBLIC_DISCORD_URL=https://discord.gg/f4ZwhAbKye
NEXT_PUBLIC_TWITTER_URL=https://x.com/hyperscapeai
NEXT_PUBLIC_GITHUB_URL=https://github.com/hyperscape-ai
```

## Port Allocation

All services have unique default ports:

| Port | Service        | Environment Variable   | Started By            |
| ---- | -------------- | ---------------------- | --------------------- |
| 3333 | Game Client    | `VITE_PORT`            | `bun run dev`         |
| 3334 | Website        | -                      | `bun run dev:website` |
| 5555 | Game Server    | `PORT`                 | `bun run dev`         |
| 8080 | Asset CDN      | -                      | Docker                |
| 5432 | PostgreSQL     | -                      | Docker                |
| 4001 | ElizaOS API    | `ELIZAOS_PORT`         | `bun run dev:elizaos` |
| 3400 | AssetForge UI  | `ASSET_FORGE_PORT`     | `bun run dev:forge`   |
| 3401 | AssetForge API | `ASSET_FORGE_API_PORT` | `bun run dev:forge`   |

## Zero-Config Development

Default values work out of the box for local development:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run dev   # Just works
```

Only configure `.env` files when:

* Using Privy authentication
* Connecting to external database
* Deploying to production
* Running AI agents

## Debug Controls

### FPS Debug Panel

Toggle the FPS debug panel with:

* **F5** (matches Minecraft's debug screen)
* **Backslash (\\)** (alternative keybind)

The debug panel shows:

* FPS (frames per second)
* Frame time (ms)
* Memory usage
* Entity count
* Network stats

<Info>
  The F5 keybind was added to match Minecraft's familiar debug screen shortcut.
</Info>

## Privy Configuration

1. Create account at [dashboard.privy.io](https://dashboard.privy.io)
2. Create a new app
3. Copy App ID and App Secret
4. Set in both client and server `.env` files

<Info>
  Without Privy, the game runs in anonymous mode with temporary identities.
</Info>

## Security Updates

### Dependency Vulnerability Fixes (commit a390b79)

Resolved 14 of 16 npm audit vulnerabilities in February 2026:

**Playwright** (high severity):

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
  "playwright": "^1.55.1"  // Fixes GHSA-7mvr-c777-76hp
}
```

**Vite** (multiple vulnerabilities):

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
  "vite": "^6.4.1"  // Fixes GHSA-g4jq-h2w9-997c, GHSA-jqfw-vq24-v9c3, GHSA-93m4-6634-74q7
}
```

**ajv** (schema validation):

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
  "ajv": "^8.18.0"  // Fixes GHSA-2g4f-4pwh-qvx6
}
```

**Root Overrides** (transitive dependencies):

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
  "overrides": {
    "@trpc/server": "^11.0.0",
    "minimatch": "^10.0.1",
    "cookie": "^1.0.2",
    "undici": "^7.2.0",
    "jsondiffpatch": "^0.6.0",
    "tmp": "^0.2.3",
    "diff": "^7.0.0",
    "bn.js": "^5.2.1",
    "ai": "^4.0.38"
  }
}
```

**Remaining Vulnerabilities:**

* `bigint-buffer`: No patched version available upstream
* `elliptic`: No patched version available upstream

**Audit Threshold:**

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# CI now only fails on critical vulnerabilities
bun audit --audit-level=critical
```

### ESLint ajv Compatibility (commit b344d9e)

Fixed ESLint crash caused by forcing ajv\@8 on @eslint/eslintrc:

**Problem:**

* Forcing `ajv>=8.18.0` override caused TypeError crash
* @eslint/eslintrc requires ajv\@6 for JSON Schema Draft-04 support
* ajv\@8 removed Draft-04 support, breaking ESLint initialization

**Solution:**

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
  "overrides": {
    // ❌ Removed - causes ESLint crash
    // "ajv": ">=8.18.0"
    
    // ✅ Only override ajv in packages that support ajv@8
    "ajv": "^8.18.0"  // Applied only to packages/shared
  }
}
```

**Impact:**

* ESLint now works correctly
* Security fix for ajv (GHSA-2g4f-4pwh-qvx6) still applied where compatible
* @eslint/eslintrc uses ajv\@6 as required

## Content Security Policy

### CSP Headers

The client includes Content Security Policy headers to mitigate XSS attacks:

```
# From packages/client/public/_headers
Content-Security-Policy: 
  default-src 'self'; 
  script-src 'self' 'unsafe-inline' 'unsafe-eval' https://auth.privy.io https://*.privy.io; 
  style-src 'self' 'unsafe-inline'; 
  img-src 'self' data: https: blob:; 
  font-src 'self' data:; 
  connect-src 'self' wss: https: ws://localhost:* http://localhost:*; 
  frame-src 'self' https://auth.privy.io https://*.privy.io; 
  worker-src 'self' blob:; 
  media-src 'self' blob:; 
  report-uri /api/csp-report;
```

**Security Notes:**

* `'unsafe-inline'` is required for Privy SDK (injects inline styles for modals)
* `'unsafe-eval'` is required for Privy SDK (uses eval for authentication flows)
* `report-uri` sends CSP violations to the server for monitoring
* Consider migrating to CSP nonces when Privy SDK adds support

<Info>
  CSP violations are throttled and reported to `/api/csp-report` for security monitoring.
</Info>

## Dependency Versions

Recent dependency updates (February 2026):

### TypeScript Ecosystem

| Package            | Version | Update Type         |
| ------------------ | ------- | ------------------- |
| `typescript`       | 5.9.3   | Patch (from 5.9.2)  |
| `@types/node`      | 25.3.0  | Major (from 24.x)   |
| `@types/react`     | 19.2.14 | Patch (from 19.2.2) |
| `@types/react-dom` | 19.2.3  | Patch (from 19.2.2) |

### Three.js and Graphics

| Package          | Version | Update Type          |
| ---------------- | ------- | -------------------- |
| `three`          | 0.183.1 | Minor (from 0.182.0) |
| `@types/three`   | 0.183.1 | Minor (from 0.180.0) |
| `three-mesh-bvh` | 0.9.8   | Minor (from 0.8.3)   |

### ElizaOS

| Package         | Version        | Update Type                 |
| --------------- | -------------- | --------------------------- |
| `@elizaos/core` | 2.0.0-alpha.26 | Patch (from 2.0.0-alpha.11) |

### Development Tools

| Package            | Version | Update Type         |
| ------------------ | ------- | ------------------- |
| `eslint`           | 10.0.2  | Major (from 9.39.3) |
| `@playwright/test` | 1.58.2  | Minor (from 1.54.2) |
| `chai`             | 6.2.2   | Major (from 4.5.0)  |
| `@capacitor/cli`   | 8.1.0   | Major (from 7.5.0)  |

### UI Libraries

| Package                      | Version | Update Type          |
| ---------------------------- | ------- | -------------------- |
| `framer-motion`              | 12.34.3 | Major (from 11.18.2) |
| `lucide-react`               | 0.575.0 | Minor (from 0.553.0) |
| `@vitejs/plugin-react`       | 5.1.4   | Minor (from 5.0.4)   |
| `vite-plugin-node-polyfills` | 0.25.0  | Minor (from 0.24.0)  |

### Validation and Utilities

| Package  | Version | Update Type          |
| -------- | ------- | -------------------- |
| `zod`    | 4.3.6   | Major (from 3.25.76) |
| `dotenv` | 17.3.1  | Major (from 16.6.1)  |

### Blockchain

| Package             | Version | Update Type         |
| ------------------- | ------- | ------------------- |
| `@coral-xyz/anchor` | 0.32.1  | Minor (from 0.31.1) |
| `@ai-sdk/anthropic` | 3.0.46  | Major (from 1.2.12) |

<Info>
  All dependency updates were merged via automated Dependabot PRs in commits 7a60135e through 55c57ed5.
</Info>

## GitHub Actions

The repository includes automated workflows for code quality and documentation:

### Claude Code Review

Automatically reviews pull requests for bugs and code quality issues:

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .github/workflows/claude-code-review.yml
name: Claude Code Review
on:
  pull_request:
    types: [opened, synchronize, ready_for_review, reopened]
```

**Features:**

* Automated code review on every PR
* Checks for bugs and CLAUDE.md compliance
* Posts review comments directly on PRs

### Claude PR Assistant

Responds to `@claude` mentions in issues and PRs:

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .github/workflows/claude.yml
name: Claude Code
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
```

**Usage:**

* Comment `@claude` in any issue or PR
* Claude will respond with code suggestions
* Can read CI results and provide context-aware help

### Documentation Updates

Automatically updates documentation when manifests change:

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .github/workflows/update-docs.yml
name: Update Docs
on:
  push:
    branches:
      - main
```

**Features:**

* Triggers on pushes to main branch
* Analyzes recent commits for manifest changes
* Creates PRs with documentation updates

### Required Secrets

Configure these in your repository settings:

| Secret                    | Purpose                    |
| ------------------------- | -------------------------- |
| `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code authentication |
| `MINTLIFY_API_KEY`        | Documentation updates      |
| `MINTLIFY_PROJECT_ID`     | Documentation project ID   |
