Overview
Hyperscape uses Drizzle ORM for database access:- Development: SQLite (zero config)
- Production: PostgreSQL with pgvector extension
PostgreSQL 16+ with pgvector: The production database requires PostgreSQL 16 or higher with the pgvector extension. Use the
pgvector/pgvector:pg16 Docker image for local development (updated in commit a9e9003, Feb 2026).Schema Location
Database schema is defined in:Tables
| Table | Purpose |
|---|---|
users | User accounts (Privy auth) |
characters | Character data (multi-character support) |
players | Active player sessions |
inventory | Player inventory items |
bank | Banked items with tab support |
bank_tabs | Bank tab configuration |
equipment | Equipped items |
npc_kills | Kill tracking |
player_deaths | Death history |
world_chunks | World state persistence |
sessions | Active game sessions |
Characters Table Schema
Thecharacters table stores all character data including skills:
prayerLevel/prayerXp— Prayer skill progressionprayerPoints— Current prayer points (0 to prayerLevel)prayerMaxPoints— Maximum prayer points (equals prayerLevel)activePrayers— JSON array of active prayer IDs (e.g.,'["thick_skin"]')
Prayer points are stored as integers in the database but tracked with fractional precision in-memory for accurate drain calculations.
SKIP_MIGRATIONS Environment Variable
WhenSKIP_MIGRATIONS=true, the server skips:
- Built-in migration execution
hasRequiredPublicTablesvalidation check- Migration recovery loop
- CI/testing environments using
drizzle-kit pushfor declarative schema creation - External schema management (avoids FK ordering issues in migration files)
- Integration tests that create schema before server startup
SKIP_MIGRATIONS=true, you MUST create the database schema externally (e.g., via drizzle-kit push) before starting the server. The server will not create tables or run migrations.
Example CI Workflow (commits eb8652a, 6a5f4ee):
- Server’s built-in migrations have FK ordering issues (migration 0050 references
arena_roundsfrom older migrations) drizzle-kit pushcreates schema declaratively without these problems- Prevents “relation already exists” errors on fresh databases
- Allows CI to use declarative schema creation instead of sequential migrations
- Fixed in commits: eb8652a (CI integration), 6a5f4ee (table validation skip)
SKIP_MIGRATIONS=true only skipped migration execution but still ran:
hasRequiredPublicTablescheck- Migration recovery loop
drizzle-kit push because the migration journal wasn’t populated.
Now, SKIP_MIGRATIONS=true skips ALL migration-related checks:
SKIP_MIGRATIONS check is placed at the beginning of the migration function, before any database validation or migration execution:
SKIP_MIGRATIONS=true, the server assumes the schema is already created and valid, and proceeds directly to application startup.
Drizzle Commands
Run frompackages/server/:
Push Schema
Apply schema changes directly (development):Generate Migrations
Create migration files for changes:Run Migrations
Apply pending migrations:Studio
Open Drizzle Studio to browse data:Configuration
packages/server/drizzle.config.ts:
Development vs Production
Development (SQLite)
No configuration needed—uses local SQLite file:Production (PostgreSQL)
SetDATABASE_URL in environment:
Connection Pooling (Supabase)
When using Supabase with the Supavisor connection pooler, prepared statements must be disabled (fixed in commits 8aaaf28, f7ab9f7):- Supavisor operates in transaction mode for connection pooling
- Transaction mode doesn’t support prepared statements (PostgreSQL limitation)
- Attempting to use prepared statements causes
XX000errors: “prepared statement does not exist” - Disabling prepared statements trades minor performance for compatibility
- Using Supabase with Supavisor pooler
- Getting XX000 errors on database queries
- Connection string contains
pooler.supabase.com
- Local PostgreSQL development
- Direct PostgreSQL connections (no pooler)
- PgBouncer in statement mode
- Neon, Railway, or other providers without transaction pooling
Migration Workflow
Automatic Migrations (Default)
By default, the server automatically runs migrations on startup:- Checks for required public tables
- Runs pending migrations from
src/database/migrations/ - Populates migration journal table
- Validates schema integrity
Skip Migrations (CI/Testing)
For CI environments where schema is created externally viadrizzle-kit push, set:
- Skips migration execution
- Skips
hasRequiredPublicTablescheck - Skips migration recovery loop
- Assumes schema is already created and valid
- CI integration tests using
drizzle-kit pushfor declarative schema creation - Avoids foreign key ordering issues in migration files
- Prevents migration journal conflicts when schema created externally
- Server’s built-in migration has FK ordering issues (migration 0050 references arena_rounds from older migrations)
drizzle-kit pushcreates schema declaratively without these problems- Allows CI to use push for clean schema creation, then skip server migration
Migrations
Migrations are stored inpackages/server/src/database/migrations/:
Recent Migrations
| Migration | Description |
|---|---|
0051_add_arena_composite_indexes.sql | Added composite indexes for arena queries |
0050_broken_krista_starr.sql | Arena betting and staking tables (fixed in commit e4b6489) |
0049_make_arena_fee_share_bet_unique.sql | Unique constraint on arena fee shares |
0048_restore_arena_multiplier_floor_at_1k.sql | Arena points multiplier floor |
0047_add_arena_staking_points_wallet_period_unique.sql | Unique constraint on staking points |
0046_fix_arena_multiplier_defaults.sql | Arena points multiplier defaults |
0045_add_arena_chain_fees_and_wallet_links.sql | Arena chain fees and wallet linking |
0044_add_arena_staking_points.sql | Arena staking points system |
0043_add_arena_invite_and_fee_share_tracking.sql | Arena invite and fee share tracking |
0042_convert_active_prayers_to_jsonb.sql | Convert activePrayers to JSONB |
0041_add_failed_transactions.sql | Failed transaction tracking |
0040_validate_constraints_and_add_agent_checks.sql | Constraint validation and agent checks |
0039_add_agent_duel_stats.sql | Agent duel statistics tracking |
0038_odd_luke_cage.sql | Combat stats and onchain outbox |
0037_track_player_combat_stats_and_onchain_outbox.sql | Player combat stats tracking |
0027_messy_dorian_gray.sql | Added operations_log table for write-ahead logging and crash recovery |
0026_add_magic_skill.sql | Added magicLevel, magicXp, and selectedSpell columns for magic combat |
0023_rapid_scorpion.sql | Added action_bar_storage table for action bar persistence |
0022_warm_spacker_dave.sql | Added activity_log, trades, and user_bans tables |
0016_add_prayer_system.sql | Added prayer skill, points, and active prayers tracking |
0015_add_smithing_skill.sql | Added smithingLevel and smithingXp columns |
0014_add_mining_skill.sql | Added miningLevel and miningXp columns |
Migration Format
IF NOT EXISTS to be idempotent and safe to re-run.
Migration 0050 Fix (commit e4b6489)
Migration 0050 was fixed to addIF NOT EXISTS guards to prevent errors on fresh databases:
Problem:
- Migration 0050 duplicated CREATE TABLE statements from earlier migrations
agent_duel_statstable was created in both migration 0039 and 0050- On fresh databases, running all migrations sequentially caused 42P07 errors
- Same table created twice without IF NOT EXISTS protection
agent_duel_statsarena_fee_sharesarena_staking_pointswallet_links
- Fresh database installations now complete all migrations successfully
- Prevents “relation already exists” errors (42P07)
- Allows clean database setup from scratch
- Fixes CI integration test failures
All migrations should use
IF NOT EXISTS guards to be idempotent and safe to re-run.The
activePrayers column stores a JSON array of prayer IDs. Format: '["thick_skin", "burst_of_strength"]'. Empty array when no prayers are active: '[]'.Schema Changes
Reset Database
Development Reset
Delete the SQLite file:Docker PostgreSQL Reset
Docker Image Update: The local PostgreSQL container now uses
pgvector/pgvector:pg16 instead of postgres:16-alpine to support vector similarity search features (commit a9e9003, Feb 2026).