Skip to main content

Environment Files

Each package has its own .env file:
PackageFilePurpose
Serverpackages/server/.envServer configuration
Clientpackages/client/.envClient configuration
Pluginpackages/plugin-hyperscape/.envAI agent config
AssetForgepackages/asset-forge/.envAsset tools config
Copy from examples:
cp packages/client/.env.example packages/client/.env
cp packages/server/.env.example packages/server/.env

Server Environment

Required

# 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

# 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

# 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

PUBLIC_PRIVY_APP_ID=your-privy-app-id
PUBLIC_PRIVY_APP_ID must match between client and server.

Production

PUBLIC_API_URL=https://api.hyperscape.lol
PUBLIC_WS_URL=wss://api.hyperscape.lol
PUBLIC_CDN_URL=https://cdn.hyperscape.lol

AI Agent Environment

# 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

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

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

# 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:
POST /admin/maintenance/enter
Headers:
  x-admin-code: <ADMIN_CODE>
  Content-Type: application/json
Body:
  {
    "reason": "deployment",
    "timeoutMs": 300000
  }
Exit Maintenance Mode:
POST /admin/maintenance/exit
Headers:
  x-admin-code: <ADMIN_CODE>
Check Status:
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

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

OPENAI_API_KEY=your-openai-key
MESHY_API_KEY=your-meshy-key
ASSET_FORGE_PORT=3400
ASSET_FORGE_API_PORT=3401

Website Environment

# 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:
PortServiceEnvironment VariableStarted By
3333Game ClientVITE_PORTbun run dev
3334Website-bun run dev:website
5555Game ServerPORTbun run dev
8080Asset CDN-Docker
5432PostgreSQL-Docker
4001ElizaOS APIELIZAOS_PORTbun run dev:elizaos
3400AssetForge UIASSET_FORGE_PORTbun run dev:forge
3401AssetForge APIASSET_FORGE_API_PORTbun run dev:forge

Zero-Config Development

Default values work out of the box for local development:
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
The F5 keybind was added to match Minecraft’s familiar debug screen shortcut.

Privy Configuration

  1. Create account at dashboard.privy.io
  2. Create a new app
  3. Copy App ID and App Secret
  4. Set in both client and server .env files
Without Privy, the game runs in anonymous mode with temporary identities.

Security Updates

Dependency Vulnerability Fixes (commit a390b79)

Resolved 14 of 16 npm audit vulnerabilities in February 2026: Playwright (high severity):
{
  "playwright": "^1.55.1"  // Fixes GHSA-7mvr-c777-76hp
}
Vite (multiple vulnerabilities):
{
  "vite": "^6.4.1"  // Fixes GHSA-g4jq-h2w9-997c, GHSA-jqfw-vq24-v9c3, GHSA-93m4-6634-74q7
}
ajv (schema validation):
{
  "ajv": "^8.18.0"  // Fixes GHSA-2g4f-4pwh-qvx6
}
Root Overrides (transitive dependencies):
{
  "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:
# 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:
{
  "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
CSP violations are throttled and reported to /api/csp-report for security monitoring.

Dependency Versions

Recent dependency updates (February 2026):

TypeScript Ecosystem

PackageVersionUpdate Type
typescript5.9.3Patch (from 5.9.2)
@types/node25.3.0Major (from 24.x)
@types/react19.2.14Patch (from 19.2.2)
@types/react-dom19.2.3Patch (from 19.2.2)

Three.js and Graphics

PackageVersionUpdate Type
three0.183.1Minor (from 0.182.0)
@types/three0.183.1Minor (from 0.180.0)
three-mesh-bvh0.9.8Minor (from 0.8.3)

ElizaOS

PackageVersionUpdate Type
@elizaos/core2.0.0-alpha.26Patch (from 2.0.0-alpha.11)

Development Tools

PackageVersionUpdate Type
eslint10.0.2Major (from 9.39.3)
@playwright/test1.58.2Minor (from 1.54.2)
chai6.2.2Major (from 4.5.0)
@capacitor/cli8.1.0Major (from 7.5.0)

UI Libraries

PackageVersionUpdate Type
framer-motion12.34.3Major (from 11.18.2)
lucide-react0.575.0Minor (from 0.553.0)
@vitejs/plugin-react5.1.4Minor (from 5.0.4)
vite-plugin-node-polyfills0.25.0Minor (from 0.24.0)

Validation and Utilities

PackageVersionUpdate Type
zod4.3.6Major (from 3.25.76)
dotenv17.3.1Major (from 16.6.1)

Blockchain

PackageVersionUpdate Type
@coral-xyz/anchor0.32.1Minor (from 0.31.1)
@ai-sdk/anthropic3.0.46Major (from 1.2.12)
All dependency updates were merged via automated Dependabot PRs in commits 7a60135e through 55c57ed5.

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:
# .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:
# .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:
# .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:
SecretPurpose
CLAUDE_CODE_OAUTH_TOKENClaude Code authentication
MINTLIFY_API_KEYDocumentation updates
MINTLIFY_PROJECT_IDDocumentation project ID