Skip to content

flaviomilan/fackel

Fackel

Fackel logo

Autonomous pentest framework powered by ReAct agents.
LLM-driven reconnaissance, scanning, triage, and report generation.

Python 3.12+ License: Apache 2.0 LangGraph


What is Fackel?

Fackel is a multi-agent pentest framework where LLMs decide what to do, not hardcoded pipelines. Each specialist agent uses the ReAct pattern (Reason + Act) to autonomously choose which tools to call, interpret results, and decide next steps.

Target → OSINT → Approval Gate → Port Scan → Vuln Scan → Triage → Report
           ↕          ↕              ↕            ↕          ↕        ↕
       27 tools  Human-in-     2 tools      12 tools   LLM-as-   LLM
       (passive)  the-Loop      (active)     (active)   a-judge  synthesis

Key features

Feature Description
Real ReAct agents Each specialist is a create_agent with its own system prompt, tools, and LLM. The model decides strategy, not code.
5-phase pipeline OSINT → Port Scan → Vulnerability Scan → Triage → Report. Each phase builds on the previous.
Human-in-the-loop An approval gate pauses before active scanning, showing discovered targets for operator review.
LLM-as-a-judge A quality evaluator scores each phase and drives adaptive routing — skip empty phases, adjust strategy for partial results.
Real-time observability Watch tool calls, results, errors, and LLM reasoning stream to the terminal as they happen.
Input validation rails Every tool validates its inputs (target type, shell metacharacters) via guard_target() — raises ToolException for code-level enforcement, not just prompt instructions.
Resilient tool execution ToolException + handle_tool_error propagate clean errors to the LLM. Circuit breakers disable flaky HTTP services after repeated failures. Configurable per-tool timeouts via environment variables.
Per-agent model config Assign different models to different agents via environment variables.
Automatic provider gating Tools requiring API keys are auto-removed when keys are missing, preventing wasted LLM calls.
Two-tier prompting Shared soul prompt (identity + anti-hallucination rules) + task-specific skill prompts per phase.
Dual reports Concise LLM report on console + comprehensive archival report saved to disk.
LangSmith tracing Set two env vars and all agent phases appear as hierarchical traces — token usage, tool I/O, latency, middleware activity.

Quick start

Requirements

Requirement Notes
Python 3.12+ Required
uv or pip Package manager
OpenAI API key Or any compatible provider (Azure, Anthropic via LangChain)
Go ≥ 1.21 For most scanning binaries (optional — only needed for active scanning)
Ruby (gem) For wpscan, whatweb (optional)
naabu, nmap For port scanning (active scan)
nuclei, httpx, katana, subfinder For vulnerability scanning (optional)

See docs/tools.md for the full list of required binaries per tool.

Automated tool install

# Install all external binaries automatically
./scripts/install-tools.sh

# Core tools only (nmap, naabu, nuclei, httpx, subfinder)
./scripts/install-tools.sh --minimal

# Audit — check which tools are installed/missing
./scripts/install-tools.sh --check

Install

# Clone and install
git clone https://github.com/your-org/fackel.git
cd fackel
uv sync --python 3.12

Or with pip:

python -m venv .venv && source .venv/bin/activate
pip install -e .

Configure

cp .env.example .env
# Edit .env — OPENAI_API_KEY is the only required key

Run

# Passive scan (OSINT only → report)
fackel example.com --no-active-scan

# Full scan (OSINT → port scan → vuln scan → triage → report)
fackel example.com

# Verbose mode — see LLM reasoning in real time
fackel example.com -v

# Save report to a specific file
fackel example.com -o report.md

# Check which provider API keys are configured
fackel example.com --check-providers --no-active-scan

Pipeline overview

                     ┌─────────────────┐
                     │   osint_node    │ ← 27 passive tools
                     │  (ReAct agent)  │   dns, whois, subdomains, etc.
                     └────────┬────────┘
                              │
                    ┌─────────▼──────────┐
                    │  route_after_osint │
                    │  (conditional)     │
                    └──┬─────────────┬───┘
                       │             │
          active_scan  │             │  no active scan
          + IPs found  │             │  or no IPs
                       ▼             │
              ┌─────────────────┐    │
              │ approval_gate   │    │
              │ (HitL interrupt)│    │
              └───┬────────┬────┘    │
          approve │        │ reject  │
                  ▼        └────┐    │
           ┌────────────┐       │    │
           │ port_scan   │      │    │
           │ (ReAct)     │      │    │
           └─────┬───────┘      │    │
                 │              │    │
       ┌─────────▼───────────┐  │    │
       │route_after_port_scan│  │    │
       │(LLM-as-a-judge)     │  │    │
       └──┬──────────────┬───┘  │    │
          │              │      │    │
          ▼              ▼      │    │
   ┌────────────┐  ┌─────────┐  │    │
   │ vuln_scan  │  │ triage  │  │    │
   │ (ReAct)    │  │(struct) │◄      │
   └─────┬──────┘  └────┬────┘       │
         │              │            │
         ▼              │            │
   ┌──────────┐         │            │
   │  triage  │         │            │
   │ (struct) │         │            │
   └─────┬────┘         │            │
         │              │            │
         ▼              ▼            ▼
   ┌──────────────────────────────────┐
   │           report_node            │
   │         (LLM synthesis)          │
   └──────────────┬───────────────────┘
                  │
                 END

