Instruction file imported from dfarre888/SPECTRAL (
.cursor/rules/03-database-schema.mdc). Copyright stays with the author.
Database Schema Reference
Supabase Client Tiers — Always Pick the Right One
// Browser (client components only) — anon key, RLS enforced
import { createBrowserClient } from '@/lib/supabase/client'
// Server (Server Components + API routes) — anon + cookie session, RLS enforced
import { createClient } from '@/lib/supabase/server'
const supabase = await createClient()
// Service role server (admin ops only, explicit call) — bypasses RLS
import { createServiceClient } from '@/lib/supabase/server'
// Node.js runtime service role (PCM WorldStateEngine + scripts only) — WebSocket transport
import { createServiceRoleNodeClient } from '@/lib/supabase/service-role-node'
Rules:
createServiceClient()andcreateServiceRoleNodeClient()NEVER appear in client components or page componentsservice-role-node.tsis forlib/pcm/worldStateEngine.tsand standalone Node scripts ONLY- Demo mode auto-elevates
createClient()to service role — never setNEXT_PUBLIC_DEMO_MODE=truein production
Migration Files
supabase/migrations/ — 30+ timestamped migration files. Apply in order.
Apply with: supabase db push OR Supabase MCP apply_migration.
Never modify existing migration files. Always create new ones: YYYYMMDDHHMMSS_description.sql
Pending (must apply in order):
20260624120000_iran_missile_platforms.sql20260624130000_us_allied_cruise_missiles.sql— has guidance_type constraint dependency on #1
Tables (Supabase PostgreSQL)
platforms — Military UAS
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
platform_id |
text unique | e.g. mq-9b-skyguardian |
name |
text | Display name |
country_of_origin |
text | ISO country name |
manufacturer |
text | |
dod_group |
text | Group 1–5 or NATO class |
role |
text[] | ISR, Strike, EW, Decoy, Relay, Swarm |
max_altitude_ft |
integer | |
max_range_km |
integer | |
max_endurance_hrs |
numeric | |
max_speed_kts |
integer | |
payload_kg |
numeric | |
combat_proven |
boolean | |
combat_theatres |
text[] | Ukraine, Syria, Libya, etc. |
status |
text | Operational / Development / Retired |
description |
text | |
specs |
jsonb | Additional KPIs |
created_at |
timestamptz |
gnss_jammers — EW Jamming Systems
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
jammer_id |
text unique | |
name |
text | |
country_of_origin |
text | |
jammer_tier |
text | tier_1_military / tier_2_sdr / tier_3_cots |
target_frequencies |
jsonb | Array of {band, min_mhz, max_mhz} |
effective_range_m |
integer | |
power_output_w |
numeric | |
spoofing_capable |
boolean | |
procurement |
text | Commercial / Military / DIY |
cost_usd_approx |
integer | |
skill_required |
text | None / Basic / Technical / Expert |
self_jamming_risk |
boolean | |
legal_status |
text | |
description |
text |
gnss_constellations — Satellite Navigation Systems
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
constellation_id |
text | gps / glonass / galileo / beidou / navic |
name |
text | |
operator_country |
text | |
satellites_total |
integer | |
satellites_operational |
integer | |
frequencies |
jsonb | L1, L2, L5, E1, E5, etc. |
accuracy_m |
numeric | Civilian |
military_signal |
text | P(Y), M-code, etc. |
status |
text |
anti_drone_systems — C-UAS
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
system_id |
text unique | e.g. drone-dome |
name |
text | |
manufacturer |
text | |
country |
text | |
defeat_methods |
text[] | Kinetic / EW / Laser / Net |
max_range_m |
integer | |
target_groups |
text[] | Group 1–5 |
mobile |
boolean | |
combat_proven |
boolean | |
description |
text |
conflict_incidents — Case Studies
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
incident_id |
text unique | |
title |
text | |
conflict |
text | Ukraine / Red Sea / Libya / etc. |
date_range |
text | |
platforms_used |
text[] | References platform_id |
lessons_learned |
text[] | |
significance |
text | tactical / operational / strategic |
description |
text |
nav_countermeasures — GPS-Denied Navigation Methods
| Column | Type | Notes |
|---|---|---|
id |
uuid PK | |
method_id |
text unique | |
name |
text | |
type |
text | Visual SLAM / INS / Terrain-Ref / Fiber-Optic / Celestial |
defeats_jamming |
boolean | |
defeats_spoofing |
boolean | |
cost_tier |
text | Low / Medium / High |
description |
text |
scenario_templates + scenario_injects
scenario_templates— Red/Blue scenario configs with terrain, ORBAT, phase timelinescenario_injects— 15 pre-seeded injects (GNSS Degraded, ROE Change, Swarm Detected, Spoof Alert, Fiber Optic FPV, etc.)
Defeat / Accredited Tables
| Table | Purpose |
|---|---|
defeat_matrix_entries |
Platform × defeat system matchings with base kinetic/EW/DEW Pk |
accredited_defeat_pk |
Accredited Pk overrides per platform × defeat system (PCM-scoped only) |
accredited_erp_profiles |
Accredited ERP emission profiles for EW defeat systems |
accredited_waveform_profiles |
Accredited waveform data for signal analysis |
Critical isolation rule: Accredited table data NEVER flows to the learner model (spectral_competency_records), client components, or any Claude call. Access via lib/operations/accredited-supplements.ts only.
PCM (Persistent Combat Model) Tables
| Table | Purpose | Write path |
|---|---|---|
spectral_scenarios |
Scenario templates for exercise creation | Migration only |
spectral_players |
Player records (role: red/blue/ds) | Exercise creation |
spectral_exercises |
Active exercise state + world state snapshot | WorldStateEngine only |
spectral_world_states |
Per-turn world state history | WorldStateEngine only |
spectral_orders |
Red/Blue orders per turn | submitOrders() route |
spectral_turn_records |
Turn adjudication results | WorldStateEngine only |
spectral_aar_documents |
After Action Review documents | finaliseExerciseAAR() only |
MOAT (Learner Model) Tables
| Table | Purpose | Write path |
|---|---|---|
spectral_competency_records |
Longitudinal trainee competency | processMoatAfterTurn() ONLY |
spectral_training_plans |
Next training assignments | processMoatAfterTurn() ONLY |
NEVER insert directly into MOAT tables. All writes must go through processMoatAfterTurn().
WOPR Tables
| Table | Purpose |
|---|---|
wopr_scenarios |
WOPR Red/Blue Arena scenario state |
Operations Tables
| Table | Purpose |
|---|---|
tenant_members |
Tenant ↔ user ↔ role mapping (Operations edition) |
audit_log |
Append-only audit trail (write via writeAuditLog() only) |
TypeScript Interfaces
All types live in lib/types/index.ts:
Platform, GnssConstellation, GnssJammer, JammerTier,
AntiDroneSystem, ConflictIncident, NavCountermeasure,
ScenarioConfig, ScenarioInject
Always import from @/lib/types — never redefine inline.
Supabase Query Patterns
Server component (correct)
import { createClient } from '@/lib/supabase/server'
export default async function PlatformsPage() {
const supabase = await createClient()
const { data, error } = await supabase
.from('platforms')
.select('*')
.eq('status', 'Operational')
.order('name')
// ...
}
Client component with realtime (correct)
'use client'
import { createClient } from '@/lib/supabase/client'
// use inside useEffect or React Query
NEVER do these
supabaseAdmin/ service role key in any client component or page componentsupabase.from('platforms').select('*')without.order()on list pages — always paginate or order- Raw SQL via
supabase.rpc()unless there's a specific reason — prefer the query builder - Bypassing RLS with
{ count: 'exact', head: true }queries in user-facing paths
RLS Policies
- Read access is open (anon key is fine for all SELECT)
- Write access requires authentication
- Service role key is server-side only (never expose to browser)
Migration Files
supabase/migrations/001_initial_schema.sql — contains the full schema + 15 scenario injects pre-seeded.
Never modify this file directly. Create new migration files sequentially: 002_..., 003_...