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 — seeport_ownership_service.py's own note that dedicateddefense_budget/operating_budgetcolumns "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:
- owner → Player (many-to-many via player_stations).
- sector → Sector (FK sector_uuid — see ./galaxy.md#sector).
- market → Market (1:1, cascade delete) — see ./economy.md.
- region → Region (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)