Database Schema Reference

Purpose: AI-readable reference for the Structs Guild Stack PostgreSQL database. Covers core game state tables, the key-value grid pattern, event categories, and ready-to-use query patterns.

Requires: Guild Stack running locally. See .cursor/skills/structs-guild-stack/SKILL.md for setup.


Schema Overview

PostgreSQL 17 with TimescaleDB extension. Four schemas:

Schema Purpose
structs Game state (~50 tables covering all game objects)
cache Blockchain event indexing (event-sourcing from chain to game state)
signer Transaction signing queue (TSA account/role/tx management)
sqitch Schema migration tracking

Database Roles

Role Access Used By
structs Superuser (owner) Administration, migrations
structs_indexer Read/write on structs.*, cache.* structsd (indexer), structs-grass
structs_webapp Read/write on most structs.*, full on signer.* Webapp, MCP, TSA
structs_crawler Read-only on select tables Crawler

For agent queries, use structs_indexer via the GRASS container (see guild-stack skill).


Core Game State Tables

structs.player

Column Type Notes
id varchar PK 1-{index} (e.g., 1-142)
index integer Numeric portion of ID
guild_id varchar Guild membership (e.g., 0-1)
planet_id varchar Home planet (e.g., 2-105)
fleet_id varchar Fleet (e.g., 9-142)
primary_address varchar Cosmos address
creator varchar Who created this player

structs.fleet

Column Type Notes
id varchar PK 9-{index}
owner varchar Player ID
status varchar on_station, away
location_type varchar planet
location_id varchar Planet ID where fleet is located
command_struct varchar Command Ship struct ID
space_slots / air_slots / land_slots / water_slots integer Available slots per ambit

structs.struct

Column Type Notes
id varchar PK 5-{index}
type integer FK to struct_type.id (1-22)
owner varchar Player ID
location_type varchar fleet or planet
location_id varchar Fleet/planet ID
operating_ambit varchar space, air, land, water
slot integer Position within ambit (0-3)
is_destroyed boolean Destruction state
destroyed_block bigint Block height when destroyed

structs.struct_type

The authoritative reference for all game balance data (~60 columns). Key columns:

Weapons (primary + secondary prefixed):

Defense:

Other:

structs.struct_attribute

Key-value attributes per struct instance.

Column Type Notes
object_id varchar Struct ID
attribute_type varchar health, status, protectedStructIndex, etc.
val integer Attribute value

structs.struct_defender

Defense assignments.

Column Type Notes
defending_struct_id varchar PK The defender
protected_struct_id varchar The struct being protected

structs.planet

Column Type Notes
id varchar PK 2-{index}
owner varchar Player ID
max_ore integer Maximum ore capacity
space_slots / air_slots / land_slots / water_slots integer Slot counts
status varchar Planet status

structs.planet_attribute

Column Type Notes
object_id varchar Planet ID
attribute_type varchar planetaryShield, etc.
val integer Attribute value

structs.planet_raid

Column Type Notes
planet_id varchar Target planet
fleet_id varchar Raiding fleet
status varchar initiated, completed, etc.
seized_ore numeric Ore taken

The Grid Table (Key-Value Pattern)

structs.grid is a key-value store for resource attributes. This is the most common source of query errors.

Column Type Notes
object_id varchar Player/planet/struct/etc ID
attribute_type varchar ore, alpha, structsLoad, capacity, fuel, power, etc.
val numeric The value

Wrong (no ore column exists):

SELECT ore FROM structs.grid WHERE object_id = '1-142';

Correct (filter by attribute_type):

SELECT val FROM structs.grid WHERE object_id = '1-142' AND attribute_type = 'ore';

Multiple attributes (use JOINs):

SELECT p.id,
    COALESCE(g_ore.val, 0) as ore,
    COALESCE(g_cap.val, 0) as capacity,
    COALESCE(g_load.val, 0) as structs_load
FROM structs.player p
LEFT JOIN structs.grid g_ore ON g_ore.object_id = p.id AND g_ore.attribute_type = 'ore'
LEFT JOIN structs.grid g_cap ON g_cap.object_id = p.id AND g_cap.attribute_type = 'capacity'
LEFT JOIN structs.grid g_load ON g_load.object_id = p.id AND g_load.attribute_type = 'structsLoad'
WHERE p.id = '1-142';

