pwn 0.5.620 → 0.5.622

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +1 -1
  4. data/documentation/Agent-Tool-Registry.md +3 -3
  5. data/documentation/Configuration.md +292 -47
  6. data/documentation/Cron.md +14 -1
  7. data/documentation/Diagrams.md +1 -1
  8. data/documentation/Extrospection.md +71 -5
  9. data/documentation/Home.md +2 -2
  10. data/documentation/How-PWN-Works.md +1 -1
  11. data/documentation/Mistakes.md +2 -1
  12. data/documentation/Persistence.md +2 -1
  13. data/documentation/Skills-Memory-Learning.md +3 -2
  14. data/documentation/Transparent-Browser.md +18 -1
  15. data/documentation/diagrams/agent-tool-registry.svg +55 -54
  16. data/documentation/diagrams/ai-integration-tool-calling.svg +24 -24
  17. data/documentation/diagrams/aws-cloud-security.svg +25 -25
  18. data/documentation/diagrams/burp-vs-zap-preference.svg +12 -12
  19. data/documentation/diagrams/code-scanning-sast.svg +25 -25
  20. data/documentation/diagrams/cron-scheduling.svg +23 -23
  21. data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
  22. data/documentation/diagrams/dot/extrospection-world-awareness.dot +29 -8
  23. data/documentation/diagrams/dot/memory-skills-detailed.dot +4 -0
  24. data/documentation/diagrams/dot/overall-pwn-architecture.dot +2 -2
  25. data/documentation/diagrams/dot/persistence-filesystem.dot +2 -1
  26. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +11 -5
  27. data/documentation/diagrams/driver-framework.svg +13 -13
  28. data/documentation/diagrams/extrospection-world-awareness.svg +229 -99
  29. data/documentation/diagrams/fuzzing-workflow.svg +24 -24
  30. data/documentation/diagrams/hardware-hacking.svg +18 -18
  31. data/documentation/diagrams/history-to-drivers.svg +18 -18
  32. data/documentation/diagrams/memory-skills-detailed.svg +124 -101
  33. data/documentation/diagrams/mistakes-negative-feedback.svg +55 -55
  34. data/documentation/diagrams/network-infra-testing.svg +27 -27
  35. data/documentation/diagrams/overall-pwn-architecture.svg +85 -85
  36. data/documentation/diagrams/penetration-testing-workflow.svg +30 -30
  37. data/documentation/diagrams/persistence-filesystem.svg +94 -85
  38. data/documentation/diagrams/plugin-ecosystem.svg +35 -35
  39. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +226 -180
  40. data/documentation/diagrams/pwn-repl-prototyping.svg +20 -20
  41. data/documentation/diagrams/reporting-pipeline.svg +18 -18
  42. data/documentation/diagrams/reverse-engineering-flow.svg +21 -21
  43. data/documentation/diagrams/sdr-radio-flow.svg +31 -31
  44. data/documentation/diagrams/sessions-cron-automation.svg +18 -18
  45. data/documentation/diagrams/swarm-multi-agent.svg +39 -39
  46. data/documentation/diagrams/web-application-testing.svg +26 -26
  47. data/documentation/diagrams/zero-day-research-flow.svg +25 -25
  48. data/documentation/pwn-ai-Agent.md +4 -4
  49. data/lib/pwn/ai/agent/assembly.rb +1 -1
  50. data/lib/pwn/ai/agent/btc.rb +1 -1
  51. data/lib/pwn/ai/agent/burp_suite.rb +1 -1
  52. data/lib/pwn/ai/agent/extrospection.rb +435 -6
  53. data/lib/pwn/ai/agent/gqrx.rb +2 -2
  54. data/lib/pwn/ai/agent/hacker_one.rb +1 -1
  55. data/lib/pwn/ai/agent/learning.rb +16 -16
  56. data/lib/pwn/ai/agent/loop.rb +1 -1
  57. data/lib/pwn/ai/agent/{introspection.rb → reflect.rb} +12 -12
  58. data/lib/pwn/ai/agent/sast.rb +1 -1
  59. data/lib/pwn/ai/agent/tools/extrospection.rb +89 -4
  60. data/lib/pwn/ai/agent/tools/learning.rb +7 -7
  61. data/lib/pwn/ai/agent/transparent_browser.rb +1 -1
  62. data/lib/pwn/ai/agent/vuln_gen.rb +2 -2
  63. data/lib/pwn/ai/agent.rb +1 -1
  64. data/lib/pwn/ai/open_ai.rb +10 -6
  65. data/lib/pwn/config.rb +5 -4
  66. data/lib/pwn/plugins/burp_suite.rb +2 -2
  67. data/lib/pwn/sast/pom_version.rb +2 -2
  68. data/lib/pwn/sast/test_case_engine.rb +1 -1
  69. data/lib/pwn/sdr/decoder/adsb.rb +14 -33
  70. data/lib/pwn/sdr/decoder/apt.rb +97 -36
  71. data/lib/pwn/sdr/decoder/base.rb +258 -171
  72. data/lib/pwn/sdr/decoder/bluetooth.rb +13 -20
  73. data/lib/pwn/sdr/decoder/dect.rb +13 -27
  74. data/lib/pwn/sdr/decoder/dsp.rb +396 -0
  75. data/lib/pwn/sdr/decoder/flex.rb +177 -210
  76. data/lib/pwn/sdr/decoder/gps.rb +12 -21
  77. data/lib/pwn/sdr/decoder/gsm.rb +21 -73
  78. data/lib/pwn/sdr/decoder/iridium.rb +11 -26
  79. data/lib/pwn/sdr/decoder/lora.rb +16 -23
  80. data/lib/pwn/sdr/decoder/lte.rb +11 -32
  81. data/lib/pwn/sdr/decoder/morse.rb +95 -21
  82. data/lib/pwn/sdr/decoder/p25.rb +19 -16
  83. data/lib/pwn/sdr/decoder/pager.rb +22 -38
  84. data/lib/pwn/sdr/decoder/pocsag.rb +175 -68
  85. data/lib/pwn/sdr/decoder/rfid.rb +24 -33
  86. data/lib/pwn/sdr/decoder/rtl433.rb +21 -30
  87. data/lib/pwn/sdr/decoder/rtty.rb +100 -27
  88. data/lib/pwn/sdr/decoder/wifi.rb +21 -42
  89. data/lib/pwn/sdr/decoder/zigbee.rb +18 -40
  90. data/lib/pwn/sdr/decoder.rb +8 -0
  91. data/lib/pwn/sdr/gqrx.rb +1 -1
  92. data/lib/pwn/version.rb +1 -1
  93. data/spec/lib/pwn/ai/agent/{introspection_spec.rb → reflect_spec.rb} +3 -3
  94. data/spec/lib/pwn/sdr/decoder/dsp_spec.rb +15 -0
  95. data/third_party/pwn_rdoc.jsonl +75 -19
  96. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7b52a560de426b6b9c99fa2716b8e93a948e66088e0e1ae78d28f8633417cea
4
- data.tar.gz: f94969b76a9ad4b856a9b5db25647f41d57f600f0c331ebde29c24b35ab59cd1
3
+ metadata.gz: f4aeb484ae013e0feb9648009d60cfc493bbd4ec98ba42193745af669ed70bcc
4
+ data.tar.gz: 2dcffdf992ee24a9c8a10ae7b83c743ad5b6b1aed83e70063da56e8cf6e9ea85
5
5
  SHA512:
