Repository navigation
Architecture
This file is for an agent or a program that uses LazyOwn as a dependency.
It is not for an agent that edits the repository. If you modify the source,
read AGENTS.md and CLAUDE.md instead. I keep the three files separate
because each answers a different question and mixing them wastes context.
I wrote this document from the source as it stands. Every name below exists in the tree today. When the source and this file disagree, the source wins and this file is a bug.
The public surface is the set of names in this document. Everything else is
internal and can change without notice. The public names follow Semantic
Versioning in the sense that a breaking change to one of them requires a
major version bump in pyproject.toml.
| Surface | Import | Role |
|---|---|---|
| Configuration wrapper |
utils.Config, utils.load_payload
|
Read and write payload.json
|
| Configuration core | core.config |
load_payload, save_payload, Config, resolve_aes_key
|
| Structural protocols | core.protocols |
Selector, LLMBackend, MemoryStore, BridgeCatalog, OutcomeEvaluator
|
| LLM backends | modules.llm_factory |
Provider selection, budget proxy, errors |
| SQLite layer | modules.db |
Workspaces, hosts, services, vulns, creds, loot, notes, nmap import |
| Module catalog | modules.module_registry |
Discover and search addons, plugins, modules, tools, playbooks |
| Payload factory | modules.payload_factory |
Generate reverse shells, PowerShell, shellcode |
| Kill chain | modules.killchain |
Phases, progress, atomic updates |
| World model | modules.world_model |
Host state machine, credentials, vulnerabilities, context |
| Hardening primitives | core.hardening |
Path, host, port, filename validation and safe process calls |
| Safe execution | core.safe_exec |
Injection-resistant command, URL, and file helpers |
From a checkout:
python3 -m pip install -e .From PyPI, when the release workflow has published the wheel:
python3 -m pip install lazyownThe console entry point is lazyown = lazyown:main. LazyOwn targets Python
3.10 and newer. The full dependency list is heavy because the framework
covers exploitation, C2, and reporting. Install .[ml], .[dev], or the
other extras only when you need them.
payload.json in the working directory is the single source of runtime
configuration. Every component reads it. The typed shape lives in
core.payload_schema as a SCHEMA mapping. I expose three functions there:
validate_payload for a whole-file report, validate_value for one field,
and coerce_value for a safe cast such as a string port to an integer.
Read configuration in process:
from utils import Config, load_payload
params = load_payload()
cfg = Config(params)
print(cfg.rhost)Outside the process, use the same import. Cross-process state travels through
payload.json and the sessions/ directory, never through new ad hoc JSON
files.
Never instantiate a concrete backend. The factory reads the provider, model,
host, and credentials from payload.json and returns the abstract backend,
wrapped by the daily-budget proxy.
from modules.llm_factory import get_llm_backend, try_get_llm_backend
backend = get_llm_backend()
print(backend.complete("You are terse.", "List two open ports."))get_llm_backend raises LLMBackendUnavailableError when the provider
cannot be built. try_get_llm_backend returns None instead. Register a new
provider in modules.llm_factory.SUPPORTED_BACKENDS after implementing the
core.protocols.LLMBackend signature.
modules.db gives a workspace-isolated SQLite store with an nmap XML
importer. The table names are fixed and validated against a frozenset, so
callers cannot inject an arbitrary table.
from modules.db import get_db
db = get_db()
workspace_id = db.workspace_create("engagement-1")
host_id = db.host_add(workspace_id, "10.10.11.5", hostname="target")
db.service_add(host_id, port=22, name="ssh", version="OpenSSH 8.2")
print(db.status(workspace_id))One LazyOwnDB instance is thread safe. Close it when you are done.
modules.module_registry indexes the five extension surfaces. The registry is
a process-wide singleton.
from modules.module_registry import ModuleRegistry
registry = ModuleRegistry.get_instance()
matches = registry.search("smb")
for module in matches:
print(module.name, module.module_type)format_module_table and format_module_detail render the catalog for a
terminal.
modules.payload_factory generates payloads without msfvenom for the
common combinations and formats them as text, Python, or shellcode.
from modules.payload_factory import PayloadFactory
factory = PayloadFactory()
factory.generate("cmd/unix/reverse_shell", lhost="10.10.14.20", lport=4444)PayloadFactory.register extends the catalog with a new PayloadTemplate.
core.hardening and core.safe_exec are the functions I use before any
filesystem, process, or network boundary. Reuse them instead of writing your
own validation. safe_path_join combines a sanitized component with an
allowed directory and rejects a path that escapes it. safe_run_argv runs a
list without a shell. validate_host, validate_network_cidr, and
validate_port_spec reject malformed network input. defused_xml_parse
parses XML without external entity expansion.
modules.killchain owns the phase definitions and the only write path,
KillChain.advance_phase. modules.world_model owns the per-host state
machine and persists to sessions/world_model.json on every mutation. Read
the kill-chain snapshot instead of deriving phases yourself.
from modules.killchain import KillChain
from modules.world_model import WorldModel, HostState
wm = WorldModel()
wm.add_host("10.10.11.5")
wm.advance_host("10.10.11.5", HostState.SCANNED)
print(KillChain.snapshot())If you build a selector, a memory store, or an outcome evaluator for the
autonomous layer, implement the matching core.protocols interface. The
orchestrator depends on the Protocol, not on your class, which keeps the
dependency direction correct.
The Model Context Protocol server lives in skills/lazyown_mcp.py. It reuses
the CLI and the C2, so a tool always maps to an existing command or helper.
Start it with python3 skills/lazyown_mcp.py, or register it with your MCP
client through .mcp.json.
pyproject.toml holds the version. version.json mirrors it for the release
workflow. I publish dev, pp, and main branches; releases are tagged on
main. I do not promise compatibility for internal modules. I do promise
that a breaking change to a name in the public table above is a major bump.
lazyc2.py, lazyown.py, the cli/ command sets, the skills/ agents, and
the web templates are entry points and orchestration, not a library. Import
them only if you intend to drive the whole framework. The modules/legacy/
package holds old single-purpose scripts that predate the current contracts;
do not depend on it.
Read QUICKSTART.md for operator onboarding, docs/SECURITY_CONTRACTS.md
for the security invariants, and AGENTS.md for the internals if you plan to
contribute. The source of truth is always the code.
Generated offline by readmenator from commit bb49626e7e46. Edits here are overwritten by readmenator . --rebuild.
Start
Wiki
- community_0_cli_commands
- community_10_modules_opsec_scorer
- community_11_modules_finding_lifecycle
- community_12_modules_kerberos_core
- community_13_modules_saas_attacks
- community_14_modules_polymorphic_engine
- community_15_contrib_legacy
- community_16_scripts_journal
- community_17_modules_backdoor
- community_18_poc_tui
- community_19_scripts_check_contract_manifest
- community_1_modules_lazyc2
- community_20_scripts_migrate_lazyown
- community_21_tools
- community_22_orphans
- community_2_cli
- community_3_modules_autonomous_daemon
- community_4_modules_lazy_loop
- community_5_lazygui_panels
- community_6_skills_claude_md_orchestrator
- community_7_static_js
- community_8_modules_llm_budget
- community_9_modules_atomic_exploit_bridge
- concepts
- queries
- REPORT
Agent Docs
- API
- API_p10
- API_p11
- API_p12
- API_p13
- API_p14
- API_p15
- API_p16
- API_p17
- API_p18
- API_p19
- API_p2
- API_p20
- API_p21
- API_p22
- API_p23
- API_p3
- API_p4
- API_p5
- API_p6
- API_p7
- API_p8
- API_p9
- ARCHITECTURE
- ARCHITECTURE_p2
- ARCHITECTURE_p3
- ARCHITECTURE_p4
- ARCHITECTURE_p5
- ARCHITECTURE_p6
- ARCHITECTURE_p7
- CONCEPTS
- GOTCHAS
- INDEX
- INDEX_p2
- INDEX_p3
- KB_backdoor
- KB_blueprints
- KB_cgi-bin
- KB_claude_md_orchestrator
- KB_cli
- KB_cli_p2
- KB_cli_p3
- KB_cli_p4
- KB_cli_p5
- KB_commands
- KB_commands_p2
- KB_commands_p3
- KB_commands_p4
- KB_config
- KB_contract
- KB_core
- KB_core_p2
- KB_devtools
- KB_extensions
- KB_hermes-lazyown
- KB_integrations
- KB_js
- KB_js_p2
- KB_js_p3
- KB_js_p4
- KB_lazyc2
- KB_lazygui
- KB_lazyown-docker
- KB_legacy
- KB_legacy_p2
- KB_misc
- KB_modules
- KB_modules_p10
- KB_modules_p11
- KB_modules_p12
- KB_modules_p13
- KB_modules_p14
- KB_modules_p2
- KB_modules_p3
- KB_modules_p4
- KB_modules_p5
- KB_modules_p6
- KB_modules_p7
- KB_modules_p8
- KB_modules_p9
- KB_modules_tools
- KB_palettes
- KB_panels
- KB_plugins
- KB_poc_tui
- KB_root
- KB_root_p2
- KB_rootkit
- KB_scripts
- KB_security
- KB_services
- KB_skills
- KB_skills_claude_md_orchestrator_tests
- KB_skills_p2
- KB_skills_p3
- KB_skills_p4
- KB_skills_p5
- KB_skills_tests
- KB_tests
- KB_tests_p10
- KB_tests_p11
- KB_tests_p12
- KB_tests_p13
- KB_tests_p14
- KB_tests_p15
- KB_tests_p16
- KB_tests_p17
- KB_tests_p2
- KB_tests_p3
- KB_tests_p4
- KB_tests_p5
- KB_tests_p6
- KB_tests_p7
- KB_tests_p8
- KB_tests_p9
- KB_theme
- KB_tools
- KB_widgets
- KB_win_rootkit
- KB_windows
- MEMORY
- SECURITY
- SYMBOLS
- SYMBOLS_p10
- SYMBOLS_p11
- SYMBOLS_p12
- SYMBOLS_p13
- SYMBOLS_p14
- SYMBOLS_p15
- SYMBOLS_p16
- SYMBOLS_p17
- SYMBOLS_p18
- SYMBOLS_p19
- SYMBOLS_p2
- SYMBOLS_p20
- SYMBOLS_p21
- SYMBOLS_p22
- SYMBOLS_p23
- SYMBOLS_p24
- SYMBOLS_p25
- SYMBOLS_p26
- SYMBOLS_p27
- SYMBOLS_p28
- SYMBOLS_p29
- SYMBOLS_p3
- SYMBOLS_p30
- SYMBOLS_p31
- SYMBOLS_p32
- SYMBOLS_p33
- SYMBOLS_p34
- SYMBOLS_p35
- SYMBOLS_p36
- SYMBOLS_p37
- SYMBOLS_p38
- SYMBOLS_p39
- SYMBOLS_p4
- SYMBOLS_p40
- SYMBOLS_p41
- SYMBOLS_p5
- SYMBOLS_p6
- SYMBOLS_p7
- SYMBOLS_p8
- SYMBOLS_p9
Recipes