Each phase is a LangGraph node. The orchestrator manages state flow, conditional routing, and accumulates findings across phases.

See docs/architecture.md for full architectural details.


CLI output

Standard mode shows tool calls and results:

Target: eversafe.info
Active scan: yes

────────────────────────────────────────────────────────────
▶ OSINT
────────────────────────────────────────────────────────────
  🔧 dns_resolve(target=eversafe.info)
  🔧 whois_lookup(domain=eversafe.info)
  🔧 subfinder_enum(domain=eversafe.info, all_sources=True)
  🔧 crtsh_subdomain_enum(domain=eversafe.info)
  ✓ OSINT complete

──────────────────────────── ▶ Approval ────────────────────
╭──────────────── ⚠ Approval Required ─────────────────────╮
│ OSINT found 4 IP(s) and 5 subdomain(s).                  │
│ Proceed with active scanning?                            │
╰──────────────────────────────────────────────────────────╯
Approve? [Y/n]: y

────────────────────────────────────────────────────────────
▶ Port Scan
────────────────────────────────────────────────────────────
  🔧 naabu_scan(host=104.21.36.250, top_ports=1000)
  🔧 nmap_port_scan(host=104.21.36.250, ports=80,443)
  📊 Quality: complete (score: 0.9) → proceed
  ✓ Port Scan complete

────────────────────────────────────────────────────────────
▶ Vuln Scan
────────────────────────────────────────────────────────────
  🔧 nuclei_scan(target=eversafe.info)
  🔧 httpx_scan(domain=eversafe.info, tech_detect=True)
  🔧 wafw00f_detect(target=eversafe.info)
  📊 Quality: complete (score: 0.85) → proceed
  ✓ Vuln Scan complete

════════════════════════════════════════════════════════════
# Penetration Test Report for eversafe.info
...
Completed in 220.9s

With -v (verbose), LLM reasoning is also shown:

  💭 ### Structured Summary
  💭 **Domain:** eversafe.info
  💭 **Discovered IP Addresses:**
  💭 - 104.21.36.250
  💭 - 172.67.201.157

Specialist agents

Agent Type Tools Purpose
OSINT ReAct 27 tools Passive reconnaissance — DNS, WHOIS, subdomains (subfinder, crt.sh, VirusTotal, Amass), reverse DNS, Shodan/Censys/FOFA, IP enrichment, TLS certs, historical DNS, passive URL discovery (gau), cloud resource enumeration (CloudBrute), web tech fingerprinting (WhatWeb), parameter discovery (ParamSpider), JS endpoint extraction (LinkFinder), subdomain takeover (Subzy), secret scanning (TruffleHog), job search, email analysis
Port Scan ReAct 2 tools Active scanning — discover open ports (naabu) and fingerprint services (nmap)
Vuln Scan ReAct 12 tools Vulnerability scanning — Nuclei templates, XSS detection (DalFox), HTTP tech detection, WAF detection, web crawling, S3 bucket audit, TLS analysis, WordPress scanning (WPScan), CORS misconfiguration (Corsy)
Triage Structured LLM (none) Gap analysis — identify technologies found but not assessed, flag coverage gaps
Report LLM chain (none) Synthesize all findings, evaluations, and gaps into a Markdown pentest report
Judge Structured LLM (none) Quality evaluator — scores each phase (0.0–1.0) and recommends routing

See docs/agents.md for detailed agent documentation.


Tool inventory

