
In-memory graph engine driver for BloodHound CE that accelerates attack-path and Cypher queries on large Active Directory and Entra forests, delegating inexact reads to PostgreSQL.
An in-memory graph engine underneath BloodHound CE, packaged as a DAWGS driver, so that attack-path analysis and every other graph query stay fast on large Active Directory environments and ordinary hardware.
Shortest paths, the structural queries BloodHound's own code issues, and a broad surface of Cypher -- including every pre-built query the UI ships, and OpenGraph data -- are served from an in-memory replica that is kept in sync with PostgreSQL write by write. Anything the engine cannot answer exactly as PostgreSQL would is delegated to PostgreSQL, so apart from a few narrow documented cases (below; WHITEPAPER.md §19 lists them) the only difference you should see is latency. On a 1M-node forest, every shipped query plus a set of adversarial ones runs in 7.9s instead of 85.6s (BENCHMARK.md).
How it works, from the background up, is in WHITEPAPER.md.
On the host that runs your BloodHound CE docker compose deployment (BloodHound v9.6.0 or newer -- see Upstream versions), from the directory that holds its compose file:
curl -fsSL https://github.com/MihhailSokolov/BloodTrail/releases/latest/download/install.sh | sh -s -- install
The installer shows what it found and asks before changing anything. It backs up the
application database, the compose file and .env, migrates the graph from Neo4j to
PostgreSQL if needed, switches the bloodhound service to the BloodTrail image, and
verifies the result. If your deployment still runs Neo4j, pause ingestion first (see
Operating notes). Then set a memory target
(how).
To undo everything, run the same one-liner with rollback from the same directory:
curl -fsSL https://github.com/MihhailSokolov/BloodTrail/releases/latest/download/install.sh | sh -s -- rollback
BloodHound answers multi-hop questions by asking its graph database to expand a frontier hop by hop: through Neo4j's record store, or through a PL/pgSQL breadth-first harness on PostgreSQL. On large forests the shipped "shortest paths" queries time out, and the official sizing guidance above 50,000 users is 96 GB of RAM.
The part of the graph that path questions need -- node ids, node kinds and edge kinds -- fits in well under a gigabyte for 5 million nodes and 50 million edges as compressed-sparse-row arrays, and a single CPU core sweeps every edge in under a second (bench/csrbench). On a 1M-node hybrid AD/Entra forest, the worst shipped prebuilt goes from 49 seconds to 63ms; 122 of 182 benchmark scenarios are faster, the worst case is 1.5x slower (20ms against 13ms), and the cost is memory. BENCHMARK.md has every scenario, the method, and what the numbers do not show.
bloodtrail, selected by
BloodHound's database_switch table (or bhe_graph_driver when that table has no row);
the installer sets both. BloodHound's ingest, analysis, API and UI are unchanged. The
image is BloodHound's own Dockerfile plus a small patch
(patches/bloodhound-driver.patch).BLOODTRAIL_SNAPSHOT_DIR set, the replica is saved to disk and
reused at the next boot, but only when a write counter in PostgreSQL, and the lineage
it counts in, prove the file is complete; otherwise the boot rebuilds from PostgreSQL.WHITEPAPER.md covers each of these in depth, with references to the code.
BloodTrail ships as a patched BloodHound image plus an installer CLI. On the host that runs the compose deployment, from the directory that holds its compose file:
curl -fsSL https://github.com/MihhailSokolov/BloodTrail/releases/latest/download/install.sh | sh -s -- install
The bootstrap script downloads the latest released CLI for your platform, verifies its
checksum (sha256sum where available, shasum -a 256 on stock macOS), runs it with the
arguments you pass after --, and deletes it again. So every command -- install,
status, verify, rollback -- is run the same way, with its name in place of
install. The CLI looks for docker-compose.yml in the current directory; pass
--compose-file (and, if the project lives elsewhere, --project-dir) otherwise.
The script refuses to run a download unless checksums.txt holds exactly one well-formed
SHA-256 entry for the archive, and that entry matches. It runs the CLI from a temporary
directory under TMPDIR (default /tmp); on a host whose /tmp is mounted noexec, set
TMPDIR to a directory that allows running programs. Each download is time-limited:
15 seconds to connect, 10 minutes in all for the archive and 1 minute for checksums.txt.
A stalled connection then ends with a message saying what to try, not a hang.
Confirmation. install and rollback show what they will change and ask first,
reading the answer from the terminal; add --yes to run them unattended.
Pinning a release. To run the exact release you audited instead of latest, set
BLOODTRAIL_VERSION:
curl -fsSL https://github.com/MihhailSokolov/BloodTrail/releases/latest/download/install.sh | BLOODTRAIL_VERSION=v0.1.2 sh -s -- install