The Problem

Investigators need to stitch together disparate OSINT artefacts—emails, usernames, images, locations—into a single, navigable view. Manual correlation is error‑prone and time‑consuming, especially when the data spans graph, timeline, and map dimensions.

What This Does

PANO supplies a desktop GUI (Qt via PySide6) that lets users drag entity objects onto a graph, then run transform plugins that enrich those entities with external data. Core code lives in three logical groups:

  • entities/ – data‑model classes such as email.py, event.py, location.py. The hub entities/base.py defines validation helpers used by 18 other modules.
  • ui/ – Qt widgets and managers. The visual node widget (ui/components/node_visual.py) alone contains 730 lines and is referenced by 11 other files; it handles image loading, sizing, and network fetches.
  • transforms/ – plug‑in style processors (email_lookup.py, reverse_image_search.py, etc.) that call external services and populate entities.

The entry point is the main function in pano.py (line 872). It builds the Qt application, loads the UI, and registers the transform system. From there user actions trigger the most‑used functions: add_node (11 callers), add_edge (9 callers), and visual property setters like set_text (10 callers).

How It Is Wired

Execution starts in pano.py::main. The function creates a QApplication, instantiates ui/managers/graph_manager.py::GraphManager, and shows the main window. User interaction (e.g., dragging a new node) calls ui/managers/graph_manager.py::add_node, which:

  1. Constructs a ui/components/node_visual.py::NodeVisual instance.
  2. Calls NodeVisual.__init__, which invokes width/height (each called from 13 places) to size the graphic.
  3. Triggers _update_scaled_pixmap, which performs a network fetch via helpers/media_analyzer.py::_convert_pos_to_pixmap → session.get (the only outbound network call in the call graph).

When a transform like Email Lookup is run, transforms/base.py::run (entry point for transforms) executes:

  • run -> _process_ghunt_results -> _create_entities -> update_label -> geocode – a chain that contacts the GHunt API (network) and updates node labels.

Key blast‑radius modules:

  • entities/base.py – 18 inbound imports, 1 outbound; any change affects validation across the whole code base.
  • ui/components/node_visual.py – 11 inbound imports, 10 outbound calls, plus a network request; it is the most complex visual component.

Circular imports exist between entities/base.py and entities/__init__.py; breaking this cycle would reduce import‑time fragility. Deep nesting (up to 8 levels) appears in entities/event.py and several UI components, making maintenance harder.

How To Use It

# Clone the fork you will work on
git clone https://github.com/moses-y/PANO.git
cd PANO

# Install dependencies (Python 3.11+ required)
python -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate
pip install -r requirements.txt

# Start the application (scripts handle env setup)
./start_pano.sh   # Linux/macOS
# or
start_pano.bat    # Windows

No additional configuration files are present; the README notes that the Email Lookup transform requires a GHunt login, which must be performed manually according to the GHunt README.

Real‑World Use

A cyber‑threat analyst loads a CSV of suspect email addresses, drags each into the graph, runs Email Lookup to pull calendar events and location history, then switches to the timeline view (ui/components/timeline_visual.py) to spot anomalous activity spikes. The visual node widget automatically fetches avatar images, and the map view (ui/components/map_visual.py) plots any extracted coordinates.

Code Health & Issues

  • HIGH – No test suite – 58 source files, zero test files.
  • HIGH – No CI workflow – repository lacks any GitHub Actions or other CI config.
  • MEDIUM – No Dependabot – only requirements.txt present, no auto‑update bot.
  • HIGH – Import cycle – entities/base.py ↔ entities/__init__.py.
  • HIGH – Deep nesting – up to 8 indentation levels in several modules, hurting readability.
  • MEDIUM – Broad exception handling – many except: blocks swallow errors in UI managers.
  • MEDIUM – Hub module – entities/base.py is a high‑impact change point.
  • MEDIUM – Oversized files – ui/components/node_visual.py, helpers/media_analyzer.py, pano.py each exceed 700 lines.

No lockfile, Dockerfile, or committed secrets were detected.

The Bottom Line

PANO delivers a functional, feature‑rich OSINT desktop platform with a clear plugin architecture, but the code base suffers from maintainability issues (cycles, deep nesting, large monolithic files) and lacks any automated testing or CI. It is suitable for engineers comfortable with Qt and willing to invest in refactoring and test scaffolding before using it in production.