Open-source project
eli-labz/Third-Eye avatar
eli-labz/Third-Eye

eli-labz/Third-Eye: sixteen data layers claimed, thirteen rows in the domain table, and a port scanner that lives elsewhere

A production-grade OSINT platform that provides situational awareness across multiple intelligence domains.

873 stars28 forksTypeScriptMIT

At a glance

What is it?
Third Eye is a Next.js and MapLibre OSINT dashboard that layers aviation, maritime, CCTV, seismic, conflict and other feeds onto one WebGL map and self-hosts from a public container image. Its own numbers disagree with each other, the compose file needs a Docker network it refuses to create, and the RECON port scanner talks to a backend that is not in this repository.
Who is it for?
Third Eye is a map you can start in a few minutes, and the layers behind it are mostly real, keyless public sources. Check three things before you count on it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 114 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Sixteen live data layers, thirteen rows in the domain table

The feature list opens with 16 live data layers, then names twelve of them and trails off with and more: aviation, maritime, CCTV, seismic, fires, news, weather, space, cyber, conflict, crypto, sanctions. The Intelligence Domains table further down carries thirteen rows, because Telegram OSINT gets one of its own. The Overview settles on a third figure and says the platform spans 13+ intelligence domains. Sixteen claimed, thirteen tabulated, thirteen-plus hedged, all on the same page. No variable in the environment template or the configuration section pins that number, and the only toggle described is per-layer visibility, so an operator cannot confirm from configuration which set of sixteen is meant. The table also mixes live and static in a single row. Maritime is labelled Static Naval Intel with 39 global ports and 10 chokepoints, while Aviation is credited to the OpenSky Network and Seismic to the USGS Earthquake API at M2.5 and above. Conflict is likewise credited to Static OSINT Intel for 13 active zones, which means two of the thirteen domains ship as coordinates rather than as feeds.

The Architecture heading has nothing under it

Between the Quick Start section and the overview of layers sits a level two heading reading Architecture, a horizontal rule, and then the next level two heading. No diagram, no file tree, no prose occupies that space, so the one section that would explain how sixteen domains, eight recon tools and a WebGL map share a single Next.js application is the section with no content in it. The Tech Stack table at the end of the file fares worse: its first data row stops at the letters La and the document ends there. package.json tells the same story more completely. Next.js is pinned at 16.2.6, React and React DOM at 19.2.4, maplibre-gl at 5.24, alongside react-map-gl, framer-motion, satellite.js, rss-parser, hls.js, sharp and ws in the runtime block. Two of those have no counterpart in any layer description. @google/generative-ai is a production dependency of a dashboard whose visible feature list never mentions a language model, and google-libphonenumber sits beside it with no phone parsing feature described either. Both are installed on every start whether or not anything calls them.

docker compose up needs a Docker network the file never creates

The compose service joins two networks, default and umami_default. The second one is declared external, which means Compose will not create it. On any host without a network of that name already present, docker compose up fails while resolving the project, before the image is pulled and before any container exists. The Docker quick start does not mention the requirement. The only hint at where the name comes from sits in two Python scripts at the repository root, fix_netdata.py and fix_umami.py, each named after the service it patches. The compose file also carries a CasaOS app-store block, an x-casaos extension that plain Compose ignores, listing architectures amd64 and arm64 and naming simplifaisoul as both author and developer. The repository lives under eli-labz while the container image is pulled from aiacos, so three names attach to a single project. Two runtime settings matter more than the store metadata. Restart policy is unless-stopped, so a container that fails to start will retry indefinitely across reboots, and extra_hosts maps host.docker.internal to the host gateway, which hands the container a route to every service the host itself can reach.

The port scanner is a client for a backend this repository does not contain

