Two different things, kept apart on purpose: what you can do with this project today, and how the finished
tool is designed to be installed.
Today, from source
In development
git clone https://github.com/Viktor25104/Cortex.git
cd Cortex
The repository is public and the clone works. The build does not: at commit
eabc16d the Go workspace file is malformed, so go build ./...
fails before it reaches any package. Restoring the build is the first task of the engine plan, and until
it lands there is nothing here you can run.
Target v1 distribution
Planned
One static binary per platformLinux, macOS and Windows on amd64 and arm64, with checksums and signatures.
No data directory, no runtime dependencyThe fingerprint database is compiled into the binary, so a fresh machine needs nothing else.
No privileges for the default scanConnect, TLS and HTTP observation need no elevation anywhere. Raw sockets are opt-in.
There is no release yet, so there is no download URL, and none is printed here. When binaries exist they
will be published on the repository's releases page with the checksums beside them.
A claim is a summary of some bytes. Keep the bytes.
Scanners tell you a port is running nginx. Almost none of them can still show you the response that made
them say so, because the moment the process exits, the reasoning is gone.
Cortex writes every probe exchange to a content-addressed store: the exact payload sent, the exact bytes
received, the pattern that matched and where it matched, the version of the fingerprint database that
supplied it, and a SHA-256 over the response.
A finding then references that hash rather than restating a conclusion. False positives become diagnosable
instead of mysterious, results become auditable, and the captured responses become the regression corpus
that keeps fingerprinting honest as it grows.
Recorded fixture
Illustrative recorded-format fixture for the finished Cortex model, from a purpose-built lab network. Not
output from today's engine. Open a row to descend from the claim to the bytes.
10.20.0.7:22/tcpOpenSSH 9.6p1confidence 96probe 0 B sent
/ 23 B received
Sentprobe NULL / 0 bytes
The NULL probe sends nothing. It waits to see whether the service speaks
first, which is itself evidence.
Received 23 bytes
Response received from 10.20.0.7:22/tcp. The span highlighted and underlined is the text the fingerprint
pattern matched.
The hash shown on each record is the real SHA-256 of the bytes displayed above it. Copy the response out of
the page and hash it yourself if you want to check.
One probe, end to end
This is the whole detection path for the record in the hero, in the order it happens, with what exists in
the repository today marked against each step.
01 Select
Probes are chosen by port and by rarity rather than tried in file order, so a filtered port cannot occupy a worker for minutes.
probe GetRequestPlanned
02 Send
The probe payload goes out on its own connection. Reusing one socket for every probe is what makes most hand-rolled scanners produce nonsense after the first response.
18 bytes outIn development
03 Receive
The response is read under a deadline derived from a measured round-trip estimate, capped, and kept verbatim. Binary stays binary.
296 bytes inIn development
04 Match
Patterns come from a parsed service-probe grammar with dynamic delimiters and escape normalisation. Constructs Go regexps cannot express are recorded as unsupported, not silently counted as parse failures.
offset 25, length 12In development
05 Extract
The version template turns the capture groups into product, version and extra information, with a confidence value and the method that produced it.
nginx 1.27.2, confidence 98Planned
06 Record
The response is stored by content address, de-duplicated across hosts, and the claim keeps a reference to it rather than a copy of it.
sha256:86319f1a…37c7c4Planned
The parser that reads the probe grammar is the oldest real code in this project and the reason it exists.
Everything marked planned above is designed and unbuilt, and the repository does not compile at its current
commit.
The second scan is the useful one
Nobody runs a scanner once. Cortex keeps every scan as a structured record, so the question it can answer
on the second run is not what is open, but what moved.
Recorded fixture
Illustrative recorded-format fixture for the finished Cortex model, from a purpose-built lab network. Not
output from today's engine.
01JQ7WA9F1 tocompared with 01JQ7XR4K2 10.20.0.0/245 changes
10.20.0.33:6379/tcpopenedRedis key-value store 7.2.5
Previous
Not present in this scan.
Current
Redis key-value store 7.2.5 no authentication required
Replaced one day after the previous certificate expired. Still self-signed.
10.20.0.42:21/tcpclosedvsftpd 3.0.5
Previous
vsftpd 3.0.5
Current
No longer present. Now answering with a connection refusal.
Now answering with a connection refusal.
142 subjectsunchangedno movement
Ports, services, certificates and evidence hashes that were byte-identical across both scans.
Collapsed by default so that the five rows above are the whole of what a reader has to look at.
They are not enumerated in this fixture.
The local interface
Cortex is a command-line tool that can serve a local viewer over its own scan store, bound to loopback,
for the times when a table beats a terminal. This is that viewer, running on the recorded fixture.
Recorded fixture
Illustrative recorded-format fixture for the finished Cortex model, from a purpose-built lab network. Not
output from today's engine. Nothing here contacts a network. Selection and view switching happen in the page.
Hosts against ports. Each cell states the port state in its accessible name.
Address
21
22
25
80
443
5432
6379
8443
10.20.0.7
10.20.0.7 port 21: not in this scan
10.20.0.7 port 25: not in this scan
10.20.0.7 port 443: not in this scan
10.20.0.7 port 5432: not in this scan
10.20.0.7 port 6379: not in this scan
10.20.0.7 port 8443: not in this scan
10.20.0.11
10.20.0.11 port 21: not in this scan
10.20.0.11 port 25: not in this scan
10.20.0.11 port 80: not in this scan
10.20.0.11 port 443: not in this scan
10.20.0.11 port 6379: not in this scan
10.20.0.11 port 8443: not in this scan
10.20.0.14
10.20.0.14 port 21: not in this scan
10.20.0.14 port 22: not in this scan
10.20.0.14 port 25: not in this scan
10.20.0.14 port 80: not in this scan
10.20.0.14 port 5432: not in this scan
10.20.0.14 port 6379: not in this scan
10.20.0.21
10.20.0.21 port 21: not in this scan
10.20.0.21 port 22: not in this scan
10.20.0.21 port 80: not in this scan
10.20.0.21 port 443: not in this scan
10.20.0.21 port 5432: not in this scan
10.20.0.21 port 6379: not in this scan
10.20.0.21 port 8443: not in this scan
10.20.0.33
10.20.0.33 port 21: not in this scan
10.20.0.33 port 22: not in this scan
10.20.0.33 port 25: not in this scan
10.20.0.33 port 80: not in this scan
10.20.0.33 port 443: not in this scan
10.20.0.33 port 5432: not in this scan
10.20.0.33 port 8443: not in this scan
10.20.0.42
10.20.0.42 port 22: not in this scan
10.20.0.42 port 25: not in this scan
10.20.0.42 port 80: not in this scan
10.20.0.42 port 443: not in this scan
10.20.0.42 port 5432: not in this scan
10.20.0.42 port 6379: not in this scan
10.20.0.42 port 8443: not in this scan
open
closed
filtered
not in this scan
Excluded from this design on purpose: summary tiles, a chart wall, and anything resembling threat
intelligence. A scan is not a business metric, and a viewer for one should stay an instrument.
How a scan is put together
Seven stages in a line, and four concerns that cut across all of them. The four are the reason the seven
can be trusted, and they are where most hand-rolled scanners quietly have nothing.
02Expanderlazy iterator, deduplicated, shuffled from a recorded seed
03Discoverywhich hosts answer, and by what reason
04Port scanconnect by default, raw sockets only when asked
05Protocol detectprobe selection, match, version extraction
06EnrichmentTLS chain and HTTP surface, observed not judged
07StoreSQLite records, evidence by content address
Scheduler
A token bucket in probes per second, plus a separate ceiling on how many can be in flight, plus a per-host cap so no single machine takes the whole scan at once.
Progress
One event stream consumed by the terminal renderer, the local viewer and the machine-readable output, so all three see the same scan.
Cancellation
A cancelled scan flushes what it already found, records where it stopped, and can be resumed from that position.
Evidence
Every stage that touches the network writes what it saw, capped and content-addressed, before anything interprets it.
One process. No broker, no queue service, no database server, no container required. The finished tool is a
single binary that holds the engine, the fingerprint database, the store and the viewer.
Restraint, as a feature
Aggressive defaults are easy to build and hard to live with. These are the constraints the finished tool is
designed around, and most of them cost throughput on purpose.
Unprivileged by default
Connect scanning, TLS observation and HTTP observation need no elevation on any platform. That path is what most people should ever use, and it is the default.
Privileged mode is explicit, and never silent
Raw sockets are opt-in. Asking for a SYN scan without the capability fails with the exact command needed to grant it, rather than quietly falling back to a different scan that produces different results.
Privileges are dropped before hostile input is read
Raw handles are acquired at startup, then the process drops to the invoking user. Parsing whatever a target chooses to send back happens unprivileged, because that parser is the largest attack surface in a scanner.
Rate control is on by default
A token bucket, a concurrency ceiling and a per-host cap, with conservative defaults and adaptive backoff when a target starts rate-limiting. A scanner that cannot be slowed down is a liability on someone else network.
Large scans have to be confirmed
Past a threshold, a scan prints the computed host count, probe count and estimated duration and waits. Accidentally scanning a /8 should take more than one typo.
Local failures are never reported as target states
No route, permission denied and file descriptor exhaustion are scan errors with their own class. Reporting them as filtered is silent data corruption, and it is the single most common defect in this category.
Evidence is treated as sensitive
Captured responses will contain credentials sooner or later. The store is written owner-only, known credential patterns are redacted before persistence with the redaction recorded, and reports can be exported redacted.
Read-only interactions, permanently
Probes send the bytes a legitimate client would send. No exploitation, no brute forcing, no fuzzing, no denial of service, no evasion features. This boundary is not a phase, it is the product definition.
Against nmap, honestly
Nmap is the reference implementation of this entire category and it wins most of these rows. Cortex is not
trying to replace it. It is trying to answer a different question about the same network.
nmapCortex
OS fingerprinting
nmap is better
Around 2,600 curated fingerprints built on sixteen carefully sequenced probes, with two decades of community submissions behind them.
None. Not in the plan either: one person cannot build that corpus, and pretending otherwise would waste the years it would take.
Scripting
nmap is better
NSE, roughly six hundred scripts and the community that maintains them.
No scripting. If it ever arrives it will be sandboxed and narrow, and it will not be a plugin ecosystem.
Raw packet handling
nmap is better
Correct link-layer framing, ARP, routing, fragmentation and IPv6, hardened across a dozen operating systems.
The current raw path is structurally wrong and is being rewritten. The unprivileged path comes first because it is what most scans need.
Portability and field testing
nmap is better
Twenty-five years of platform work. It runs where you need it and it has been wrong in public often enough to be right now.
Three platforms are targeted. Zero releases exist. Nothing here has been field tested by anyone.
Speed
different goals
Adaptive timing, retransmission and congestion control. For pure throughput, masscan and its relatives are faster still.
Not competing. No throughput number has been measured, so none is published, and that race is already won by other tools.
Change between scans
different goals
ndiff compares two saved XML files and reports host and port differences.
Diff is a verb over a persistent store, so it can also compare evidence hashes, certificate rotation and version movement, not only presence.
Machine-readable output
Cortex is designed better
XML designed in 1997, which every consumer wraps in a parser library. There is still no JSON output upstream.
JSON and JSONL from the first line, schema-versioned and deterministically ordered so two runs can be compared byte for byte.
The bytes behind a claim
Cortex is designed better
Debug output is unstructured text on a terminal, and it is gone when the process exits.
Every probe exchange is stored by content address and referenced by the claim it supports. This is the reason the project exists.
Distribution and licence
Cortex is designed better
Needs installation and a data directory, under a custom licence restrictive enough that many commercial products avoid embedding it.
Targets one static binary with the fingerprint database compiled in, under Apache-2.0.
Four rows to nmap, three to Cortex, two that are not the same question. A comparison table where one column
wins everything is an advertisement, not information.
What is actually built
Nothing on this page is a released capability. At commit eabc16d the
repository does not compile, there are no tests, and no binary has ever been published.
This site describes the tool Cortex is designed to be. It exists ahead of the engine on purpose, as the
specification the engine is being built against. Every screen you have scrolled past is driven by an
authored fixture in the finished data format, and every one of them says so on the object itself.
The register below is the whole truth of the current state, written the way it would have to be written if
a reviewer opened the repository straight afterwards. They can, and they should.
The oldest real code here. Dynamic delimiters, escape normalisation, and constructs Go regexps cannot express recorded as unsupported rather than as failures. Soft matches and fallback chains are not handled.
Probe selection by port and rarity backend/scanner/probes.go
Planned
The rarity, ports and sslports directives are parsed and discarded, so every probe is tried in file order.
Probe exchange and banner capture backend/scanner/tcp_connect.go
In development
Probes run, but all of them share one connection and the first response of any kind wins. A name is extracted; product, version, extra information and CPE are not.
Plain text and JSON output backend/cli/cli.go
In development
Both exist. Ordering is not deterministic yet, which is a prerequisite for diff.
SYN scanning backend/scanner/tcp_syn.go
In development
Present and structurally wrong: a capture handle per port, no link-layer framing, no retransmission. Scheduled for rewrite, and it may be deferred past the first release.
UDP scanning backend/scanner/udp.go
In development
Sends a single null byte. The parsed UDP probe corpus is loaded and never used.
Product and version extraction backend/scanner/probes.go
Planned
Designed. The version template parser is an open task in the source.
TLS and HTTP observation
Planned
Designed. Nothing imports a TLS library today.
Content-addressed evidence store
Planned
Designed. This is the centre of the product and none of it is built.
Scan store, list and show
Planned
Designed against SQLite. Today results are held in memory or in a Redis instance that is being removed.
Diff between scans
Planned
Designed. Depends on the store and on deterministic ordering.
Rate limiting, concurrency ceilings, cancellation
Planned
Designed. The current engine has none of the three, which is its most dangerous property.
Host discovery
Planned
Designed. Every listed host is currently assumed to be up.
Designed as verbs. Today the binary takes positional arguments and switches to server mode on a raw string match.
Local viewer, cortex ui frontend/src/app/services/scan.service.ts
Planned
Designed, and previewed on this page from a fixture. The interface in the repository today is a mock that never contacts the backend.
Single binary with embedded fingerprints
Planned
Designed. The binary currently refuses to start without an external data file that is not shipped.
Released, signed binaries
Planned
None exist. No installation path is published anywhere.
Reference documentation and changelog
Planned
Neither exists. The README describes a repository layout that has already changed.
Automated tests
Planned
There are none in the repository. Zero, of any kind.
No performance figure appears anywhere on this site, because no measurement of any kind exists yet. When a
benchmark harness is published, the numbers will arrive with the hardware, the settings and the commit that
produced them, and with the cases where nmap is faster.
Read the source
There is no documentation site yet, and writing one before the interfaces settle would only produce
something to correct later. Until then, the repository is the documentation.