Skip to content

Planet — Schema

Status: 🚧 Partial — Planet core, citadel/siege/terraforming/genesis, and ADR-0073 naming are all committed, including landing_rights (enforced at land-time) and tax_rate (✅ Shipped on teammate stockpile→cargo withdraw — see ../FEATURES/planets/colonization.md#ownership-controls). Only the ARTIFICIAL planet type remains without a world-generation or production path. (re-verified 2026-08-17 · LEG-INI-29)

Habitable / colonizable bodies and their lifecycle state. Companion docs: ../FEATURES/planets/colonization.md, ../FEATURES/planets/citadels.md, ../FEATURES/planets/defense.md.

Schema status

Per ADR-0066 D-V1, schema-level implementation status is consolidated here. Field descriptions describe the target schema.

Design-only on Planet:

  • ARTIFICIAL — one enum value on type with no world-generation or production path yet. The other 11 values (TERRA / DESERT / OCEANIC / ICE / VOLCANIC / BARREN / MOUNTAINOUS / GAS_GIANT / JUNGLE / ARCTIC / TROPICAL) are live canonical types.

All other columns on this page are committed, including tax_rate and landing_rights per ../FEATURES/planets/colonization.md ownership-controls section. landing_rights is fully enforced at land-time. tax_rate is ✅ Shipped on teammate stockpile→cargo withdraw (planetary_service.withdraw_stockpile_to_cargo / POST /planets/{planetId}/stockpile/withdraw); the [0.00, 0.20] clamp lives in planetary_service.clamp_tax_rate.


Planet

Source: services/gameserver/src/models/planet.py

Purpose: Habitable / colonizable body in a sector; the long-form economic and defensive base for players.

Fields:

name type constraints notes
id UUID PK
name String(100) not null original worldgen string; the final fallback in the display chain
auto_name String(100) nullable generated default name (654-name corpus + prefix/suffix compound generator, services/gameserver/src/services/planet_naming_service.py / data/planet_names.py); backfilled = name for pre-existing worlds, set at genesis. See ../FEATURES/galaxy/star-systems.md#planet-discovery-naming
custom_name String(100) nullable discoverer-set name; layered over auto_name. Display precedence: custom_name → auto_name → name. Written by POST /api/v1/planets/{id}/name, gated on discovered_by
discovered_by UUID FK players.id nullable the player who first discovered this world (null = undiscovered / worldgen-seeded); sole rename authority. Set when a player views the sector's /system
discovered_at DateTime nullable timezone-aware timestamp of first discovery
sector_id Integer not null integer alongside UUID
sector_uuid UUID FK sectors.id nullable, CASCADE
owner_id UUID nullable not a FK constraint — ownership is also tracked via the player_planets association table
type Enum planet_type not null 12 values total (per ADR-0014). 11 are live canonical types: TERRA / DESERT / OCEANIC / ICE / VOLCANIC / BARREN / MOUNTAINOUS / GAS_GIANT / JUNGLE / ARCTIC / TROPICAL. GAS_GIANT, JUNGLE, ARCTIC, and TROPICAL carry neutral 1.0 production multipliers (services/gameserver/src/services/planetary_service.py NEUTRAL_TYPE_EFFICIENCY); nexus_generation_service generates JUNGLE and TROPICAL as colonizable worlds and GAS_GIANT as an uncolonizable body. ARTIFICIAL has no world-generation or production path yet. Genesis devices roll into a subset of these per ADR-0014.
status Enum planet_status default UNINHABITABLE UNINHABITABLE/HABITABLE/COLONIZED/DEVELOPED/TERRAFORMING/DYING/RESTRICTED
size, position Integer defaults 5 / 3 1-10; orbital position
gravity Float default 1.0
specialization String(50) nullable
atmosphere String nullable
temperature, water_coverage, habitability_score, radiation_level numeric various
resource_richness Float default 1.0 0.0-3.0 multiplier
resources JSONB default {}
special_resources ARRAY(String) default []
fuel_ore, organics, equipment, fighters Integer defaults 0 stockpiles
colonized_at DateTime nullable
population, max_population BigInteger defaults 0 Total demographic count — includes working-age colonists plus dependents (children, retirees, non-contributors). The actuarial number that drifts upward over a colony's lifetime. BigInteger for very large established colonies; widened in migration dbbfad27a7ef. max_population = habitability_score × 1,000 (canonical formula, see ../FEATURES/planets/colonization.md#population-capacity); recomputed when habitability changes.
population_growth Float default 0.0 Per-tick growth rate applied to population (and proportionally to colonists).
colonists, max_colonists Integer default 0 / 1000 Working-age contributing settlers — the gameplay-facing counter that drives production formulas. Always colonists ≤ population. max_colonists is the citadel-tier workforce cap (L1 1k → L5 200k per ../FEATURES/planets/citadels.md#levels-and-capacities); recomputed when the citadel upgrades. Population continues to grow at higher tiers as the city accumulates dependents.
fuel_allocation, organics_allocation, equipment_allocation Integer defaults 0 Head-count of colonists assigned to each production role, not a percentage. Sum ≤ colonists; unallocated head-count is idle. Multiplied directly into the per-tick production formula — see ../SYSTEMS/planetary-production-tick.md.
economy, production JSONB defaults production has fuel/organics/equipment/research keys
production_efficiency Float default 1.0
factory_level, farm_level, mine_level, research_level Integer defaults 0
defense_level, shields, weapon_batteries, defense_turrets, defense_shields, defense_fighters Integer defaults 0
last_attacked, last_production DateTime nullable
active_events JSONB default []
under_siege Boolean default false
siege_started_at, siege_attacker_id mixed nullable siege state added in migration a1b2c3d4e5f6
citadel_level Integer default 0 0-5 (migration e3f4a5b6c7d8)
citadel_upgrading Boolean default false
citadel_upgrade_started_at, citadel_upgrade_complete_at DateTime nullable
citadel_safe_credits, citadel_safe_max BigInteger defaults 0
citadel_drone_capacity Integer default 0
citadel_max_population BigInteger default 0
genesis_created Boolean default false
genesis_device_id UUID FK genesis_devices.id nullable
genesis_tier String(20) nullable basic/enhanced/advanced (migration a3f7c2d91e54)
formation_status String(20) nullable forming/complete
formation_started_at, formation_complete_at DateTime nullable
region_id UUID FK regions.id nullable
tax_rate Float nullable (NULL ⇒ 0.0) ✅ Shipped — teammate stockpile→cargo withdraw pays clamp_tax_rate(Planet.tax_rate) (0.00–0.20) skimmed into the owner's citadel safe; owner withdraws pay 0%; fail-closed when tax > 0 without a citadel / safe capacity (planetary_service.withdraw_stockpile_to_cargo, route POST /planets/{planetId}/stockpile/withdraw). See ../FEATURES/planets/colonization.md#ownership-controls
landing_rights JSONB default {"mode": "public"} landing-rights JSONB; modepublic / team_only / private / whitelist / denylist; whitelist and denylist are arrays of player UUIDs (see ../FEATURES/planets/colonization.md#ownership-controls)

Relationships: - ownerPlayer (many-to-many via player_planets association). - sectorSector (FK sector_uuid). - genesis_deviceGenesisDevice (the device that formed this planet — see ./genesis-devices.md). - formationPlanetFormation (1:1 — see ./genesis-devices.md#planetformation). - regionRegion (see ./galaxy.md#region).

player_planets (association)

Columns: player_id, planet_id (composite PK), acquired_at. Both FKs CASCADE delete.