Skip to Content
Alpha — full Android pipeline + iOS toolkit + live dynamic loop. API still shifting; pin to commits in CI.
ReferenceArchitecture

Architecture

One paragraph per layer, then the connection diagram. Read this when you want to know where a bug lives or which file owns a behaviour.

Layers

Engines (mnexus/engines/)

Adapter classes — one per external tool — that subclass BaseEngine. Each engine exposes health_check(), an action API surface (analyze_apk, extract_manifest, decompile, etc.), and translates the tool’s output into Finding / AttackSurface model fields.

EngineWrapsRole
adb_engineadbDevice control, APK pull.
apkeep_engineapkeepPull APK from Google Play.
apktool_engineapktoolAndroidManifest + smali + resources.
jadx_enginejadxJava/Kotlin decompilation.
ghidra_engineGhidra headlessNative .so analysis.
mobsf_engineMobSF RESTSecond-opinion static.
ipatool_engineipatoolIPA download from App Store.
frida_engineFrida coreHook injection.
burp_engine / caido_engine / moxy_engineproxy RESTTraffic capture + replay.
play_intel_engine(built-in)Stream APK from Play; Firebase + secrets scan.
vphone_enginetartiOS lab VMs (research only).
firebase_enginealias of play_intel_engineUI-facing rename.

Orchestrator (mnexus/core/)

MedusaNexus (the class) coordinates engine fan-out, builds the Project/AttackSurface/Finding graph, and persists everything via the ArtifactStore. The pipeline is four phases:

  1. Ingest — copy bytes into the workspace, write the Project row, extract manifest meta.
  2. Static fan-out — run every static engine in parallel (when MNEXUS_PARALLEL_ENGINES=1), collect findings.
  3. Surface buildAttackSurface synthesises components, deeplinks, native libs, etc. from finding evidence.
  4. Intelligence — auto-hook generator, correlator, manifest/findings diff against prior scans.

Runtime (mnexus/runtime/)

Lives separately from engines because it owns state, not just adapters. FridaSession, MemoryOps, APKPatcher, IPAPatcher, IPADecryptor, PipelineExecutor. Each holds a long-lived handle to something external (Frida session, mutated APK bytes, decrypt subprocess) and exposes async methods the API layer drives.

Intelligence (mnexus/intelligence/)

Pure functions that read findings and produce more findings. No I/O. hook_generator, traffic_findings, manifest_diff, findings_diff, runtime_scripts, android_flags, correlator. The cleanest layer to unit-test.

Models (mnexus/models/)

Pydantic v2 schemas — Finding, Severity, FindingCategory, AttackSurface, Project, PlayAccount, PlayScan. Mitigation required at the model layer: any CRITICAL or HIGH finding without a remediation block fails validation, before it ever hits the DB.

Reporting (mnexus/reporting/)

ReportGenerator + ReportTemplate + ReportFormat. Templates: executive / technical / owasp-matrix / diff. Formats: markdown / json / html / pdf / png. PDF via WeasyPrint, PNG via headless Chromium — both fall back to HTML when the dependency is missing.

API (mnexus/api/)

FastAPI app — 136 endpoints across 21 resource buckets. SPA-friendly JSON, plus SSE for live dynamic streams. Static SPA assets live in mnexus/api/static/ and are served at /.

CLI (mnexus/cli.py)

Click + Rich. Two modes share one entrypoint:

  • mnexus (no args) → interactive REPL with 28 slash commands.
  • mnexus <subcommand> → flat one-shot commands (scan, doctor, serve, mcp-serve, etc.).

See CLI reference for the live matrix.

MCP driver (mnexus/mcp_server.py)

JSON-RPC 2.0 over stdio, no external deps. Exposes 10 read-only tools to AI assistants — every tool routes through the local FastAPI server so the same driver works against remote Nexus instances. See MCP →.

Connection diagram

┌──────────────────────────────────────────────────────────────────────┐ │ YOU │ │ (CLI · REPL · web UI · AI assistant via MCP · curl) │ └────────┬────────────────────────────┬─────────────────────┬──────────┘ │ click + rich │ JSON / SSE │ JSON-RPC ▼ ▼ ▼ over stdio ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ CLI │ │ FastAPI │◀────▶│ MCP │ │ (cli.py) │──────────────▶│ app │ │ driver │ └─────────────┘ in-process └──────────────┘ └─────────────┘ ┌───────────────────┐ │ Orchestrator │ │ (MedusaNexus) │ └─────┬──────┬──────┘ │ │ ┌───────────────┘ └──────────────┐ ▼ ▼ ┌──────────┐ ┌───────────────┐ │ Engines │ │ Runtime │ │ (jadx, │ ─── findings ────────▶│ (FridaSession,│ │ ghidra, │ │ Patchers, │ │ mobsf, │ │ Pipelines) │ │ frida, │ └───────┬───────┘ │ …) │ │ └────┬─────┘ │ │ │ ▼ ▼ ┌────────────────────────────────────────────────┐ │ Intelligence layer │ │ correlator · hook_generator · diff · risk │ └──────────────────────┬─────────────────────────┘ ┌───────────────────┐ │ ArtifactStore │ │ (SQLite, single │ │ file) │ └───────────────────┘

Single-file SQLite store

Everything — projects, findings, attack surfaces, runtime sessions, diffs, reports, Play accounts, vphone state — lands in one file ($MNEXUS_DB_PATH, default ~/.mnexus/nexus.sqlite3). Schema lives in mnexus/core/artifact_store.py. WAL mode is on; concurrent reads + one writer is the access pattern.

Why one file: portability. You can cp nexus.sqlite3 … and reproduce the analyst’s state on another machine, no migrations required.

Where to look when…

You’re debuggingOpen
A MISS enginemnexus/engines/<engine>.pyhealth_check()
A finding looks wrongThe engine that emitted it (check finding.source_engine) + mnexus/intelligence/correlator.py
The risk scoremnexus/intelligence/risk_score.py
A 500 from the APImnexus/api/main.py (route handler) + the orchestrator method it calls
A Frida hook that won’t loadmnexus/runtime/frida_session.py + the generated hook in ~/.mnexus/workspace/<pid>/hooks/
A report rendering wrongmnexus/reporting/generator.py + the template in mnexus/reporting/templates/
The web UI talks to a missing routemnexus/api/static/app.js