The RECON toolkit is presented as built in, with eight entries: a TCP connect port scan with service fingerprinting, DNS resolution across A, AAAA, MX, NS, TXT and CNAME records, WHOIS with an automatic OFAC SDN cross-check, an SSL/TLS certificate chain inspector, IP intelligence covering geolocation, ASN and threat reputation, a CVE lookup against the NVD database, BTC and ETH wallet tracing, and a full-text OFAC sanctions search across persons, organisations, vessels and aircraft. Seven of those eight run against public keyless sources. The port scanner does not, and neither does the Custom Scanner that the Cyber domain row credits next to the NVD. Both read two environment variables:

env
# RECON scanner backend — generate key with: openssl rand -hex 32
SCANNER_URL=
SCANNER_KEY=

Neither ships with a value, no scanner service appears among the repository's top-level entries, and the configuration note states the consequence directly: without them the RECON toolkit returns 503. SCANNER_KEY is expected to be 32 bytes of hex, generated with openssl rand -hex 32. The operator therefore has to source a scanning backend, host it, put a shared secret in front of it, and decide what it is allowed to touch. On scope, nothing visible names a target list, a dry-run flag, or a way to stop a scan already in flight. A TCP connect scan with service fingerprinting aimed at infrastructure you do not own is not reconnaissance work. The defensible reading is that this control needs a backend whose own access controls you have already reviewed, and neither the layer toggle nor the 503 response will enforce them for you.

Eight variables in the template, and one that only appears in prose

The configuration block is short and mostly optional. THIRDEYE_PORT sets the published host port, and the note beside it says the container itself always listens on 3000, which the compose file honours through the mapping ${THIRDEYE_PORT:-3000}:3000. Everything else is either a scanner endpoint or a rate limit key: FIRMS_API_KEY for NASA FIRMS hotspots, OPENSKY_CLIENT_ID and OPENSKY_CLIENT_SECRET for OpenSky OAuth2, N2YO_API_KEY for satellites, and AIS_API_KEY for aisstream.io maritime. That last one sits oddly beside the domains table, which credits Maritime to static naval intel rather than to a live transponder feed, so a keyed live channel and a static layer occupy the same row. One variable is documented outside the template completely: THIRDEYE_TELEGRAM_CHANNELS overrides the channel list for the Telegram layer, which is described as scraped from the unauthenticated t.me/s/ web preview with no Bot API token and no MTProto, then geoparsed against a place dictionary spanning English, Cyrillic and Arabic. The compose file marks .env as not required, so a missing file is tolerated and the container falls back to keyless feeds. That leniency is why the undocumented variable matters more than it looks. A channel list settable only by already knowing the name has no discoverable default and no sample value anywhere.

The container you run is published from another GitHub org

The image reference in the compose file is ghcr.io/aiacos/third-eye:latest, and the shortest path in the quick start pulls the same one:

bash
docker pull ghcr.io/aiacos/third-eye:latest
docker run -d -p 3000:3000 --env-file .env ghcr.io/aiacos/third-eye:latest

The source tree is eli-labz/Third-Eye. The registry path belongs to a different organisation, so the image an operator actually runs is not built from the account being read, and nothing in the repository ties the two together or records which commit produced the tag. The compose file hedges by keeping a build block for hosts that cannot pull, pointing at the local Dockerfile. That Dockerfile is a three-stage build on node:22-alpine: npm ci against the lockfile, next build, then a runner stage that creates a system group and user at gid and uid 1001, copies public, .next/standalone and .next/static, switches to that user, exposes 3000 and starts node server.js. Nothing else ships, so no source, no .env and no lockfile exist in the running container. One compose line deserves a second read: NODE_OPTIONS is pinned to --dns-result-order=ipv4first, forcing IPv4 answers ahead of IPv6 for every lookup the application makes.

Version 0.1.0, marked private, and one release tagged with the repo name

