pwn 0.5.784 → 0.5.785

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +6 -6
  4. data/README.md +37 -0
  5. data/bin/pwn +2 -1
  6. data/bin/pwn_web_cache_deception +35 -7
  7. data/documentation/AI-Integration.md +7 -5
  8. data/documentation/Agent-Tool-Registry.md +2 -2
  9. data/documentation/Backup-Retention.md +70 -15
  10. data/documentation/Configuration.md +10 -6
  11. data/documentation/Contributing.md +1 -1
  12. data/documentation/Cron.md +2 -2
  13. data/documentation/Diagrams.md +2 -2
  14. data/documentation/General-PWN-Usage.md +4 -3
  15. data/documentation/Home.md +6 -5
  16. data/documentation/How-PWN-Works.md +6 -5
  17. data/documentation/Installation.md +12 -9
  18. data/documentation/Mission-Cancellation.md +134 -0
  19. data/documentation/Operability.md +17 -0
  20. data/documentation/Persistence.md +7 -5
  21. data/documentation/Plugin-Degradation.md +12 -0
  22. data/documentation/Plugins.md +3 -2
  23. data/documentation/Runtime-Source-Protection.md +162 -0
  24. data/documentation/Transparent-Browser.md +11 -0
  25. data/documentation/Troubleshooting.md +3 -2
  26. data/documentation/What-is-PWN.md +2 -2
  27. data/documentation/YubiKey-Vaults.md +183 -0
  28. data/documentation/YubiKey.md +339 -0
  29. data/documentation/diagrams/cron-scheduling.svg +130 -130
  30. data/documentation/diagrams/dot/cron-scheduling.dot +3 -3
  31. data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
  32. data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
  33. data/documentation/diagrams/persistence-filesystem.svg +172 -142
  34. data/documentation/diagrams/plugin-ecosystem.svg +64 -64
  35. data/documentation/pwn-ai-Agent.md +10 -4
  36. data/etc/default_cron/session_cleanup.rb +43 -27
  37. data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
  38. data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
  39. data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
  40. data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
  41. data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
  42. data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
  43. data/etc/default_skills/pwn/backups/SKILL.md +54 -0
  44. data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
  45. data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
  46. data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
  47. data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
  48. data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
  49. data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
  50. data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
  51. data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
  52. data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
  53. data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
  54. data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
  55. data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
  56. data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
  57. data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
  58. data/lib/pwn/ai/agent/dispatch.rb +26 -15
  59. data/lib/pwn/ai/agent/learning.rb +14 -18
  60. data/lib/pwn/ai/agent/loop.rb +17 -1
  61. data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
  62. data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
  63. data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
  64. data/lib/pwn/ai/agent/request_runtime.rb +122 -18
  65. data/lib/pwn/ai/agent/reward.rb +5 -3
  66. data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
  67. data/lib/pwn/ai/agent/skill_review.rb +9 -5
  68. data/lib/pwn/ai/agent/swarm.rb +4 -0
  69. data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
  70. data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
  71. data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
  72. data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
  73. data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
  74. data/lib/pwn/ai/agent.rb +1 -0
  75. data/lib/pwn/ai/anthropic.rb +8 -33
  76. data/lib/pwn/ai/cli.rb +34 -5
  77. data/lib/pwn/ai/gemini.rb +1 -0
  78. data/lib/pwn/ai/grok.rb +39 -28
  79. data/lib/pwn/ai/ollama.rb +4 -2
  80. data/lib/pwn/ai/open_ai.rb +11 -32
  81. data/lib/pwn/ai/open_web_ui.rb +62 -4
  82. data/lib/pwn/ai/router.rb +8 -1
  83. data/lib/pwn/ai/server_tools.rb +231 -0
  84. data/lib/pwn/ai.rb +1 -0
  85. data/lib/pwn/backups.rb +347 -0
  86. data/lib/pwn/config.rb +70 -39
  87. data/lib/pwn/migrate.rb +85 -280
  88. data/lib/pwn/module_skills.rb +7 -0
  89. data/lib/pwn/plugins/ai_sandbox.rb +538 -1
  90. data/lib/pwn/plugins/authentication_helper.rb +74 -4
  91. data/lib/pwn/plugins/basic_auth.rb +2 -1
  92. data/lib/pwn/plugins/google_workspace.rb +4 -21
  93. data/lib/pwn/plugins/jobs.rb +1 -1
  94. data/lib/pwn/plugins/log.rb +3 -1
  95. data/lib/pwn/plugins/preflight_checker.rb +34 -9
  96. data/lib/pwn/plugins/recon.rb +4 -1
  97. data/lib/pwn/plugins/repl/ai.rb +293 -26
  98. data/lib/pwn/plugins/repl/ai_console.rb +268 -88
  99. data/lib/pwn/plugins/repl/ai_swarm.rb +177 -58
  100. data/lib/pwn/plugins/repl/mesh.rb +3 -16
  101. data/lib/pwn/plugins/repl/vault.rb +4 -15
  102. data/lib/pwn/plugins/repl.rb +1 -216
  103. data/lib/pwn/plugins/sandbox/driver.rb +4 -0
  104. data/lib/pwn/plugins/transparent_browser.rb +215 -0
  105. data/lib/pwn/plugins/vault.rb +412 -135
  106. data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
  107. data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
  108. data/lib/pwn/plugins/yubikey/management.rb +686 -0
  109. data/lib/pwn/plugins/yubikey/oath.rb +766 -0
  110. data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
  111. data/lib/pwn/plugins/yubikey/otp.rb +897 -0
  112. data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
  113. data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
  114. data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
  115. data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
  116. data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
  117. data/lib/pwn/plugins/yubikey.rb +844 -0
  118. data/lib/pwn/plugins.rb +1 -0
  119. data/lib/pwn/redaction.rb +69 -12
  120. data/lib/pwn/session_trace.rb +4 -0
  121. data/lib/pwn/setup.rb +114 -24
  122. data/lib/pwn/version.rb +1 -1
  123. data/lib/pwn/www/app_cobalt_io.rb +16 -5
  124. data/lib/pwn/www/bug_crowd.rb +16 -5
  125. data/lib/pwn/www/coinbase_pro.rb +16 -5
  126. data/lib/pwn/www/github.rb +30 -14
  127. data/lib/pwn/www/paypal.rb +15 -4
  128. data/lib/pwn/www/synack.rb +16 -5
  129. data/lib/pwn/www/twitter.rb +17 -6
  130. data/lib/pwn.rb +1 -0
  131. data/spec/documentation/installation_md_spec.rb +1 -1
  132. data/spec/fixtures/yubikey/apdu_frames.json +162 -0
  133. data/spec/integration/authentication_helper_oath_spec.rb +117 -0
  134. data/spec/integration/controller_spill_spec.rb +48 -0
  135. data/spec/integration/harness_process_spec.rb +97 -0
  136. data/spec/integration/mission_cancel_spec.rb +79 -0
  137. data/spec/integration/mission_curses_pty_spec.rb +44 -0
  138. data/spec/integration/readme_security_spec.rb +47 -0
  139. data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
  140. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
  141. data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
  142. data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
  143. data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
  144. data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
  145. data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
  146. data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
  147. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
  148. data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
  149. data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
  150. data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
  151. data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
  152. data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
  153. data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
  154. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
  155. data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
  156. data/spec/lib/pwn/ai/cli_spec.rb +6 -9
  157. data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
  158. data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
  159. data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
  160. data/spec/lib/pwn/ai/router_spec.rb +1 -1
  161. data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
  162. data/spec/lib/pwn/backups_spec.rb +114 -0
  163. data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
  164. data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
  165. data/spec/lib/pwn/migrate_spec.rb +29 -81
  166. data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
  167. data/spec/lib/pwn/module_skills_spec.rb +8 -0
  168. data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
  169. data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
  170. data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
  171. data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
  172. data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
  173. data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
  174. data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
  175. data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
  176. data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
  177. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +110 -75
  178. data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
  179. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +19 -16
  180. data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
  181. data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
  182. data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
  183. data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
  184. data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
  185. data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
  186. data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
  187. data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
  188. data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
  189. data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
  190. data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
  191. data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
  192. data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
  193. data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
  194. data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
  195. data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
  196. data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
  197. data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
  198. data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
  199. data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
  200. data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
  201. data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
  202. data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
  203. data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
  204. data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
  205. data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
  206. data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
  207. data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
  208. data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
  209. data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
  210. data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
  211. data/spec/lib/pwn/session_cleanup_default_spec.rb +7 -5
  212. data/spec/lib/pwn/session_cleanup_payload_spec.rb +11 -11
  213. data/spec/lib/pwn/session_cleanup_recovery_spec.rb +34 -20
  214. data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
  215. data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
  216. data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
  217. data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
  218. data/spec/spec_helper.rb +15 -0
  219. data/spec/support/harness/cli_faults.rb +13 -9
  220. data/spec/support/harness/controller_spill.rb +26 -0
  221. data/spec/support/harness/controller_state.rb +54 -0
  222. data/spec/support/harness/evaluate.rb +12 -1
  223. data/spec/support/harness/experiments.rb +1 -1
  224. data/spec/support/harness/fake_provider.rb +22 -6
  225. data/spec/support/harness/fault_preload.rb +3 -1
  226. data/spec/support/harness/mission_console.rb +54 -0
  227. data/spec/support/harness/mission_curses_pty.rb +41 -0
  228. data/spec/support/harness/mission_dispatch.rb +30 -0
  229. data/spec/support/harness/mission_fixture_preload.rb +24 -0
  230. data/spec/support/harness/mission_job_lifetime.rb +25 -0
  231. data/spec/support/harness/mission_legacy.rb +25 -0
  232. data/spec/support/harness/mission_normal_exit.rb +34 -0
  233. data/spec/support/harness/mission_swarm.rb +23 -0
  234. data/spec/support/harness/mission_unsupported.rb +29 -0
  235. data/spec/support/harness/package_acceptance.rb +33 -5
  236. data/spec/support/harness/session_acceptance.rb +4 -4
  237. data/spec/support/harness/swarm_acceptance.rb +1 -1
  238. data/spec/support/harness/swarm_control.rb +44 -0
  239. data/spec/support/mission_namespace.rb +14 -0
  240. data/spec/support/mission_pipe_queue.rb +33 -0
  241. data/third_party/pwn_rdoc.jsonl +589 -19
  242. metadata +129 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41b180063b404ae6511a5df181eb969dcf2970b1ac128225369d6ec69afb30cc
4
- data.tar.gz: 7936dc0f2c4552327650e74f3f0af790da4fbd353ca5d48cb651542d3f550acd
3
+ metadata.gz: fef74967cd315acf1e070378d8e12000d552604b8d0f273d42638014d0a13a8c
4
+ data.tar.gz: 3d68aa5c28678b95a4b20acbe878a36c6d0cd8c04357ee3d18c768a7e0435c75
5
5
  SHA512:
6
- metadata.gz: 1184f82a572f136b6421f04059b5e05e79642e7f68337f376392c816aeb023b0bd0d74c15f0916c84078ad7188d255f0776ac04d2a87cbb31e9ee8fa59e6dc16
7
- data.tar.gz: 851035353589425ab5ff5fdefb04c9c2a1f2c91fce7cb5d1e02067bd0eebb16905284bbf6cd7cde6373fa2ea9b7b1f6dbf531089ee9319988bd62ec7e83cdeb1
6
+ metadata.gz: '0871b196e754cfe2134ba0372f4108b24113d9a1f7b0355257b31a032833c4b91bd1e52f53ff467a4e9e817375b9138b5691c15ef621c6026a34d32672f77d2b'
7
+ data.tar.gz: 2fef6064addee1ba16706d641f19af7a8bbf99432dfb1a13426fbd1d534d01b89d4a7f4dced9db9ad934d30db94dfcf908e88c993cb779424b706a0296db1cd2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.6
1
+ 4.0.9
data/Gemfile CHANGED
@@ -49,8 +49,8 @@ gem 'jwt', '3.3.0'
49
49
  gem 'libusb', '0.8.0'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
- gem 'mcp', '1.8.2'
53
- gem 'meshtastic', '0.0.189'
52
+ gem 'mcp', '1.9.0'
53
+ gem 'meshtastic', '0.0.192'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.26.0'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -59,7 +59,7 @@ gem 'net-ldap', '0.21.0'
59
59
  gem 'net-openvpn', '0.8.7'
60
60
  gem 'net-smtp', '0.5.2'
61
61
  gem 'nexpose', '7.3.0'
62
- gem 'nokogiri', '1.19.4'
62
+ gem 'nokogiri', '1.19.7'
63
63
  gem 'nokogiri-diff', '0.3.0'
64
64
  gem 'oily_png', '1.2.1'
65
65
  gem 'open3', '0.3.0'
@@ -81,7 +81,7 @@ gem 'rmagick', '7.2.0'
81
81
  gem 'rqrcode', '3.2.0'
82
82
  gem 'rspec', '3.13.2'
83
83
  gem 'rtesseract', '3.1.4'
84
- gem 'rubocop', '1.92.1'
84
+ gem 'rubocop', '1.92.2'
85
85
  gem 'rubocop-rake', '0.7.1'
86
86
  gem 'rubocop-rspec', '3.10.2'
87
87
  gem 'ruby-audio', '1.6.1'
@@ -89,8 +89,8 @@ gem 'ruby-nmap', '1.0.3'
89
89
  gem 'ruby-saml', '1.18.1'
90
90
  gem 'rvm', '1.11.3.9'
91
91
  gem 'savon', '2.17.5'
92
- gem 'selenium-devtools', '0.155.0'
93
- gem 'selenium-webdriver', '4.51.0'
92
+ gem 'selenium-devtools', '0.156.0'
93
+ gem 'selenium-webdriver', '4.52.0'
94
94
  gem 'slack-ruby-client', '3.3.0'
95
95
  gem 'socksify', '1.8.1'
96
96
  gem 'spreadsheet', '1.3.5'
data/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  - [Intro](#intro)
10
10
  * [What is PWN](#what-is-pwn)
11
11
  * [Why PWN](#why-pwn)
12
+ * [Vault and Sandboxing](#vault-and-sandboxing)
12
13
  * [How PWN Works](#how-pwn-works)
13
14
  - [Documentation](#documentation)
14
15
  - [Installation](#installation)
@@ -88,6 +89,42 @@ is driving security decisions without you watching every click.
88
89
 
89
90
  Full page: [Why PWN](documentation/Why-PWN.md)
90
91
 
92
+ ##### **Vault and Sandboxing** #####
93
+
94
+ Agentic security research, bug bounty hunting and penetration testing put
95
+ credentials, client evidence and untrusted target content in the same tool loop.
96
+ Vault and sandboxing reduce accidental secret persistence and limit local damage
97
+ from untrusted PoCs or target-supplied prompt injection:
98
+
99
+ - **Keep secrets separate from the conversation.** [Vault](lib/pwn/plugins/vault.rb)
100
+ encrypts configuration and captured credential values at rest. Use
101
+ `{{vault:label}}` references: [Dispatch](lib/pwn/ai/agent/dispatch.rb) resolves
102
+ them at the tool boundary and redacts known stored secrets from returned results.
103
+ Together with transcript/log redaction, this reduces copying raw secrets into
104
+ prompts, logs and reusable context. Store/query loot under explicit engagement
105
+ IDs with host/service/finding provenance. Use unique labels: reference expansion
106
+ can search across engagements, so these IDs are not credential access controls.
107
+ - **Contain tool effects, not just model instructions.** The
108
+ [mandatory runtime boundary](documentation/Runtime-Source-Protection.md) uses
109
+ Linux workers to separate tool writes from protected source and controller state;
110
+ [mission containment](documentation/Mission-Cancellation.md) owns their descendants.
111
+ Unnamed writes default to `~/.pwn/workspaces/runtime`. Other destinations must
112
+ come from the original operator request, not tool output; naming a file can
113
+ grant its parent directory. Protected source stays read-only even if named.
114
+ Missing required Landlock/seccomp or namespace support makes execution fail closed.
115
+ Optional `strict`/`permissive`/`off` profiles do not disable that mandatory boundary.
116
+
117
+ **Know the limits.** Encryption at rest does not protect secrets from an active
118
+ process that has unlocked them or promise LLM-provider privacy. Vault is not
119
+ whole-filesystem or evidence/artifact encryption, and redaction can miss secrets.
120
+ Sandboxing is not authorization, complete network isolation, a perfect container,
121
+ or a guarantee against bugs or prompt injection. Operators still need target
122
+ authorization and scope, least-privilege credentials, and separate evidence access,
123
+ retention and encryption controls.
124
+
125
+ Details: [Configuration](documentation/Configuration.md) ·
126
+ [Optional hardware-backed vaults and recovery](documentation/YubiKey-Vaults.md)
127
+
91
128
  #### **How PWN Works** ####
92
129
 
93
130
  PWN is five layers. Dependencies only point downward, so each level stays
data/bin/pwn CHANGED
@@ -81,7 +81,8 @@ begin
81
81
  warn result.to_s[0, 700]
82
82
  end
83
83
 
84
- final = PWN::AI::Agent::Loop.run(
84
+ require 'pwn/plugins/repl/ai'
85
+ final = PWN::Plugins::REPL.pwn_ai_run_steerable(
85
86
  request: prompt,
86
87
  session_id: sess[:id],
87
88
  enabled_toolsets: PWN::Env.dig(:ai, :agent, :toolsets),
@@ -33,6 +33,18 @@ PWN::Driver::Parser.new do |options|
33
33
  opts[:mfa] = f
34
34
  end
35
35
 
36
+ options.on('--yubikey-account=NAME', '<Optional - Exact OATH account; supports GitHub, Synack, Twitter, BugCrowd, CoinbasePro, AppCobaltIO; Paypal SMS adapter unsupported; requires --yubikey-confirm; enables MFA even with --no-mfa; never an OTP slot>') do |name|
37
+ opts[:yubikey_account] = name
38
+ end
39
+
40
+ options.on('--yubikey-confirm', '<Required with --yubikey-account - Explicit OATH consent: HOTP advances counters; ykman may authenticate/delete remembered keys and delete corrupted OATH credentials>') do |confirm|
41
+ opts[:yubikey_confirm] = confirm
42
+ end
43
+
44
+ options.on('--yubikey-serial=SERIAL', '<Optional - Decimal YubiKey serial; requires --yubikey-account>') do |serial|
45
+ opts[:yubikey_serial] = serial
46
+ end
47
+
36
48
  options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || tor>') do |p|
37
49
  opts[:proxy] = p
38
50
  end
@@ -60,6 +72,24 @@ pwn_www_mod_str = opts[:pwn_www_mod_str].to_s.scrub.strip.chomp
60
72
 
61
73
  username = opts[:username].to_s.scrub.strip.chomp
62
74
 
75
+ yubikey = nil
76
+ raise ArgumentError, 'YubiKey serial requires --yubikey-account with an exact OATH account name' if opts[:yubikey_account].nil? && !opts[:yubikey_serial].nil?
77
+ raise ArgumentError, '--yubikey-confirm requires --yubikey-account with an exact OATH account name' if opts[:yubikey_account].nil? && !opts[:yubikey_confirm].nil?
78
+
79
+ unless opts[:yubikey_account].nil?
80
+ supported_oath_modules = %w[GitHub Synack Twitter BugCrowd CoinbasePro AppCobaltIO]
81
+ raise ArgumentError, "YubiKey OATH is supported only with --pwn-www-mod=#{supported_oath_modules.join('|')}; the Paypal adapter is SMS-only" unless supported_oath_modules.include?(pwn_www_mod_str)
82
+
83
+ yubikey = { account: opts[:yubikey_account], confirm: opts[:yubikey_confirm] }
84
+ unless opts[:yubikey_serial].nil?
85
+ serial = opts[:yubikey_serial]
86
+ raise ArgumentError, 'YubiKey serial must be a decimal string without whitespace' unless serial.is_a?(String) && serial.match?(/\A[0-9]+\z/)
87
+
88
+ yubikey[:serial] = serial
89
+ end
90
+ PWN::Plugins::AuthenticationHelper.validate_mfa_options(yubikey: yubikey, mfa: opts[:mfa], mfa_token: opts[:mfa_token])
91
+ end
92
+
63
93
  password = opts[:password] # Don't cast to string to detect if PWN::Plugins::AuthenticationHelper is necessary
64
94
  if password.nil?
65
95
  password = PWN::Plugins::AuthenticationHelper.mask_password
@@ -67,11 +97,7 @@ else
67
97
  password = opts[:password].to_s.scrub.strip.chomp
68
98
  end
69
99
 
70
- if opts[:mfa]
71
- mfa = true
72
- else
73
- mfa = false
74
- end
100
+ mfa = opts[:mfa] || false
75
101
 
76
102
  proxy = opts[:proxy].to_s.scrub.strip.chomp unless opts[:proxy].nil?
77
103
 
@@ -151,12 +177,14 @@ begin
151
177
  browser_obj = pwn_www_mod.open(browser_type: :chrome)
152
178
  end
153
179
  # Now AuthN via PWN::WWW::<MODULE>
154
- browser_obj = pwn_www_mod.login(
180
+ login_opts = {
155
181
  browser_obj: browser_obj,
156
182
  username: username,
157
183
  password: password,
158
184
  mfa: mfa
159
- )
185
+ }
186
+ login_opts[:yubikey] = yubikey unless yubikey.nil?
187
+ browser_obj = pwn_www_mod.login(login_opts)
160
188
  puts "#{@green}complete.#{@end_of_color}\n\n\n"
161
189
  elsif pwn_www_mod_str == '' && proxy
162
190
  browser_obj = PWN::Plugins::TransparentBrowser.open(
@@ -15,7 +15,7 @@ agent code never cares which model is behind it.
15
15
  | `grok` | `PWN::AI::Grok` | `key:` **or** `oauth: true` | OAuth = RFC-8628 device-code flow using xAI's public Grok-CLI client id (no secret) - see skill `xai_grok_oauth_device_flow` |
16
16
  | `gemini` | `PWN::AI::Gemini` | `key:` | function-calling native |
17
17
  | `ollama` | `PWN::AI::Ollama` | none (stock) | Direct Ollama server (default `http://127.0.0.1:11434`). Native `/api/chat` (`num_ctx`, `num_predict`, `keep_alive`, low `tool_temp`) and `/api/embed` for `PWN::MemoryIndex`. No prefix-cache field. |
18
- | `openwebui` | `PWN::AI::OpenWebUI` | `key:` (JWT / API token) + `base_uri:` | Open WebUI gateway - OpenAI-compatible `/api/v1/chat/completions` plus proxied Ollama `/ollama/api/*` (including embed) |
18
+ | `openwebui` | `PWN::AI::OpenWebUI` | `key:` (JWT / API token) + `base_uri:` | Open WebUI gateway - OpenAI-compatible `/api/v1/chat/completions` plus proxied Ollama `/ollama/api/*`. Workspace tools such as Ask Jev run server-side through `tool_ids` on `chat` / `workspace_tool`. A client `tools` array on that same request disables them. |
19
19
 
20
20
  > PWN is **model-agnostic**. `ai.<engine>.model` is passed straight through to
21
21
  choose a provider-supported model ID (or a tag from `ollama list`).
@@ -56,12 +56,14 @@ The trailing `nil` prevents the console from echoing the returned bearer.
56
56
  Successful enrollment updates the live environment and calls the private
57
57
  `persist_oauth_to_vault` helper, as token refresh does. It uses the configured
58
58
  `driver_opts.pwn_env_path` and `pwn_dec_path`, defaulting to
59
- `~/.pwn/pwn.yaml` and its `.decryptor` file. Existing decryption artifacts are
60
- required; missing artifacts leave tokens in the session only and produce a
59
+ `~/.pwn/pwn.yaml` and its legacy `.decryptor` file. A [YubiKey vault](YubiKey-Vaults.md)
60
+ uses its hardware selector instead and does not need a software sidecar.
61
+ Unavailable credentials or hardware leave tokens in the session only and produce a
61
62
  notice. Tokens are not printed by the enrollment success message.
62
63
 
63
- Persistence retains unrelated settings and the existing key/IV, encrypts a
64
- private temporary file, and replaces the vault only after encryption succeeds.
64
+ Persistence retains unrelated settings and the encryption backend, decrypts and
65
+ merges in memory, writes only ciphertext to a private temporary file, and
66
+ replaces the vault only after encryption succeeds.
65
67
  The resulting vault has mode `0600`. The original vault remains unchanged on
66
68
  a persistence failure; the helper does not create or rotate decryptor secrets.
67
69
 
@@ -6,13 +6,13 @@ 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 to tools (16 toolsets · 150 tools)
9
+ ## Toolsets to tools (16 toolsets · 152 tools)
10
10
 
11
11
  | Toolset | Tools | Backed by |
12
12
  |---|---|---|
13
13
  | `http` | `http_proxy_start` · `http_proxy_stop` · `http_proxy_entries` · `http_proxy_rules` · `http_replay` | `PWN::Plugins::MitmProxy`: native HTTP HAR capture/replay; opaque CONNECT tunnels |
14
14
  | `terminal` | `pty_expect` · `job_run` · `job_status` · `job_result` · `job_tail` · `job_kill` · `pty_open` · `pty_send` · `pty_read` · `pty_close` · `pty_session` · `shell` | Foreground host commands plus `PWN::Plugins::Jobs` durable supervisors |
15
- | `pwn` | `binary_triage` · `browser_goto` · `capabilities` · `debug_session` · `gdb_run_to_crash` · `binary_diff` · `crash_triage` · `emulate` · `detonate` · `decompile` · `exploitdev` · `finding_record` · `finding_report` · `chain_score` · `fuzz_campaign` · `intel_lookup` · `loot_query` · `nmap_scan` · `nuclei_scan` · `r2_functions` · `r2_disasm` · `recon_run` · `pwn_eval` · `sandbox_run` · `sandbox_fuzz` · `sbom_scan` · `swarm_map` | `TOPLEVEL_BINDING.eval` in the live REPL process, after `ToolGuard`; SBOM engines via `PWN::Plugins::SBOM` store CVE leads as recon observations, not findings; r2pipe JSON via `PWN::Plugins::Radare2`; gdb MI3 crash reports via `PWN::Plugins::GDBMI`; nmap XML ingest/diff via `PWN::Plugins::NmapIt`, which skips a known port unless `refresh: true`; nuclei/httpx JSONL via `PWN::Plugins::Nuclei` writes a recon observation (a template match is not a finding) and accepts a `Recon.handoff` record; TransparentBrowser navigation captures screenshot/DOM/HAR via `browser_goto`; engagement loot via `PWN::Plugins::Vault` |
15
+ | `pwn` | `binary_triage` · `browser_goto` · `browser_act` · `workspace_tool` · `capabilities` · `debug_session` · `gdb_run_to_crash` · `binary_diff` · `crash_triage` · `emulate` · `detonate` · `decompile` · `exploitdev` · `finding_record` · `finding_report` · `chain_score` · `fuzz_campaign` · `intel_lookup` · `loot_query` · `nmap_scan` · `nuclei_scan` · `r2_functions` · `r2_disasm` · `recon_run` · `pwn_eval` · `sandbox_run` · `sandbox_fuzz` · `sbom_scan` · `swarm_map` | `TOPLEVEL_BINDING.eval` in the live REPL process, after `ToolGuard`; SBOM engines via `PWN::Plugins::SBOM` store CVE leads as recon observations, not findings; r2pipe JSON via `PWN::Plugins::Radare2`; gdb MI3 crash reports via `PWN::Plugins::GDBMI`; nmap XML ingest/diff via `PWN::Plugins::NmapIt`, which skips a known port unless `refresh: true`; nuclei/httpx JSONL via `PWN::Plugins::Nuclei` writes a recon observation (a template match is not a finding) and accepts a `Recon.handoff` record; TransparentBrowser navigation captures screenshot/DOM/HAR via `browser_goto`; engagement loot via `PWN::Plugins::Vault` |
16
16
  | `mcp` | `mcp` | `PWN::AI::MCP` session broker → any `PWN::AI::MCP::*` stdio client |
17
17
  | `memory` | `memory_remember` · `memory_recall` · `memory_forget` · `memory_clear` · `memory_lean` | `PWN::Memory` → `~/.pwn/memory.json` |
18
18
  | `skills` | `skills_recall` · `skills_update` · `skill_list` · `skill_view` · `skill_create` · `skills_create` · `skill_add_reference` · `skill_delete` · `skill_migrate_legacy` · `skills_consolidate` | `~/.pwn/skills/<name>/SKILL.md` (**[agentskills.io](https://agentskills.io) spec**; legacy flat `*.md` auto-migrated) |
@@ -1,27 +1,82 @@
1
- # Historical migration backup retention
1
+ # Shared recovery backup retention
2
2
 
3
- ## Explicit legacy diagnostic and generated-copy expiry
3
+ All PWN-owned recovery producers use `PWN::Backups` and the single
4
+ `~/.pwn/backup` store. The limit is **seven total generation folders**, not seven
5
+ per producer. Producer payloads and recovery receipts are grouped inside their
6
+ transaction's generation. No new adjacent learning, preference, skill or
7
+ Terminator `.bak` / `.pwnbak` files are produced.
4
8
 
5
- `PWN::SessionCleanup.expire_legacy` is a separate, explicit maintenance entry point, defaulting to a preview. Apply requires `dry_run: false` and a **new external** `manifest:` path for one compressed metadata ledger; it does not archive payloads back into the managed home or run session/swarm retirement.
9
+ ## Producers
6
10
 
7
- Only recognized expired closed request logs, the exact `learning.jsonl.bak-lean-YYYYMMDD` and `preferences.jsonl.bak-p15-YYYYMMDDhhmmss` recovery families, and verified duplicate BinaryParser triage caches qualify. Root recovery retains at least the latest three nonempty parseable copies per family, all literal reference pins, and copies younger than thirty days. Cache expiry requires byte-identical retained ArtifactRegistry evidence; absent inputs are not claimed reproducible. Decompiler stdout caches are not adopted by this policy.
11
+ | Producer | Contents |
12
+ | --- | --- |
13
+ | Migrate | Exact registry-owned state paths, encrypted configuration and decryptor; verified snapshot manifest; no recursive logs/cache/backup copies |
14
+ | Learning | Learning rewrites and original evidence-migration index |
15
+ | Reward | Preference rewrite recovery |
16
+ | SkillReview / SkillConsolidation | Original skill bytes |
17
+ | PolicyEvaluation | Pinned routing-policy rollback snapshot |
18
+ | Setup | Original Terminator configuration |
19
+ | SessionCleanup | Session/layout recovery, dependency removal, continuation retirement and disposable quarantine payloads with their recovery receipts |
8
20
 
9
- The operation holds the canonical maintenance lock, rejects unsafe path/inode identities, checks retained references without exposing content, and checks process descriptors before classification and unlink. Existing producer leases must be available. Retained recovery/evidence proofs and candidate hashes are rechecked before unlink. An unreadable process inventory fails closed; do not bypass it when an older client is active. Coordinate any legacy writers before applying, and keep the learning producer lock during root recovery expiry. The external ledger records prepared versus completed state; it is not an undo archive. Existing immutable workers require a separately verified matched release before this entry point is deployed; it is not automatically enabled by importing the source.
21
+ Immutable learning evidence, artifacts, evaluation inputs and user-requested
22
+ exports are not disposable backup generations. Device read/write backup commands,
23
+ GitHub gist exports and Jenkins-to-S3 transfer operate on operator-selected data,
24
+ not PWN's internal recovery state.
10
25
 
11
- ## Migration generations
26
+ ## Admission, locking and failures
12
27
 
13
- Migration backups are recovery generations, not an indefinite archive of every distinct historical byte. `PWN::Migrate.prune_backups` stays duplicate-only until an operator explicitly authorizes historical expiry. Its optional `expire_history: true` argument or private `~/.pwn/backup/.retention-policy.json` enables generation retention:
28
+ The permanent `.retention.lock` serializes cooperating producers. Maintenance
29
+ acquires its maintenance lock, then the Learning writer lock, then the backup
30
+ lock. Ordinary backup producers acquire Learning before backups and never acquire
31
+ maintenance. Learning reentrancy is bound to the owning PID and thread; an
32
+ attempt to acquire Learning underneath an unowned backup transaction is rejected.
33
+ Maintenance still defers when the Learning writer is busy.
14
34
 
15
- ```json
16
- {"version":1,"expire_history":true,"keep":3,"pins":[]}
17
- ```
35
+ Before a new folder is admitted, oldest completed, unpinned generations may be
36
+ expired to make room. Prepared/interrupted generations, explicit pins and current
37
+ literal evidence references remain protected. Malformed reference data fails
38
+ closed when expiry would be needed. Pins count **inside** the seven-folder limit:
39
+ if they occupy capacity, the next producer fails before modifying its source.
40
+ Unknown legacy folders count toward capacity but are not silently certified or
41
+ expired. A pre-existing over-limit store can therefore block admission until an
42
+ explicit offline remediation is performed; importing this source does not fix a
43
+ live store or grant permission to prune it.
18
44
 
19
- `keep` cannot be less than three. `pins` contains exact timestamp generation directory names. Current structured evidence paths and prepared/interrupted or explicitly pinned maintenance records also retain their referenced generations. Quoted directory inventories and historical prose are not evidence dependencies. No logs, metrics, or historical learning versions are copied aside merely because their bytes are unique.
45
+ Transactions publish `prepared` before payload/source changes and `complete`
46
+ only after successful return. Exceptions, interrupted/blocked results and failed
47
+ receipts leave prepared recovery. Copies are hash-verified and fsynced. Keep the
48
+ transaction open through source mutation and receipt publication. Restoring one
49
+ store does not undo all children of a maintenance operation.
20
50
 
21
- Before expiry, the newest retained point must cover every existing current migration-owned input path, and current encrypted configuration and decryptor bytes must match retained recovery copies. Every retained and expiring payload is hashed, current inputs and references are rechecked, symlinks are rejected, and deletion shares the snapshot producer's permanent `.retention.lock`. The ordinary maintenance transaction supplies its own maintenance lock and cooperative work admission. Manual backup-only calls must also coordinate live writers before apply. Default automatic batches consider at most ten older generations; exhausted admission defers expiry rather than weakening protection.
51
+ Use the owning store's recovery API (`SessionCleanup.recover`,
52
+ `Learning.rollback_evidence`, skill review rollback or policy rollback) first.
53
+ `Backups.reconcile` only acknowledges recovery: it requires the exact generation,
54
+ its inspected manifest SHA-256 and explicit confirmation. It retains a pin by
55
+ default; `pinned: false` permits normal retention but does not override live
56
+ references. It does not restore payloads or declare a failed operation successful.
57
+ Never edit shared/hardlinked historical payloads in place; restore by copying.
22
58
 
23
- The single mode-0600 `.retention-manifest.json.gz` contains prepared/committed inventories and expiry names, not a second payload tree. Older unpinned versions are permanently expired: that manifest is an audit record, not an undo payload. Incomplete historical snapshots are never retrospectively certified complete. Their extant payloads can be verified for bounded recovery; the newly produced complete snapshot establishes current coverage. Restore by **copying only migration-owned paths**, not overlaying an entire old home, and never edit hardlinked backup bytes in place. Keep explicit pins for any recovery boundary needed during an active migration.
59
+ ## Upgrade and legacy relocation
24
60
 
25
- Scheduled retention is called by the managed v10 `SessionCleanup.maintain` helper through PWN's internal cron worker. No native scheduler or systemd installation is needed. Use `PWN::Migrate.prune_backups(dry_run: true)` for a strictly backup-only preview; do not invoke full session maintenance to test backup expiry. When no generation can expire, this preview avoids scanning evidence bodies and reports `evidence_scan: not_needed_no_expiry`; it is not a fresh payload-integrity audit.
61
+ Schema 12 changes the existing retention policy from three to seven, preserving
62
+ pins and other policy fields. A second migration is byte-stable. The old
63
+ `Migrate.prune_backups` entry point is a compatibility report only; it cannot run
64
+ an independent three-generation pruning policy. Retention happens at producer
65
+ admission, not via another scheduler.
26
66
 
27
- An isolated maintenance release may coexist with older interactive clients: run the worker from the release's immutable `lib` load path, verify source/release/live-helper SHA-256 equality, and leave the active gem and clients untouched. The cron helper does not retrofit methods into those clients. A later normal application upgrade is separate from this scoped maintenance deployment.
67
+ `Backups.relocate_legacy(paths: ..., dry_run: true)` previews exact recognized
68
+ legacy paths. Applying requires `dry_run: false, writers_stopped: true`, owned
69
+ non-symlink sources, unchanged verified copies and no references to their old
70
+ paths. The shared generation contains an origin mapping before originals are
71
+ removed. Unknown, referenced or over-capacity inputs must be resolved explicitly,
72
+ not bypassed. Existing cleanup recovery readers remain available for legacy
73
+ ledgers. Legacy diagnostic/cache expiry is a separate explicit operation and is
74
+ not a new backup producer.
75
+
76
+ ## Deployment boundary
77
+
78
+ Source changes do not upgrade installed gems, existing clients or immutable cron
79
+ releases. Deploy a matched release and restart only explicitly authorized
80
+ processes separately. Use only PWN's existing internal cron worker; no systemd,
81
+ native scheduler, live provider calls or live backup pruning is required for
82
+ fixture verification. Tests must use an isolated HOME and scratch storage.
@@ -5,18 +5,22 @@ Provider and plugin configuration lives in **one encrypted YAML file**, loaded b
5
5
  constant **`PWN::Env`** (whose nested settings are mutable) (a redacted copy is available as
6
6
  `PWN::EnvRedacted`).
7
7
 
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, and reloads `PWN::Env`. If `PWN::Config` rejects the
8
+ The file is **AES-encrypted at rest** by `PWN::Plugins::Vault`. Legacy CBC key/IV
9
+ values live in a sibling **`~/.pwn/pwn.yaml.decryptor`** file (or in
10
+ `PWN_DECRYPTOR_KEY` / `PWN_DECRYPTOR_IV` env vars). Explicitly selected
11
+ [YubiKey vaults](YubiKey-Vaults.md) instead use an authenticated hardware-derived
12
+ AES-GCM envelope and need no software decryptor. Never edit ciphertext by hand;
13
+ use the **`pwn-vault`** REPL command, which edits a private temporary copy,
14
+ re-encrypts before replacing the original, and reloads `PWN::Env`. If `PWN::Config` rejects the
13
15
  edited file, the editor reopens until the vault loads cleanly, so you do not
14
16
  leave the REPL stuck on a decrypt / re-edit / encrypt loop.
15
17
 
16
18
  If `~/.pwn/pwn.yaml` does not exist on first run, `PWN::Config.default_env`
17
19
  writes a fully-commented template with every key below (values set to
18
20
  `'optional - ...'` / `'required - ...'` placeholder strings), encrypts it, and
19
- generates the decryptor.
21
+ generates the legacy decryptor. `default_env(yubikey: { slot: 2 })` explicitly
22
+ selects hardware protection for a new vault and omits that sidecar; it does not
23
+ provision the device slot or silently convert an existing file.
20
24
 
21
25
  > **Before configuring**, run `pwn setup` - the
22
26
  > [doctor](Installation.md#pwn-setup--the-post-install-doctor--provisioner)
@@ -6,7 +6,7 @@
6
6
  lib/pwn/ # all namespaces
7
7
  lib/pwn/setup.rb # PWN::Setup - doctor/provisioner data tables
8
8
  lib/pwn/migrate.rb # PWN::Migrate - ~/.pwn state doctor / auto-migrator
9
- lib/pwn/plugins/ # 107 top-level plugin autoloads
9
+ lib/pwn/plugins/ # 108 top-level plugin autoloads
10
10
  lib/pwn/ai/agent/ # agent core
11
11
  lib/pwn/ai/agent/tools/ # LLM tool registrations
12
12
  bin/ # 56 executables: 53 pwn_* drivers + pwn / pwn-ai / pwn-capd (incl. pwn_setup)
@@ -9,8 +9,8 @@ jobs require that worker, while manual commands do not. `--no-cron` disables
9
9
  the internal worker without changing host services.
10
10
 
11
11
  **Housekeeping must never use systemd or another native scheduler.**
12
- The three enabled housekeeping defaults below use only this existing worker
13
- or manual commands. `install_native_job` and each platform-specific job
12
+ The one enabled housekeeping default, `session_cleanup`, uses only this existing
13
+ worker or a manual command. `install_native_job` and each platform-specific job
14
14
  installer explicitly skip housekeeping (`housekeeping_internal_only`),
15
15
  including recognizable cleanup payloads under another name. Native worker
16
16
  contexts exclude housekeeping from ticks and direct job execution.
@@ -24,7 +24,7 @@ a dependency guarantee. External services/radios require separate live verificat
24
24
  [source](diagrams/dot/persistence-filesystem.dot) · doc: [Persistence](Persistence.md)
25
25
  ![persistence-filesystem](diagrams/persistence-filesystem.svg)
26
26
 
27
- ### Plugin Ecosystem (107 modules)
27
+ ### Plugin Ecosystem (108 modules)
28
28
  [source](diagrams/dot/plugin-ecosystem.dot) · doc: [Plugins](Plugins.md)
29
29
  ![plugin-ecosystem](diagrams/plugin-ecosystem.svg)
30
30
 
@@ -69,7 +69,7 @@ a dependency guarantee. External services/radios require separate live verificat
69
69
  [source](diagrams/dot/ai-integration-tool-calling.dot) · doc: [AI Integration](AI-Integration.md)
70
70
  ![ai-integration-tool-calling](diagrams/ai-integration-tool-calling.svg)
71
71
 
72
- ### Agent Tool Registry (16 toolsets · 150 tools)
72
+ ### Agent Tool Registry (16 toolsets · 152 tools)
73
73
  [source](diagrams/dot/agent-tool-registry.dot) · doc: [Agent Tool Registry](Agent-Tool-Registry.md)
74
74
  ![agent-tool-registry](diagrams/agent-tool-registry.svg)
75
75
 
@@ -18,13 +18,14 @@ $ pwn setup --migrate --fix # after `gem update pwn`: verify + autofix ~/.pwn
18
18
  ```
19
19
 
20
20
  See [Installation](Installation.md) for every flag and the
21
- `PWN::Setup` / `PWN::Migrate` API.
21
+ `PWN::Setup` / `PWN::Migrate` API. Unspecified `pwn-ai` writes use
22
+ `~/.pwn/workspaces/runtime`; see [Operability](Operability.md).
22
23
 
23
24
  ## Inside the REPL
24
25
 
25
26
  ```ruby
26
27
  PWN.help # top-level help
27
- PWN::Plugins.constants.sort # list all 107 plugins
28
+ PWN::Plugins.constants.sort # list all 108 plugins
28
29
  PWN::Plugins::NmapIt.help # per-plugin usage
29
30
  PWN::Setup.check # capability doctor from inside the REPL
30
31
  PWN::Migrate.status # ~/.pwn state-file compatibility rows
@@ -40,7 +41,7 @@ history # what you've typed → copy into a driver
40
41
  | `pwn-ai` | Enter the agent TUI (SHIFT+ENTER = newline, ENTER = submit) |
41
42
  | `pwn-asm` | Multi-line assembly ↔ opcode workbench |
42
43
  | `pwn-mesh` | Meshtastic serial / bluetooth / TCP / MQTT client (Ruby ≥ 4.0) |
43
- | `pwn-vault` | Decrypt → edit `~/.pwn/pwn.yaml` in `$EDITOR` → re-encrypt |
44
+ | `pwn-vault` | Edit a private temporary copy, then replace `~/.pwn/pwn.yaml` only after encryption succeeds (legacy or YubiKey) |
44
45
  | `pwn-ai-memory` | Inspect / edit `~/.pwn/memory.json` |
45
46
  | `pwn-ai-sessions` | List / view / delete transcripts |
46
47
  | `pwn-ai-cron` | List / run scheduled jobs |
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **PWN** (/pōn/) - an open-source offensive-security automation framework and
4
4
  > continuous-security-integration platform written in Ruby.
5
- > 107 plugins · 48 SAST modules · 90 AWS wrappers · 22 WWW drivers · 56 CLI
5
+ > 108 plugins · 48 SAST modules · 90 AWS wrappers · 22 WWW drivers · 56 CLI
6
6
  > executables · 6 LLM engines · a self-improving multi-agent AI · one REPL.
7
7
 
8
8
  **Repo root:** `/opt/pwn` · **This wiki:** `/opt/pwn/documentation/` ·
@@ -19,7 +19,7 @@
19
19
  | [How PWN Works](How-PWN-Works.md) | The five layers, with the architecture diagram |
20
20
  | [Installation](Installation.md) | `gem install --verbose pwn` → `pwn setup` doctor/provisioner · capability profiles · `--migrate` state doctor |
21
21
  | [General Usage](General-PWN-Usage.md) | Day-one cheat sheet |
22
- | [Operability and Evidence](Operability.md) | Completion, regressions, frozen evaluation, mesh and deployment boundaries |
22
+ | [Operability and Evidence](Operability.md) | Runtime writes, backups, cron, completion and deployment boundaries |
23
23
  | [Configuration](Configuration.md) | `~/.pwn/pwn.yaml` (encrypted) - engines, keys, agent options · `pwn-vault` |
24
24
  | **[All Data-Flow Diagrams](Diagrams.md)** | **29 SVGs** in one scrollable page |
25
25
 
@@ -37,7 +37,7 @@
37
37
  | | |
38
38
  |---|---|
39
39
  | [AI / LLM Integration](AI-Integration.md) | OpenAI · Anthropic · Grok (OAuth) · Gemini · Ollama · Open WebUI |
40
- | [Agent Tool Registry](Agent-Tool-Registry.md) | 16 toolsets · **150** LLM-callable tools |
40
+ | [Agent Tool Registry](Agent-Tool-Registry.md) | 16 toolsets · **152** LLM-callable tools |
41
41
  | [Memory · Skills · Learning](Skills-Memory-Learning.md) | Introspection, reviewed skill updates and bundled SOP/module skills on first launch |
42
42
  | [Mistakes](Mistakes.md) | **Negative feedback** - fingerprint failures · do-NOT-repeat · `[REPEATING]`/`[REGRESSED]` · inline self-correction |
43
43
  | [Reinforcement Learning](Reinforcement-Learning.md) | **`Reward` + `Curriculum` + `Policy`** - outcome/process judges · preference ledger · self-play practice · live Q / REINFORCE (advisory) · export-ready LoRA gate |
@@ -45,19 +45,20 @@
45
45
  | [Swarm (Multi-Agent)](Swarm.md) | Personas · ask · debate · broadcast · shared bus |
46
46
  | [Sessions](Sessions.md) | Transcript persistence + reflection |
47
47
  | [Session Workflow](Session-Workflow.md) | Keywords: `continue` / last session / write-then-read |
48
- | [Cron](Cron.md) | Scheduled jobs. Hygiene seeds on; curriculum practice/judge/train ship off |
48
+ | [Cron](Cron.md) | Scheduled jobs. Fresh install seeds one `session_cleanup` coordinator; curriculum is not seeded |
49
49
 
50
50
  ## 🧩 Capability Namespaces (`lib/pwn/*`)
51
51
 
52
52
  | | |
53
53
  |---|---|
54
- | [Plugins (107)](Plugins.md) | Every `PWN::Plugins::*` module by category |
54
+ | [Plugins (108)](Plugins.md) | Every `PWN::Plugins::*` module by category |
55
55
  | &nbsp;&nbsp;↳ [BurpSuite](BurpSuite.md) ⭐ | Preferred web proxy / scanner |
56
56
  | &nbsp;&nbsp;↳ [TransparentBrowser](Transparent-Browser.md) | Headless / visible browser automation |
57
57
  | &nbsp;&nbsp;↳ [NmapIt](NmapIt.md) | Network discovery |
58
58
  | &nbsp;&nbsp;↳ [Metasploit](Metasploit.md) | RPC exploitation |
59
59
  | &nbsp;&nbsp;↳ [Fuzzing](Fuzzing.md) | `Fuzz` · `Sock` · `Packet` |
60
60
  | &nbsp;&nbsp;↳ [Hardware](Hardware.md) | Serial · BusPirate · MSR206 · Android · BareSIP |
61
+ | &nbsp;&nbsp;↳ [YubiKey](YubiKey.md) | Device management, hardware vaults and OATH MFA |
61
62
  | [SAST (48 rules)](SAST.md) | Static analysis + test-case engine |
62
63
  | [WWW (22 drivers)](WWW.md) | Browser automations for real sites |
63
64
  | [AWS (90 services)](AWS.md) | Cloud-security enumeration |
@@ -28,7 +28,7 @@ hardware).
28
28
  |---|---|
29
29
  | `Loop` | plan → **TaskSummarizer** briefs → dispatch tool_calls → observe → repeat until `may_finalize?`. No iteration-budget abort |
30
30
  | **`TaskSummarizer`** | Executive UX: every request gets an English task compass (`emit_plan!` · `about_to` as `task k/n`) - no statement/question/goal type |
31
- | `Registry` | JSON-Schema function definitions grouped into 16 **toolsets** · **150 tools** · `CORE_TOOLS` = `DEFAULT_PREFERENCE` (`memory_recall` · `session_recall` · `skills_recall` · `pwn_eval` · `shell` · `mistakes_record` · `mistakes_resolve` · `learning_note_outcome` · `memory_remember` · `skills_update` · `artifact_read` · `artifact_grep` · `job_run` · `job_status` · `job_tail` · `job_result` · `job_kill`) |
31
+ | `Registry` | JSON-Schema function definitions grouped into 16 **toolsets** · **152 tools** · `CORE_TOOLS` = `DEFAULT_PREFERENCE` (`memory_recall` · `session_recall` · `skills_recall` · `pwn_eval` · `shell` · `mistakes_record` · `mistakes_resolve` · `learning_note_outcome` · `memory_remember` · `skills_update` · `artifact_read` · `artifact_grep` · `job_run` · `job_status` · `job_tail` · `job_result` · `job_kill`) |
32
32
  | `Dispatch` / `Result` | execute a tool, capture stdout/value/error/duration |
33
33
  | `PromptBuilder` | inject MEMORY / SKILLS / LEARNING / **KNOWN MISTAKES + FIXES** / METRICS / **POLICY** / EXTROSPECTION / RECENT TURNS |
34
34
  | `Metrics` · `Learning` · `Reflect` · **`Policy`** | **introspection** - how well am I doing? (Policy is live Q / REINFORCE, advisory rank only) |
@@ -44,7 +44,7 @@ call, and [Reinforcement Learning](Reinforcement-Learning.md) for how
44
44
 
45
45
  ## L3 - Capability namespaces (`lib/pwn/*`)
46
46
 
47
- `Plugins` (107) · `SAST` (48) · `WWW` (22) · `AWS` (90) · `SDR` · `Blockchain` ·
47
+ `Plugins` (108) · `SAST` (48) · `WWW` (22) · `AWS` (90) · `SDR` · `Blockchain` ·
48
48
  `Bounty` · `Reports` · `FFI` · `Banner` · **`Setup`** · **`Migrate`**. First launch also seeds bundled skills from `etc/default_skills/`. Each is
49
49
  a plain module of `public_class_method def self.x(opts = {})` methods -
50
50
  callable the same way from the REPL, from `pwn_eval`, or from a driver.
@@ -74,9 +74,10 @@ Q / REINFORCE only for eligible resolved outcomes with independently attributed
74
74
  (MEMORY · SKILLS · LEARNING · KNOWN MISTAKES/FIXES · TOOL EFFECTIVENESS ·
75
75
  POLICY · EXTROSPECTION · RECENT TURNS) are re-injected into the next system
76
76
  prompt.
77
- Nightly hygiene cron trims `~/.pwn` stores. Curriculum practice, offline
78
- judge, and weekly LoRA train ship seeded but disabled; turn them on with
79
- `cron_enable` when you want that loop.
77
+ Nightly hygiene is the one seeded `session_cleanup` coordinator. It is not three
78
+ separate enabled jobs, and it does not use systemd. Curriculum practice, offline
79
+ judge, and weekly LoRA train are optional templates; they are not seeded, and
80
+ deleting them does not cause the next launch to recreate them.
80
81
 
81
82
  `Metrics.esr` is `verified_exploit_tools / vulnerable_tools`. `Metrics.asr` is `successful_attacks / total_attack_attempts`. `Learning.rsi_tick` compares ESR with the previous snapshot and writes a lesson when it falls. A file hash, a nuclei template match, or a CVE id does not move ESR. Only a reproduced exploit does.
82
83
 
@@ -34,7 +34,7 @@ anchors:
34
34
  | state | `swarm/`, `cron/`, `agents.yml`, `jobs/`, `missions/`, `runs/`, metrics | Preserve continuation associations and active work |
35
35
  | logs | `logs/` | Only explicitly registered disposable closed logs are eligible; unknown logs remain |
36
36
  | cache | `cache/` | Only producer-registered, checksum-verified, inactive disposable files |
37
- | tmp | `tmp/` | Same registration contract; never glob-delete arbitrary temp files |
37
+ | tmp | `workspaces/runtime/` | Same registration contract; never glob-delete arbitrary temp files |
38
38
  | maintenance | `maintenance/`, legacy `session_cleanup_archive/` | Policy, permanent locks, metadata ledgers and recoverable quarantine |
39
39
 
40
40
  `maintenance/policy.json` is non-secret JSON, version 1. `batch_size` defaults to
@@ -42,7 +42,7 @@ anchors:
42
42
  `work_seconds: 30` and `work_bytes: 67108864` for dependency and maintenance
43
43
  payload reads, plus reserved disposable backup writes. Existing policy
44
44
  files acquire these defaults in memory, without rewriting operator values.
45
- `maintenance/layout.json` upgrades recognized inventory v1 to v2 separately from
45
+ `maintenance/layout.json` upgrades recognized inventory v1 or v2 to v3 separately from
46
46
  policy; unknown inventory versions are refused. `retention_days` defaults to sessions 0, logs 30,
47
47
  cache 7, tmp 1, maintenance 90. Null disables that category's expiry. Session
48
48
  age is an additional floor, never an override of leases, provenance or evidence.
@@ -149,7 +149,7 @@ deployment or provider run is implied.
149
149
 
150
150
  The audit covers `Dir.tmpdir`, `Tempfile`, `mktmpdir` and atomic replacement
151
151
  call sites, not a filesystem filename sweep:
152
- - Festival speech input now uses `SessionCleanup.temporary` in `~/.pwn/tmp`, with
152
+ - Festival speech input now uses `SessionCleanup.temporary` in `~/.pwn/workspaces/runtime`, with
153
153
  a lifetime lease and exact registration. Concurrent calls have unique names;
154
154
  normal completion unlinks the input and a crashed consumer leaves registered bytes.
155
155
  - Ghidra/BinaryParser canonical cache publication and managed temporary inputs
@@ -315,7 +315,7 @@ pwn setup --list-profiles
315
315
  | `vision` | `OCR` · `ScannableCodes` · `Reports` · `extro_vision` · tesseract / zbar / graphviz |
316
316
  | `voice` | `PWN::Plugins::Voice` · `extro_voice` · espeak-ng / sox |
317
317
  | `exploit` | `Metasploit` · `sqlmap` |
318
- | `hardware` | `Serial` · `BusPirate` · `Android` · `BareSIP` · `extro_serial` · `extro_telecomm` |
318
+ | `hardware` | `Serial` · `BusPirate` · `Android` · `BareSIP` · `YubiKey` (`ykman`) · `extro_serial` · `extro_telecomm` |
319
319
  | `re` | `Radare2` · `GDB` · `ExploitDev` · `Frida` · Android RE (`apktool`/`jadx`) · `pwndbg` · `ropper` · `rizin` |
320
320
  | `full` | everything above |
321
321
 
@@ -365,7 +365,8 @@ What `--migrate` does:
365
365
  (`PWN::Migrate::STATE_FILES` - no re-implemented parsers). If the raw
366
366
  file has bytes but the owner returned its empty-fallback, the file is
367
367
  flagged incompatible.
368
- 3. **Autofix** (with `--fix`): timestamped backup under `~/.pwn/backup/<ts>/`
368
+ 3. **Autofix** (with `--fix`): one shared recovery generation under
369
+ `~/.pwn/backup/` (seven folders total; see [Backup Retention](Backup-Retention.md))
369
370
  → ordered schema migrations → per-file repair (quarantine corrupt to
370
371
  `~/.pwn/quarantine/`, re-seed missing dirs, strip unparsable JSONL lines,
371
372
  convert legacy flat skills to the [agentskills.io](https://agentskills.io)
@@ -410,8 +411,10 @@ and schedule-only deletion. Already-running old binaries must be upgraded before
410
411
  their startup/config-refresh hooks can be relied on to honor the new defaults.
411
412
 
412
413
  **Housekeeping never uses systemd or any other native scheduler.**
413
- `session_cleanup`, `learning_consolidate_nightly`, and `pwn_stores_lean_nightly`
414
- run only through PWN's built-in cron worker or manual commands. Native job
414
+ `session_cleanup` is the only seeded housekeeping job. It runs only through
415
+ PWN's built-in cron worker or a manual command. Older split names
416
+ (`learning_consolidate_nightly`, `pwn_stores_lean_nightly`) are not seeded and
417
+ are not removed automatically. Native job
415
418
  installers explicitly skip them with `housekeeping_internal_only`; this also
416
419
  covers renamed payloads that invoke `SessionCleanup` or `session_cleanup.rb`.
417
420
  Generic scheduler APIs remain available for unrelated custom jobs. Native
@@ -680,7 +683,7 @@ row as `MISSING` until a key is set.
680
683
  Everything above is a thin CLI over two autoloaded modules:
681
684
 
682
685
  ```ruby
683
- PWN::Setup.check # → { ok:, native_gems_missing:, toolchain_missing:, state:, pkg_manager:, os:, arch: }
686
+ PWN::Setup.check # → { ok:, native_gems_missing:, toolchain_missing:, state:, pkg_manager:, os:, arch:, native_libraries_missing: }
684
687
  PWN::Setup.deps(profile: :web, yes: true) # install, then re-check
685
688
  PWN::Setup.list_profiles
686
689
  PWN::Setup.migrate(fix: true) # thin wrapper → PWN::Migrate.run
@@ -715,7 +718,7 @@ Vagrant, CI) picks it up automatically.
715
718
  ```ruby
716
719
  pwn[CURRENT_VERSION]:001 >>> PWN::Setup.check[:ok] # => true
717
720
  pwn[CURRENT_VERSION]:002 >>> PWN::Migrate.needed? # => false
718
- pwn[CURRENT_VERSION]:003 >>> PWN::Plugins.constants.count # => 107
721
+ pwn[CURRENT_VERSION]:003 >>> PWN::Plugins.constants.count # => 108
719
722
  pwn[CURRENT_VERSION]:004 >>> PWN::SAST.constants.count # => 48
720
723
  pwn[CURRENT_VERSION]:005 >>> pwn-ai # launches agent TUI
721
724
  ```