Grid Attribute Types

attribute_type Found On Meaning
ore player, planet Ore balance (player = mined/stealable; planet = remaining)
capacity player, reactor, substation Energy capacity
structsLoad player Energy consumed by active structs
fuel reactor Total ualpha infused
power infusion Energy generated from fuel
connectionCapacity substation Available capacity per connection
connectionCount substation Active connections
load player Base player load

planet_activity (Event Log)

TimescaleDB hypertable for all planet-level events.

Column Type Notes
time timestamp Event time
seq integer Sequence number (monotonically increasing, use as high-water mark)
planet_id varchar Planet where event occurred
category enum Event type (see below)
detail JSONB Event-specific data

Event Categories

Category Description
block New block committed
raid_status Raid initiated/completed/failed
fleet_arrive Fleet arrived at planet
fleet_advance Fleet movement in progress
fleet_depart Fleet departed from planet
struct_attack Combat attack event
struct_defense_add Defense assignment added
struct_defense_remove Defense assignment removed
struct_status Struct status change (online/offline/destroyed)
struct_move Struct moved between slots/ambits
struct_block_build_start Build PoW started
struct_block_ore_mine_start Mining PoW started
struct_block_ore_refine_start Refining PoW started
struct_health Struct health changed (damage)
guild_consensus Guild consensus events
guild_meta Guild metadata changes
guild_membership Membership changes
player_consensus Player consensus events
player_meta Player metadata changes

Polling Pattern (Real-Time Monitoring)

-- Initialize: set high-water mark
SELECT COALESCE(MAX(seq), 0) as last_seq
FROM structs.planet_activity
WHERE planet_id IN ('2-105');

-- Poll every ~6 seconds
SELECT seq, planet_id, category, detail::text
FROM structs.planet_activity
WHERE planet_id IN ('2-105', '2-127')
    AND seq > $LAST_SEQ
ORDER BY seq ASC;

The detail column for struct_attack includes attackerStructId, targetStructId, and eventAttackShotDetail with per-shot damage breakdowns.


Energy Commerce Tables

Table Key Columns Notes
reactor id, guild_id, validator Links validator address to guild
infusion destination_id, address, fuel, power, commission Composite PK: (destination_id, address)
allocation id, source_id, destination_id, locked Energy routing
substation id, owner Power distribution nodes
provider id, rate_amount, rate_denom, access_policy Energy marketplace listings
agreement id, provider/consumer refs, capacity, duration Active purchase contracts

Other Time-Series Tables (TimescaleDB)

Hypertable Purpose Key Columns
ledger Financial transaction log time, address, amount, action, direction, denom
stat_ore Ore value history time, object_type, object_index, value
stat_capacity Capacity history Same pattern
stat_fuel Fuel history Same pattern
stat_load Load history Same pattern
stat_power Power history Same pattern
stat_struct_health Struct health over time time, object_index, value
stat_struct_status Struct status over time Same pattern
stat_structs_load structsLoad over time Same pattern

Ledger Action Types

genesis, received, sent, migrated, infused, defusion_started, defusion_cancelled, defusion_completed, mined, refined, seized, forfeited, minted, burned, diversion_started, diversion_completed


Signer Schema (Transaction Signing Agent)

The TSA manages a pool of signing accounts. Services insert rows into signer.tx with status='pending'. TSA claims them, signs, and broadcasts.

Table Key Columns Notes
signer.role id, player_id, guild_id, status Status: stub, generating, pending, ready
signer.account id, role_id, address, status Status: stub, generating, pending, available, signing
signer.tx id, module, command, args (JSONB), status Status: pending, claimed, broadcast, error. 90+ command types.

ID Format Reference

All game object IDs follow {type_prefix}-{index}. See entity-relationships.md for the complete type code table.

Prefix Object Type Example
0- Guild 0-1
1- Player 1-142
2- Planet 2-105
3- Reactor 3-1
4- Substation 4-5
5- Struct 5-1165
6- Allocation 6-10
9- Fleet 9-142
10- Provider 10-2
11- Agreement 11-1

See Also