Oracle Backend
The scoring engine that reads World API data, computes civilization health, and writes on-chain.
Overview
The Oracle Backend is a Node.js service that runs on a 10-minute cron cycle. Each cycle follows a three-phase pipeline: Ingest game data, Process scores, and Write results to the Sui blockchain via batched Programmable Transaction Blocks.
Oracle Cycle Pipeline
Fetch Systems
GET /v2/solarsystems — all 24,502 systems (parallel paginated)
Enrich Data
GET /v2/smartassemblies + /v2/killmails — infrastructure + combat
Score Systems
computeSystemHealth() — activity, trust, infra, combat per system
Score Players
computePlayerReputation() — 5 dimensions + archetype per player
Compute CHI
computeGlobalCHI() — 6 sub-indices → overall score + diagnosis
Detect Anomalies
detectAnomalies() — pattern matching for blackouts, trust collapses, etc.
Write On-Chain
Batched PTBs (50/batch) → PulseRegistry on Sui Testnet
async function runCycle() {
// 1. Fetch solar systems from World API
const systems = await fetchAllSystems();
// 2. Enrich with smart assembly + killmail data
const enrichment = await enrichSystems(systems);
// 3. Compute system health scores (up to 500 per cycle)
const systemScores = systemSubset.map(sys =>
computeSystemHealth(sys, enrichment.get(sys.id))
);
// 4. Detect anomalies
const alerts = detectAnomalies(systemScores, systemNames);
// 5. Compute global CHI
const chi = computeGlobalCHI(systemScores);
// 6. Compute player reputations from enrichment
const playerScores = computePlayerReputation(addr, stats);
// 7. Write to Sui in batches (50 per tx)
await writeSystemHealthBatch(oracleCapId, batch);
await writePlayerReputationBatch(oracleCapId, batch);
await writeGlobalCHI(oracleCapId, chi);
await emitAlertsBatch(oracleCapId, alerts);
}System Health Scoring
Each star system receives a health snapshot based on observable data. When real enrichment data (smart assemblies, killmails) is available, it's used. Otherwise, a deterministic hash function provides consistent fallback scores.
Real Data Path
// Inputs from World API enrichment:
const playerCount = enrichment.activePlayerAddresses.size;
const infraCount = enrichment.smartAssemblyCount;
const kills = enrichment.recentKills;
// Activity: weighted combination of players, infrastructure, combat
const playerScore = Math.min(playerCount * 5, 100);
const infraScore = Math.min(infraCount * 3, 100);
const combatScore = Math.min(kills * 8, 100);
const activityLevel = clamp(
playerScore * 0.4 + infraScore * 0.35 + combatScore * 0.25
);
// Trust: inverse of combat ratio, boosted by infrastructure
const combatRatio = playerCount > 0 ? kills / playerCount : 0;
const baseTrust = clamp(100 - combatRatio * 50);
const infraBoost = Math.min(infraCount * 2, 20);
const trustLevel = clamp(baseTrust + infraBoost);
// Local CHI: 40% activity + 60% trust
const localChi = Math.floor(
(activityLevel * 40 + trustLevel * 60) / 100
);Deterministic Fallback
scoring.ts) and frontend (vitals.ts) for display consistency.Player Reputation (Agora Engine)
The Agora Engine computes 5 behavioral dimensions from observable on-chain activity:
// Inputs: assemblyCount, killCount, deathCount, systemsVisited
reliability = clamp(40 + assemblyCount * 5 + systemsVisited * 2)
commerce = clamp(30 + assemblyCount * 8)
diplomacy = clamp(50 - aggressionRatio * 40 + systemsVisited * 3)
stewardship = clamp(20 + assemblyCount * 10)
volatility = clamp(totalCombat * 5 + |kills - deaths| * 3)
// Archetype classification (first match):
Civilization Builder → stewardship >= 80 && reliability >= 70
Trusted Trader → commerce >= 80 && reliability >= 70
Diplomat → diplomacy >= 75 && volatility < 30
Warlord → volatility >= 70 && commerce < 40
Wildcard → volatility >= 50 && volatility < 70
Newcomer → defaultGlobal CHI Calculation
The Civilization Health Index aggregates all system health scores into a single composite score (0-100) using 6 weighted sub-indices:
// Sub-index computation (from all system scores):
economicVitality = avg(txFrequency) * 0.6 + avg(infraCount * 5) * 0.4
securityIndex = clamp(100 - avg(combatIncidents) * 8)
growthRate = (activeSystems / totalSystems) * 100
connectivity = avg(activityLevel) * 1.1
trustIndex = avg(trustLevel)
socialCohesion = trustIndex * 0.4 + securityIndex * 0.3
+ avg(playerCount) * 3 * 0.3
// Weighted overall (matches smart contract formula):
overall = (
economicVitality * 20 +
securityIndex * 15 +
growthRate * 15 +
connectivity * 15 +
trustIndex * 20 +
socialCohesion * 15
) / 100
// Diagnosis thresholds:
// ≥ 80 Flourishing | ≥ 65 Thriving | ≥ 50 Stable
// ≥ 35 Stressed | ≥ 20 Declining | < 20 CollapsingAnomaly Detection
Each cycle, the oracle scans system scores for unusual patterns and emits alerts as on-chain events:
| Alert Type | Trigger | Severity |
|---|---|---|
| Blackout | infra > 5 but activity < 10 | Critical (0) |
| Trust Collapse | trust < 20 with players > 5 | High (1) |
| Combat Hotspot | combat incidents > 8 | Medium (2) |
| Trade Spike | tx > 85 with players > 20 | Warning (3) |
On-Chain Writer
The suiWriter.ts module handles all blockchain interactions using the Sui TypeScript SDK. Scores are written via Programmable Transaction Blocks (PTBs), which bundle multiple Move calls into a single transaction for gas efficiency.
export async function writeSystemHealthBatch(
oracleCapId: string,
scores: SystemHealthScore[],
): Promise<string> {
const tx = new Transaction();
for (const s of scores) {
tx.moveCall({
target: target("update_system_health"),
arguments: [
tx.object(oracleCapId),
tx.object(config.registryId),
tx.object(SUI_CLOCK),
tx.pure.u64(BigInt(s.systemId)),
tx.pure.u64(BigInt(s.activityLevel)),
tx.pure.u64(BigInt(s.trustLevel)),
tx.pure.u64(BigInt(s.playerCount)),
tx.pure.u64(BigInt(s.infrastructureCount)),
tx.pure.u64(BigInt(s.txFrequency)),
tx.pure.u64(BigInt(s.combatIncidents)),
],
});
}
return await signAndExecute(tx);
}suiprivkey1...), hex, and base64.