# globalthreatmap: a self-hosted OSINT map that plots events, conflicts and military bases

> A Next.js and Mapbox dashboard that turns Valyu search, answer and deep research calls into pins, country conflict dossiers and briefing exports. It is a thin client over two paid APIs, and its accuracy is only as good as the geocoding step.

**unicodeveloper/globalthreatmap** — Global threat map. Learn wars, conflicts, military bases and history of nations. 

- Repository: https://github.com/unicodeveloper/globalthreatmap
- Website: https://globalthreatmap.up.railway.app
- Stars: 1,842 · Forks: 300
- Language: TypeScript
- License: not declared
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/unicodeveloper-globalthreatmap

## What globalthreatmap actually is, and who it is built for

The README describes the project as "a real-time global situational awareness platform that plots security events, geopolitical developments, and threat indicators on an interactive map", and calls it an OSINT command center. That framing is accurate about the interface and loose about the sourcing. Nothing in the repository fetches news directly. The map is a presentation layer over Valyu's search, answer and deep research APIs, plus a static set of military base records.

The intended user is someone who already does open source monitoring and wants a single screen: a conflict analyst, a journalist tracking a region, a security team that wants a map rather than a feed reader. It is not an alerting system for production infrastructure, and the repository contains no server-side scraper, no database and no ingestion pipeline you could point at your own sources. If you want a map fed by your own collection, this is the wrong starting point.

## How the event pipeline works, from Valyu call to map pin

The data flow is visible in the project structure. API routes live under app/api: events, entities, reports, countries/conflicts and military-bases. Each route is a server-side Next.js handler that calls into lib/valyu.ts, which the README describes as the Valyu client and API functions. The browser never holds the Valyu key; VALYU_API_KEY is a server-side variable, while NEXT_PUBLIC_MAPBOX_TOKEN is exposed to the client by design.

Events arrive from Valyu search, then pass through lib/event-classifier.ts, which assigns a category and a threat level. The README lists the levels as Critical, High, Medium, Low and Info, and the categories as Conflict, Protest, Disaster, Diplomatic and others. Before an event can be pinned, a place name in the text has to become coordinates. That is lib/geocoding.ts. The README states that OPENAI_API_KEY enables "AI-powered location extraction for better accuracy", and that without it "the app falls back to regex-based extraction". That single line is the most consequential design fact in the project. Every pin on the map depends on a string-matching step, and the fallback path is a regular expression.

Client state sits in stores/map-store.ts, described as holding viewport, layers and bases, managed with Zustand. Map rendering is Mapbox GL JS through react-map-gl, with clustering at low zoom and a toggleable heatmap. The country conflict modal is a separate component that fires its own API call when you click a country, which is why the README mentions the selected country filling with red and blinking while data loads.

## Installing globalthreatmap and getting a first event on the map

The README gives a five step local setup. Node 18+ is listed as the prerequisite, though package.json is stricter: its engines field requires Node >=22.13.0 and it pins pnpm@11.7.0 as the package manager. Trust the engines field over the prose if the install misbehaves.

Clone the repository and install dependencies with npm, as the README shows:

```bash
npm install
```

Next, create .env.local in the root directory. The README gives this exact block:

```env
NEXT_PUBLIC_MAPBOX_TOKEN=your_mapbox_token_here
VALYU_API_KEY=your_valyu_api_key_here
NEXT_PUBLIC_APP_MODE=self-hosted

# Optional: Enable AI-powered location extraction for better accuracy
OPENAI_API_KEY=your_openai_api_key_here
```

Three keys are involved. The Mapbox token comes from account.mapbox.com/access-tokens, the Valyu key from valyu.ai, and the optional OpenAI key from platform.openai.com/api-keys. The .env.example file also documents OPENAI_MODEL, with gpt-4.1-nano shown as the default override, and notes that self-hosted is the default app mode while a valyu mode uses OAuth and requires contacting contact@valyu.ai for early access.

Start the dev server and open the app:

```bash
npm run dev
```

The README says to open http://localhost:3000. On first load the map initializes and, per the README, military base data loads automatically: US bases as green markers and NATO installations as blue. Clicking a base shows its name, type and host country. If the map renders but stays empty of events, the Valyu key or the geocoding step is the first thing to check, not the Mapbox token, since bases and events use different paths.

There is also a container path. The repository ships a Dockerfile based on node:22-alpine and a compose.yaml with a single app service, port 3000:3000, reading its keys from a file named dot_env. That file name is unusual and worth noticing: compose does not read .env.local, so a working local setup will not automatically work under compose.

## Where the design breaks: geocoding, sourcing and the cost of a dossier

The weakest link is location extraction. A regex fallback over free text will misplace events, and a misplaced pin on a threat map is worse than no pin, because it looks authoritative. The OpenAI path is optional and off unless you set the key, so the default self-hosted experience is the weaker one. The README does not describe how conflicts between multiple place names in one article are resolved, and it does not document any manual correction step in the interface.

Sourcing is the second constraint. Country conflict data is, in the README's words, "synthesized using Valyu Answer API with cited sources". Citations are present, but the synthesis step means you are reading a generated summary of sources rather than the sources. For a briefing that is often fine. For anything you would publish as fact, the underlying documents are the artifact, not the modal.