6
- metadata.gz: dcd6287345b8f7230aeeeddc8a5d8de97d4000ae6bddecd77b4b2957f8ddf480de5c9ba8ba6a96a9fcf77f64a30f0b3d9a41219ad15512deab556c5b7e60c360
7
- data.tar.gz: 92b3f2b90679984a47ed9cff3e1440100803e4220fc6f78eac386bd5cd125f24acbdd7b44504c20f783ba653895726495a69b7ce5d9cd5fd883a98cb67a11ef9
6
+ metadata.gz: 2468ec9291d44be94de1f3877441380e60cde34e24953f6ac677bd39f0679c82ab6f82f6554b7fcacf1348b0c751c63bf8d8a23a369ac665023b5a9e74cc3a01
7
+ data.tar.gz: 722765fd660ca9da266a170da45f65eb061886b7629557143436a0765412dc89d3de3e56ac8f5bd8e9b32e5f25831d3644ac3568d851f6ac5f7629531b9e7768
data/Gemfile CHANGED
@@ -50,7 +50,7 @@ gem 'libusb', '0.7.2'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
52
  gem 'mcp', '0.23.0'
53
- gem 'meshtastic', '0.0.169'
53
+ gem 'meshtastic', '0.0.171'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.24.1'
56
56
  gem 'msfrpc-client', '1.1.2'
data/README.md CHANGED
@@ -52,7 +52,7 @@ Five layers, edges only ever go down:
52
52
 
53
53
  The AI layer closes a **self-improvement loop** on every turn — Metrics +
54
54
  Learning + **Mistakes** (introspection / negative feedback) joined with
55
- Snapshot + Drift + Intel + RF (extrospection) via `extro_correlate`, so the
55
+ Snapshot + Drift + Intel + RF + **Web** (extrospection) — plus browser-backed **`extro_verify`** self fact-checking — via `extro_correlate`, so the
56
56
  agent knows whether a failure was *its* fault or *the world* changed —
57
57
  **and does not repeat the same mistake twice**:
58
58
 
@@ -6,7 +6,7 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
6
6
 
7
7
  ![Tool registry](diagrams/agent-tool-registry.svg)
8
8
 
9
- ## Toolsets → Tools (10 toolsets · 52 tools)
9
+ ## Toolsets → Tools (10 toolsets · 54 tools)
10
10
 
11
11
  | Toolset | Tools | Backed by |
12
12
  |---|---|---|
@@ -15,9 +15,9 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
15
15
  | `memory` | `memory_remember` · `memory_recall` · `memory_forget` · `memory_clear` | `PWN::Memory` → `~/.pwn/memory.json` |
16
16
  | `skills` | `skill_list` · `skill_view` · `skill_create` · `skill_add_reference` · `skill_delete` | `~/.pwn/skills/*.md` |
17
17
  | `sessions` | `sessions_list` · `sessions_view` · `sessions_current` · `sessions_delete` · `sessions_stats` | `PWN::Sessions` → `~/.pwn/sessions/` |
18
- | `learning` | `learning_note_outcome` · `learning_reflect` · `learning_distill_skill` · `learning_stats` · `learning_outcomes` · `learning_consolidate` · `learning_reset` · `learning_auto_reflect_toggle` · **`mistakes_list`** · **`mistakes_record`** · **`mistakes_resolve`** · **`mistakes_reset`** | `PWN::AI::Agent::Learning` + `PWN::AI::Agent::Mistakes` → `~/.pwn/learning.jsonl` + `~/.pwn/mistakes.json` |
18
+ | `learning` | `learning_note_outcome` · `learning_reflect` · `learning_distill_skill` · `learning_stats` · `learning_outcomes` · `learning_consolidate` · `learning_reset` · `learning_auto_introspect_toggle` · **`mistakes_list`** · **`mistakes_record`** · **`mistakes_resolve`** · **`mistakes_reset`** | `PWN::AI::Agent::Learning` + `PWN::AI::Agent::Mistakes` → `~/.pwn/learning.jsonl` + `~/.pwn/mistakes.json` |
19
19
  | `metrics` | `metrics_summary` · `metrics_reset` | `PWN::AI::Agent::Metrics` → `~/.pwn/metrics.json` |
20
- | `extrospection` | `extro_snapshot` · `extro_drift` · `extro_observe` · `extro_observations` · `extro_intel` · `extro_correlate` · `extro_stats` · `extro_reset` · `extro_auto_toggle` | `PWN::AI::Agent::Extrospection` → `~/.pwn/extrospection.json` |
20
+ | `extrospection` | `extro_snapshot` · `extro_drift` · `extro_observe` · `extro_observations` · `extro_intel` · **`extro_watch`** · **`extro_verify`** · `extro_correlate` · `extro_stats` · `extro_reset` · `extro_auto_toggle` | `PWN::AI::Agent::Extrospection` (+ `PWN::Plugins::TransparentBrowser`) → `~/.pwn/extrospection.json` |
21
21
  | `cron` | `cron_list` · `cron_create` · `cron_run` · `cron_enable` · `cron_disable` · `cron_remove` | `PWN::Cron` → `~/.pwn/cron/jobs.yml` |
22
22
  | `swarm` | `agent_list` · `agent_spawn` · `agent_ask` · `agent_debate` · `agent_broadcast` · `swarm_bus` · `swarm_list` | `PWN::AI::Agent::Swarm` → `~/.pwn/agents.yml` + `~/.pwn/swarm/` |
23
23
 
@@ -1,66 +1,311 @@
1
- # Configuration — `~/.pwn/config.yml`
1
+ # Configuration — `~/.pwn/pwn.yaml`
2
2
 
3
- Everything configurable lives in one YAML file, loaded by `PWN::Config` at
4
- startup and available in-process as `PWN::Env`.
3
+ Everything configurable in PWN lives in **one encrypted YAML file**, loaded by
4
+ `PWN::Config.refresh_env` at driver startup and exposed in-process as the
5
+ frozen constant **`PWN::Env`** (a redacted copy is available as
6
+ `PWN::EnvRedacted`).
5
7
 
6
- ## Minimal example
8
+ The file is **AES-encrypted at rest** by `PWN::Plugins::Vault`. Its key/IV
9
+ live in a sibling **`~/.pwn/pwn.yaml.decryptor`** file (or in
10
+ `PWN_DECRYPTOR_KEY` / `PWN_DECRYPTOR_IV` env vars). Never edit `pwn.yaml`
11
+ by hand — use the **`pwn-vault`** REPL command, which decrypts → opens
12
+ `$EDITOR` → re-encrypts → reloads `PWN::Env`.
13
+
14
+ If `~/.pwn/pwn.yaml` does not exist on first run, `PWN::Config.default_env`
15
+ writes a fully-commented template with every key below (values set to
16
+ `'optional - …'` / `'required - …'` placeholder strings), encrypts it, and
17
+ generates the decryptor.
18
+
19
+ ---
20
+
21
+ ## Full annotated example
7
22
 
