LuaN1ao Breakdown: Pinning Every Pentest Conclusion Back to the Evidence Chain, Instead of Leaving It in the Model's Memory
The v2 of a 1.3k-star autonomous pentest agent: Planner-Executor-Observer roles + task / reasoning / operation graphs, turning Evidence → Hypothesis → Vulnerability → Exploit into a traceable structured chain. Rewritten entirely in TypeScript on the Pi SDK. AGPL-3.0.
1. At a Glance: Is It Worth Your Time
Rating: ★★★★☆ (4 / 5)
Why this score: it is the open-source pentest project I have seen that takes “what exactly entitles an agent to a conclusion” the most seriously. In most autonomous pentest tools, the reasoning lives only in the model’s context — the run ends, you get a report, and the step “why is there a hole here” remains a black box that cannot be reviewed or replayed. LuaN1ao v2 inverts that: every confirmed Vulnerability node and every successful Exploit node must carry an evidence reference, or it cannot be written at all. It turns “evidence” from a slogan into a schema-level hard constraint.
Look deeper and it gets one harder thing right: planning is not a linear checklist but a dependency graph. When new evidence arrives, the plan is not torn up and regenerated — only the affected nodes and edges get patched. A long task no longer gets globally reshuffled just because something new turned up midway.
The missing star goes to four places, and none of them is trivial: AGPL-3.0 (strongly viral — corporate legal will block it); Node.js 25+ (it relies on the built-in node:sqlite, a threshold that shuts out most existing environments); no v2 benchmark yet — the README states explicitly that v1 results do not automatically carry over to v2, and v1 is the version that placed in Tencent’s competition, so the track record you see belongs to the old implementation; and human approval gates (manual sign-off for high-risk actions) are still unchecked on the roadmap — as things stand, it really will keep attacking on its own.
Key Data (as of 2026-09-29)
| Language | TypeScript (Pi SDK runtime) |
| Stars / Forks | 1,316 / 187 |
| Commits / Open Issues | 151 / 4 |
| License | AGPL-3.0 |
| First commit | 2025-12-04 (~10 months) |
| Last commit | 2026-09-15 (14 days ago) |
| Runtime requirements | Node.js 25+ (needs built-in node:sqlite), Docker recommended |
| Model access | OpenAI-compatible (Chat Completions default, Responses API optional) |
| Official tags | agents, autonomous-agents, causal-graphs, penetration-testing, pi-sdk, plan-execute-reflect |
Who It’s For
| Audience | Score | Why |
|---|---|---|
| Red team / pentest engineers | 5 / 5 | Replayable evidence chains, hard scope constraints, Docker egress lockdown — reports can go straight to review |
| Security researchers (agent architecture) | 5 / 5 | The P-E-O responsibility boundaries + three-graph separation are a rarely clear design worth stealing |
| Enterprise security operations | 3 / 5 | Capable, but AGPL-3.0 and the missing approval gates keep it out of production workflows for now |
| Developers (CI integration) | 2 / 5 | --jsonl output exists, but it is built for interactive long tasks, not pipeline gating |
| Individuals / learners | 2 / 5 | Node 25 + Docker + image builds are three hurdles in a row, and the executor runs shell — real environment risk |
Building on It
| Tier | Difficulty | Notes |
|---|---|---|
| Configuration | Low — set LLM_API_KEY / LLM_API_BASE_URL / LLM_DEFAULT_MODEL in .env; pass --goal --scope --max-cycles on the CLI |
|
| Integration | Medium — --jsonl event stream (last record is type: "result"), web-server.ts, React web workbench |
real hooks for external systems |
| Kernel | High — three-graph persistence + P-E-O contracts + 8 runtime invariants interlock; touching one means minding the rest |
2. What It Is, What It Isn’t
Draw the boundary first, or it is easy to expect the wrong thing from it:
- Not a vulnerability scanner. It does not match versions against CVE rulesets, and there is no fixed “scan → produce a list” pipeline.
- Not a report generator. The report is a projection of the reasoning graph, not the goal itself.
- Not an in-place refactor of v1. The README marks it IMPORTANT: v2 is a new implementation — configuration, persistence, agent lifecycle and the observability contracts all changed. Python v1 lives on in the
v1branch and thev1.0.0release. - Not a “here’s an agent, let it roam” framework. It is precisely freedom fenced in — who edits the task graph, who picks tools, and who projects facts is written into the Runtime invariants.
Its self-positioning in the Introduction is blunt (paraphrased): every significant conclusion must be traceable to persisted events, artifacts, and graph evidence. The entire README is one long argument for that single sentence.
Three keywords: graph-based cognitive reasoning, explicit agent boundaries, evidence-backed memory.
3. P-E-O: Three Roles, Each Staying in Its Lane
v2 replaces v1’s P-E-R (Planner / Executor / Reflector) with P-E-O. The most important difference: the Reflector’s “shared-history reflection loop” is split into two independent modes:
| Role | Manages | Submits via | Explicitly does NOT |
|---|---|---|---|
| Planner | goals, scope, dependencies, task budget, graph-level scheduling | planner_submit |
dictate concrete actions |
| Executor | picks the tool strategy autonomously within one TaskEnvelope boundary |
task_result_submit |
change task topology |
| Observer · Supervisor | hot-path control: continue / checkpoint / stop / hand back to Planner | control_submit |
project semantic graph facts |
| Observer · Projector | asynchronously turns observations into reasoning-graph / operation-graph deltas | graph_delta_submit |
modify task nodes |
A few design details I particularly appreciate:
1) Every agent call has an explicit termination-tool contract. The Planner must end with planner_submit, the Executor with task_result_submit. This turns “the agent stopped mid-run not knowing what to output” from an uncertainty into a schema-validation problem.
2) The Observer’s two modes each run in a brand-new Pi session, sharing no hidden model history. This one matters — if the supervisor shares context with the executor, it will “understand” the executor’s line of thinking and be more likely to let errors slide. Cutting the history forces it to judge purely from normalized observations.
3) The Planner does not wait for an entire parallel wave to finish before scheduling. It reads a compact task / reasoning / operation graph view and reconciles ready tasks against available capacity the moment the graph changes. Concurrency is incremental, not batch.
4) Large outputs never enter the model context — they become content-addressed artifacts. The Executor writes tool outputs over a threshold as immutable artifacts and passes only a reference and a preview to the model. This directly mitigates context explosion in long tasks.
Plan-on-Graph: Patching the Plan, Not Regenerating It
This is the one point I think deserves its own section. Comparison table (from the README):
| Capability | Linear task checklist | LuaN1ao’s PoG |
|---|---|---|
| Plan structure | ordered steps | dependency graph |
| Adaptation | regenerate the entire plan | patch the affected nodes and edges |
| Scheduling | manual ordering | dependency-aware admitted waves |
| Traceability | natural-language history | structured commands + persisted events |
The Planner’s “planning language” is five structured graph operations: create_tasks, patch_task, replace_dependencies, set_task_status, set_node_status. Every Planner command must carry a reason, and may reference the graph nodes or events it relies on.
Tasks and actions are separated: goals, tasks, milestones, blockers and scope live in the Task Graph; low-level tool actions stay in the append-only ExecutionLog. This layering keeps the “plan” and the “execution traces” from contaminating each other.
4. The Three-Graph Architecture and the Causal Reasoning Chain
What v2 persists is three graphs, plus one append-only event ledger:
1 | flowchart LR |
- Task Graph: goals, tasks, dependencies, milestones, blockers, scope. The Planner’s turf.
- Reasoning Graph: the Evidence / Hypothesis / Vulnerability / Exploit causal chain. The Projector’s turf.
- Operation Graph: concrete entities like WebEndpoint and Service. The reasoning graph links to it via cross-graph context.
- ExecutionLog: the append-only ledger of execution events — the input source for everything above.
The four constraints of the causal chain (this is the “hard constraint” from the opening, made concrete):
- Evidence first — reasoning nodes and edges must retain references to the events that support them.
- Explicit uncertainty — a hypothesis and a confirmed vulnerability / successful exploit are different node types; conflating them is not allowed.
- Enforced provenance — confirmed Vulnerability nodes and successful Exploit nodes cannot be written without an evidence reference.
- Asynchronous projection — the Projector turns bounded observation batches into graph deltas asynchronously, without blocking the Executor’s loop.
Constraint 3 is the entire point of the project. The problem with most “AI pentest tools” is not that the model is not smart enough — it is that when it says there is a hole, you cannot verify why it says so. When the schema forces an evidence reference, you structurally shut most of the “hallucinate into a vulnerability” path at the data-structure level. The remaining half depends on whether the reviewer bothers to look at the evidence.
Runtime Invariants (8 — read before touching the code)
The README lists these separately, and I believe they are the part the author most wants people to see:
- The Planner has exclusive task-graph decision authority; the Executor never changes task topology
- The Executor has exclusive low-level action choice within a
TaskEnvelopeboundary - The Supervisor only decides whether to continue; it never projects semantic graph facts
- The Projector only writes the reasoning / operation graphs; it never modifies task nodes
- Every agent call has an explicit termination-tool contract
- The Projector’s desired / committed watermark is monotonic
- Graph changes are atomic with committed projection watermarks
- Persisted events and artifacts are the single source of truth for observability
5. Sandbox and Egress Lockdown
This section is the dividing line between it and a toy project.
Docker runs one Executor per task, on a private internal network. The Executor and its Gateway sit in different network namespaces, and the Gateway is the sole egress for that task’s network. HTTP/HTTPS is transparently captured on any TCP port; other TCP is relayed as-is.
The permission hygiene is equally clean:
- The Executor container runs as UID 1000, zero capabilities, read-only root filesystem, with a size-limited
/tmp; only/workspaceis persistent and host-visible. - The Gateway container drops all capabilities first, then adds back only
NET_ADMIN/SETUID/SETGID; PID 1 sets up the TUN device and policy routing, starts the Go gateway process, and that process then clears its own capability bounding set. - Without Docker: Seatbelt on macOS, Bubblewrap on Linux, with
workspaceas an explicit development fallback. - Host paths outside the Executor workspace fail closed under enforced sandbox mode.
Scope is the network authorization root — not a prompt. --scope accepts comma-separated IPv4 / CIDR / exact domains / leading-wildcard domains. Bare IPs are normalized to /32. When --scope is omitted, the Planner extracts only IPv4 and CIDR explicitly present in --goal, and deterministic validation rejects fabricated or widened ranges; domain scope must be given explicitly. If the goal contains no explicit IPv4 target at all, startup fails outright and asks you to supply --scope.
This is, in my view, stricter than its peers: scope is not a line in the system prompt begging the model to stay inside — it is written into the Gateway’s policy, and out-of-scope traffic is rejected in the kernel. Domains get an extra layer: the Gateway filters DNS queries, adds the returned IPv4 addresses into a per-task, expiring kernel set before responses are allowed through; domain-derived UDP/ICMP may only reach addresses learned via that Gateway-controlled DNS.
Session Directory Layout
Every CLI invocation creates an isolated session (.agent-runtime/sessions/<session>/):
| Path | Purpose |
|---|---|
state.sqlite |
three graphs, execution events, projector watermarks, artifacts, runtime state |
execution.jsonl |
append-only audit mirror of normalized execution events |
graph-deltas.jsonl |
replayable mirror of graph deltas |
artifacts/ |
large outputs and cross-task persistent artifacts |
sandboxes/ |
per-task persistent workspace + host-only Pi session root |
executor-sessions/ |
Pi session lineage for the same task across epochs |
traffic/ |
segmented .mitm traffic, .net.jsonl telemetry, public CA, route/index metadata |
web-auth.sqlite |
local users and sessions for the web workbench |
6. Deployment and Getting Started
Hard Environment Requirements
| Component | Requirement | Notes |
|---|---|---|
| OS | macOS or Linux | Windows is not validated as a v2 release target |
| Node.js | 25+ | must support the built-in node:sqlite |
| Docker | recommended | on Linux the current user must reach the daemon (rootless Docker, or sudo usermod -aG docker $USER and re-login); otherwise it falls back to the host-native backend |
| LLM API | OpenAI-compatible | Chat Completions default |
| Terminal | ANSI-capable TTY | required by the interactive timeline |
| Browser | current Chromium / Firefox / Safari | required by the authenticated web workbench |
Install
1 | git clone https://github.com/SanMuzZzZz/LuaN1aoAgent.git |
Configure .env (git-ignored — never commit it):
1 | LLM_API_KEY=your-api-key |
A First Run
1 | npm start -- \ |
Main CLI arguments:
1 | --goal <text> agent goal |
Do not pass --goal or --scope together with --resume — resume restores the original session’s goal and scope, and passing them again can override the authorized scope. This is an easy safety trap to step on.
Machine-readable output: the last JSONL record is type: "result"; everything before it is type: "event". Use this to integrate with external systems.
Two optional keys beyond the LLM: BRAVE_SEARCH_API_KEY (web_search; degrades to an HTML-search fallback without it) and NVD_API_KEY (rate-limit boost for vulnerability_search; not required).
7. Boundaries and Risks (the part that must be said honestly)
1) AGPL-3.0 is the most practical hurdle. This is not MIT / Apache “take it and use it” — deploying it as a network service triggers source-disclosure obligations. In an enterprise, legal will very likely block it. Personal research, teaching and authorized red-team self-use are fine.
2) There is no v2 benchmark yet. The README states in a NOTE that v1 results do not automatically carry over to v2, and v2 numbers will only be published once they can be “reproduced on a frozen release.” The Tencent competition badge you see at the top of the README belongs to the v1 era. There is no public v2 head-to-head evaluation data at this stage — if you want to assess it, you have to build the environment and run it yourself.
3) No human approval gates for high-risk actions. Human approval gates for high-risk actions is still unchecked on the roadmap. In other words, in the current version, when the Executor decides to exploit something inside scope, it does. Practical advice: run it only in a fully isolated range or a disposable VM — never on any network that carries production traffic.
4) The Node.js 25+ threshold is higher than it looks. Because it depends on the built-in node:sqlite, this is not just “install a newer version” — many teams’ CI images and internal mirrors simply do not have a 25.x. And v2 is a new implementation that only started in 2025-12, so production-environment validation samples are scarce.
5) Docker permissions on Linux are a latent risk. The docs suggest sudo usermod -aG docker $USER, and the docker group is effectively root. If other things run on that machine, weigh the side effects yourself; without it you only get the Seatbelt / Bubblewrap fallback, and the egress lockdown gets weaker.
6) Several key roadmap items remain unfinished: a stable v2 extension API, a packaged container runtime and deployment profiles, a reproducible public benchmark, and cross-run capability memory. There is no stable API for extending the toolset today — adding your own tools means waiting or chewing through internal interfaces yourself.
7) Only 4 open issues is not necessarily “very stable.” The project is 10 months old, v2 is a rewrite, and the user base is still small — a low issue count more likely reflects not enough users yet rather than converged quality.
8. Getting Started (in This Order)
- Install first in a disposable VM or container. The executor can run shell commands, and the README’s own WARNING says to use an isolated host. Do not run your first attempt on your daily development machine.
- Get the Docker backend working before anything else.
npm run build:executor-image+npm run build:network-imageare the foundation of the egress lockdown and the sandbox; skipping them turns off the most important safety mechanism. - Keep the scope minimal on the first run. Use a
/32single IP instead of a/24, and confirm the Gateway actually blocks out-of-scope traffic (check the.net.jsonltelemetry undertraffic/) before widening it. - Start with
--max-cycles 3 --max-parallel-tasks 1. Watch how the Planner builds the graph, how the Executor uses tools, and how the Projector projects — before touching concurrency. In the interactive TUI,Tab/Shift+Tabswitch between the all-tasks and single-task views, and both are instructive. - Look at the Reasoning Graph, not the final report. This project’s value is in the process, not the artifact. Walk the Evidence → Hypothesis → Vulnerability → Exploit chain once in the web workbench and you will immediately see how it differs from everything else.
- For automation, use
--jsonl— the last record oftype: "result"is the terminal state. But do not expect it to work as a CI gate — its positioning and runtime are in the long-task class. - Before asking “can I use it”, decide whether you want v1 or v2. If you want an existing track record, look at the
v1branch; if you want the new architecture, look atmain. Do not mix the two.
9. The One-Line Verdict
If what you care about is “can every AI pentest conclusion be reviewed”, LuaN1ao v2 is one of the few open-source works that does it at the schema level — worth reading its Runtime invariants carefully; but if you want “install today, report tomorrow”, it is still missing the v2 benchmark, the approval gates, and a less discouraging license.
First move for red teamers and researchers: get the Docker backend running in an isolated VM, do a --max-cycles 3 short run with a /32 single-IP scope, then walk the causal chain end to end in the web workbench. Once you have seen that, you will know exactly how much it is worth to you.