Turning EVE Online Intel into a Real-Time Decision Surface

EVE Online capital ship used as the featured image for Snitchboard

TL;DR: EVE Online wormhole intelligence arrives as disconnected fragments: system classifications, corporation identities, recent killmails, and directional scans copied from the game client. Snitchboard turns those fragments into an explainable score inside a live Go TUI. The score does not claim to predict the game. It gives an operator a consistent way to decide where attention is most valuable.

The input is fragmented by design

A wormhole operator rarely receives one complete answer. They may know that a corporation is associated with a system, see a handful of recent losses on zKillboard, and paste a directional scan containing ships whose tactical meaning depends on context.

Each signal is useful and none is sufficient alone:

  • Wormhole class describes static opportunity and access patterns.
  • Recent killmail activity indicates whether a target is active.
  • Repeated activity can matter more than one isolated event.
  • Known hunter involvement changes the interpretation of a loss.
  • Capitals and interdiction ships on d-scan can change the immediate risk.

Snitchboard combines those signals without hiding how the result was produced. The list ranks seeded systems, while the detail view exposes every factor and the underlying directional-scan summary.

Seed first, observe continuously

The application's primary unit is a seed: a solar system paired with a corporation or alliance and an operator-facing label. Seeds persist locally in ~/.snitchboard/seeds.json.

When a seed is added, the application uses EVE's ESI API to resolve names into stable identifiers. A rate-limited zKillboard client then retrieves relevant killmails for the selected system and entity. The TUI refreshes the scores on a timer without asking the operator to rebuild the workspace.

A directional scan follows a different path. The operator pastes raw clipboard output, the parser identifies known ship rows, normalizes names and quantities, estimates field value, and counts tactically relevant classes such as capitals and interdictors. The parsed result is fed back into the same scoring engine.

seeded system + entity
  -> ESI identity resolution
  -> zKillboard activity
  -> domain scoring rules
  -> ranked TUI row

pasted d-scan
  -> row normalization
  -> ship classification
  -> tactical score additions
  -> refreshed detail view

The two streams remain separate until the application layer composes them. That keeps a malformed d-scan from corrupting persisted seed identity and keeps an unavailable external feed from breaking local navigation.

A score should be inspectable

Snitchboard does not delegate scoring to a single function with a large collection of conditions. Each factor implements the same domain rule interface and receives a narrow context.

Factor Signal
Static class Baseline derived from wormhole classification
Corporation activity Recent losses, active target corporations, and high-value events
Recurrence Time-decayed killmail activity over seven days
Hunter heatmap Activity involving configured hunter groups
D-scan additions Capitals and interdiction ships in the current scan

The engine applies the rules in sequence and records each contribution by name. The TUI can therefore answer both questions an operator cares about: "which seed is highest?" and "why is it highest?"

This also makes calibration possible. A score is a heuristic, not a fact. When field experience shows that a factor is overweighted, its rule and tests can change independently. The interface does not need to know how the number was calculated.

DDD earns its place at the edges

Domain-driven design is often applied ceremonially to small tools. Here it solves a real coupling problem.

The domain packages know about systems, entities, seeds, killmails, directional scans, and scores. They do not know about HTTP, Bubble Tea, JSON files, ESI payloads, or zKillboard response fields. The application layer defines the ports required to resolve identities and fetch activity. Infrastructure adapters translate remote payloads into domain objects.

That boundary produces practical benefits:

  • Scoring tests need no network and no terminal.
  • API changes are contained in adapters.
  • The TUI can render domain state without parsing provider payloads.
  • JSON persistence can be replaced without rewriting seed behavior.
  • The d-scan parser can be tested against raw clipboard fixtures.

The architecture remains small: domain, application, infrastructure, and interface packages inside one Go module. There is no service boundary where a package boundary is enough.

The TUI is the workflow

Snitchboard uses Bubble Tea and Lip Gloss rather than treating the terminal as a stream of log lines. The screen has explicit list, detail, d-scan, add-seed, and help states. Window resize events recompute table dimensions, selection is restored when asynchronous rows arrive, and pasted scans trigger a rescore without discarding the current seed.

The list is deliberately dense. System, scope, score, trend, and update state remain scannable without opening a separate page. The detail view then expands factors, d-scan highlights, and a recommended next action.

This is a case where a web dashboard would add operational distance. The data is consumed while the game, voice communications, and other tools already occupy the desktop. A keyboard-driven terminal view is faster to summon, refresh, and dismiss.

Reliability around public APIs

The zKillboard client enforces one request per second with a burst of one and uses context-aware HTTP requests. It reports rate limiting separately from transport and decoding failures. ESI resolution uses bounded HTTP clients and maps corporation, alliance, and system responses into the identifiers the domain expects.

The complete Go test suite passes across the application, domain, parser, HTTP adapters, JSON store, and TUI behavior. HTTP tests use local servers, while scoring and parsing tests stay pure.

The current implementation still has calibration work ahead. Hunter-group identifiers should move from code into operator configuration, and scoring weights need real field feedback before they should be interpreted as anything more than ranking signals. Those are domain improvements, not reasons to blur the architecture.

The useful outcome

Snitchboard converts several minutes of tab switching and mental arithmetic into one ranked, explainable workspace. It preserves the uncertainty of the source data while making the decision process consistent.

That is the design goal: not automated certainty, but a better surface for human judgment under time pressure.