Skip to content

Stations & TradeDocks — Schema

Status: 🚧 Partial — Station core (CLASS_0-11, commodities, services, defenses) is committed; station-protection tier (security/tractor/deny-list/fee-distribution), syndicate fence flag … (Re-verified 2026-08-21 vs Sectorwars2102 HEAD 46bce720.)

Trade hubs in sectors. Station covers the canonical Class-0…Class-11 stations (player-ownable, faction-affiliated). TradeDock is the premium NPC-only shipyard hub with its own slip-rental construction pipeline. Companion docs: ../FEATURES/economy/trading.md, ../FEATURES/economy/port-ownership.md, ../FEATURES/economy/tradedock-shipyard.md.

Schema status

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

Design-only columns — present in the spec, not yet committed:

  • Station: defense_budget (no tracked field yet — see port_ownership_service.py's own note that dedicated defense_budget/operating_budget columns "would be cleaner" than the current split), has_syndicate_fence, defense_state.

Shipped, but as Station.security JSONB keys, not discrete columns (re-verified 2026-08-06 against services/gameserver/src/services/station_security_service.py + port_ownership_service.py — this table previously listed these alongside the design-only fields above, and separately as if committed typed columns in the field table below; neither was accurate): tractor_strength, wanted_threshold, deny_list_player_ids, fee_distribution. region_assignment_role is a real committed column (services/gameserver/src/models/station.py, String(32)) and was mis-listed as design-only.

Shipped — the TradeDock discriminator is a tier column on Station, not a dedicated model: Station.tradedock_tier (nullable; NULL = not a TradeDock). The slip-rental construction pipeline is a real, shipped model: ConstructionReservation (construction_reservations table, services/gameserver/src/models/construction.py) tracks one ship-construction project end-to-end (reservation → milestone payments → resource delivery → claim). ConstructionSlip and ShipConstructionProject were never built as separate models — the shipped design consolidates both into ConstructionReservation. See ../FEATURES/economy/tradedock-shipyard.md for the full pipeline status.

All other columns on this page are committed.


Station

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

Purpose: Trade/service hub in a sector; also the locus for the haggling AI and player-acquirable ownership.

Fields:

name type constraints notes
id UUID PK
name String(100) not null
sector_id Integer not null
sector_uuid UUID FK sectors.id nullable, CASCADE
owner_id UUID nullable many-to-many via player_stations
station_class Enum station_class not null CLASS_0…CLASS_11; encodes trading pattern (mining/agri/industrial/distribution/collection/black-hole/nova/luxury/etc.)
type Enum station_type not null TRADING/MILITARY/INDUSTRIAL/MINING/SCIENTIFIC/SHIPYARD/OUTPOST/BLACK_MARKET/DIPLOMATIC/CORPORATE
status Enum station_status default OPERATIONAL OPERATIONAL/DAMAGED/UNDER_CONSTRUCTION/UNDER_ATTACK/LOCKDOWN/ABANDONED/RESTRICTED
size Integer default 5 1-10
faction_affiliation String nullable
trade_volume Integer default 100
market_volatility Integer default 50 0-100
commodities JSONB not null per-commodity (ore/organics/equipment/fuel/luxury_goods/gourmet_food/exotic_technology/colonists) blob with quantity, capacity, base_price, current_price, production_rate, price_variance, buys, sells
trader_personality JSONB not null type, haggling_difficulty, preferred_appeal_types, memory_duration, trust_level, quirks
price_modifiers JSONB default {}
services JSONB not null ship_dealer, ship_repair, ship_maintenance, ship_upgrades, insurance, drone_shop, genesis_dealer, mine_dealer, diplomatic_services, storage_rental, market_intelligence, refining_facility, luxury_amenities
service_prices JSONB default {}
defenses JSONB not null defense_drones, max_defense_drones, auto_turrets, defense_grid, shield_strength, patrol_ships, military_contract — siege defense + outward sector security per ../FEATURES/economy/port-ownership.md.
security JSONB default {} Inward docked-ship protection: {tier, defense_drones_active, defense_drones_max, guard_npc_ids, guard_captain_npc_id, barracks_id, tractor_lock_active, tractor_locked_ship_id, tractor_locked_at, last_attack_at, last_tractor_event_at}. Distinct from defenses — see ../FEATURES/economy/station-protection.md.
security_level @property (not a stored column) derived from security["tier"] none / basic / standard / premium. Defaults to none when security is NULL or has no tier key. Drives docked-ship protection guarantees, anti-theft tractor strength, hired-guard count, and per-dock fee multiplier.
defense_budget (design-only, no field yet) Credits in the station's defense fund; daily upkeep ticks consume it; runs negative for 7 days → station auto-downgrades to none. Not yet tracked anywhere — see Schema status above.
tractor_strength security["tractor_strength"] key (JSONB, not a column) derived, default none none / weak / strong / immobilizing. Derived from security_level. Reads on every undock attempt (station_security_service.check_tractor_lock); activates on stolen-ship / wanted-pilot / deny-listed pilot.
wanted_threshold security["wanted_threshold"] key (JSONB, not a column) default -500 Personal-rep cutoff for tractor activation on wanted pilots. Owner-tunable.
deny_list_player_ids security["deny_list_player_ids"] key (JSONB, not a column) default [] Owner-set blocklist of player UUIDs barred from undocking.
fee_distribution JSONB, set via port_ownership_service.set_fee_distribution() default {"defense_pct": 0.4, "owner_pct": 0.3, "operating_pct": 0.3} Docking-fee revenue split: 40% defense fund / 30% owner / 30% operating. Owner-tunable within bounds (defense 30-60%, owner 10-50%, operating fixed at 30%).
ownership JSONB nullable player ownership details when claimed
last_market_update DateTime server default now
market_update_frequency Integer default 6 hours
reputation_threshold Integer default 0 min reputation to dock
is_quest_hub, is_faction_headquarters, is_player_ownable Boolean defaults false/false/true
is_spacedock Boolean default false Bang's Port.isSpaceDock sentinel; also set on StarDock-special-location hosts so queries that don't load the parent sector's special_features array can still identify them.
is_starport_prime Boolean default false Set only on the single Central Nexus Starport Prime station; distinguishes it from a regional Capital (both CLASS_0) for per-station-class docking-slip pool sizing (200 transient / 50 long-term vs. 80 / 30).
treasury_balance Integer default 0 Station treasury; docking fees and trade tax accrue here.
tax_rate Float default 0.10 Trade tax charged on buy/sell; owner-adjustable within the port-ownership tariff-lever bounds.
acquisition_requirements JSONB not null min_trade_volume, min_faction_standing, base_price
last_attacked DateTime nullable
is_destroyed Boolean default false
recovery_time DateTime nullable
active_events JSONB default []
special_services ARRAY(String) default []
region_id UUID FK regions.id nullable
has_syndicate_fence (design-only, no field yet) 📐 Design-only — not a committed column on station.py (origin/feat 46bce720) or bang. Target: set at galaxy generation on ~8% of eligible non-Federation, non-Nova, non-TradeDock stations; gates the embedded Shadow Syndicate fence sub-port. See ../FEATURES/economy/black-market.md#syndicate-fence-venues. Do not read this row as a shipped Boolean.
tradedock_tier String(1) nullable default NULL The TradeDock discriminator — no separate boolean flag exists. Shipped vocabulary is A (Warp-Jumper-capable, specialized construction slips) / B (standard construction) — not the gateway/industrial/luxury per-tier specialization table in ADR-0041. NULL when not a TradeDock; TradeDocks are NPC-neutral, never player-ownable.
region_assignment_role Enum nullable default NULL Per ADR-0041. welcome / frontier_anchor / tradedock_a / tradedock_b per the per-region station-role table. Null for procedural stations without a designated role.
defense_state (design-only, no field yet) 📐 Design-only — not a committed JSONB column on station.py (origin/feat 46bce720) or bang. Target shape per ADR-0065 M-J2: {current_strength: float 0..1, last_damage_at: iso8601 \| null, last_recovery_tick_at: iso8601, armor_layer_intact: bool, shield_layer_intact: bool, weapon_systems_online: bool}. Used by the pirate-holding-raid recovery path (see ../SYSTEMS/pirate-holding-raid.md). Do not read this row as a shipped column.

Relationships: - ownerPlayer (many-to-many via player_stations). - sectorSector (FK sector_uuid — see ./galaxy.md#sector). - marketMarket (1:1, cascade delete) — see ./economy.md. - regionRegion (see ./galaxy.md#region).

player_stations (association)

Columns: player_id, station_id (composite PK), acquired_at.


TradeDock

Source: services/gameserver/src/models/tradedock.py (target — does not exist)

Live tip (not this table): TradeDock identity is Station.tradedock_tier (A / B / NULL). Construction berth counts are SLIP_POOLS in construction_service.py (B: 12 standard / 0 specialized; A: 8 standard + 4 specialized) — there is no live total_slips column anywhere on gameserver tip.

Purpose (design-target): Premium trading hub plus multi-slip shipyard. Distinct from plain stations because TradeDocks are NPC-owned, can't be captured/destroyed, run a slip-rental shipyard, and have their own rare-commodity inventory layer. See ../FEATURES/economy/tradedock-shipyard.md.

name type constraints notes
id UUID PK
name String(100) not null
sector_id UUID FK sectors.id not null, CASCADE
region_id UUID FK regions.id not null
controlling_faction String(50) not null Federation / Mercantile / Frontier / etc.
station_class_equivalent Integer default 11 reputation-and-defense-equivalent class for service availability
total_slips Integer default 12 Design-target only — not a live column; tip uses Station.tradedock_tier + SLIP_POOLS
occupied_slips Integer default 0 denormalized count (design-target)
rare_commodity_inventory JSONB not null per-rare-commodity stock (titanium_alloys, lumen_crystals, quantum_shards, etc.)
premium_spread_bonus Float default 0.125 10–15% better-than-base spread (±0.025)
transaction_fee_pct Float default 0.0 TradeDocks waive transaction fees
bulk_discount_per_1k Float default 0.05 5% per 1,000-unit purchase, 20% cap
reputation_gate Integer default 200 minimum faction reputation to enter slip queue
guest_fee Integer default 100000 one-time fee for cross-faction guests
is_destroyed Boolean default false always false — TradeDocks can't be destroyed; column reserved
region_funded Boolean default false true if built by a player-region (5% income to region treasury)
created_at, updated_at DateTime server defaults

Relationships: sector, region, slips (1:many → ConstructionSlip), controlling_faction_obj (FK to Faction)

ConstructionSlip

Source: services/gameserver/src/models/construction_slip.py (target)

Purpose: One of 12 shipbuilding slips at a TradeDock. Holds an in-progress ShipConstructionProject while it builds.

name type constraints notes
id UUID PK
tradedock_id UUID FK tradedocks.id not null, CASCADE
slip_number Integer not null 1–12 within the TradeDock
status Enum slip_status default available available / reserved / building / claim_pending / abandoned
current_project_id UUID FK ship_construction_projects.id nullable active project occupying the slip
reserved_until DateTime nullable reservation expiry for reserved status (24h hold)
reserved_by UUID FK players.id nullable

Relationships: tradedock, current_project (1:1 to active ShipConstructionProject), reserver (Player)

ShipConstructionProject

Source: services/gameserver/src/models/ship_construction_project.py (target)

Purpose: A player's ship-build project running on a slip. Tracks construction phase, milestone payments, and customization.

name type constraints notes
id UUID PK
slip_id UUID FK construction_slips.id not null
player_id UUID FK players.id not null builder
target_ship_type Enum ship_type not null Scout / Light Freighter / Fast Courier / Cargo Hauler / Defender / Colony Ship / Carrier / Warp Jumper (8 craft-able types — Escape Pod is issued, not slip-built)
construction_phase Enum construction_phase default frame frame / systems / outfitting / final
progress_pct Float default 0.0 0.0–1.0 across all phases
started_at DateTime server default
target_completion_at DateTime not null start + planned build days
total_project_cost Integer not null all-in cost (slip + labour + resources at market)
credits_paid Integer default 0 cumulative milestone payments received
deposit_paid Boolean default false initial 10–25% deposit
keel_milestone_paid Boolean default false first 25% milestone
hull_milestone_paid Boolean default false second 25% milestone
final_payment_paid Boolean default false final 25–40% on claim
resources_delivered JSONB default {} per-resource delivery counters
resources_required JSONB not null resource bundle for the target ship
customization JSONB default {} paint scheme, name, optional modifications
space_engineer_assigned UUID FK players.id nullable profession-bonus assignee
event_log JSONB default [] construction events (Quality Discovery / Resource Shortage / etc.)
status Enum project_status default building building / paused (rent overdue) / cancelled / complete / claimed

Relationships: slip, player, space_engineer (Player)