pwn 0.5.783 → 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 (264) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +8 -8
  4. data/README.md +37 -0
  5. data/Rakefile +19 -1
  6. data/bin/pwn +2 -1
  7. data/bin/pwn_setup +1 -1
  8. data/bin/pwn_web_cache_deception +35 -7
  9. data/build_gem.sh +1 -1
  10. data/documentation/AI-Integration.md +7 -5
  11. data/documentation/Agent-Tool-Registry.md +2 -2
  12. data/documentation/Backup-Retention.md +82 -0
  13. data/documentation/Configuration.md +10 -6
  14. data/documentation/Contributing.md +1 -1
  15. data/documentation/Cron.md +64 -35
  16. data/documentation/Diagrams.md +2 -2
  17. data/documentation/General-PWN-Usage.md +4 -3
  18. data/documentation/Home.md +6 -5
  19. data/documentation/How-PWN-Works.md +7 -6
  20. data/documentation/Installation.md +254 -10
  21. data/documentation/Mission-Cancellation.md +134 -0
  22. data/documentation/Operability.md +17 -0
  23. data/documentation/Persistence.md +8 -6
  24. data/documentation/Plugin-Degradation.md +12 -0
  25. data/documentation/Plugins.md +3 -2
  26. data/documentation/Reinforcement-Learning.md +5 -6
  27. data/documentation/Runtime-Source-Protection.md +162 -0
  28. data/documentation/Transparent-Browser.md +11 -0
  29. data/documentation/Troubleshooting.md +3 -2
  30. data/documentation/What-is-PWN.md +2 -2
  31. data/documentation/YubiKey-Vaults.md +183 -0
  32. data/documentation/YubiKey.md +339 -0
  33. data/documentation/diagrams/cron-scheduling.svg +130 -128
  34. data/documentation/diagrams/dot/cron-scheduling.dot +5 -5
  35. data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
  36. data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
  37. data/documentation/diagrams/persistence-filesystem.svg +172 -142
  38. data/documentation/diagrams/plugin-ecosystem.svg +64 -64
  39. data/documentation/pwn-ai-Agent.md +10 -4
  40. data/etc/default_cron/session_cleanup.rb +1245 -22
  41. data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
  42. data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
  43. data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
  44. data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
  45. data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
  46. data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
  47. data/etc/default_skills/pwn/backups/SKILL.md +54 -0
  48. data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
  49. data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
  50. data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
  51. data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
  52. data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
  53. data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
  54. data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
  55. data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
  56. data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
  57. data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
  58. data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
  59. data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
  60. data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
  61. data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
  62. data/lib/pwn/ai/agent/dispatch.rb +26 -15
  63. data/lib/pwn/ai/agent/extrospection.rb +6 -7
  64. data/lib/pwn/ai/agent/learning.rb +62 -25
  65. data/lib/pwn/ai/agent/loop.rb +17 -1
  66. data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
  67. data/lib/pwn/ai/agent/policy.rb +5 -0
  68. data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
  69. data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
  70. data/lib/pwn/ai/agent/request_runtime.rb +122 -18
  71. data/lib/pwn/ai/agent/reward.rb +5 -3
  72. data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
  73. data/lib/pwn/ai/agent/skill_review.rb +9 -5
  74. data/lib/pwn/ai/agent/swarm.rb +35 -16
  75. data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
  76. data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
  77. data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
  78. data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
  79. data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
  80. data/lib/pwn/ai/agent.rb +1 -0
  81. data/lib/pwn/ai/anthropic.rb +8 -33
  82. data/lib/pwn/ai/cli.rb +34 -5
  83. data/lib/pwn/ai/gemini.rb +1 -0
  84. data/lib/pwn/ai/grok.rb +39 -28
  85. data/lib/pwn/ai/ollama.rb +4 -2
  86. data/lib/pwn/ai/open_ai.rb +11 -32
  87. data/lib/pwn/ai/open_web_ui.rb +62 -4
  88. data/lib/pwn/ai/router.rb +53 -3
  89. data/lib/pwn/ai/server_tools.rb +231 -0
  90. data/lib/pwn/ai.rb +1 -0
  91. data/lib/pwn/backups.rb +347 -0
  92. data/lib/pwn/config.rb +70 -39
  93. data/lib/pwn/cron.rb +120 -49
  94. data/lib/pwn/migrate.rb +150 -68
  95. data/lib/pwn/module_skills.rb +7 -0
  96. data/lib/pwn/plugins/ai_sandbox.rb +538 -1
  97. data/lib/pwn/plugins/authentication_helper.rb +74 -4
  98. data/lib/pwn/plugins/basic_auth.rb +2 -1
  99. data/lib/pwn/plugins/binary_parser.rb +5 -4
  100. data/lib/pwn/plugins/ghidra.rb +4 -3
  101. data/lib/pwn/plugins/google_workspace.rb +4 -21
  102. data/lib/pwn/plugins/jobs.rb +1 -1
  103. data/lib/pwn/plugins/log.rb +21 -3
  104. data/lib/pwn/plugins/preflight_checker.rb +34 -9
  105. data/lib/pwn/plugins/recon.rb +4 -1
  106. data/lib/pwn/plugins/repl/ai.rb +305 -27
  107. data/lib/pwn/plugins/repl/ai_console.rb +273 -89
  108. data/lib/pwn/plugins/repl/ai_swarm.rb +181 -62
  109. data/lib/pwn/plugins/repl/mesh.rb +3 -16
  110. data/lib/pwn/plugins/repl/vault.rb +4 -15
  111. data/lib/pwn/plugins/repl.rb +1 -216
  112. data/lib/pwn/plugins/sandbox/driver.rb +4 -0
  113. data/lib/pwn/plugins/transparent_browser.rb +215 -0
  114. data/lib/pwn/plugins/vault.rb +412 -135
  115. data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
  116. data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
  117. data/lib/pwn/plugins/yubikey/management.rb +686 -0
  118. data/lib/pwn/plugins/yubikey/oath.rb +766 -0
  119. data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
  120. data/lib/pwn/plugins/yubikey/otp.rb +897 -0
  121. data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
  122. data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
  123. data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
  124. data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
  125. data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
  126. data/lib/pwn/plugins/yubikey.rb +844 -0
  127. data/lib/pwn/plugins.rb +1 -0
  128. data/lib/pwn/redaction.rb +69 -12
  129. data/lib/pwn/session_trace.rb +4 -0
  130. data/lib/pwn/sessions.rb +58 -16
  131. data/lib/pwn/setup.rb +123 -41
  132. data/lib/pwn/version.rb +1 -1
  133. data/lib/pwn/www/app_cobalt_io.rb +16 -5
  134. data/lib/pwn/www/bug_crowd.rb +16 -5
  135. data/lib/pwn/www/coinbase_pro.rb +16 -5
  136. data/lib/pwn/www/github.rb +30 -14
  137. data/lib/pwn/www/paypal.rb +15 -4
  138. data/lib/pwn/www/synack.rb +16 -5
  139. data/lib/pwn/www/twitter.rb +17 -6
  140. data/lib/pwn.rb +1 -0
  141. data/scripts/build_gate.rb +58 -0
  142. data/spec/documentation/installation_md_spec.rb +1 -1
  143. data/spec/fixtures/yubikey/apdu_frames.json +162 -0
  144. data/spec/integration/authentication_helper_oath_spec.rb +117 -0
  145. data/spec/integration/build_gate_spec.rb +73 -0
  146. data/spec/integration/controller_spill_spec.rb +48 -0
  147. data/spec/integration/harness_process_spec.rb +97 -0
  148. data/spec/integration/mission_cancel_spec.rb +79 -0
  149. data/spec/integration/mission_curses_pty_spec.rb +44 -0
  150. data/spec/integration/persistence_roundtrip_spec.rb +1 -7
  151. data/spec/integration/readme_security_spec.rb +47 -0
  152. data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
  153. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
  154. data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
  155. data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
  156. data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
  157. data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
  158. data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
  159. data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
  160. data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
  161. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
  162. data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
  163. data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
  164. data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
  165. data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
  166. data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
  167. data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
  168. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
  169. data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
  170. data/spec/lib/pwn/ai/cli_spec.rb +6 -9
  171. data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
  172. data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
  173. data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
  174. data/spec/lib/pwn/ai/router_spec.rb +31 -3
  175. data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
  176. data/spec/lib/pwn/backups_spec.rb +114 -0
  177. data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
  178. data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
  179. data/spec/lib/pwn/cron_spec.rb +113 -6
  180. data/spec/lib/pwn/migrate_spec.rb +108 -5
  181. data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
  182. data/spec/lib/pwn/module_skills_spec.rb +8 -0
  183. data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
  184. data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
  185. data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
  186. data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
  187. data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
  188. data/spec/lib/pwn/plugins/log_spec.rb +7 -0
  189. data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
  190. data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
  191. data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
  192. data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
  193. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +125 -78
  194. data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
  195. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +30 -16
  196. data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
  197. data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
  198. data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
  199. data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
  200. data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
  201. data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
  202. data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
  203. data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
  204. data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
  205. data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
  206. data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
  207. data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
  208. data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
  209. data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
  210. data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
  211. data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
  212. data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
  213. data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
  214. data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
  215. data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
  216. data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
  217. data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
  218. data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
  219. data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
  220. data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
  221. data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
  222. data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
  223. data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
  224. data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
  225. data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
  226. data/spec/lib/pwn/redaction_spec.rb +13 -10
  227. data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
  228. data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
  229. data/spec/lib/pwn/session_cleanup_default_spec.rb +167 -2
  230. data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
  231. data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
  232. data/spec/lib/pwn/session_cleanup_recovery_spec.rb +403 -0
  233. data/spec/lib/pwn/sessions_spec.rb +112 -0
  234. data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
  235. data/spec/lib/pwn/setup_spec.rb +3 -1
  236. data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
  237. data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
  238. data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
  239. data/spec/spec_helper.rb +15 -0
  240. data/spec/support/gate_failure_formatter.rb +14 -0
  241. data/spec/support/harness/cli_faults.rb +13 -9
  242. data/spec/support/harness/controller_spill.rb +26 -0
  243. data/spec/support/harness/controller_state.rb +54 -0
  244. data/spec/support/harness/evaluate.rb +12 -1
  245. data/spec/support/harness/experiments.rb +1 -1
  246. data/spec/support/harness/fake_provider.rb +22 -6
  247. data/spec/support/harness/fault_preload.rb +3 -1
  248. data/spec/support/harness/mission_console.rb +54 -0
  249. data/spec/support/harness/mission_curses_pty.rb +41 -0
  250. data/spec/support/harness/mission_dispatch.rb +30 -0
  251. data/spec/support/harness/mission_fixture_preload.rb +24 -0
  252. data/spec/support/harness/mission_job_lifetime.rb +25 -0
  253. data/spec/support/harness/mission_legacy.rb +25 -0
  254. data/spec/support/harness/mission_normal_exit.rb +34 -0
  255. data/spec/support/harness/mission_swarm.rb +23 -0
  256. data/spec/support/harness/mission_unsupported.rb +29 -0
  257. data/spec/support/harness/package_acceptance.rb +33 -5
  258. data/spec/support/harness/session_acceptance.rb +4 -4
  259. data/spec/support/harness/swarm_acceptance.rb +1 -1
  260. data/spec/support/harness/swarm_control.rb +44 -0
  261. data/spec/support/mission_namespace.rb +14 -0
  262. data/spec/support/mission_pipe_queue.rb +33 -0
  263. data/third_party/pwn_rdoc.jsonl +599 -15
  264. metadata +140 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43f675e7e241c2f9c2ac2b96c52b43259b337871bc3227fb2cbac832457f7f23