8
23
  ```yaml
9
- # ~/.pwn/config.yml
24
+ # ~/.pwn/pwn.yaml (shown DECRYPTED — file is AES-encrypted on disk)
25
+
10
26
  ai:
11
- engine: anthropic # openai | anthropic | grok | gemini | ollama
12
- anthropic:
13
- key: sk-ant-…
14
- openai:
15
- key: sk-…
27
+ active: grok # Which engine backs `pwn-ai`. One of: openai | anthropic | grok | gemini | ollama.
28
+ module_reflection: false # Master gate for LLM self-analysis (SAST triage, Burp enrichment, Learning.llm_reflect).
29
+
16
30
  grok:
17
- oauth: true # RFC-8628 device flow, no client_secret
31
+ base_uri: https://api.x.ai/v1 # xAI API base URL. Override for a self-hosted proxy / private endpoint.
32
+ key: xai-… # xAI API key. If blank AND no oauth.* below, PWN prompts interactively at load.
33
+ model: grok-4.5 # Model id sent on every chat / tool-loop request.
34
+ system_role_content: 'You are an ethically hacking xAI Grok agent.' # Base system prompt (MEMORY/SKILLS/LEARNING/EXTROSPECTION blocks are appended to this).
35
+ temp: 1.0 # Sampling temperature passed to the chat endpoint.
36
+ max_prompt_length: 256000 # Soft input-context ceiling (chars) used for prompt truncation / chunking.
37
+ oauth: # RFC-8628 device-flow (SuperGrok subscription) — public client, no client_secret.
38
+ refresh_token: ~ # Durable OAuth refresh token; enables silent re-auth without an API key.
39
+ bearer_token: ~ # Short-lived OAuth access JWT; auto-refreshed each run when refresh_token is set.
40
+ client_id: b1a00492-073a-47ea-816f-4c329264a828 # xAI's PUBLIC OAuth client id for the "Grok CLI" application (not sensitive).
41
+ client_secret: ~ # Only for confidential-client OAuth flows; unused by the public Grok-CLI client.
42
+ scope: 'openid profile email offline_access grok-cli:access api:access' # OAuth scope string requested during device-flow enrollment.
43
+ token_uri: https://auth.x.ai/oauth2/token # OAuth token endpoint (override for enterprise IdP).
44
+ enroll: false # true → force RFC-8628 device-flow enrollment on load even when `key:` is set.
45
+
46
+ openai:
47
+ base_uri: https://api.openai.com/v1 # OpenAI API base URL. Override for Azure OpenAI / VPC gateway / local proxy.
48
+ key: sk-… # OpenAI API key (`sk-…`). Prompted interactively if blank.
49
+ model: gpt-4o # Model id sent on every chat / tool-loop request.
50
+ system_role_content: 'You are an ethically hacking OpenAI agent.' # Base system prompt for this engine.
51
+ temp: 1.0 # Sampling temperature.
52
+ max_prompt_length: 128000 # Soft input-context ceiling (chars) for truncation / chunking.
53
+ max_tokens: 16384 # Max OUTPUT tokens per response. Mapped to OpenAI wire param `max_completion_tokens`.
54
+
55
+ anthropic:
56
+ base_uri: https://api.anthropic.com/v1 # Anthropic API base URL. Override for Bedrock / private gateway.
57
+ key: sk-ant-… # Anthropic API key (`sk-ant-…`). Prompted interactively if blank.
58
+ model: claude-3-5-sonnet-20240620 # Model id sent on every chat / tool-loop request.
59
+ system_role_content: 'You are an ethically hacking Anthropic agent.' # Base system prompt for this engine.
60
+ temp: 1.0 # Sampling temperature.
61
+ max_tokens: 8192 # Max OUTPUT tokens per response. Raise if tool-call JSON truncates.
62
+ max_prompt_length: 200000 # Soft input-context ceiling (chars) for truncation / chunking.
63
+
64
+ gemini:
65
+ base_uri: https://generativelanguage.googleapis.com/v1beta # Google Generative Language API base URL.
66
+ key: AIza… # Google AI Studio API key (`AIza…`). Prompted interactively if blank.
67
+ model: gemini-2.5-pro # Model id sent on every chat / tool-loop request.
68
+ system_role_content: 'You are an ethically hacking Gemini agent.' # Base system prompt for this engine.
69
+ temp: 1.0 # Sampling temperature.
70
+ max_prompt_length: 1000000 # Soft input-context ceiling (chars) — Gemini supports very large contexts.
71
+
18
72
  ollama:
19
- base_url: http://localhost:11434
20
- model: llama3.1:70b
73
+ base_uri: https://ollama.local # REQUIRED for ollama — Open WebUI / ollama-serve base URL (no vendor default).
74
+ key: eyJ… # Open WebUI JWT (Settings → Account → API Key). Prompted if blank.
75
+ model: llama3.1:70b # Local model tag as `ollama list` shows it.
76
+ system_role_content: 'You are an ethically hacking Ollama agent.' # Base system prompt for this engine.
77
+ temp: 1.0 # Sampling temperature.
78
+ max_prompt_length: 32000 # Soft input-context ceiling (chars) — tune per local model's real context window.
79
+
21
80
  agent:
22
- max_iters: 40 # tool-loop cap per turn
23
- max_depth: 3 # swarm recursion cap
24
- auto_reflect: true # Learning.auto_reflect after every final answer
25
- auto_extrospect: true # Extrospection.auto_extrospect likewise
26
-
27
- burp:
28
- jar: /opt/burpsuite_pro/burpsuite_pro.jar
29
- api_key:
30
-
31
- metasploit:
32
- host: 127.0.0.1
33
- port: 55553
34
- user: msf
35
- pass:
81
+ native_tools: true # Use provider-native tool_calls / function-calling. false → legacy text-parsed tool protocol.
82
+ max_iters: 25 # Hard cap on tool-call rounds per user turn before a forced final answer.
83
+ max_depth: 3 # Recursion guard: how many levels deep agent_ask/agent_debate sub-agents may spawn sub-agents.
84
+ auto_introspect: true # Run Learning.auto_introspect (outcome logging + lesson mining) after every final answer.
85
+ auto_extrospect: false # Also run Extrospection.auto_extrospect (host/toolchain/net/repo drift capture) after every final answer.
86
+ toolsets: ~ # Allow-list of toolsets exposed to the agent. nil = all. Valid: cron, extrospection, learning, memory, metrics, pwn, sessions, skills, swarm, terminal.
87
+ extrospection:
88
+ web:
89
+ anchors: # URLs the headless browser fingerprints on extro_snapshot(sections:[:web]). Alias: web_anchors.
90
+ - https://nvd.nist.gov
91
+ - https://www.exploit-db.com
92
+ proxy: ~ # Upstream proxy for TransparentBrowser during probe_web/verify/watch (e.g. 'tor', http://127.0.0.1:8080).
93
+ max_anchors: 8 # Cap on how many anchors are rendered per snapshot (protects run time).
94
+ per_page_timeout: 15 # Seconds before a single page render is abandoned and recorded as unreachable.
95
+ screenshot: false # Persist a PNG per anchor to ~/.pwn/extrospection/web/ (disk-heavy; off by default).
96
+ allow_targets: false # true → also merge top-level `targets:` into anchors (opt-in — off so in-scope hosts aren't touched unprompted).
97
+
98
+ plugins:
99
+ asm:
100
+ arch: x86_64 # Target architecture for `pwn-asm` inline assembler/disassembler. Default: PWN::Plugins::DetectOS.arch.
101
+ endian: little # Endianness for `pwn-asm` (little | big). Default: PWN::Plugins::DetectOS.endian.
102
+ blockchain:
103
+ bitcoin:
104
+ rpc_host: localhost # bitcoind JSON-RPC host for PWN::Blockchain::BTC.
105
+ rpc_port: 8332 # bitcoind JSON-RPC port.
106
+ rpc_user: … # bitcoind RPC username (rpcauth / rpcuser in bitcoin.conf).
107
+ rpc_pass: … # bitcoind RPC password. Redacted in PWN::EnvRedacted.
108
+ hunter:
109
+ api_key: … # hunter.how API key — passed as api_key: to PWN::Plugins::Hunter.search.
110
+ jira_data_center:
111
+ base_uri: https://jira.company.com/rest/api/latest # Jira Data Center REST base URL.
112
+ token: … # Jira Personal Access Token for PWN::Plugins::JiraDataCenter. Redacted.
113
+ meshtastic:
114
+ admin_key: … # Public key authorised to send admin messages to mesh nodes via `pwn-mesh`.
115
+ serial:
116
+ port: /dev/ttyUSB0 # Serial device path for a locally-attached Meshtastic node.
117
+ baud: 115200 # Serial baud rate.
118
+ bits: 8 # Serial data bits.
119
+ stop: 1 # Serial stop bits.
120
+ parity: none # Serial parity (none | even | odd).
121
+ mqtt:
122
+ host: mqtt.meshtastic.org # MQTT broker hostname for Meshtastic-over-MQTT.
123
+ port: 1883 # MQTT broker port (8883 for TLS).
124
+ tls: false # Use TLS to the MQTT broker.
125
+ user: meshdev # MQTT username (public Meshtastic broker default shown).
126
+ pass: large4cats # MQTT password (public Meshtastic broker default shown). Redacted.
127
+ channel:
128
+ active: LongFast # Which named channel block below `pwn-mesh` uses for TX/RX.
129
+ LongFast: # Channel definition — name is arbitrary, referenced by `active:` above.
130
+ psk: 'AQ==' # Channel pre-shared key (base64). 'AQ==' = Meshtastic default public key. Redacted.
131
+ region: US/UT # LoRa region tag (regulatory band).
132
+ topic: 2/e/# # MQTT topic filter to subscribe/publish for this channel.
133
+ channel_num: 8 # Meshtastic channel index (slot number on the device).
134
+ from: '!deadbeef' # Sender node id used on outbound packets. Optional — defaults to !<mqtt client_id>.
135
+ PWN: # Example second (private) channel definition.
136
+ psk: … # Private channel pre-shared key (base64). Redacted.
137
+ region: US/UT # LoRa region tag for this channel.
138
+ topic: 2/e/PWN/# # MQTT topic filter for this channel.
139
+ channel_num: 99 # Meshtastic channel index for this channel.
140
+ shodan:
141
+ api_key: … # Shodan API key — passed as api_key: to PWN::Plugins::Shodan.*. Redacted.
142
+
143
+ memory:
144
+ enabled: true # Reserve — persistent-memory subsystem on/off (currently always active; future gate).
145
+ provider: file # Storage backend for PWN::Memory: file (~/.pwn/memory.json). `sqlite` reserved.
146
+
147
+ sessions:
148
+ enabled: true # Reserve — transcript recording on/off (currently always active; future gate).
149
+ provider: jsonl # Transcript format under ~/.pwn/sessions/ (one .jsonl per session).
150
+
151
+ cron:
152
+ enabled: true # Reserve — scheduled-job subsystem on/off (currently always active; future gate).
153
+ provider: yaml # Job store format for PWN::Cron (~/.pwn/cron/jobs.yml).
154
+
155
+ targets: # Optional — engagement-scope URLs/hosts. Merged into :web snapshot anchors
156
+ - https://target.example.com # ONLY when ai.agent.extrospection.web.allow_targets: true.
36
157
  ```
37
158
 
159
+ ---
160
+
38
161
  ## Reading / writing at runtime
39
162
 
40
163
  ```ruby
41
- PWN::Env[:ai][:engine] # => :anthropic
42
- PWN::Env[:ai][:agent][:auto_reflect] # => true
43
- PWN::Config.reload!
164
+ PWN::Env[:ai][:active] # => :grok
165
+ PWN::Env.dig(:ai, :agent, :max_iters) # => 25
166
+ PWN::EnvRedacted[:ai][:grok][:key] # => ">>> REDACTED >>> …"
167
+
168
+ # Edit + re-encrypt + reload without leaving the REPL:
169
+ pwn-vault
170
+
171
+ # Force a reload from disk (e.g. after pwn-vault in another shell):
172
+ PWN::Config.refresh_env
44
173
  ```
45
174
 
46
- ## Sections PWN looks for
175
+ ---
176
+
177
+ ## Exhaustive key reference
178
+
179
+ ### `ai` — AI engines & agent loop
180
+
181
+ | Key path | Type | Default | Consumed by | Purpose |
182
+ |---|---|---|---|---|
183
+ | `ai.active` | String | `grok` | `PWN::Config.refresh_env`, `PWN::AI::Agent::Loop`, `PWN::Plugins::REPL`, `PWN::Cron` | Which AI engine backs `pwn-ai`. One of `openai` · `anthropic` · `grok` · `gemini` · `ollama`. |
184
+ | `ai.module_reflection` | Boolean | `false` | `PWN::AI::Agent::Reflect`, `PWN::SAST::*`, `PWN::Plugins::BurpSuite` | Master gate for LLM-driven self-analysis (SAST triage, Burp finding enrichment, `Learning.llm_reflect`). |
185
+ | `ai.<engine>.base_uri` | String | provider default | `PWN::AI::<Engine>.rest_call` | Override the API base URL (self-hosted proxy, private endpoint, Azure/VPC gateway). **Required** for `ollama`. |
186
+ | `ai.<engine>.key` | String | — | `PWN::AI::<Engine>` | API key / bearer token. If blank AND no OAuth is configured, PWN prompts interactively at load. |
187
+ | `ai.<engine>.model` | String | provider default | `PWN::AI::<Engine>.chat` / `.chat_tool_loop` | Model id sent on every request (e.g. `grok-4.5`, `gpt-4o`, `claude-3-5-sonnet-20240620`, `gemini-2.5-pro`, `llama3.1:70b`). |
188
+ | `ai.<engine>.system_role_content` | String | ethical-hacker persona | `PWN::AI::Agent::PromptBuilder`, `PWN::Plugins::REPL` | Base system prompt prepended to MEMORY / SKILLS / LEARNING / EXTROSPECTION blocks. |
189
+ | `ai.<engine>.temp` | Float | `1.0` | `PWN::AI::<Engine>.chat` | Sampling temperature. |
190
+ | `ai.<engine>.max_prompt_length` | Integer | per-engine | `PWN::AI::<Engine>`, `PWN::Plugins::REPL` | Soft input-context ceiling used for prompt truncation / chunking. |
191
+ | `ai.anthropic.max_tokens` | Integer | `8192` | `PWN::AI::Anthropic.chat_tool_loop` | Max **output** tokens per response. Raise if tool-call JSON truncates. |
192
+ | `ai.openai.max_tokens` | Integer | `16384` | `PWN::AI::OpenAI.chat` | Max **output** tokens per response. Mapped to OpenAI's wire param `max_completion_tokens` (legacy env key `max_completion_tokens` still accepted). |
193
+ | `ai.grok.oauth.refresh_token` | String | — | `PWN::AI::Grok.resolve_auth` | Durable OAuth refresh token (from `PWN::AI::Grok.obtain_oauth_bearer_token` device flow). Enables silent re-auth without an API key. |
194
+ | `ai.grok.oauth.bearer_token` | String | — | `PWN::AI::Grok.resolve_auth` | Short-lived OAuth access JWT. Auto-refreshed each run when `refresh_token` is present; live-cached back into this hash. |
195
+ | `ai.grok.oauth.client_id` | String | Grok-CLI public id | `PWN::AI::Grok` | Override the public OAuth client id used for device-flow / refresh. |
196
+ | `ai.grok.oauth.client_secret` | String | — | `PWN::Config.refresh_env`, `PWN::AI::Grok` | Only for confidential-client OAuth flows. Unused by the default public Grok-CLI client. |
197
+ | `ai.grok.oauth.scope` | String | see example | `PWN::AI::Grok` | OAuth scope string requested during device-flow enrollment. |
198
+ | `ai.grok.oauth.token_uri` | String | `https://auth.x.ai/oauth2/token` | `PWN::AI::Grok` | OAuth token endpoint (override for enterprise IdP). |
199
+ | `ai.grok.oauth.enroll` | Boolean | `false` | `PWN::AI::Grok` | `true` → always run RFC-8628 device-flow enrollment on load, even when `ai.grok.key` is set. |
200
+
201
+ ### `ai.agent` — pwn-ai autonomous loop
202
+
203
+ | Key path | Type | Default | Consumed by | Purpose |
204
+ |---|---|---|---|---|
205
+ | `ai.agent.native_tools` | Boolean | `true` | `PWN::Plugins::REPL` (`pwn-ai` cmd) | Use provider-native `tool_calls` / function-calling. `false` falls back to the legacy text-parsed tool protocol. |
206
+ | `ai.agent.max_iters` | Integer | `25` | `PWN::AI::Agent::Loop.run`, `PWN::AI::Agent::Swarm` | Hard cap on tool-call rounds per user turn before a forced final answer. |
207
+ | `ai.agent.max_depth` | Integer | `3` | `PWN::AI::Agent::Swarm` | Recursion guard for `agent_ask` / `agent_debate` sub-agents spawning sub-agents. |
208
+ | `ai.agent.auto_introspect` | Boolean | `true` | `PWN::AI::Agent::Learning.auto_introspect` | Run outcome logging + lesson mining after every final answer. Toggle live via `learning_auto_introspect_toggle`. |
209
+ | `ai.agent.auto_extrospect` | Boolean | `false` | `PWN::AI::Agent::Extrospection.auto_extrospect` | Also capture host/toolchain/net/repo drift after every final answer. Toggle live via `extro_auto_toggle`. |
210
+ | `ai.agent.toolsets` | Array\<String\> \| `nil` | `nil` (all) | `bin/pwn`, `PWN::Plugins::REPL`, `PWN::AI::Agent::Registry` | Allow-list of toolsets exposed to the agent. Valid: `cron`, `extrospection`, `learning`, `memory`, `metrics`, `pwn`, `sessions`, `skills`, `swarm`, `terminal`. |
211
+ | `ai.agent.extrospection.web.anchors` | Array\<String\> | `DEFAULT_WEB_ANCHORS` | `PWN::AI::Agent::Extrospection.probe_web` | URLs the headless browser fingerprints on `extro_snapshot(sections:[:web])`. Alias: `web_anchors`. |
212
+ | `ai.agent.extrospection.web.proxy` | String | — | `Extrospection.probe_web` / `.verify` / `.watch` | Upstream proxy for `PWN::Plugins::TransparentBrowser` (e.g. `tor`, `http://127.0.0.1:8080`). |
213
+ | `ai.agent.extrospection.web.max_anchors` | Integer | `8` | `Extrospection.probe_web` | Cap on anchors rendered per snapshot. |
214
+ | `ai.agent.extrospection.web.per_page_timeout` | Integer | `15` | `Extrospection` (headless browser) | Seconds before a page render is abandoned. |
215
+ | `ai.agent.extrospection.web.screenshot` | Boolean | `false` | `Extrospection.probe_web` / `.watch` | Persist a PNG per anchor to `~/.pwn/extrospection/web/`. |
216
+ | `ai.agent.extrospection.web.allow_targets` | Boolean | `false` | `Extrospection.web_anchors` | Merge top-level `targets:` into the anchor list (opt-in — off by default to avoid touching in-scope hosts unprompted). |
217
+
218
+ ### `plugins` — module credentials & wiring
219
+
220
+ | Key path | Type | Default | Consumed by | Purpose |
221
+ |---|---|---|---|---|
222
+ | `plugins.asm.arch` | String | `DetectOS.arch` | `PWN::Plugins::REPL` (`pwn-asm`) | Target architecture for the inline assembler / disassembler prompt (`x86_64`, `arm64`, …). |
223
+ | `plugins.asm.endian` | String | `DetectOS.endian` | `PWN::Plugins::REPL` (`pwn-asm`) | Endianness for the inline assembler (`little` / `big`). |
224
+ | `plugins.blockchain.bitcoin.rpc_host` | String | `localhost` | `PWN::Blockchain::BTC` | bitcoind JSON-RPC host. |
225
+ | `plugins.blockchain.bitcoin.rpc_port` | Integer | `8332` | `PWN::Blockchain::BTC` | bitcoind JSON-RPC port. |
226
+ | `plugins.blockchain.bitcoin.rpc_user` | String | — | `PWN::Blockchain::BTC` | bitcoind RPC username. |
227
+ | `plugins.blockchain.bitcoin.rpc_pass` | String | — | `PWN::Blockchain::BTC` | bitcoind RPC password. |
228
+ | `plugins.hunter.api_key` | String | — | `PWN::Plugins::Hunter` | hunter.how API key (passed as `api_key:` to `Hunter.search`). |
229
+ | `plugins.jira_data_center.base_uri` | String | — | `PWN::Plugins::JiraDataCenter` | Jira DC REST base (e.g. `https://jira.company.com/rest/api/latest`). |
230
+ | `plugins.jira_data_center.token` | String | — | `PWN::Plugins::JiraDataCenter` | Jira Personal Access Token. |
231
+ | `plugins.meshtastic.admin_key` | String | — | `PWN::Plugins::REPL` (`pwn-mesh`) | Public key authorised to send admin messages to mesh nodes. |
232
+ | `plugins.meshtastic.serial.port` | String | `/dev/ttyUSB0` | `pwn-mesh` (serial) | Serial device path for a locally-attached Meshtastic node. |
233
+ | `plugins.meshtastic.serial.baud` | Integer | `115200` | `pwn-mesh` (serial) | Serial baud rate. |
234
+ | `plugins.meshtastic.serial.bits` | Integer | `8` | `pwn-mesh` (serial) | Serial data bits. |
235
+ | `plugins.meshtastic.serial.stop` | Integer | `1` | `pwn-mesh` (serial) | Serial stop bits. |
236
+ | `plugins.meshtastic.serial.parity` | Symbol | `:none` | `pwn-mesh` (serial) | Serial parity. |
237
+ | `plugins.meshtastic.mqtt.host` | String | `mqtt.meshtastic.org` | `pwn-mesh` → `Meshtastic::MQTT.connect` | MQTT broker hostname. |
238
+ | `plugins.meshtastic.mqtt.port` | Integer | `1883` | `pwn-mesh` | MQTT broker port. |
239
+ | `plugins.meshtastic.mqtt.tls` | Boolean | `false` | `pwn-mesh` | Use TLS to the MQTT broker. |
240
+ | `plugins.meshtastic.mqtt.user` | String | `meshdev` | `pwn-mesh` | MQTT username. |
241
+ | `plugins.meshtastic.mqtt.pass` | String | `large4cats` | `pwn-mesh` | MQTT password. |
242
+ | `plugins.meshtastic.channel.active` | String | `LongFast` | `pwn-mesh` | Which named channel block below is used for TX/RX. |
243
+ | `plugins.meshtastic.channel.<NAME>.psk` | String (b64) | `AQ==` | `pwn-mesh` | Channel pre-shared key. |
244
+ | `plugins.meshtastic.channel.<NAME>.region` | String | — | `pwn-mesh` | LoRa region tag (e.g. `US/UT`). |
245
+ | `plugins.meshtastic.channel.<NAME>.topic` | String | — | `pwn-mesh` | MQTT topic filter to subscribe/publish (e.g. `2/e/#`). |
246
+ | `plugins.meshtastic.channel.<NAME>.channel_num` | Integer | — | `pwn-mesh` | Meshtastic channel index. |
247
+ | `plugins.meshtastic.channel.<NAME>.from` | String | `!<mqtt client_id>` | `pwn-mesh` | Sender node id used on outbound packets. |
248
+ | `plugins.shodan.api_key` | String | — | `PWN::Plugins::Shodan` | Shodan API key (passed as `api_key:` to `Shodan.*`). |
249
+
250
+ ### `memory` / `sessions` / `cron`
251
+
252
+ | Key path | Type | Default | Consumed by | Purpose |
253
+ |---|---|---|---|---|
254
+ | `memory.enabled` | Boolean | `true` | `PWN::Memory` | Reserve — persistent memory on/off (currently always active; future gate). |
255
+ | `memory.provider` | String | `file` | `PWN::Memory` | Storage backend: `file` (`~/.pwn/memory.json`). `sqlite` reserved. |
256
+ | `sessions.enabled` | Boolean | `true` | `PWN::Sessions` | Reserve — transcript recording on/off (currently always active; future gate). |
257
+ | `sessions.provider` | String | `jsonl` | `PWN::Sessions` | Transcript format under `~/.pwn/sessions/`. |
258
+ | `cron.enabled` | Boolean | `true` | `PWN::Cron` | Reserve — scheduled-job subsystem on/off (currently always active; future gate). |
259
+ | `cron.provider` | String | `yaml` | `PWN::Cron` | Job store format (`~/.pwn/cron/jobs.yml`). |
260
+
261
+ ### Top-level / miscellaneous
262
+
263
+ | Key path | Type | Default | Consumed by | Purpose |
264
+ |---|---|---|---|---|
265
+ | `targets` | Array\<String\> | — | `PWN::AI::Agent::Extrospection.web_anchors` | Engagement-scope URLs/hosts. Merged into `:web` snapshot anchors when `ai.agent.extrospection.web.allow_targets: true`. |
266
+
267
+ ---
268
+
269
+ ## Runtime-only keys on `PWN::Env` (NOT stored in `pwn.yaml`)
270
+
271
+ These are injected by `PWN::Config.refresh_env` / `PWN::AI::Agent::Loop` after
272
+ the YAML is decrypted. They will appear on `PWN::Env` in-process but are
273
+ **overwritten on every load**, so putting them in `pwn.yaml` has no effect.
274
+
275
+ | Key path | Set by | Purpose |
276
+ |---|---|---|
277
+ | `driver_opts.pwn_env_path` | `PWN::Config.refresh_env` / `PWN::Driver::Parser` | Resolved path to the active `pwn.yaml` (from `--pwn_env` or default). |
278
+ | `driver_opts.pwn_dec_path` | `PWN::Config.refresh_env` / `PWN::Driver::Parser` | Resolved path to the decryptor YAML (from `--pwn_dec` or default). |
279
+ | `pwn_skills_path` | `PWN::Config.refresh_env` | Absolute path to `~/.pwn/skills/`. |
280
+ | `pwn_memory_path` | `PWN::Config.refresh_env` | Absolute path to `~/.pwn/memory.json`. |
281
+ | `pwn_sessions_path` | `PWN::Config.refresh_env` | Absolute path to `~/.pwn/sessions/`. |
282
+ | `pwn_cron_path` | `PWN::Config.refresh_env` | Absolute path to `~/.pwn/cron/`. |
283
+ | `ai.<engine>.response_history` | `PWN::Config.refresh_env` | Rolling chat history for the active engine (reset on every reload). |
284
+ | `ai.session_id` | `PWN::AI::Agent::Loop.run` | Active `PWN::Sessions` id for the current turn (read by `sessions_current`, `mistakes_record`). |
285
+
286
+ ---
287
+
288
+ ## Redaction
289
+
290
+ `PWN::Config.redact_sensitive_artifacts` (and therefore `PWN::EnvRedacted`)
291
+ masks any key named: `admin_key`, `api_key`, `auth_client_secret`,
292
+ `bearer_token`, `client_secret`, `consumer_key`, `key`, `pass`, `password`,
293
+ `psk`, `refresh_token`, `secret_key`, `token`. Use those key names for any
294
+ custom secrets you add so they never leak into logs, transcripts, or the
295
+ agent system prompt.
296
+
297
+ ---
47
298
 
48
- | Key path | Consumed by |
49
- |---|---|
50
- | `ai.engine` | `PWN::AI::Agent::Loop` — which client to instantiate |
51
- | `ai.<engine>.key` / `.oauth` | `PWN::AI::OpenAI` / `Anthropic` / `Grok` / `Gemini` / `Ollama` |
52
- | `ai.agent.max_iters` | hard stop on tool-call rounds |
53
- | `ai.agent.max_depth` | `Swarm` recursion guard |
54
- | `ai.agent.auto_reflect` | toggle `Learning.auto_reflect` |
55
- | `ai.agent.auto_extrospect` | toggle `Extrospection.auto_extrospect` |
56
- | `burp.*` | `PWN::Plugins::BurpSuite` |
57
- | `metasploit.*` | `PWN::Plugins::Metasploit` |
58
- | `zap.*` | `PWN::Plugins::Zaproxy` |
59
- | `shodan.key` / `hunter.key` / … | respective OSINT plugins |
60
- | `aws.*` | `PWN::AWS::*` (falls back to standard AWS SDK env/instance-profile) |
299
+ ## Related files under `~/.pwn/`
61
300
 
62
- ## Other files under `~/.pwn/`
301
+ `pwn.yaml` is the only file you edit; everything else is machine-written
302
+ state. See **[Persistence](Persistence.md)** for the full map
303
+ (`memory.json`, `learning.jsonl`, `mistakes.json`, `metrics.json`,
304
+ `extrospection.json`, `sessions/`, `skills/`, `cron/`, `agents.yml`,
305
+ `swarm/`).
63
306
 
64
- See [Persistence](Persistence.md) for the full map.
307
+ Multi-agent personas are **not** configured here — they live in
308
+ `~/.pwn/agents.yml` and are managed with `agent_spawn` / `agent_list`
309
+ (see **[Swarm](Swarm.md)**).
65
310
 
66
- [← Home](Home.md)
311
+ [← Home](Home.md) · [Persistence](Persistence.md) · [pwn-ai Agent](pwn-ai-Agent.md) · [Extrospection](Extrospection.md)
@@ -37,10 +37,23 @@ cron_create(
37
37
  ```
38
38
 
39
39
  At 02:00 the system cron fires `PWN::Cron.run`, which spins up a headless
40
- `pwn-ai` turn. Because `auto_reflect` and `auto_extrospect` are on, the run
40
+ `pwn-ai` turn. Because `auto_introspect` and `auto_extrospect` are on, the run
41
41
  also updates Learning/Metrics/Extrospection — so tomorrow's interactive
42
42
  session already knows what changed overnight.
43
43
 
44
+ ```ruby
45
+ cron_create(
46
+ name: 'memory_revalidate',
47
+ schedule: '0 4 * * 0',
48
+ ruby: 'PWN::AI::Agent::Extrospection.revalidate_memory'
49
+ )
50
+ ```
51
+
52
+ Weekly, headless-browser fact-check of every `PWN::Memory` `:fact` containing
53
+ a CVE / version string / URL. Refuted entries get prefixed `[UNVERIFIED
54
+ yyyy-mm-dd]` so the injected MEMORY block stops calcifying into
55
+ confidently-wrong priors — see [Extrospection § revalidate_memory](Extrospection.md).
56
+
44
57
  **See also:** [Sessions](Sessions.md) · [Extrospection](Extrospection.md) ·
45
58
  [CLI Drivers](CLI-Drivers.md)
46
59
 
@@ -59,7 +59,7 @@ groups) so lines never criss-cross.
59
59
  [source](diagrams/dot/ai-integration-tool-calling.dot) · doc: [AI Integration](AI-Integration.md)
60
60
  ![ai-integration-tool-calling](diagrams/ai-integration-tool-calling.svg)
61
61
 
62
- ### Agent Tool Registry (10 toolsets · 52 tools)
62
+ ### Agent Tool Registry (10 toolsets · 54 tools)
63
63
  [source](diagrams/dot/agent-tool-registry.dot) · doc: [Agent Tool Registry](Agent-Tool-Registry.md)
64
64
  ![agent-tool-registry](diagrams/agent-tool-registry.svg)
65
65
 
@@ -17,22 +17,31 @@ like right now, and has it changed under me?"*
17
17
  | `:repo` | `probe_repo` | HEAD · branch · dirty · origin |
18
18
  | `:env` | `probe_env` | ruby / gem / bundler / PWN version |
19
19
  | **`:rf`** | **`probe_rf`** | **RTL-SDR / HackRF presence · SoapySDR devices · GQRX socket (127.0.0.1:7356) · Flipper serial · `/dev/tty{USB,ACM}*` · band-plan count** |
20
+ | **`:web`** *(opt-in)* | **`probe_web`** | **Renders `web_anchors` via `PWN::Plugins::TransparentBrowser` (`:headless`) — HTTP status · final URL · `<title>` · meta[generator] · `Server` · SHA-256 of *rendered* DOM text · TLS cert fp / notAfter · optional screenshot → `~/.pwn/extrospection/web/<host>.png`** |
20
21
 
21
22
  `RF_BINS = %w[rtl_sdr rtl_test rtl_433 hackrf_info gqrx dump1090 multimon-ng SoapySDRUtil]`
22
23
  — these are also merged into `PROBE_BINS` so RF-toolchain drift shows up under
23
24
  `toolchain.*` diff keys.
24
25
 
26
+ `DEFAULT_WEB_ANCHORS` = NVD CVE 2.0 API, Exploit-DB, upstream `pwn/version.rb`.
27
+ Override / extend via `PWN::Env[:ai][:agent][:extrospection][:web][:anchors]`; set
28
+ `allow_targets: true` to also fingerprint `PWN::Env[:targets]`. **`:web` is never
29
+ run by `auto_extrospect`** — a headless browser is ~1–3 s cold vs ~50 ms for
30
+ `probe_host`, so it's opt-in (`extro_snapshot(sections: %i[web])`).
31
+
25
32
  ## Verbs
26
33
 
27
34
  | Tool | Does | Persists to |
28
35
  |---|---|---|
29
- | `extro_snapshot` | Fingerprint host/net/toolchain/repo/env/**rf** → hash | `extrospection.json` (`snapshot` + rotates `previous`) |
36
+ | `extro_snapshot` | Fingerprint host/net/toolchain/repo/env/**rf**/*web* → hash | `extrospection.json` (`snapshot` + rotates `previous`) |
30
37
  | `extro_drift` | Diff live-vs-stored (or stored-vs-previous) | — returns `{changed, added, removed}` with dotted keys |
31
- | `extro_observe` | Record a fact — `category:` one of `recon vuln intel target network env` **`rf`** `misc` | `observations[]` |
38
+ | `extro_observe` | Record a fact — `category:` one of `recon vuln intel target network env` **`rf` `web`** `misc` | `observations[]` |
32
39
  | `extro_observations` | Query recorded facts by source/category/target/tag | — |
33
40
  | `extro_intel` | Query NVD / CIRCL / Exploit-DB for keyword or CVE | optional `record: true` → `:intel` observations |
41
+ | **`extro_watch`** | **Render a URL headless, hash the *rendered* DOM, screenshot, diff vs prior** | `observations[]` (`category: :web`) |
42
+ | **`extro_verify`** | **Browser-backed self fact-check of a claim (`:cve` `:version` `:doc` `:generic`) → `:confirmed`/`:refuted`/`:unknown`** | `Mistakes.record` / `observe(:intel)` / `Learning.note_outcome` |
34
43
  | `extro_correlate` | **Join** introspection ↔ extrospection | — actionable findings |
35
- | `extro_stats` | snapshot age · observation count · drift counts · **rf_devices** | — |
44
+ | `extro_stats` | snapshot age · observation count · drift counts · **rf_devices** · **web_anchors** | — |
36
45
  | `extro_reset` | Wipe snapshot + observations (new engagement) | — |
37
46
  | `extro_auto_toggle` | Enable/disable auto-snapshot after every final answer | `PWN::Env[:ai][:agent][:auto_extrospect]` |
38
47
 
@@ -43,6 +52,9 @@ like right now, and has it changed under me?"*
43
52
  (b) Learning failures on day X × host/net/repo drift on day X
44
53
  (c) recorded :intel observations × installed component versions
45
54
  (d) recorded :rf observations × missing SDR hardware / RF_BINS
55
+ (e) recorded :web DOM drift on target × Learning failures citing that host
56
+ (f) extro_verify :refuted claims × stale PWN::Memory :fact entries
57
+ (g) :intel obs whose feed anchor is × probe_web-unreachable → downgrade weight
46
58
  ```
47
59
 
48
60
  Output tells the agent whether a failure was **its own fault** ("I called the
@@ -51,23 +63,77 @@ API wrong" → belongs in [Mistakes](Mistakes.md)) or **the world changed**
51
63
  that distinction is written back into MEMORY so the next run doesn't waste
52
64
  iterations rediscovering it.
53
65
 
66
+ ## `extro_verify` — proactive fact-checking (the browser as a sense organ)
67
+
68
+ `probe_rf` gave Extrospection **ears**; `PWN::Plugins::TransparentBrowser` gives
69
+ it **eyes**. `extro_verify(claim:)` renders a canonical source *with JavaScript
70
+ executed* and returns a verdict:
71
+
72
+ | `kind:` | Canonical source (rendered headless) | Verdict logic |
73
+ |---|---|---|
74
+ | `:cve` | `nvd.nist.gov/vuln/detail/<id>` + `cve.org/CVERecord?id=` | CVE exists? affected-product string overlaps claimed product/version? |
75
+ | `:version` | rubygems.org / PyPI / GitHub search | claimed "latest is X" vs scraped latest |
76
+ | `:doc` | the URL the model cited | 200? title/body contains ≥ 40 % of the quoted-snippet tokens (fuzzy)? |
77
+ | `:generic` | DuckDuckGo HTML (proxy-able, no API key) | ≥ 50 % token overlap in top result snippets |
78
+
79
+ ```text
80
+ ┌── :refuted → Mistakes.record(tool:'assumption', …) → KNOWN MISTAKES block ∀ future runs
81
+ extro_verify ─────┼── :confirmed→ observe(category::intel, ttl:30d) → EXTROSPECTION block, freshness-bound
82
+ └── :unknown → Learning.note_outcome(tags:[needs_human])
83
+ ```
84
+
85
+ This adds a **proactive** trigger to the negative-feedback loop: today
86
+ `Mistakes.record` fires only *reactively* (a tool blew up, or a human said
87
+ "that's wrong"). `extro_verify` lets the agent catch itself being wrong about
88
+ the world **before** a human does. `commit: false` returns the verdict without
89
+ side-effects.
90
+
91
+ ### `revalidate_memory` — the browser as GC for `PWN::Memory`
92
+
93
+ `learning_consolidate` only dedupes/truncates; it never asks *"is this still
94
+ true?"*. `PWN::AI::Agent::Extrospection.revalidate_memory` walks every
95
+ `PWN::Memory` `:fact` containing a CVE / version string / URL, runs `verify()`
96
+ on it, and prefixes stale ones with `[UNVERIFIED yyyy-mm-dd]` so the injected
97
+ MEMORY block stops calcifying into confidently-wrong priors. Schedule it:
98
+
99
+ ```ruby
100
+ cron_create(name: 'memory_revalidate', schedule: '0 4 * * 0',
101
+ ruby: 'PWN::AI::Agent::Extrospection.revalidate_memory')
102
+ ```
103
+
104
+ ### Configuration
105
+
106
+ ```ruby
107
+ PWN::Env[:ai][:agent][:extrospection][:web] = {
108
+ anchors: ['https://target.acme/api/version', …], # else DEFAULT_WEB_ANCHORS
109
+ proxy: 'tor', # or 'http://127.0.0.1:8080' (Burp) — honoured by verify/watch/probe_web
110
+ max_anchors: 8,
111
+ per_page_timeout: 15,
112
+ screenshot: false, # → ~/.pwn/extrospection/web/<host>.png
113
+ allow_targets: false # OPSEC: also fingerprint PWN::Env[:targets] (active recon!)
114
+ }
115
+ ```
116
+
117
+
54
118
  ## Typical engagement flow
55
119
 
56
120
  ```ruby
57
121
  extro_reset(confirm: true) # clean slate for new scope
58
- extro_snapshot # baseline (all six sections)
122
+ extro_snapshot # baseline (six local sections; add sections:%i[web] for anchors)
59
123
  # … recon …
60
124
  extro_observe(source: 'nmap', target: '10.0.0.5',
61
125
  category: 'recon', data: 'OpenSSH 8.2p1 Ubuntu')
62
126
  extro_observe(source: 'gqrx', target: '433.920MHz',
63
127
  category: 'rf', data: 'peak -34.2 dBFS bw=200k FSK — likely garage remote')
64
128
  extro_intel(query: 'OpenSSH 8.2p1', record: true)
129
+ extro_watch(url: 'https://target.acme/api/version') # DOM-hash + screenshot; changed:true on next run
130
+ extro_verify(claim: 'CVE-2026-12345 affects OpenSSL 3.2.1') # → Mistakes/Memory/observe on verdict
65
131
  # … days later, something breaks …
66
132
  extro_drift # what moved?
67
133
  extro_correlate # why did it break?
68
134
  ```
69
135
 
70
136
  **See also:** [Skills, Memory & Learning](Skills-Memory-Learning.md) ·
71
- [Mistakes](Mistakes.md) · [SDR](SDR.md) · [pwn-ai Agent](pwn-ai-Agent.md)
137
+ [Mistakes](Mistakes.md) · [SDR](SDR.md) · [Transparent Browser](Transparent-Browser.md) · [Cron](Cron.md) · [pwn-ai Agent](pwn-ai-Agent.md)
72
138
 
73
139
  [← Home](Home.md)
@@ -36,10 +36,10 @@
36
36
  | | |
37
37
  |---|---|
38
38
  | [AI / LLM Integration](AI-Integration.md) | OpenAI · Anthropic · Grok (OAuth) · Gemini · Ollama |
39
- | [Agent Tool Registry](Agent-Tool-Registry.md) | 10 toolsets · 52 LLM-callable tools |
39
+ | [Agent Tool Registry](Agent-Tool-Registry.md) | 10 toolsets · 54 LLM-callable tools |
40
40
  | [Memory · Skills · Learning](Skills-Memory-Learning.md) | Introspection — the self-improvement loop |
41
41
  | [Mistakes](Mistakes.md) | **Negative feedback** — fingerprint failures · do-NOT-repeat · `[REPEATING]`/`[REGRESSED]` · inline self-correction |
42
- | [Extrospection](Extrospection.md) | World-awareness — snapshot · drift · intel · correlate · **rf** |
42
+ | [Extrospection](Extrospection.md) | World-awareness — snapshot · drift · intel · **watch** · **verify** · correlate · **rf** · **web** |
43
43
  | [Swarm (Multi-Agent)](Swarm.md) | Personas · ask · debate · broadcast · shared bus |
44
44
  | [Sessions](Sessions.md) | Transcript persistence + reflection |
45
45
  | [Cron](Cron.md) | Scheduled autonomous jobs |
@@ -27,7 +27,7 @@ hardware).
27
27
  | Module | Role |
28
28
  |---|---|
29
29
  | `Loop` | plan → dispatch tool_calls → observe → repeat until final answer |
30
- | `Registry` | JSON-Schema function definitions grouped into 10 **toolsets** · 52 tools |
30
+ | `Registry` | JSON-Schema function definitions grouped into 10 **toolsets** · 54 tools |
31
31
  | `Dispatch` / `Result` | execute a tool, capture stdout/value/error/duration |
32
32
  | `PromptBuilder` | inject MEMORY / SKILLS / LEARNING / **KNOWN MISTAKES + FIXES** / METRICS / EXTROSPECTION blocks |
33
33
  | `Metrics` · `Learning` | **introspection** — how well am I doing? |
@@ -42,7 +42,7 @@ PromptBuilder <-------------------- Mistakes.to_context (KNOWN MISTAKES + KN
42
42
  model --(tool call)---------------> mistakes_record / mistakes_resolve
43
43
  ```
44
44
 
45
- ## Four ingest paths — nothing slips through
45
+ ## Five ingest paths — nothing slips through
46
46
 
47
47
  | Source | Trigger | What is recorded |
48
48
  |---|---|---|
@@ -50,6 +50,7 @@ model --(tool call)---------------> mistakes_record / mistakes_resolve
50
50
  | `:loop` | iteration budget exhausted with no final answer | *automatic* — `Loop.run` epilogue |
51
51
  | `:user_correction` | next user message matches `CORRECTION_RX` (*"no that's wrong"*, *"still broken"*, *"try again"*, …) | `check_user_correction` — also flips the previous `Learning` outcome to `success:false` |
52
52
  | `:model` | the model itself calls `mistakes_record` | wrong assumption, wrong file, hallucinated API — failures that are **not** dispatch errors |
53
+ | **`:model` (proactive)** | **`extro_verify(claim:)` returns `:refuted`** | **`Mistakes.record(tool:'assumption', error:'REFUTED …: <claim>')` — the browser caught the model being wrong about the world *before* a human did** |
53
54
 
54
55
  ## Tools
55
56