The third constraint is cost and latency. Intel dossiers are described as a roughly 50 page intelligence report, a CSV export with locations, coordinates, key figures, related organizations and events, and an 8 slide PowerPoint briefing. Those are deep research calls against a paid API. There is no caching layer described in the repository layout and no rate limiting, so a room full of people clicking countries will spend real money. The README does not document rollback, retries or any queue between the UI and the Valyu calls.

Finally, the licence. package.json declares "license": "ISC", but no LICENSE file appears in the top-level repository entries and the README says nothing about terms. ISC is permissive, but a package.json string is not a licence grant, and the repository also has no releases, so there is no tagged version to pin.

## globalthreatmap compared with Liveuamap and other live threat maps

The obvious reference point, and one people search for alongside this project, is Liveuamap. The difference is not the map. Both put colored pins on a world map with categories and time context. The difference is who does the collection. Liveuamap is an editorial operation: humans and a newsroom decide what becomes a pin, and the map is the product. globalthreatmap is an interface: Valyu's APIs decide what becomes a pin, and the map is a client.

That inverts the trade-offs. A self-hosted Valyu client can be pointed at any query you write, and you can export the results as CSV or a slide deck, which a closed news map will not give you. It also means you inherit every failure of the upstream API and the geocoder, with no editor to catch a wrong pin. If your need is a curated public situation map, a newsroom product is the better tool. If your need is a programmable pipeline that turns a query into a mapped, exportable dossier, this project is the more useful shape, and the export formats are the reason.

A second comparison worth making is against building the same thing yourself. The stack here is Next.js 16 App Router, Tailwind v4, Zustand, zod and valyu-js. None of that is exotic, and the repository is small enough that a team with Mapbox experience could reproduce the map layer in a week. What you are adopting is the glue: the API routes, the classifier, the conflict modal and the export path. Judge it on the glue.

## Deploying it: Railway template, Docker, or your own host

The README offers a Deploy on Railway button pointing at a template, and the project has a live instance at globalthreatmap.up.railway.app. That is the fastest route if you do not want to manage a build. The Docker route is documented in the repository rather than the README prose: a multi-stage-free Dockerfile that enables corepack, activates pnpm@11.7.0, installs with pnpm install --frozen-lockfile, runs pnpm run build and starts with pnpm start on port 3000.

The Dockerfile takes five build arguments: NEXT_PUBLIC_APP_MODE, NEXT_PUBLIC_MAPBOX_TOKEN, NEXT_PUBLIC_REDIRECT_URI, NEXT_PUBLIC_VALYU_AUTH_URL and NEXT_PUBLIC_VALYU_CLIENT_ID, and promotes each to an environment variable before the build. Because they are NEXT_PUBLIC variables, they are inlined into the client bundle at build time. Changing your Mapbox token means rebuilding the image, not restarting the container. The compose file passes keys through env_file: dot_env, so the file you populate must be named dot_env, not .env.local.

Upgrade cost is low in code terms and non-zero in practice. There are no releases and no tags, so upgrading means pulling main and reconciling whatever changed. Dependencies are pinned to caret ranges on fast-moving packages: Next.js 16, React 19, Tailwind 4, mapbox-gl 3 and valyu-js 2. The lockfiles (package-lock.json and pnpm-lock.yaml) both exist, which suggests the project has been built with npm and pnpm at different times; the Dockerfile uses pnpm, so that is the one to follow.

## Conclusion

Adopt it if you already have Mapbox and Valyu keys and want a self-hosted interface for Valyu's search, answer and deep research endpoints, or if you are studying how to wire an LLM intelligence API into a Next.js map. Skip it if you need a free, offline, or independently sourced feed: every event, conflict record and dossier comes from Valyu, and without OPENAI_API_KEY the geocoding falls back to regex extraction. Before deploying, confirm three things: the licence, since package.json says ISC while the repository carries no LICENSE file; your Valyu quota and pricing, because a single dossier is described as a roughly 50 page report; and whether you intend to run the documented docker compose stack or the Railway template, since the Dockerfile bakes NEXT_PUBLIC_MAPBOX_TOKEN in at build time via ARG.

## FAQ

### Is the live cyber threat map real?

For globalthreatmap, the map is real in the sense that it renders live API responses, but the events are not collected by the project itself. They come from Valyu search results, classified by lib/event-classifier.ts and placed using lib/geocoding.ts, which falls back to regex extraction unless OPENAI_API_KEY is set. Treat each pin as a lead to verify against the cited source, not as a confirmed observation.

### What are some popular threat intelligence sites?

The project's own reference point is Liveuamap, which people search for alongside globalthreatmap. The difference is that Liveuamap is an editorial newsroom map, while globalthreatmap is a self-hosted client for Valyu's search, answer and deep research APIs. The README does not name or compare any other threat intelligence site.

### Where can I find a live threat map for cyber attacks?

globalthreatmap is not a cyber attack feed. Its categories are Conflict, Protest, Disaster, Diplomatic and similar, and its data comes from Valyu's APIs rather than network telemetry. If you want a live map of cyber attacks specifically, this project's documentation does not point to one.

## Sources

- [Issues](https://github.com/unicodeveloper/globalthreatmap/issues)
- [Project website](https://globalthreatmap.up.railway.app)
- [README](https://github.com/unicodeveloper/globalthreatmap/blob/main/README.md)
- [unicodeveloper/globalthreatmap on GitHub](https://github.com/unicodeveloper/globalthreatmap)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/unicodeveloper-globalthreatmap