package.json carries version 0.1.0 and private set to true, which is the correct default for an application that is never meant to be installed from a registry. The release history is where the versioning story stops. The single published release is tagged Third-Eye, named Third Eye, and went out at 2026-06-13T06:29:57Z, one second after the last push recorded for the branch. A tag that repeats the repository name is not a version, so there is no semver to pin, no upgrade path between builds and no changelog to diff. Anyone tracking a build has to track a digest instead, and the only other dated fact is that last push on 2026-06-13. The surrounding tooling is more conventional. eslint-config-next sits at 16.2.6 to match the framework, Tailwind 4 runs through PostCSS, TypeScript is 5, and vitest 3.2.6 is wired to npm test alongside npm run dev, npm run build, npm start and npm run lint. The root also carries DO_NOT_PUSH.md, GUARDRAILS.md, SECURITY.md, AGENTS.md and CLAUDE.md, which is a heavy stack of governance notes for a first cut, and the repository declares no topics at all.

Sixty frames per second is asserted, never measured

The performance case is stated three times and measured nowhere. Every data point renders through WebGL instead of the DOM, thousands of concurrent entities run at 60fps, and edge requests dropped 75% compared with the initial release. There is no benchmark script at the repository root to produce any of those numbers, and the test runner is vitest, configured for unit tests rather than load measurement. The 75% figure also has no baseline to divide by, since the release list contains nothing older than the first cut. Underneath the claims sit four measures that can be checked by reading them: layers fetch on demand when switched on, only the visible region is requested, stable categories poll on 15 to 30 minute intervals, and static data is held in memory. That last one contradicts a feature claim. The performance notes say news feeds cost zero external API calls because they are served from memory, while the feature list promises 25 or more live 24/7 streams from NBC, CBS, ABC, Sky News, Al Jazeera, France 24, NHK and WION. The hls.js dependency resolves the tension. The video plays in the browser straight from the broadcaster, so what the map holds in memory is metadata about where to point a player, not the stream itself.

Editorial conclusion

Third Eye is a map you can start in a few minutes, and the layers behind it are mostly real, keyless public sources. Check three things before you count on it. The port scanner needs a backend you have to source, host and secret-protect yourself, and nothing visible describes target selection, dry-run or how to stop a scan. The compose file attaches to a Docker network named umami_default that it will not create, so a clean host fails at startup. The image you pull is published under an organisation other than the one hosting the source. The 60fps figure, the sixteen layers and the 75% reduction in edge requests are unmeasured claims attached to a 0.1.0 codebase whose only release is tagged with its own repository name.

Frequently asked questions

Does Third Eye need any API keys to start?

No. The npm path is git clone, cd Third-Eye, npm install, npm run dev, and the container path is a single image pull. Every core layer draws on a public keyless source, and the compose file marks .env as not required, so a missing configuration file still leaves the keyless feeds working.

Which Third Eye features stop working without credentials?

Only the RECON scanner backend. SCANNER_URL and SCANNER_KEY are empty by default and without them the toolkit returns 503. FIRMS_API_KEY, OPENSKY_CLIENT_ID, OPENSKY_CLIENT_SECRET, N2YO_API_KEY and AIS_API_KEY are all labelled optional and only raise rate limits on NASA FIRMS, OpenSky, N2YO and aisstream.io.

Where does the Third Eye container image come from?

From ghcr.io/aiacos/third-eye:latest, a registry path under the aiacos organisation, while the source tree sits under eli-labz. The compose file keeps a build block pointing at the local Dockerfile as a fallback, so a host that cannot pull can build from the repository instead.

How many data layers does Third Eye actually have?

The three figures in the project do not agree. The feature list claims 16 live data layers, the Intelligence Domains table has 13 rows counting Telegram OSINT, and the Overview says 13+ intelligence domains. Two of those rows, Maritime and Conflict, are attributed to static naval and OSINT intel rather than to a live feed.

Can Third Eye trace crypto wallets?

Yes, without keys. BTC lookups go through the Esplora API at blockstream.info and ETH lookups through Blockscout's public instance, returning balance and transaction history. Every result is cross-checked against the OFAC SDN sanctioned address list, and a match surfaces a red sanctioned badge in the interface.

Official sources

  1. eli-labz/Third-Eye on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/eli-labz-third-eye.svg)](https://hysenlabs.com/projects/eli-labz-third-eye)