4
- data.tar.gz: c053c15cff377d88ee43b25103203928eaa037c3bca976fd6d6483d5543ad5fd
3
+ metadata.gz: fef74967cd315acf1e070378d8e12000d552604b8d0f273d42638014d0a13a8c
4
+ data.tar.gz: 3d68aa5c28678b95a4b20acbe878a36c6d0cd8c04357ee3d18c768a7e0435c75
5
5
  SHA512:
6
- metadata.gz: eca4d79da6a33b24ec42cbc39c7c07afd0cfec053f98a49655b2bd6ef3c93bbba32f91aa90e755fe88eb8ec7bb13db892f87ade9e6b715b2d14f0b867415e576
7
- data.tar.gz: 8c8dad0dd07d97a0b4941fadf03683dd14cfe7fe045c06431d7f725c39ea4dadaac76988a722efaa9186abe71fa4ba731b95c8ae0fa58b1efd0d7baa367f45b1
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
@@ -22,7 +22,7 @@ gem 'brakeman', '8.1.0'
22
22
  gem 'bson', '5.2.0'
23
23
  gem 'bundler', '>=4.0.23'
24
24
  gem 'bundler-audit', '>=0.9.3'
25
- gem 'bunny', '3.4.0'
25
+ gem 'bunny', '3.5.0'
26
26
  gem 'colorize', '1.1.0'
27
27
  gem 'credit_card_validations', '10.0.0'
28
28
  gem 'curses', '1.7.0'
@@ -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.1'
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,12 +89,12 @@ 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'
97
- gem 'sqlite3', '2.10.1'
97
+ gem 'sqlite3', '2.10.2'
98
98
  gem 'thin', '2.0.1'
99
99
  gem 'tty-prompt', '0.23.1'
100
100
  gem 'tty-spinner', '0.9.3'
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/Rakefile CHANGED
@@ -5,12 +5,30 @@
5
5
  exec('bundle', 'exec', 'rake', *ARGV) unless ENV['BUNDLE_BIN_PATH'] || ENV['RUBYOPT'].to_s.include?('bundler/setup')
6
6
 
7
7
  require 'bundler/setup'
8
+ require 'English'
8
9
  require 'bundler/gem_tasks'
9
10
  require 'rspec/core/rake_task'
10
11
  require 'rdoc/task'
11
12
  require 'rubocop/rake_task'
12
13
 
13
- RSpec::Core::RakeTask.new(:spec)
14
+ # RSpec's stock task maps a signalled child to exit 1 without naming the
15
+ # signal. Preserve that evidence when an interrupted run has no summary.
16
+ class DiagnosticSpecTask < RSpec::Core::RakeTask
17
+ def run_task(verbose)
18
+ super
19
+ rescue SystemExit
20
+ status = $CHILD_STATUS
21
+ if status
22
+ result = status.signaled? ? "signal=#{Signal.signame(status.termsig)}" : "exit=#{status.exitstatus}"
23
+ warn "RSpec child failed: #{result}"
24
+ end
25
+ raise
26
+ end
27
+ end
28
+
29
+ DiagnosticSpecTask.new(:spec) do |task|
30
+ task.rspec_opts = "#{task.rspec_opts} --require ./spec/support/gate_failure_formatter.rb --format progress --format GateFailureFormatter"
31
+ end
14
32
  task :silence_rspec_filter_banner do
15
33
  ENV['PWN_RAKE_SPEC'] = '1'
16
34
  end
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),
data/bin/pwn_setup CHANGED
@@ -58,7 +58,7 @@ pwn_driver = PWN::Driver::Parser.new do |options|
58
58
  end
59
59
 
60
60
  options.on('--[no-]cron',
61
- '<Optional - Enable (default) or disable OS-native persistence of pwn-ai cron jobs>') do |o|
61
+ '<Optional - Enable (default) or disable the built-in pwn-ai cron worker; no OS scheduler changes>') do |o|
62
62
  opts[:cron] = o
63
63
  end
64
64
  end
@@ -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(
data/build_gem.sh CHANGED
@@ -44,7 +44,7 @@ if [[ $old_ruby_version == $new_ruby_version ]]; then
44
44
  export rvmsudo_secure_path=1
45
45
  rvmsudo /bin/bash --login -c "cd ${pwn_root} && ./reinstall_gemset.sh"
46
46
  cd /tmp && cd "$pwn_root"
47
- rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake"
47
+ ruby "${pwn_root}/scripts/build_gate.rb" rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake"
48
48
  rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake install"
49
49
  rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake rerdoc"
50
50
  echo "Invoking bundle-audit Gemfile Scanner..."
@@ -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) |
@@ -0,0 +1,82 @@
1
+ # Shared recovery backup retention
2
+
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.
8
+
9
+ ## Producers
10
+
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 |
20
+
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.
25
+
26
+ ## Admission, locking and failures
27
+
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.
34
+
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.
44
+
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.
50
+
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.
58
+
59
+ ## Upgrade and legacy relocation
60
+
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.
66
+
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)
@@ -2,17 +2,26 @@
2
2
 
3
3
  `PWN::Cron` (`lib/pwn/cron.rb`) stores job definitions in
4
4
  `~/.pwn/cron/jobs.yml`. `pwn setup` starts (or reuses) a background cron
5
- worker and installs an OS-native keep-alive so YAML jobs fire on schedule
6
- without a per-job crontab line:
7
-
8
- - Linux: `systemd --user` (Restart=always), else `crontab @reboot`
9
- - macOS: `launchd`
10
- - Windows: `schtasks`
11
- - otherwise: `crontab @reboot` when `crontab(1)` exists
12
-
13
- You can still pass `install_crontab: true` on `cron_create` to add a
14
- per-job system line. `PWN::Cron.install_worker_crontab` is idempotent and
15
- does not remove existing per-job lines.
5
+ worker. Setup, migration and startup only seed internal job definitions;
6
+ they never install, start, repair or remove native scheduler units. After a
7
+ reboot, start/reuse the portable worker with `pwn setup --cron`; automatic
8
+ jobs require that worker, while manual commands do not. `--no-cron` disables
9
+ the internal worker without changing host services.
10
+
11
+ **Housekeeping must never use systemd or another native scheduler.**
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
+ installer explicitly skip housekeeping (`housekeeping_internal_only`),
15
+ including recognizable cleanup payloads under another name. Native worker
16
+ contexts exclude housekeeping from ticks and direct job execution.
17
+
18
+ Generic scheduler APIs remain for unrelated custom jobs: `install_crontab:
19
+ true` can still add their per-job system lines. They are not called by setup,
20
+ migration or default seeding. Existing housekeeping units/drop-ins are left
21
+ untouched and require explicit operator approval for removal; they are never
22
+ re-enabled automatically. See [Installation](Installation.md) for the legacy
23
+ service migration boundary. Do not create another worker or run cleanup just
24
+ to validate scheduling.
16
25
 
17
26
  ![Cron scheduling](diagrams/cron-scheduling.svg)
18
27
 
@@ -34,33 +43,53 @@ does not remove existing per-job lines.
34
43
  | `cron_enable` / `cron_disable` | Toggle without deleting |
35
44
  | `cron_remove` | Delete from `jobs.yml` (does **not** scrub crontab - `crontab -e` yourself) |
36
45
 
37
- ## Seeded self-improvement jobs (`PWN::Cron.install_defaults`)
38
-
39
- `pwn setup --migrate` and first-launch default installation seed six jobs into a fresh
40
- `~/.pwn/cron/jobs.yml` via `PWN::Cron.install_defaults`:
41
-
42
- | Name | Schedule | Enabled | Ruby |
43
- |---|---|---|---|
44
- | `curriculum_practice_nightly` | `0 3 * * *` | **no** | `Curriculum.practice(limit: 3)` - opt-in; runs `Loop.run` with real tools |
45
- | `curriculum_offline_judge` | `30 3 * * *` | **no** | `Curriculum.offline_judge(...)` - opt-in; spends API tokens |
46
- | `curriculum_train_weekly` | `0 4 * * 0` | **no** | `Curriculum.train_and_gate(dry_run: true)` - opt-in; needs trainer+GPU to actually train |
47
- | `session_cleanup` | `45 3 * * *` | **yes** | Guarded unsummarized-session cleanup with backups, dependency checks and activity leases; see [Installation](Installation.md) |
48
- | `learning_consolidate_nightly` | `0 5 * * *` | **yes** | `Learning.consolidate` - memory GC |
49
- | `pwn_stores_lean_nightly` | `15 5 * * *` | **yes** | `Learning.gc_stores!` - lean memory, learning.jsonl, mistakes, policy, sessions |
46
+ ## Default housekeeping and explicit curriculum opt-in
47
+
48
+ `PWN::Cron.install_defaults` seeds **one** enabled job on a fresh install:
49
+ `session_cleanup`, daily at `45 3 * * *`, guarded until its first scheduled slot.
50
+ It calls `SessionCleanup.coordinate` through the existing internal worker:
51
+
52
+ 1. Shared maintenance: preflight, evidence migration, protected-session cleanup,
53
+ and disposable storage maintenance.
54
+ 2. Memory consolidation (`Learning.consolidate`, including Memory lean).
55
+ 3. Learning outcome retention (`Learning.prune_outcomes!`).
56
+ 4. Mistakes retention (`Mistakes.lean!`).
57
+ 5. Policy trajectory retention (`Policy.lean!`).
58
+
59
+ The coordinator holds the permanent maintenance lock across these phases and
60
+ records their separate results. Blocked/incomplete maintenance skips retention;
61
+ dry-run does not consolidate memory or mutate stores. Store writer locks and
62
+ existing gold/recent/protected-entry policies remain in force. It does not call
63
+ `gc_stores!`, which would repeat memory consolidation and session cleanup.
64
+
65
+ Existing jobs are preserved verbatim, including disabled/custom schedules and
66
+ legacy curriculum aliases. Existing `learning_consolidate_nightly` and
67
+ `pwn_stores_lean_nightly` are **not** removed automatically. If either exists
68
+ when a missing session job is seeded, that job uses maintenance-only payloads
69
+ rather than duplicate retention. Consolidate those live schedules only after
70
+ all writers and the matched helper have been safely upgraded and tested.
71
+
72
+ Curriculum schedules are **not seeded at all**, including after deletion,
73
+ startup, or repeat migration. `CURRICULUM_JOBS` contains optional templates for
74
+ `curriculum_practice_nightly`, `curriculum_train_weekly`, and
75
+ `curriculum_offline_judge`. Explicitly create a selected template only when
76
+ wanted; templates default disabled:
50
77
 
51
- - **practice / judge / train** ship **disabled**. Practice self-plays via `Loop.run` (shell, browsers). Judge hits the live model. Train is a no-op without a LoRA trainer. Enable with `cron_enable` when you want that loop.
52
- - **consolidate + lean + session cleanup** stay on: they operate on local `~/.pwn` state; cleanup can delete eligible transcripts after verified backups and guards, so the injected MEMORY / LEARNING / session tail stays high-signal.
53
-
54
- - **practice** - top unresolved `Mistakes` under `Reward.judge`; auto-`resolve` with ≥2 holdouts
55
- - **offline_judge** - backfill outcome/process labels + plan calibration from PLAN `p(success)=` so `:failure_only` local introspect does not starve the corpus; also runs `Reward.warm_sentinel` so the reward-sentinel window can fill on local hosts
56
- - **dedupe** - legacy alias `offline_judge_nightly` counts as the same job; `install_defaults` will not double-seed `30 3 * * *` and disables the alias when both exist
57
- - **train** - export SFT + balanced DPO, LoRA-train `pwn-vN+1`, replay Mistakes.top, promote only on win. `dry_run: false` only with a trainer+GPU
58
- - **consolidate** - memory GC (semantic merge + importance eviction) so the injected MEMORY block stays high-signal
78
+ ```ruby
79
+ spec = PWN::Cron::CURRICULUM_JOBS.find { |job| job[:name] == 'curriculum_practice_nightly' }
80
+ PWN::Cron.create(spec.merge(enabled: false, sync_native: false))
81
+ ```
59
82
 
60
- See [Reinforcement Learning](Reinforcement-Learning.md).
83
+ Practice runs real tools, judge can spend provider tokens, and training needs
84
+ its own explicit configuration/approval. See [Reinforcement Learning](Reinforcement-Learning.md).
61
85
 
62
- `cron_disable(id:)` turns either off; `install_defaults` is idempotent (including legacy alias collapse) and
63
- never overwrites a job you already have with the same name.
86
+ For schedule-only deletion without touching native schedulers, use
87
+ `PWN::Cron.remove(id: exact_id, sync_native: false)`. It preserves job logs and
88
+ other evidence. Registry writes use a permanent lock and atomic replacement;
89
+ completion updates merge into the current registry and never resurrect a deleted
90
+ job. Old workers without this protocol must drain safely before a registry edit;
91
+ never use a forced stop while a job is writing. A current worker reloads the
92
+ registry each tick; ordinary schedule changes need no restart.
64
93
 
65
94
  ## Example
66
95
 
@@ -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 |