Technical Briefing: Crucix

The Problem

Intelligence analysts, researchers, and journalists need real-time visibility across dozens of open-data feeds—satellite fire detection, flight tracking, economic indicators, conflict events—but each feed lives on a different government API or research institution with no unified interface. Checking them individually is infeasible, and existing platforms lock data behind paywalls or require security clearances. There is no self-contained, zero-cloud solution that aggregates 27 OSINT feeds on demand and pushes alerts when something changes.

What This Does

Crucix is a Node.js application that pulls data from 27 open-source intelligence feeds in parallel every 15 minutes and renders them on a single dashboard. The core architecture flows through server.mjs as the entry point, which initiates an intelligence sweep by calling apis/utils/fetch.mjs — the hub module that 25 other modules depend on (instability 0.93). That fetch module coordinates requests to individual source files under apis/sources/ (e.g., acled.mjs, adsb.mjs, yfinance.mjs), then funnels results through lib/llm/index.mjs for LLM routing. When meaningful changes detect, alerts flow via lib/alerts/discord.mjs and lib/alerts/telegram.mjs to messaging platforms. The dashboard runs in dashboard/inject.mjs and renders the consolidated view at http://localhost:3117. The codebase is 54 JavaScript files with 93 import edges and no circular dependencies, but 14 repeated 6-line blocks appear across lib/alerts/discord.mjs, lib/alerts/telegram.mjs, lib/llm/minimax.mjs, and lib/llm/openai.mjs, violating DRY principles.

How It Is Wired

Execution starts at server.mjs, which sets up an Express server and triggers the first intelligence sweep. The call graph routes through apis/utils/fetch.mjs (Ca=2, Ce=28), which fans out to 27 source modules under apis/sources/. Each source module pulls from a specific API—gdelt.mjs for global event data, firms.mjs for satellite fire detection, adsb.mjs for flight tracking, among others. Results flow into lib/llm/index.mjs, which dispatches to providers like lib/llm/openai.mjs, lib/llm/anthropic.mjs, and lib/llm/gemini.mjs. The LLM layer can generate trade ideas or respond to /brief and /sweep commands. Alerts exit the process via lib/alerts/discord.mjs and lib/alerts/telegram.mjs. The dashboard entry dashboard/inject.mjs pushes the rendered HTML to the browser. Outside the process, the API surface at server.mjs accepts inbound webhook calls from Telegram and Discord, and the Docker setup (Dockerfile, docker-compose.yml) packages the whole stack for deployment.

What this code touches outside itself: Outbound HTTP requests to 27 OSINT APIs; outbound messages to Discord and Telegram webhooks; filesystem writes when saving briefings via apis/save-briefing.mjs; containerized runtime via Docker.

Hub/blast-radius risk: apis/utils/fetch.mjs is the single point through which all 27 source modules flow. Changing its error handling, rate-limiting, or timeout behavior affects the entire data pipeline.

How To Use It

Setup

git clone https://github.com/moses-y/Crucix.git
cd Crucix
npm install
cp .env.example .env

Configuration Copy .env.example to .env and add API keys for the feeds you intend to use. The environment file is read by apis/utils/env.mjs. Required keys depend on which source modules are imported—the .env.example lists the expected variables.

Running it

npm run dev

If the script exits silently, run:

node --trace-warnings server.mjs

or diagnose with:

node diag.mjs

The dashboard opens at http://localhost:3117. The first sweep takes 30–60 seconds as all 27 sources query in parallel.

Real-World Use

A researcher subscribes to Telegram alerts from Crucix. Every 15 minutes the system queries 27 feeds; if firms.mjs detects a new fire hotspot in a protected area, or acled.mjs records a new conflict event, the LLM in lib/llm/index.mjs evaluates whether the change meets the alert threshold. If so, lib/alerts/telegram.mjs pings the researcher with a summary and a link to the dashboard. The researcher can also open the dashboard and type /sweep to trigger an on-demand refresh, or /brief to get an LLM-summarized briefing of current intelligence.

Code Health & Issues

The static analysis produced 28 findings across 2 high and 26 medium severity categories. The code health audit lists 7 findings ranked by severity and production reach:

  • [HIGH] Add a workflow that builds and tests this repository — 54 source files, no CI configuration. Every change merges with nobody having run the build.
  • [HIGH] Add a build gate for the deployable artifacts here — Dockerfile exists but no automated step has ever validated the image.
  • [HIGH] Replace the wildcard CORS origin with an explicit allow list — server.mjs sets Access-Control-Allow-Origin': '*', which combined with credentials allows any page on the internet to call the API with the browser's cookies attached.
  • [HIGH] Remove eval over a runtime value — server.mjs uses exec() over a computed value, enabling arbitrary code execution wherever the input originated.
  • [MEDIUM] Enable Dependabot or Renovate — 1 manifest, no update bot configured.
  • [MEDIUM] Pin the container base image by digest — Dockerfile uses node:22-alpine without a digest, meaning today's build contains different libc and a different CVE set than last month's.
  • [MEDIUM] Add a non-root USER to the image — Dockerfile has no USER directive; the process runs as root against every mounted volume.

The measured analysis also flagged apis/utils/fetch.mjs as a high-instability hub (25 dependents, instability 0.93) and duplicated 6-line blocks across the alert and LLM provider files.

The Bottom Line

Crucix delivers on its promise: a zero-cloud intelligence terminal that aggregates 27 OSINT feeds and renders them on a self-contained dashboard. The architecture is coherent, the import graph is clean (no circular deps), and the 15-minute sweep cycle is practical. However, the repository has no CI/CD, the Dockerfile uses a mutable base image, CORS is wide-open with credentials, and eval appears over a runtime value — all production-grade risks. It is suitable for researchers, journalists, and analysts who want to run the stack locally and accept responsibility for securing their API keys and deployment environment.