Skip to main content
Every tool execution in EtherReaper is tracked in the SQLite database and produces a timestamped output file in recon/. This makes all results available after the fact, even across app restarts.

Scan history table

The scans table records every tool execution:
CREATE TABLE scans (
    id           TEXT PRIMARY KEY,  -- UUID generated at launch time
    scan_type    TEXT,              -- tool name (e.g. "nmap", "kerberoast")
    target       TEXT,              -- IP, hostname, or CIDR
    status       TEXT,              -- running | completed | failed | orphaned
    output_file  TEXT,              -- absolute path to output file in recon/
    created_at   TIMESTAMP,
    completed_at TIMESTAMP
)

Scan statuses

StatusMeaning
runningTool process is actively executing
completedTool finished with a successful exit code
failedTool exited with an error or the scan was explicitly killed
orphanedScan was running when the app last restarted (process no longer exists)
Orphaned scans are distinct from failed scans. A scan is marked orphaned when the app restarts with a running entry in the database — indicating the previous server process was interrupted, not that the tool itself failed.

write_to_history() helper

Every scan module calls write_to_history() immediately at launch to create the database record:
def write_to_history(scan_type: str, target: str, status: str, output_file: str = None):
    scan_id = str(uuid.uuid4())
    cursor.execute("""
        INSERT INTO scans (id, scan_type, target, status, output_file, created_at, completed_at)
        VALUES (?, ?, ?, ?, ?, ?, ?)
    """, (
        scan_id,
        scan_type,
        target,
        status,
        output_file,
        datetime.now(),
        datetime.now() if status == 'completed' else None  # completed_at only set if already done
    ))
    return scan_id
The returned scan_id UUID is used by the scan endpoint to update the record as the tool runs.

update_scan_status() helper

Called when a tool process exits, this function sets the final status and records the completion timestamp:
def update_scan_status(scan_id, status, message=None):
    cursor.execute("""
        UPDATE scans SET status = ?, completed_at = ?
        WHERE id = ?
    """, (status, datetime.now(), scan_id))
The message parameter is accepted for API compatibility but is not persisted (there is no message column in the schema).

Startup recovery: cleanup_orphaned_scans()

On every app startup, before any requests are accepted, cleanup_orphaned_scans() runs as part of the lifespan sequence:
def cleanup_orphaned_scans():
    cursor.execute("""
        UPDATE scans
        SET status = 'orphaned', completed_at = ?
        WHERE status = 'running'
    """, (datetime.now(),))

    orphaned_count = cursor.rowcount
    if orphaned_count > 0:
        print(f"Marked {orphaned_count} orphaned scan(s) from previous session")
This prevents the Scan History page from showing stale running entries for processes that no longer exist.

Output file patterns

All output is written to the recon/ directory. Filenames always include a %Y%m%d_%H%M%S timestamp to avoid collisions between multiple runs of the same tool.
PatternContents
nmap_*.txt / nmap_*.xmlNmap scan results (text and XML formats)
masscan_*.txtMasscan host/port discovery output
nuclei_*.txtNuclei vulnerability scan results
bloodhound_*.zipBloodHound AD collection archive
kerberoast_*.txtKerberoastable TGS hashes ($krb5tgs$...)
asreproast_*.txtAS-REP hashes ($krb5asrep$...)
adcs_*.txtCertipy ADCS enumeration output
responder_*.txtResponder session log
mitm6_*.txtmitm6 session log
asrepcatcher_*.txtASRepCatcher session log
webscreenshot_*.jsonScreenshot scan results with tech tags and value scores
screenshots/*.pngIndividual web screenshot images
recon/ccache/*.ccacheKerberos ccache files (TGTs)
recon/loads/*.lnkMasterBaiter LNK payload files
recon/loads/*.library-msMasterBaiter Library-MS payload files
recon/loads/*.scfMasterBaiter SCF payload files
recon/loads/*.zipMasterBaiter zip-packaged payload bundles

Viewing scan history

The DATA → Scan History page lists all records from the scans table, sorted by created_at descending. Each row shows:
  • Scan type and target
  • Status badge (running, completed, failed, orphaned)
  • Start and completion timestamps
  • A link to the output file (if one was recorded)
Clicking the output file link opens or downloads the raw tool output directly from the recon/ directory.

Killing a running scan

Long-running scans can be stopped from the UI. The frontend sends:
POST /api/scans/kill
The endpoint looks up the process in the appropriate global process dictionary (e.g. RESPONDER_PROCESSES, MITM6_PROCESSES) and sends SIGTERM. The scan record is then updated to status = 'failed'.
If the kill button does not stop the underlying process, check the Scan History page to confirm the status was updated. In rare cases a stuck netexec process may need to be killed manually from a terminal.

Build docs developers (and LLMs) love