HollowpathHollowpath

Architecture

Three interfaces, one engine, one domain model — and a deliberate rule about which one is allowed to touch what.

CLIscripted / labConsoleREPLGUIdesktopEnginesingle source of truthreads / writesdomain modelAssetServiceFindingEvidenceno interface talks to the domain model directly — every path runs through the engine

Three interfaces, one engine

Hollowpath has three ways to interact with it: a scriptable CLI (used for lab-mode runs against a defined IP range), a REPL console in the style of an interactive security tool, and a desktop GUI. All three call the same underlying engine functions — there is no interface-specific business logic, and no logic is duplicated across them. If a rule is enforced in one interface, it’s enforced in all three, because it lives in exactly one place.

The domain model

Deliberately minimal, and deliberately not “the whole planned model built up front.” What exists today:

  • Asset — an IP address and/or hostname.
  • Service — a port/protocol on an Asset, with product/version/banner.
  • Finding — what was discovered. Explicitly categorized as an observation, a detection, or a confirmed vulnerability — never a flat boolean. Severity, confidence, and verification status are tracked as separate fields.
  • Evidence — the material backing a Finding, marked raw or parsed.
  • ExploitCandidate — a SearchSploit or Metasploit match, confidence-scored.
  • FindingLink — an explicit, operator-asserted relationship between two Findings. Never created automatically from text or host similarity — Hollowpath does not treat similar-looking evidence as independent corroboration.

Relationships between objects are plain ID strings, never nested object references — this keeps every object trivially serializable and testable without a running engine or GUI.

What’s deliberately not built yet

A correlation engine, CVE/CPE intelligence, and a persisted Engagement/session concept are all explicitly deferred — not forgotten. The stated principle: build the smallest thing that solves today’s actual problem, not the full model a long-term vision document describes.

Reading and querying the data

A RelationshipIndex builds a read-only, in-memory lookup over one run’s objects, so “what services does this asset have” is answered once, centrally, instead of every view rebuilding its own lookup. An InvestigationContext sits on top of it and tracks what the operator currently has selected — Asset, optionally a Service, optionally a Finding — narrowing each level clears what’s below it. Both are read-only and make no scope or security decisions of their own.

Implementation notes

The engine is Python, deliberately standard-library only aside from the GUI toolkit (PySide6) — every new dependency requires explicit justification. There is no ORM, no framework magic, and no metaclass tricks in the domain layer, which keeps it portable if parts of the system ever need a different language for performance reasons later.