How VulnClaw's Persistent Pentesting Mode Handles State Preservation Across Cycles

VulnClaw's persistent pentesting mode preserves state by continuously serializing the SessionState object to disk after every autonomous round and cycle, ensuring findings, target data, and reflexion memories survive interruptions and accumulate across multiple bounded execution cycles.

The Unclecheng-li/VulnClaw repository implements a robust state preservation architecture that transforms autonomous security testing from a single-shot operation into a long-running, resumable process. Unlike standard pentesting loops that lose context upon termination, persistent mode treats the SessionState as the single source of truth, incrementally writing snapshots to JSON files after each execution round. This design enables assessments to pause, resume, or iterate continuously while building a cumulative vulnerability knowledge base.

How Persistent Mode Maintains Session Continuity

At the core of VulnClaw's persistence mechanism lies the SessionState object, which acts as the container for all discovered intelligence, execution history, and target metadata.

The SessionState Object as the Single Source of Truth

The SessionState class holds the current target definition, discovered findings, operational notes, and a complete list of executed steps. Within the auto_pentest loop in vulnclaw/agent/loop_controller.py, every round concludes with an explicit call to persist this state. According to the source code at lines 61-62, the method invokes agent.context.state.save() to write the entire state object to a JSON file under the sessions/ directory:


# Inside auto_pentest → after each round

agent.context.state.save()

This operation guarantees that later rounds or entirely new cycles start from the exact same state, eliminating data loss between bounded execution windows.

Continuous Persistence After Each Round

The persistence mechanism operates synchronously with the execution loop. After the agent completes a round of autonomous testing, the system immediately serializes the updated context. This approach ensures that even if the process terminates unexpectedly, the last completed round's state remains intact on disk.

Reflexion Memory Integration at Cycle Boundaries

When a cycle completes, the agent may have accumulated reflexion data—statistics about failed paths and constraint observations that inform future decision-making. As implemented in vulnclaw/agent/loop_controller.py at lines 80-84, the system checks for the presence of _save_reflexion_snapshot and merges this memory back into the session before writing the final state:

if hasattr(agent, "_save_reflexion_snapshot"):
    agent._save_reflexion_snapshot()
    agent.context.state.save()

This step ensures that learned behaviors and historical failure patterns persist across cycle boundaries, improving the efficiency of subsequent iterations.

Cycle Architecture and Context Reuse

The persistent_pentest method orchestrates multiple autonomous runs while maintaining a single, shared AgentContext instance.

Bounded Cycles with Shared State

Rather than recreating the execution context for each iteration, the method initializes AgentContext once and reuses it across all cycles. As shown in lines 30-46 of vulnclaw/agent/loop_controller.py, the loop repeatedly calls auto_pentest with the same state object:

results = await agent.auto_pentest(
    user_input=...,  # Include previous findings

    target=agent.context.state.target,
    max_rounds=rounds_per_cycle,
    on_step=_make_step_callback(cycle_num),
    stream_sink=stream_sink,
)

Because the agent.context.state object is never recreated, findings discovered in earlier cycles remain immediately available for reference in later cycles, enabling compound discoveries where each cycle builds upon the last.

Configuration-Driven Resource Limits

To prevent runaway resource consumption during long-running assessments, VulnClaw exposes configurable limits through its schema definition in vulnclaw/config/schema.py at lines 280-284:

persistent_rounds_per_cycle: int = Field(
    default=100, description="Rounds per persistent pentest cycle"
)
persistent_max_cycles: int = Field(
    default=10, description="Max cycles for persistent pentest (0=unlimited)"
)

These settings allow operators to cap CPU usage and API calls while still preserving progress between cycles. When persistent_max_cycles is set to 0, the system runs indefinitely until manually interrupted, with state preservation occurring at every step.

Automatic Cycle Reporting and Artifact Generation

At the conclusion of each cycle, the system generates persistent cycle reports that complement the JSON state files. The generate_persistent_cycle_report function in vulnclaw/report/generator.py (starting at line 589) creates markdown artifacts containing cycle numbers, timestamps, and snapshots of the current SessionState:

from vulnclaw.report.generator import generate_persistent_cycle_report

report_path = generatepersistent_cycle_report(
    session=agent.context.state,
    cycle_num=cycle_num,
    ...
)

These reports serve as human-readable checkpoints alongside the machine-readable JSON state files, facilitating audit trails and manual review of intermediate findings.

Practical Implementation Examples

CLI Usage

Launch a persistent pentest against a target using default configuration values:

vulnclaw persistent example.com

Programmatic Control with Custom Limits

Run a persistent pentest from Python with explicit cycle and round limits:

from vulnclaw.agent.core import AgentCore
from vulnclaw.config import load_config

cfg = load_config()  # Loads config including persistent settings

agent = AgentCore(cfg)

# Launch with 200 rounds per cycle, max 5 cycles

cycle_results = await agent.persistent_pentest(
    user_input="Perform an authorized persistent penetration test against example.com.",
    target="example.com",
    rounds_per_cycle=200,
    max_cycles=5,
    auto_report=True,
)

print(f"Completed {len(cycle_results)} cycles – final report at {cycle_results[-1].report_path}")

Recovering and Inspecting Saved State

To resume a session or inspect accumulated findings offline, load the JSON state file directly:

from vulnclaw.target_state.store import load_session_state

state = load_session_state("sessions/example.com.json")
print(state.findings)  # Shows all vulnerabilities discovered across cycles

Key Files and Responsibilities

File Purpose Direct Link
vulnclaw/agent/loop_controller.py Contains auto_pentest loop and persistent_pentest wrapper logic loop_controller.py
vulnclaw/config/schema.py Defines persistent_rounds_per_cycle and persistent_max_cycles configuration fields schema.py
vulnclaw/report/generator.py Implements generate_persistent_cycle_report for markdown artifact generation generator.py
vulnclaw/target_state/store.py Handles JSON serialization/deserialization of SessionState objects store.py
vulnclaw/cli/main.py CLI entry point that routes persistent commands to AgentCore cli/main.py

Summary

  • SessionState is the single source of truth: The agent.context.state object captures target data, findings, notes, and execution history, with save() called after every round in loop_controller.py lines 61-62.
  • Reflexion data persists across cycles: The system merges failure-pattern statistics back into the session state at cycle boundaries (lines 80-84) before writing the final snapshot.
  • Context reuse enables accumulation: The persistent_pentest method reuses the same AgentContext across all cycles (lines 30-46), ensuring findings compound rather than reset.
  • Configuration controls resource usage: persistent_rounds_per_cycle and persistent_max_cycles in config/schema.py limit execution while preserving intermediate state.
  • Dual-format persistence: JSON state files enable programmatic resumption, while markdown cycle reports from generator.py provide human-readable audit trails.

Frequently Asked Questions

What happens if a persistent pentest is interrupted mid-cycle?

If the process terminates during a cycle, the state remains preserved up to the last completed round. Since agent.context.state.save() executes after every round in loop_controller.py, resuming the session reloads the most recent snapshot from the sessions/ directory, losing only the in-progress round's partial data.

How does reflexion memory improve state preservation?

Reflexion memory stores statistics about failed exploitation paths and environmental constraints observed during previous cycles. By calling _save_reflexion_snapshot and re-saving state at cycle boundaries (lines 80-84), VulnClaw ensures that the agent avoids previously failed approaches in subsequent cycles, making the persistent mode increasingly efficient over time.

Can I resume a pentest from a saved session file on a different machine?

Yes. The load_session_state function in vulnclaw/target_state/store.py deserializes the JSON state file independently of the original execution environment. Transferring the sessions/target.json file to another system with VulnClaw installed allows immediate resumption, provided the target network configuration remains accessible.

What are the default limits for persistent pentesting cycles?

According to vulnclaw/config/schema.py lines 280-284, the default configuration allows 100 rounds per cycle (persistent_rounds_per_cycle) and a maximum of 10 cycles (persistent_max_cycles). Setting persistent_max_cycles to 0 removes the upper bound, enabling indefinite execution with continuous state preservation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →