## File: docs/architecture.md

# PentestGPT architecture

Status: 2026-07-12

This is the internal source of truth for the current repository shape. The root `README.md` is the
public project page and is intentionally being revised separately.

## Repository family

```text
PentestGPT_Project/
├── PentestGPT/          # framework, legacy interactive client, and tool image
├── UnifedAgentWrapper/  # canonical unified-agent package
└── xbow-benchmark/      # reference-only benchmark harness and historical results
```

The three directories are independent Git repositories. The XBOW checkout is retained as a
reference corpus only; the product CLI, runtime, and CI do not depend on it. Benchmark logic and
result archives must not be added here.

## Maintained runtime

`pentestgpt_agent/` is the autonomous framework. It is a nested uv project with its own lockfile and
environment. The loop deliberately has only two LLM roles:

```text
RunSnapshot -> Supervisor -> compile_plan -> one TaskLease
                                             |
TraceStore <- EpisodeRunner <- Executor <----+
     |                              |
     +---- compile_execution -------+
                    |
               MemoryKernel
```

- The Supervisor chooses one task or proposes completion.
- The Executor performs one leased task and returns a typed result.
- Both roles use fresh provider sessions and `FULL_ACCESS`. The deployment environment is the
  isolation boundary; PentestGPT does not maintain a second tool or filesystem sandbox.
- Deterministic code owns scope validation, leases, evidence provenance, retries, revisions, and
  canonical state.
- SQLite is canonical memory. Provider transcripts are diagnostic traces, not memory.
- There is no always-on judge, RAG service, speculative backlog, or parallel scheduler.

`pentestgpt_legacy/` is the maintained human-driven implementation of the USENIX 2024 workflow. It
has its own lightweight provider clients and does not use `unified_agent`.

## Why unified-agent remains useful

The external `UnifedAgentWrapper` is a real seam with two production adapters: Claude Code and
Codex. PentestGPT depends on its small interface for:

- provider selection, model, effort, workspace, and permission configuration;
- structured-output invocation;
- normalized command, tool, file, session, usage, cost, and terminal events;
- shared task rendering and provider error handling.

Removing that module would duplicate provider SDK churn inside `pentestgpt_agent.trace` and
`pentestgpt_agent.trial`. It therefore earns its place as a deep module. PentestGPT policy must stay
outside it: task kinds, memory, evidence, scope, scheduling, and completion belong to this repo.

The dependency is pinned to a commit of the public package in `pentestgpt_agent/pyproject.toml`, and
`tests/test_dependency.py` verifies that the nested project imports the installed dependency rather
than the repository-root copy.

## Root unified_agent copy

The root `unified_agent/` is an older, drifted compatibility copy. No maintained PentestGPT runtime
imports it:

- `pentestgpt_agent` uses the external package;
- `pentestgpt_legacy` uses its own provider clients;
- only root packaging, its duplicate tests, and the current tool-image health check retain it.

Do not develop features in this copy. Removing it is desirable, but it is a separate public-package
cleanup because the root wheel currently exports the package and the Docker image copies it. That
cleanup must update the root package metadata, Docker health tests, lockfile, and public README in
one deliberate change.

## Deep modules

| Module | Interface | Hidden implementation |
|---|---|---|
| `PentestLoop` | `run(RunSpec) -> RunSnapshot` | recovery ordering, retries, episode identity, failure settlement |
| `MemoryKernel` | create/open/snapshot and atomic commits | SQLite schema, transactions, revisions, leases, dependency liveness |
| `compile_plan` | decision + snapshot -> valid plan | scope, dependency, phase, completion, and size validation |
| `compile_execution` | result + lease + trace -> valid execution | exact receipt matching, evidence fallback, identity, recovery rules |
| `EpisodeRunner` | one typed episode -> normalized result | provider invocation and durable append-only trace files |
| external `UnifiedAgent` | one task over Claude or Codex | SDK differences, native options, event normalization |

These interfaces are the preferred test surfaces. New provider behavior belongs behind
`UnifiedAgent`; new canonical-state behavior belongs behind the Memory Kernel or compilers.

## Memory and retrieval

Stored state is complete and retrieval is bounded. The Supervisor receives the open working set,
recent closed work, required basis/dependency context, selected observations, history counts, and
recent diagnostics. The Executor receives one task, explicit basis, same-task evidence, and a retry
diagnostic. A future retriever may select canonical IDs, but it must not replace SQLite or turn
summaries into evidence.

The current controller weakness is convergence, not database capacity: long runs can lose compact
coverage information and revisit completed surfaces. The next design slice should improve the
deterministic strategy projection and duplicate/branch policy before adding another agent or RAG.

## Runtime and benchmark ownership

- Local framework development runs from `pentestgpt_agent/`.
- The root Docker image supplies pentest tools, provider CLIs, legacy code, and persisted auth. It
  does not currently bake in the maintained framework.
- The sibling `xbow-benchmark` checkout is a historical/reference artifact, not a supported runtime
  or verification path. PentestGPT owns no XBOW runner.
- HTB execution happens only on the authorized remote attack box described in the parent project
  guide, never directly from the development Mac.

## Current design priorities

1. Preserve the two-role loop and deterministic memory authority.
2. Improve Supervisor coverage retrieval and convergence using saved-trace replay tests.
3. Keep both roles fully enabled inside a restricted deployment environment.
4. Remove the root `unified_agent` compatibility copy in a coordinated public-package cleanup.
5. Add a goal-specific verifier only when non-CTF completion semantics require it.

---

## File: docs/docker-dev-plan.md

# Docker runtime status

Status: 2026-07-12

This document describes the repository as it exists now. It replaces the original implementation
plan, whose phase matrix and in-repo benchmark paths are obsolete.

## Current image responsibility

`pentestgpt:latest` is a disposable pentest-tool and provider-CLI environment. It contains:

- Ubuntu 24.04, Python 3.12, Node 20, `uv`, Claude Code, and Codex;
- common network/pentest tools such as nmap, gobuster, dirb, netcat, curl, DNS utilities, jq, and
  ripgrep;
- the root `pentestgpt_legacy` package;
- the old root `unified_agent` compatibility copy;
- persistent Claude and Codex authentication helpers.

It deliberately excludes benchmark fixtures/results, credentials, workspaces, and run artifacts.

The maintained `pentestgpt_agent` nested project is **not baked into this image**. Consequently,
`make docker-run` deliberately fails fast with a wiring diagnostic instead of invoking an absent
CLI. There is no product-owned benchmark workaround; framework-image wiring remains an independent
deployment task.

## Repository ownership

```text
PentestGPT/          image, auth helpers, framework source, legacy client
UnifedAgentWrapper/  canonical provider-wrapper package
xbow-benchmark/      reference-only benchmark harness and historical results
```

The product does not support or invoke the sibling benchmark harness. Keep result JSONL, target
orchestration, and benchmark-specific adapters out of this repository.

## Persistent provider login

Authentication state lives in named volumes and is never copied into image layers:

```text
pentestgpt-claude -> /home/pentester/.claude
pentestgpt-codex  -> /home/pentester/.codex
```

The providers require different setup paths:

- Claude uses a long-lived `setup-token`, stored as `.claude/oauth_token` and exported as
  `CLAUDE_CODE_OAUTH_TOKEN` by the entrypoint.
- Codex performs its own in-container OAuth login. Its callback is forwarded through a `socat` hop;
  host `auth.json` must not be copied because ChatGPT refresh tokens rotate.

Useful commands:

```bash
make docker-build
make docker-login
make docker-auth-status
ROUNDTRIP=1 make docker-auth-status   # spends a minimal provider call
make docker-shell
make docker-down                      # keeps auth volumes
make docker-nuke                      # deliberately removes auth volumes
```

The auth-status check is advisory by default. Named volumes are credentials and must be protected
like a logged-in workstation.

## Isolation contract

Both PentestGPT roles use provider `FULL_ACCESS`. The container or dedicated attack box is therefore
the blast radius and security boundary. A deployment must:

- contain only authorized target routes;
- avoid mounting unrelated source, home directories, tokens, or host sockets;
- mount run state only when persistence is required;
- treat traces and SQLite state as sensitive;
- tear down the environment after the assessment.

The tool image runs as `pentester`, which has passwordless sudo. It is isolation from the developer
host only when mounts, capabilities, devices, and networking are deliberately constrained.

## Framework-image decision still open

There are two reasonable future shapes:

1. Build framework and `unified-agent` wheels outside Docker, then copy them into a dedicated runtime
   image. This matches the proven qualification runner and preserves exact package hashes.
2. Publish both packages and install pinned releases during the Docker build.

Do not copy the root `unified_agent/` directory into the maintained framework. It is version 0.1-era
compatibility code; the agent requires the pinned external 0.3 package.

Whichever shape is selected must make these checks true in a fresh container:

```bash
pentestgpt-agent --help
python -c "import pentestgpt_agent, unified_agent; print(unified_agent.__version__)"
```

Only after that should `make docker-run` be advertised as supported.

## Build cleanup opportunities

These are independent of framework design:

- remove `apt-get upgrade` for faster, more reproducible builds;
- install `socat` in the main apt layer;
- combine global npm installs;
- use BuildKit cache mounts for apt, npm, and uv;
- install from lockfiles/wheels before copying frequently changing source;
- remove the root `unified_agent` copy and its SDK dependencies when the public-package cleanup is
  performed.

## External benchmark reference

The sibling `xbow-benchmark` repository is retained only for historical reference. The product
Makefile, CLI, CI, and Docker runtime do not invoke it. If a future evaluation reuses that corpus,
design the adapter in an external evaluation repository rather than restoring a benchmark runner
inside PentestGPT.

---

## File: pentestgpt_agent/README.md

# PentestGPT Agent

Small, durable penetration-testing loop built on the external
[`unified-agent`](https://github.com/PentestGPT-Project/UnifedAgentWrapper) package. It is the
maintained autonomous framework; the root `pentestgpt_legacy` package remains the human-driven
USEN-2024 workflow.

The supported deployment boundary is an isolated, disposable environment containing only
authorized targets and the required provider credentials.

## Runtime shape

```text
SQLite snapshot
  -> recover an active lease or invoke a fresh Supervisor
  -> validate one typed decision and atomically lease one task
  -> recover its trace or invoke a fresh Executor
  -> validate trace identity and exact receipt evidence
  -> atomically commit attempt, observation, and transition
  -> repeat, finish, safely retry, or fail closed
```

- The Supervisor and Executor both receive all provider tools and `FULL_ACCESS` filesystem/process
  permissions. PentestGPT relies on deployment isolation rather than a second in-process sandbox.
- The Supervisor proposes at most one new task and selects exactly one ready task or completion.
- The Executor receives one leased task with an explicit task kind and bounded provider turn budget.
- Every episode is fresh (`resume = null`); provider conversation history is not memory.
- Deterministic code owns scope, dependencies, leases, receipt provenance, retries, completion
  bases, and canonical state transitions.
- Provider actions and file writes are retained in traces and audit totals, but are allowed.
- There is no speculative backlog, parallel scheduler, RAG service, or always-on judge.

See [`CONTEXT.md`](CONTEXT.md) for the exact domain language and invariants, and
[`../docs/architecture.md`](../docs/architecture.md) for repository-level decisions.

## Memory and traces

SQLite stores runs, typed tasks, attempts, bounded exact observations, and one transition per
revision. An observation must resolve to one exact contiguous slice of one eligible command or tool
receipt. Completed nonzero commands may ground negative findings; provider/tool transport failures
cannot. CRLF/LF normalization is the only accepted textual transport normalization.

An oversized receipt contributes at most an exact 4,000-character suffix and can commit only
`progress`. A no-action attempt may reuse exact evidence from an earlier attempt of the same task;
unsupported paraphrases degrade to `progress` and never create evidence.

The Supervisor receives a bounded projection: open work, four recent closed tasks, required
dependency/basis context, selected observations, aggregate history, and recent diagnostics. The
Executor receives one task, its explicit basis, up to two same-task observations, and one retry
diagnostic. Future retrieval may select canonical IDs, but SQLite remains authoritative.

Each episode directory contains:

```text
input.json     exact role input, prompt/schema hashes, provider policy
events.jsonl   chronological normalized tool, command, file, and terminal events
output.json    normalized result, usage, cost, duration, and failure
```

Traces contain sensitive target output and provider session identifiers. They are mode-restricted,
not encrypted or tamper-evident.

## Failure and restart behavior

- Operational failures retry only when no external-action receipt exists.
- Actionful failures are never replayed automatically.
- Existing terminal traces are compiled and committed before any provider reinvocation.
- A terminal event can reconstruct a missing `output.json`.
- Supervisor decisions are recovered by state revision; invalid plans receive one bounded retry.
- Attempt and decision budgets survive restart.
- `--resume` requires an exact `trial-config.json` match, including goal, targets, provider, model,
  effort, prompt/schema hashes, budgets, both full-access role policies, environment, and
  `unified-agent` version.

## Install and verify

Run from this directory so the repository-root compatibility copy cannot shadow the dependency.

```bash
uv sync --extra claude       # or: codex / all
uv run python -m pytest -q
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy src
uv lock --check
uv build
```

The normal suite uses scripted provider adapters and a loopback target. The opt-in live smoke spends
provider tokens:

```bash
PENTESTGPT_AGENT_LIVE=1 \
PENTESTGPT_AGENT_BACKEND=claude \
PENTESTGPT_AGENT_MODEL=claude-opus-4-8 \
uv run python -m pytest tests/live/test_local_model.py -q -s
```

## Run and resume

```bash
uv run pentestgpt-agent \
  --goal "Capture the flag from this authorized target." \
  --target http://127.0.0.1:8080 \
  --backend claude \
  --model claude-opus-4-8 \
  --effort xhigh \
  --run-id example

uv run pentestgpt-agent \
  --goal "Capture the flag from this authorized target." \
  --target http://127.0.0.1:8080 \
  --backend claude \
  --model claude-opus-4-8 \
  --effort xhigh \
  --run-id example \
  --resume
```

Audit a held-out oracle:

```bash
uv run pentestgpt-agent-audit \
  --run-dir runs/example \
  --expected-flag 'FLAG{...}' \
  --output runs/example-audit.json
```

Audit schema 2 checks structural completion, canonical oracle evidence, settled leases/episodes,
exact receipt grounding, identity, dependency/basis integrity, current TEST provenance for EXPLOIT,
completion basis, revision continuity, and settled failures. Supervisor actions and provider-memory
writes are reported as diagnostics rather than failures.

## UnifiedAgent dependency

`unified-agent==0.3.0` is pinned to the merged inspector commit in `pyproject.toml`. It is the provider seam
for Claude Code and Codex: task invocation, model/effort configuration, structured output, and
normalized events. PentestGPT keeps task policy, memory, evidence, and completion outside that
dependency.

`tests/test_dependency.py` verifies that imports resolve to the installed external package, not the
obsolete repository-root `unified_agent/` copy.

### Live process inspector

UnifiedAgent 0.3 adds a read-only local web inspector. PentestGPT gives every monitored provider
episode a stable `--<episode-id>` identity and attaches the role, state revision,
task, and attempt as metadata. The framework's trace journal remains canonical; the inspector
database is an additional real-time diagnostic projection.

To inspect local wrapper changes before publishing a new immutable dependency revision, explicitly
opt into the sibling checkout:

```bash
export PYTHONPATH="$(cd ../../UnifedAgentWrapper && pwd)"
export UNIFIED_AGENT_MONITOR_DB="$(pwd)/runs/inspector.sqlite3"
uv run --no-sync pentestgpt-agent \
  --goal "Assess this explicitly authorized target." \
  --target http://127.0.0.1:8080 \
  --backend codex \
  --model gpt-5.5 \
  --run-id inspector-example

# In another terminal, from ../../UnifedAgentWrapper:
PYTHONPATH=. .venv/bin/python -m unified_agent.monitor_web \
  --database "$(cd ../PentestGPT/pentestgpt_agent && pwd)/runs/inspector.sqlite3" \
  --open
```

The pinned 0.3 dependency provides the same monitor behavior in normal synchronized environments;
the sibling override is only for wrapper development.

## External evaluation references

The sibling `../../xbow-benchmark` checkout is retained only as a historical/reference corpus.
There is no supported XBOW command, runner, or CI path in `pentestgpt-agent`, and benchmark-specific
orchestration must stay outside this product repository. Historical engineering records may still
name earlier XBEN qualifications; they are not current regression guarantees.

The later HTB Enigma qualification did not solve the target; see
[`HTB_ENIGMA_QUALIFICATION_20260712.md`](HTB_ENIGMA_QUALIFICATION_20260712.md).

## Current limits

- `FULL_ACCESS` is intentional. Either role may read or alter mounted state and credentials; the
  surrounding container/VM must be treated as the blast radius.
- Provider `max_turns` is not a portable command/action limit, especially on Codex. It bounds the
  provider interaction where supported, not external side effects.
- Controller convergence is the main demonstrated performance problem: older coverage can fall out
  of the Supervisor projection and trigger repeated discovery.
- `COMPLETED` proves structural evidence citation, not arbitrary natural-language entailment. CTF
  audits use a held-out oracle; general goals need a goal-specific verifier.
- SQLite has no versioned migration framework. Trace storage is sensitive and not power-loss or
  tamper hardened.
- Structured findings, report generation, and a PentestGPT product UI remain future work. The
  wrapper-level process inspector is available for live diagnostics.