Tool Target Type Requires Agent
dns_resolve HOST OSINT
whois_lookup DOMAIN whois binary OSINT
shodan_lookup (custom) SHODAN_API_KEY OSINT
censys_lookup HOST CENSYS_API_ID + CENSYS_API_SECRET OSINT
dnsdumpster_lookup DOMAIN OSINT
virustotal_subdomain_enum DOMAIN VIRUSTOTAL_API_KEY OSINT
crtsh_subdomain_enum DOMAIN OSINT
subfinder_enum DOMAIN subfinder binary OSINT
reverse_dns_lookup IP OSINT
ipinfo_lookup IP OSINT
bgp_lookup IP OSINT
httpx_scan HOST_OR_URL httpx binary OSINT, Vuln Scan
tlscert_lookup DOMAIN OSINT
securitytrails_history DOMAIN SECURITYTRAILS_API_KEY OSINT
urlscan_search DOMAIN OSINT
otx_passive_dns DOMAIN OTX_API_KEY OSINT
fofa_search (custom) FOFA_EMAIL + FOFA_KEY OSINT
gau_urls DOMAIN gau binary OSINT
cloudbrute_enum (keyword) cloudbrute binary OSINT
job_search (free text) OSINT
analyze_email (email) HIBP_API_KEY / EMAILREP_API_KEY OSINT
naabu_scan HOST naabu binary Port Scan
nmap_port_scan HOST nmap binary Port Scan
nuclei_scan DOMAIN nuclei binary Vuln Scan
wafw00f_detect HOST_OR_URL wafw00f binary Vuln Scan
graphql_scan URL Vuln Scan
feroxbuster_scan HOST_OR_URL feroxbuster binary Vuln Scan
katana_crawl HOST_OR_URL katana binary Vuln Scan
testssl_scan HOST testssl.sh binary Vuln Scan
dalfox_scan HOST_OR_URL dalfox binary Vuln Scan
s3scanner_scan (bucket name) s3scanner binary Vuln Scan
extract_webpage_content URL Vuln Scan
amass_enum DOMAIN amass binary OSINT
subzy_check DOMAIN subzy binary OSINT
paramspider_crawl DOMAIN paramspider binary OSINT
whatweb_scan HOST_OR_URL whatweb binary OSINT
linkfinder_extract HOST_OR_URL linkfinder binary OSINT
trufflehog_scan (repo URL) trufflehog binary OSINT
wpscan_scan HOST_OR_URL wpscan binary + WPSCAN_API_TOKEN Vuln Scan
corsy_scan HOST_OR_URL corsy binary Vuln Scan

See docs/tools.md for complete tool reference with input schemas and validation rules.


Configuration

Model per agent

Each agent reads its model from an environment variable, falling back to gpt-5-mini:

Variable Agent Default
FACKEL_MODEL_OSINT OSINT agent gpt-5-mini
FACKEL_MODEL_PORT_SCAN Port scan agent gpt-5-mini
FACKEL_MODEL_VULN_SCAN Vuln scan agent gpt-5-mini
FACKEL_MODEL_TRIAGE Triage agent gpt-5-mini
FACKEL_MODEL_REPORT Report generator gpt-5-mini
FACKEL_MODEL_JUDGE Phase quality evaluator gpt-5-mini
# Use a more capable model for report generation
export FACKEL_MODEL_REPORT=gpt-4o

API keys

Variable Required Used by
OPENAI_API_KEY Yes All agents (LLM)
SHODAN_API_KEY No shodan_lookup
VIRUSTOTAL_API_KEY No virustotal_subdomain_enum
CENSYS_API_ID / CENSYS_API_SECRET No censys_lookup
FOFA_EMAIL / FOFA_KEY No fofa_search
SECURITYTRAILS_API_KEY No securitytrails_history
OTX_API_KEY No otx_passive_dns
HIBP_API_KEY No analyze_email (graceful degradation)
EMAILREP_API_KEY No analyze_email (graceful degradation)
WPSCAN_API_TOKEN No wpscan_scan

Tools with missing API keys (and hard_fail=True) are automatically removed from agents, preventing the LLM from attempting calls that would fail.

Infrastructure (optional)

# Start MongoDB persistence stack
docker compose up -d

The docker-compose.yml provides:

  • MongoDB 7 — scan persistence and query system

Observability (optional)

Fackel uses LangSmith for LLM observability. Set the env vars and all agent traces appear automatically:

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=lsv2_pt_...
export LANGSMITH_PROJECT=fackel

See docs/configuration.md for full configuration reference.


Python API

from fackel.agents.orchestrator import run

# Blocking — returns final state
result = run("example.com", active_scan=True)
print(result["report"])

Adding new tools

  1. Create a new file in src/tools/ with a @tool-decorated function and Pydantic input schema:
# src/tools/recon/my_tool.py
from langchain_core.tools import ToolException, tool
from pydantic import BaseModel, Field

from fackel.tooling import TargetType, format_tool_output, guard_target


class MyToolInput(BaseModel):
    target: str = Field(description="Domain or IP to scan.")

@tool(args_schema=MyToolInput)
def my_recon_tool(target: str) -> dict:
    """Describe what this tool does — the LLM reads this docstring."""
    target = guard_target(target, "my_recon_tool", TargetType.HOST)
    #  guard_target raises ToolException on invalid input

    # ... implementation ...
    return format_tool_output("my_recon_tool", target, "success", data=result)

# Enable LangChain error propagation — the LLM sees errors as tool results.
my_recon_tool.handle_tool_error = True  # type: ignore[attr-defined]
  1. Import and add it to the relevant agent's tools list.

  2. The LLM will autonomously decide when and how to use it based on its docstring and the agent's system prompt.

See docs/development.md for the full development guide.


Project structure

src/
├── cli/
│   └── main.py                      # Typer CLI with real-time Rich rendering
├── fackel/
│   ├── agents/
│   │   ├── config.py                # build_llm(), get_model(), default_middleware()
│   │   ├── prompts/
│   │   │   ├── __init__.py          # Prompt loader with caching
│   │   │   ├── soul.md              # Shared agent identity + rules
│   │   │   └── skills/
│   │   │       ├── osint.md         # OSINT playbook
│   │   │       ├── port_scan.md     # Port scan strategy
│   │   │       ├── vuln_scan.md     # Vuln scan playbook
│   │   │       ├── triage.md        # Coverage gap analysis
│   │   │       ├── report.md        # Report writing rules
│   │   │       └── judge.md         # Quality scoring guide
│   │   ├── orchestrator/
│   │   │   ├── state.py             # ScanState (TypedDict + reducers)
│   │   │   ├── graph.py             # StateGraph + SqliteSaver checkpointer
│   │   │   ├── streaming.py         # Dual-mode agent streaming + HITL
│   │   │   ├── evaluator.py         # LLM-as-a-judge quality scoring
│   │   │   ├── extractors.py        # IP/subdomain/fingerprint extraction
│   │   │   ├── main.py              # Public API: run()
│   │   │   └── nodes/               # Graph node functions
│   │   │       ├── osint.py         # OSINT node + quality-gated retry
│   │   │       ├── port_scan.py     # Port scan node + evaluator
│   │   │       ├── vuln_scan.py     # Vuln scan node + evaluator
│   │   │       ├── triage.py        # Triage node
│   │   │       └── report_and_gates.py  # Report node + approval gate
│   │   ├── osint/agent.py           # OSINT ReAct agent (27 tools)
│   │   ├── port_scan/agent.py       # Port scan ReAct agent (2 tools)
│   │   ├── vuln_scan/agent.py       # Vuln scan ReAct agent (12 tools)
│   │   ├── triage/agent.py          # Triage structured output
│   │   └── report/agent.py          # Report synthesis
│   ├── tooling/
│   │   ├── validators.py            # guard_target() (raises ToolException)
│   │   ├── execution.py             # run_command, require_binary, get_tool_timeout
│   │   ├── sanitizers.py            # Input sanitisation helpers
│   │   ├── ip_classifier.py         # IP classification (CDN, cloud, hosting)
│   │   └── ddgs.py                  # DuckDuckGo search wrapper
│   ├── provider_keys.py             # API key gating + tool filtering
│   └── report_writer.py             # Full archival report builder
└── tools/
    ├── circuit_breaker.py           # Per-service circuit breaker
    ├── recon/                       # 22 passive reconnaissance tools
    ├── osint/                       # 3 open-source intelligence tools
    ├── scanning/                    # 7 active scanning tools
    └── vuln/                        # 5 vulnerability assessment tools

Documentation

Document Description
docs/architecture.md System architecture, graph flow, state management, prompt system
docs/agents.md Agent specifications, prompts, LLM-as-a-judge evaluator
docs/tools.md Complete tool reference — schemas, validation, binaries
docs/input-validation.md Input validation system — TargetType, guard_target, security
docs/configuration.md Environment variables, API keys, model selection, infrastructure
docs/development.md Contributing guide, adding tools, coding standards, testing

Development

# Install dev dependencies
uv sync --python 3.12 --extra dev

# Lint
uv run ruff check src/

# Type check
uv run mypy src/

# Tests
uv run pytest tests/

# Format
uv run ruff format src/

License

Apache 2.0 — see LICENSE.

About

🔥 OSINT (Open Source Intelligence) analysis tool that uses the power of an autonomous AI agent

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages