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
@@ -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
  |   ↳ [BurpSuite](BurpSuite.md) ⭐ | Preferred web proxy / scanner |
56
56
  |   ↳ [TransparentBrowser](Transparent-Browser.md) | Headless / visible browser automation |
57
57
  |   ↳ [NmapIt](NmapIt.md) | Network discovery |
58
58
  |   ↳ [Metasploit](Metasploit.md) | RPC exploitation |
59
59
  |   ↳ [Fuzzing](Fuzzing.md) | `Fuzz` · `Sock` · `Packet` |
60
60
  |   ↳ [Hardware](Hardware.md) | Serial · BusPirate · MSR206 · Android · BareSIP |
61
+ |   ↳ [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 |
@@ -20,7 +20,7 @@ hardware).
20
20
  | `pwn --ai PROMPT` | `bin/pwn` | Headless one-shot agent (CI-friendly) |
21
21
  | `pwn setup` | `lib/pwn/setup.rb` · `bin/pwn_setup` | Post-install doctor + capability provisioner + `--migrate` state doctor (also `pwn --setup[=PROFILE]`) |
22
22
  | `bin/` (including `pwn-ai` and `pwn-capd`) | 56 files | Thin OptionParser wrappers plus the `pwn` REPL / one-shot agent |
23
- | `PWN::Cron` | `lib/pwn/cron.rb` | Scheduled jobs + background worker (`pwn setup`). Fresh install seeds hygiene on (`learning_consolidate_nightly`, `pwn_stores_lean_nightly`) and curriculum practice/judge/train off |
23
+ | `PWN::Cron` | `lib/pwn/cron.rb` | Scheduled jobs + background worker (`pwn setup`). Fresh install seeds one `session_cleanup` housekeeping coordinator; curriculum schedules require explicit creation. Existing schedules are preserved |
24
24
 
25
25
  ## L2 - AI agent core (`lib/pwn/ai/agent/`)
26
26
 
@@ -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
 
@@ -1,5 +1,190 @@
1
1
  # Installation
2
2
 
3
+ ## Housekeeping deployment and diagnostics
4
+
5
+ The packaged `etc/default_cron/session_cleanup.rb` is the shared implementation
6
+ for `/maintenance run`, `/maintenance dry-run`, `/sessions cleanup`, and the
7
+ managed `session_cleanup` cron job. `/maintenance status` reports physical file
8
+ counts, bytes, configured retention, budgets, incomplete scans and safe error
9
+ classes. These are storage counts, not the session menu's pagination count.
10
+ Curses executes maintenance on its existing command worker, not its render/input
11
+ thread. Ctrl+C requests a safe boundary; committed work is not rolled back.
12
+
13
+ Upgrade the installed gem, run `pwn setup --migrate --fix`, then restart PWN
14
+ clients **and the existing cron worker**. Source-checkout edits alone do not
15
+ update installed endpoints. Schema 11 seeds a version-1 housekeeping policy and
16
+ refreshes the managed helper. Existing job IDs, schedules, disabled states,
17
+ custom Ruby payloads, custom unmarked helpers and policy edits are preserved.
18
+ A custom helper is not automatically upgraded. Fresh setup is idempotent and
19
+ registration never runs cleanup; the default job retains its next-slot guard.
20
+ Startup performs fixed-path checks and coalesces a daily background request in
21
+ `maintenance/background.json`. The existing cron worker consumes it through the
22
+ existing enabled job, never through a second worker. Disabled jobs stay disabled.
23
+ Do not apply maintenance while old untracked clients are running: unknown
24
+ legacy ownership blocks mutation rather than guessing which sessions are active.
25
+
26
+ Storage is organized into logical categories without relocating literal evidence
27
+ anchors:
28
+
29
+ | Category | Existing/new locations under `~/.pwn` | Retention contract |
30
+ |---|---|---|
31
+ | sessions | `sessions/` including support and requests | Eligible unsummarized, inactive, unreferenced transcripts only; physical deletion with backups |
32
+ | learning | `learning.jsonl`, `learning/`, `memory*.json`, `mistakes.json`, policy/curriculum/extrospection stores, finetune and preference records | Evidence migration retains exact verified snapshots; unknown records remain protected |
33
+ | artifacts | `artifacts/`, `exports/`, `findings.jsonl`, engagements, corpora and binary workspaces | Durable evidence, not disposable cache |
34
+ | state | `swarm/`, `cron/`, `agents.yml`, `jobs/`, `missions/`, `runs/`, metrics | Preserve continuation associations and active work |
35
+ | logs | `logs/` | Only explicitly registered disposable closed logs are eligible; unknown logs remain |
36
+ | cache | `cache/` | Only producer-registered, checksum-verified, inactive disposable files |
37
+ | tmp | `workspaces/runtime/` | Same registration contract; never glob-delete arbitrary temp files |
38
+ | maintenance | `maintenance/`, legacy `session_cleanup_archive/` | Policy, permanent locks, metadata ledgers and recoverable quarantine |
39
+
40
+ `maintenance/policy.json` is non-secret JSON, version 1. `batch_size` defaults to
41
+ 100; `scan_limit` to 100000. Cooperative admission defaults to `work_items: 100000`,
42
+ `work_seconds: 30` and `work_bytes: 67108864` for dependency and maintenance
43
+ payload reads, plus reserved disposable backup writes. Existing policy
44
+ files acquire these defaults in memory, without rewriting operator values.
45
+ `maintenance/layout.json` upgrades recognized inventory v1 or v2 to v3 separately from
46
+ policy; unknown inventory versions are refused. `retention_days` defaults to sessions 0, logs 30,
47
+ cache 7, tmp 1, maintenance 90. Null disables that category's expiry. Session
48
+ age is an additional floor, never an override of leases, provenance or evidence.
49
+ Learning/artifacts/state remain non-expiring: their policy slots are descriptive,
50
+ not authorization to erase evidence. `budget_bytes` enforces pressure eviction
51
+ for verified inactive disposable files (logs 256 MiB, cache 512 MiB, tmp 256 MiB,
52
+ maintenance 1 GiB), including before ordinary expiry. Null retention still
53
+ disables deletion. Budgets never evict protected evidence. Over-budget protected storage needs
54
+ operator review, not automatic deletion. Inventory scans are bounded and report
55
+ incompleteness rather than claiming a complete total. `bytes` counts logical file
56
+ bytes; `unique_bytes` counts each inode once per category. Directory discovery is
57
+ streamed for inventory and dependency trees. Work/time admission is cooperative
58
+ between phases and items, not a hard deadline: an admitted read, hash or atomic
59
+ commit completes. Exhausted/partial dependency indexes block deletion and report
60
+ `incomplete`/`interrupted`. Retrying recomputes dependencies; no timeout kills a writer.
61
+ Disposable cleanup reserves the complete payload transaction before its first
62
+ hash: one bounded read for preview, or five payload-size passes for verification,
63
+ backup read/write, backup verification and final source verification on apply.
64
+ Reads reject size changes and cannot follow unlimited concurrent growth. Quarantine,
65
+ legacy-backup inventory/compaction, atomic-temp recovery and transaction-status
66
+ reads also use byte admission. Policy/cursor metadata and durable ledger writes
67
+ are control overhead, not a hard total-filesystem-I/O quota; directory name
68
+ discovery is not a payload-byte allowance. An oversized item is retained, with
69
+ the overall maintenance run marked `incomplete`/`interrupted`; raise the explicit
70
+ work budget to process it. Once admitted, backup/commit finishes even if the
71
+ deadline expires mid-write.
72
+
73
+ Producer integration uses `PWN::SessionCleanup.register_disposable(path:,
74
+ category:, expires_at:)` after closing a reproducible file. Producers must hold
75
+ its permanent `<path>.lease` exclusively while writing, and readers hold it
76
+ shared throughout use. The registry records exact SHA-256, inode and byte count.
77
+ Changed, unknown, symlinked, active and invalid files are retained with reasons.
78
+ Disposable scans persist a lexical round-robin cursor in
79
+ `maintenance/disposable.cursor`; `scan_limit` bounds inspected manifests per run.
80
+ Completed/missing manifests remain for audit, including normal temporary-producer
81
+ unlinks, but cannot monopolize later scans. Active/changed entries are revisited
82
+ on the next circuit. The cursor is scheduling state only: cleanup never removes
83
+ or relocates registry entries, and rechecks registration bytes under the producer
84
+ lease so a concurrent renewal cannot authorize deletion using an old expiry.
85
+ Dry-run does not advance the cursor. A partial circuit reports `incomplete`.
86
+ Registration is not permission to classify evidence as disposable. Legacy caches
87
+ and logs are not implicitly registered. Transcript compaction/append instead use
88
+ a permanent transcript `.write.lock`; compaction temporary files are unique,
89
+ cleaned in ensure, fsynced and atomically replaced. Corrupt transcripts raise,
90
+ not silently compact into partial transcripts.
91
+
92
+ Ghidra decompile and BinaryParser triage now publish and read their canonical
93
+ caches under permanent leases. Ordinary `Log.append` diagnostics now go to
94
+ `logs/pwn.log`, rotating at 1,024,000 bytes into registered closed segments.
95
+ Debug streams hold a lifetime lease and register after request closure; active
96
+ streams cannot be evicted. Historical `/tmp/pwn.log` and learning logs are not
97
+ implicitly deleted. Cache writes and transcript compaction clean their own
98
+ temporary outputs on failure rather than leaving routine temp garbage.
99
+
100
+ Routine disposable cleanup writes exact private backups and prepared/committed
101
+ ledgers under `maintenance/quarantine/`. Only verified committed disposable
102
+ backups expire after the maintenance retention period or under budget pressure.
103
+ Interrupted `expiry_prepared` operations resume from verified remaining backups.
104
+ Broad operations also have prepared/committed/interrupted metadata ledgers under
105
+ `maintenance/runs/`, exposed by status. Prepared/corrupt ledgers
106
+ and unique evidence are never expired. `prune_legacy_backups` recognizes actual
107
+ committed session snapshots, dependency-removal backups, learning migration and
108
+ continuation-retirement ledgers. After retention or under maintenance pressure it
109
+ reclaims **redundant physical bytes** using checksum/readback-verified atomic
110
+ hardlink replacement. Recovery filenames remain intact; the last copy is never
111
+ removed. Unknown/uncommitted records are excluded. A cross-filesystem link failure
112
+ retains the original. Prepared/committed compaction journals are resumable.
113
+
114
+ `PWN::SessionCleanup.recovery_status(ledger: absolute_ledger_path)` identifies
115
+ supported actions. `recover(ledger:, action:, dry_run: true)` is explicit:
116
+ - `restore`: quarantine or legacy session copies, with no overwrite of different bytes;
117
+ - `rollback`: Learning's validated rollback or an unchanged continuation retirement;
118
+ - `rollback_dependencies`: exact targeted store rollback, after restoring source sessions;
119
+ - `resume`: re-evaluate broad maintenance, resume backup compaction, or re-evaluate
120
+ offline layout migration (apply requires `writers_stopped: true`).
121
+ Use `dry_run: false` only after reviewing the plan. Restoring a metadata ledger
122
+ never undoes data changes. Broad operations have independently committed children,
123
+ not a fictitious all-or-nothing rollback. Unknown schemas require manual review.
124
+ `reorganize` remains explicit and offline; this upgrade does not move existing paths.
125
+
126
+ ### Housekeeping acceptance ledger (isolated fixtures)
127
+
128
+ `spec/lib/pwn/session_cleanup_recovery_spec.rb` exercises the actual maintenance
129
+ entry point, without live stores or providers:
130
+
131
+ | Requirement | Regression evidence | Boundary |
132
+ |---|---|---|
133
+ | Byte admission precedes disposable payload I/O | Expired 1 MiB cache with `work_bytes: 1024` remains intact; no payload read/hash or backup, run incomplete/interrupted | Cooperative allowance, not a hard filesystem-I/O cap |
134
+ | Repeated bounded scans progress | `scan_limit: 1`, two expired caches and a normally unlinked temporary producer; repeated runs delete both caches and retain all manifests | Registry pathname discovery still enumerates names; inspection is bounded |
135
+ | Active/mutated data remains protected | Held shared lease and changed payload survive while later eligible data is processed; released lease is revisited | Requires cooperating permanent producer leases |
136
+ | Re-registration is not lost | Renewing the same file between discovery and lease acquisition retains it; republished cache survives a later cursor circuit | Cursor never moves/deletes registry records |
137
+ | Admitted commit finishes safely | Deadline expires after backup publication; exact backup and committed child deletion survive, enclosing run is interrupted | Not all-or-nothing rollback |
138
+ | Related quarantine reads respect admission | Expired 1 MiB backup exceeds an 8 KiB work allowance and remains committed/unexpired | Legacy/atomic paths also admit reads; their existing integrity/recovery regressions remain enabled |
139
+
140
+ The completed isolated-HOME `bundle exec rake` gate passed 4,568 examples with
141
+ zero failures and inspected 1,161 files with no RuboCop offenses (exit 0).
142
+ Cancellation regressions distinguish preservation before deletion admission from
143
+ completion of an already-admitted commit; redaction covers the canonical log and
144
+ debug/progress/mirror/stderr/fallback sinks. These are fixture-based verification,
145
+ not live deployment acceptance: no live-store maintenance, installed-helper
146
+ deployment or provider run is implied.
147
+
148
+ ### Temporary producer ownership audit
149
+
150
+ The audit covers `Dir.tmpdir`, `Tempfile`, `mktmpdir` and atomic replacement
151
+ call sites, not a filesystem filename sweep:
152
+ - Festival speech input now uses `SessionCleanup.temporary` in `~/.pwn/workspaces/runtime`, with
153
+ a lifetime lease and exact registration. Concurrent calls have unique names;
154
+ normal completion unlinks the input and a crashed consumer leaves registered bytes.
155
+ - Ghidra/BinaryParser canonical cache publication and managed temporary inputs
156
+ journal atomic ownership (inode/device, expected hash, exact target) under
157
+ `maintenance/atomic`. `recover_atomic_temps` rejects active writers, changed or
158
+ partial bytes, missing journals and unknown paths. Routine maintenance applies
159
+ retention; explicit recovery can inspect an abandoned journal immediately.
160
+ - Sandbox/Detonate, GPS, RFID, OSINT bridge and GhidraHeadless work directories,
161
+ GDB stdin, and policy-evaluation/Solve verification trees are scoped block or
162
+ owner-cleaned resources. Old unregistered crash remnants are retained, not
163
+ inferred disposable from a prefix. GhidraHeadless's legacy/custom cache is not
164
+ silently enrolled as a canonical cache.
165
+ - Nmap/Nuclei scan output, proxy HAR, browser screenshots, APK output, Workspace
166
+ downloads, history spill and Burp diagnostics may be unique evidence. Their
167
+ existing literal paths remain; no age-based global `/tmp` cleanup is authorized.
168
+ - OAuth/vault/selection, learning/session/map and SDR image atomic outputs may be
169
+ only-copy state or evidence. Their own ensure/rollback protocols remain; unknown
170
+ remnants are not automatically discarded. Solve lock inodes are permanent.
171
+ This is an intentional ownership/evidence boundary, not a claim that every plugin
172
+ scratch file is automatically reclaimable.
173
+
174
+ Swarm registry retirement alone deliberately keeps its saved conversation.
175
+ The command route exposes `/swarm retire NAME --continuation` explicitly.
176
+ `PWN::AI::Agent::Swarm.retire(name:, swarm_id:, local: true, continuation: true)`
177
+ explicitly retires the inactive association with a backup and prepared/committed
178
+ ledger. It does not itself delete the transcript. A later persona request creates
179
+ a new session; active associations cannot be retired. Persona map writers and
180
+ cleanup share a permanent continuation lock.
181
+
182
+ Safe checks: `/maintenance status` and `/maintenance dry-run` do not call a
183
+ provider. Do not use `run` as a diagnostic on production data without reviewing
184
+ the preview. Permission and disk failures retain recoverable bytes and report
185
+ error classes; no blanket root-directory chmod is attempted.
186
+
187
+
3
188
  PWN ships as a **single Ruby gem** whose runtime is 100 % `autoload`ed -
4
189
  a plugin whose native extension or OS binary is missing costs nothing until
5
190
  you actually touch that constant. That means the install is **two steps**:
@@ -82,7 +267,7 @@ External toolchain used by
82
267
  skills/ drift 1 legacy flat file(s) PWN::Config → run --migrate --fix
83
268
  ...
84
269
 
85
- cron worker ok systemd --user pid=...
270
+ cron worker ok internal worker pid=...
86
271
 
87
272
  31 / 36 capabilities usable · 5 degraded
88
273
 
@@ -130,7 +315,7 @@ pwn setup --list-profiles
130
315
  | `vision` | `OCR` · `ScannableCodes` · `Reports` · `extro_vision` · tesseract / zbar / graphviz |
131
316
  | `voice` | `PWN::Plugins::Voice` · `extro_voice` · espeak-ng / sox |
132
317
  | `exploit` | `Metasploit` · `sqlmap` |
133
- | `hardware` | `Serial` · `BusPirate` · `Android` · `BareSIP` · `extro_serial` · `extro_telecomm` |
318
+ | `hardware` | `Serial` · `BusPirate` · `Android` · `BareSIP` · `YubiKey` (`ykman`) · `extro_serial` · `extro_telecomm` |
134
319
  | `re` | `Radare2` · `GDB` · `ExploitDev` · `Frida` · Android RE (`apktool`/`jadx`) · `pwndbg` · `ropper` · `rizin` |
135
320
  | `full` | everything above |
136
321
 
@@ -180,7 +365,8 @@ What `--migrate` does:
180
365
  (`PWN::Migrate::STATE_FILES` - no re-implemented parsers). If the raw
181
366
  file has bytes but the owner returned its empty-fallback, the file is
182
367
  flagged incompatible.
183
- 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))
184
370
  → ordered schema migrations → per-file repair (quarantine corrupt to
185
371
  `~/.pwn/quarantine/`, re-seed missing dirs, strip unparsable JSONL lines,
186
372
  convert legacy flat skills to the [agentskills.io](https://agentskills.io)
@@ -196,9 +382,11 @@ predates the running gem (`PWN::Migrate.needed?`).
196
382
  Schema `v1` also seeds `PWN::Cron.install_defaults` - the nightly
197
383
  `curriculum_practice` and weekly `curriculum_train` self-improvement jobs
198
384
  (see [Reinforcement Learning](Reinforcement-Learning.md)). `pwn setup`
199
- also starts the background cron worker and persists it via the OS scheduler
200
- (systemd --user / launchd / schtasks / crontab) so those YAML jobs run
201
- without a per-job crontab line. Live Policy files (`policy.json`,
385
+ also starts or reuses the single portable background cron worker. Setup,
386
+ migration and startup seeding never install, enable, repair or remove OS
387
+ scheduler units. Defaults live only in `~/.pwn/cron/jobs.yml`; the worker
388
+ must be running for automatic execution (start it again after reboot with
389
+ `pwn setup --cron`). Manual commands remain available without a worker. Live Policy files (`policy.json`,
202
390
  `policy_traj.jsonl`) are created on first agent turn when `ai.agent.policy`
203
391
  is on.
204
392
 
@@ -208,8 +396,42 @@ in the scheduler's local timezone. First launch also seeds it through
208
396
  is installed to `~/.pwn/cron/session_cleanup.rb`; managed copies refresh on
209
397
  subsequent launches/migrations, while unmarked custom helpers are preserved.
210
398
  An existing job named `session_cleanup` keeps its ID, schedule, enabled state,
211
- payload and native scheduler drop-ins. New jobs defer cleanup until their first
212
- scheduled time, rather than deleting during initial catch-up.
399
+ payload. Legacy native scheduler files are left untouched for an explicit
400
+ operator-approved removal; they are never refreshed or re-enabled by setup.
401
+ New jobs defer cleanup until their first scheduled time, rather than deleting
402
+ during initial catch-up.
403
+
404
+ Fresh installs now seed only this job, with `SessionCleanup.coordinate`: shared
405
+ evidence-first maintenance, memory consolidation, then separate learning,
406
+ mistakes and policy retention phases. Existing split maintenance schedules are
407
+ preserved until explicitly consolidated after a safe matched-code deployment.
408
+ Curriculum schedules require explicit creation; startup and repeat migrations
409
+ never recreate deleted curriculum jobs. See [Cron](Cron.md) for opt-in templates
410
+ and schedule-only deletion. Already-running old binaries must be upgraded before
411
+ their startup/config-refresh hooks can be relied on to honor the new defaults.
412
+
413
+ **Housekeeping never uses systemd or any other native scheduler.**
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
418
+ installers explicitly skip them with `housekeeping_internal_only`; this also
419
+ covers renamed payloads that invoke `SessionCleanup` or `session_cleanup.rb`.
420
+ Generic scheduler APIs remain available for unrelated custom jobs. Native
421
+ worker launchers mark their execution context and exclude housekeeping;
422
+ legacy PWN systemd cgroups are also excluded. An inherited terminal
423
+ `INVOCATION_ID` alone is not a scheduler context.
424
+
425
+ Existing installations require an explicit operator decision before stopping
426
+ legacy services or processes. Inventory `pwn-cron-*.timer` and `.service`, map
427
+ job IDs to `jobs.yml`, preserve custom schedules, then (only with approval)
428
+ disable and stop the housekeeping timers/services and the legacy shared
429
+ worker service, archive their unit files/drop-ins, remove those exact files,
430
+ and run the user manager's daemon-reload. Do not remove unrelated custom job
431
+ units or change lingering globally. Coordinate old clients before deploying
432
+ the new code, retain `jobs.yml` unchanged, then start/reuse one internal
433
+ worker and verify its PID and guarded tick. Do not run cleanup to validate
434
+ this transition or re-enable legacy housekeeping units.
213
435
 
214
436
  Cleanup calls a session named only when its persisted metadata has
215
437
  `title_source: summary`, a nonblank title and summary, and a positive integer
@@ -321,6 +543,19 @@ Loop requests and title writers hold independent leases, and crashes release
321
543
  descriptors automatically. Stale lock contents or reused PIDs confer no ownership.
322
544
  Never unlink lock files: that would split the synchronization inode.
323
545
 
546
+ Routine maintenance supports multiple simultaneously running upgraded clients;
547
+ there is no blanket shutdown requirement. Evidence migration takes exclusive
548
+ nonblocking leases per source transcript through index publication, skips active
549
+ sources (`evidence_migration.deferred_active_sessions`), and revisits them on a
550
+ later run after their writers finish. Unrelated eligible sessions can still be
551
+ removed. A busy Learning writer returns
552
+ `evidence_migration.deferred: learning_writer_active` with `apply_blocked: true`
553
+ rather than waiting or interrupting the writer; retry on a later cron tick or
554
+ manual invocation. The permanent maintenance lock admits one applying owner;
555
+ a second receives `Session maintenance already running` immediately. Title
556
+ workers and durable job dependencies retain their existing protections.
557
+ This is routine maintenance, not the explicitly offline layout migration.
558
+
324
559
  Old installed processes cannot be upgraded by refreshing the cron helper alone.
325
560
  `legacy_owners` reports launchers without the held PID/start-identity capability
326
561
  marker; `unknown_legacy_ownership` means their in-memory session IDs are unknown,
@@ -347,6 +582,15 @@ rake install # or: rvmsudo rake install (multi-user RVM)
347
582
  pwn setup --profile full --yes # same provisioner, same data tables
348
583
  ```
349
584
 
585
+ The `upgrade_pwn.sh` → `build_gem.sh` validation gate still runs through
586
+ `rvmsudo` on multi-user RVM installations. Its merged stdout/stderr is streamed
587
+ and retained in the invoking user's `~/.pwn/logs/build-gate.log` (mode 0600,
588
+ last 1 MiB, replaced on the next gate). The path is printed before execution
589
+ and on failure; installation does not continue after a failed gate. `rake spec`
590
+ also prints each failure immediately and reports the RSpec child's exit status
591
+ or signal, so a later terminated example cannot hide earlier failure details.
592
+ A signal report is diagnostic evidence, not a claim about who sent the signal.
593
+
350
594
  The legacy `./install.sh` / `packer/provisioners/pwn.sh` bash paths still
351
595
  work, but they now simply delegate to `pwn setup` - the `case $os` package
352
596
  lists have been consolidated into `PWN::Setup::NATIVE_GEMS` / `::TOOLCHAIN`.
@@ -439,7 +683,7 @@ row as `MISSING` until a key is set.
439
683
  Everything above is a thin CLI over two autoloaded modules:
440
684
 
441
685
  ```ruby
442
- 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: }
443
687
  PWN::Setup.deps(profile: :web, yes: true) # install, then re-check
444
688
  PWN::Setup.list_profiles
445
689
  PWN::Setup.migrate(fix: true) # thin wrapper → PWN::Migrate.run
@@ -474,7 +718,7 @@ Vagrant, CI) picks it up automatically.
474
718
  ```ruby
475
719
  pwn[CURRENT_VERSION]:001 >>> PWN::Setup.check[:ok] # => true
476
720
  pwn[CURRENT_VERSION]:002 >>> PWN::Migrate.needed? # => false
477
- pwn[CURRENT_VERSION]:003 >>> PWN::Plugins.constants.count # => 107
721
+ pwn[CURRENT_VERSION]:003 >>> PWN::Plugins.constants.count # => 108
478
722
  pwn[CURRENT_VERSION]:004 >>> PWN::SAST.constants.count # => 48
479
723
  pwn[CURRENT_VERSION]:005 >>> pwn-ai # launches agent TUI
480
724
  ```
@@ -0,0 +1,134 @@
1
+ # Mission process lifetime and cancellation
2
+
3
+ ## Implemented boundary
4
+
5
+ The curses ordinary-request path, local slash-command workers, each curses
6
+ swarm job, and the non-curses `pwn_ai_run_steerable` entrypoint execute provider,
7
+ controller, and tool work
8
+ in a mission-owned Linux PID namespace. CTRL+C kills
9
+ namespace PID 1 through a pidfd, waits for its supervisor to reap it, joins the
10
+ IPC reader, and only then releases the UI's running state. A process group is
11
+ not the ownership boundary: setsid, double forks, and nested child namespaces
12
+ remain descendants of the mission namespace. Other sessions and unrelated
13
+ processes are not signalled.
14
+
15
+ There is no systemd, cgroup, application compiler, or PGID fallback.
16
+ `PWN::AI::Agent::MissionNamespace` is Ruby code using stdlib Fiddle for Linux pidfds and
17
+ process-death signals. Its runtime prerequisite is **util-linux `unshare`**
18
+ (`pwn setup --profile ai`). Linux user/PID namespaces and pidfds must be enabled;
19
+ unsupported kernels or local policy fail closed with setup guidance.
20
+
21
+ Ruby recreates its timer thread after fork, so creating a user namespace from a
22
+ Ruby fork is not supported. For ordinary launches the controller **execs** an
23
+ `unshare --user --map-current-user --keep-caps` supervisor. That fresh Ruby
24
+ supervisor creates a PID namespace and forks its init. It passes a pidfd back
25
+ through a private Unix socket; the init waits for acknowledgement before running
26
+ any request. Killing the supervisor also kills its init through PDEATHSIG.
27
+ The controller, its cwd, and unrelated embedded PWN REPL modes are unchanged.
28
+
29
+ Curses/native, legacy, slash, swarm and one-shot CLI requests use named,
30
+ serializable descriptors, not serialized Ruby closures. Configuration, loaded
31
+ skill metadata and routing context travel as typed JSON over inherited private
32
+ sockets, never in argv or plaintext staging files. RequestRuntime's tool-worker
33
+ snapshot excludes locks and caller cancellation callbacks; the enclosing owner
34
+ provides cancellation. Replies and events remain JSON with generation fencing.
35
+ Loaded Ruby skill scripts are required in the worker. Embedders with additional
36
+ trusted plugin initializers can explicitly set
37
+ `PWN::AI::Agent::MissionNamespace.bootstrap_requires = ['/absolute/plugin/bootstrap.rb']`.
38
+ Arbitrary monkey patches, IO objects and Proc values are not serializable.
39
+
40
+ For applications explicitly launched in a single-identity user namespace,
41
+ MissionNamespace can fork its PID supervisor directly. This preserves trusted
42
+ in-memory Ruby closures without calling unshare(NEWUSER) from Ruby. The caller
43
+ must bootstrap explicitly with `unshare --user --map-current-user --keep-caps`;
44
+ there is no automatic re-exec of an embedded application. The unit-test runner
45
+ uses this real-kernel mode for in-memory mocks; exec/PTy fixtures explicitly
46
+ force the ordinary descriptor path. Both paths create a real per-mission PID
47
+ namespace and reject unavailable containment.
48
+
49
+ Ordinary-request events carry a generation; swarm child messages carry job
50
+ IDs, and console swarm callbacks carry an epoch invalidated by CTRL+C.
51
+ Provider calls, including ordinary-request title refresh, remain in the child.
52
+ Steering uses a separate JSON control pipe. The UI never deserializes Ruby
53
+ objects from the child.
54
+
55
+ ## Detached jobs
56
+
57
+ Detached jobs started by a tool inside a mission inherit that mission's
58
+ namespace. They can continue after the individual tool returns, but **not**
59
+ after mission completion, cancellation, or namespace-owner death. Normal
60
+ mission completion also tears down remaining detached descendants. This is
61
+ intentionally different from cross-session durable jobs. A standalone
62
+ `AISandbox.protect_call` without an enclosing mission creates its own namespace
63
+ and ends that lifetime when the call ends.
64
+
65
+ Existing files and already-submitted remote operations are not rolled back.
66
+ The local process guarantee does not revoke work accepted by an external
67
+ service or remote host. Kernel-blocked uninterruptible I/O may delay reaping;
68
+ the UI must remain busy rather than claim readiness early.
69
+
70
+ ## Controller-state separation
71
+
72
+ Tool workers are denied writes to their RequestRuntime artifact/checkpoint
73
+ root, including its spill directories and inherited descriptors. The actual
74
+ worker-symlink -> parent-spill reproducer now fails at the worker's symlink
75
+ with permission denied, and the synthetic source directory remains unchanged.
76
+ The audited controller stores (sessions/traces, transcript artifacts,
77
+ learning/policy/metrics, skills, logs, vault/configuration and mission state) are
78
+ also excluded from worker grants. See the finite writer checklist in
79
+ [Runtime-Source-Protection](Runtime-Source-Protection.md). Unspecified tool
80
+ writes stay in `~/.pwn/workspaces/runtime`; another path is allowed only when
81
+ the original operator request names it. Naming a missing file grants its parent,
82
+ including when that parent contains unrelated unreadable directories, so the
83
+ named file can be created. Controller state and tool output
84
+ workspaces must be disjoint: a parent directory containing protected state
85
+ cannot safely receive a broad Landlock write grant.
86
+
87
+ ## Verification and remaining scope
88
+
89
+ RSpec owns the subprocess fixtures under `spec/support/harness/mission_*.rb`.
90
+ `spec/integration/mission_cancel_spec.rb` exercises ordinary console next-request
91
+ readiness and generation fencing, blocked-provider descendants, setsid escape,
92
+ normal-exit double forks, unrelated-process preservation, mission-lived tool
93
+ jobs, swarm cancellation, and fail-closed bootstrap. The namespace-denial
94
+ fixture injects EPERM; namespace creation/teardown tests use the real kernel.
95
+ `spec/integration/controller_spill_spec.rb` exercises actual RequestRuntime,
96
+ Dispatch, sandbox restriction, and parent spill persistence with synthetic paths.
97
+
98
+ Local slash-command maintenance/model/MCP workers now use the same hard process
99
+ ownership. Their completion and UI-state messages are generation-fenced JSON;
100
+ CTRL+C reaps the owner before returning ready. Maintenance already committed
101
+ before cancellation is not rolled back. Model persistence decrypts and encrypts
102
+ in memory; its staging file contains only ciphertext (or is empty). An offline
103
+ SIGKILL regression interrupts encryption startup and verifies unchanged original
104
+ ciphertext and absence of plaintext in the abandoned stage. Native curses PTY
105
+ coverage includes cancellation during a blocked local command and a successful
106
+ next mission.
107
+
108
+ The legacy regex-ReAct fallback now runs its provider/parser/tool loop in the
109
+ same MissionProcess owner. Completed history is transferred by JSON; native
110
+ containment failures are terminal rather than an excuse to retry uncontained.
111
+ Its actual after_read hook and PTY fixture cover detached Ruby/bash descendants,
112
+ CTRL+C, no late writes and a successful next request. Workspace policy
113
+ clarification does not block these offline gates.
114
+
115
+ Native PTY regressions drive actual terminal CTRL+C through both the curses
116
+ console and `pwn_ai_run_steerable`, assert no late descendant writes, then complete
117
+ a second mission. Compiler-free package acceptance builds and installs into a
118
+ private scratch GEM_HOME with compiler/make commands replaced by failing shims,
119
+ activates that exact specification, checks an empty extensions list and no
120
+ mission shared object, then executes a descriptor. Root and nonroot acceptance
121
+ must be reported separately; these are offline checks, not provider or hardware
122
+ evidence.
123
+
124
+ Root loses host-wide DAC override when it enters a user namespace. A host-root
125
+ mission therefore cannot traverse another user's mode-0700 home merely because
126
+ its parent could. Test roots and deployment workspaces must be traversable by
127
+ the mapped host identity. Do not chmod the operator's home to make a fixture
128
+ pass. Filesystem fixtures beneath a private nonroot HOME must use that identity,
129
+ including when their outer test runner is root. The integrated root fixture uses
130
+ an isolated mount namespace: only its task-owned scratch layout is bound at a
131
+ root-traversable path, with HOME, TMPDIR and inherited stdout/stderr logs opened
132
+ inside that layout. Inherited logs opened below the inaccessible real home would
133
+ correctly fail the protected-descriptor preflight. Neither the host mount table
134
+ nor the operator's directory permissions are changed.
@@ -57,6 +57,23 @@ PKI DMs remain unsupported. Topic `region` is an opaque prefix, not the radio's
57
57
  regulatory-band setting; `radio_index` is distinct from legacy `channel_num`.
58
58
  See [General Usage](General-PWN-Usage.md) and [REPL](pwn-REPL.md).
59
59
 
60
+ ## Current runtime contract
61
+
62
+ These are source contracts. A checkout edit does not change an installed gem or
63
+ a process that is already running. Restart after installing the matching package.
64
+
65
+ | Contract | Current behavior |
66
+ | --- | --- |
67
+ | Tool writes | Unspecified `pwn-ai` writes and `TMPDIR` use `~/.pwn/workspaces/runtime`. Another path is writable only when the original operator request names it, including `mv`/`to`. A missing file grants its parent so the file can be created; that parent is still granted when it contains unrelated unreadable directories. `/opt/pwn` and the loaded package stay read-only. |
68
+ | Mission lifetime | CTRL+C kills the mission through `PWN::AI::Agent::MissionNamespace`. That owner is Ruby plus util-linux `unshare` and Linux pidfds. There is no compiled `pwn_mission` extension and no systemd fallback. |
69
+ | Recovery copies | Every PWN recovery producer uses `PWN::Backups` and `~/.pwn/backup/`, seven generation folders total. Adjacent `.bak` files are not the current producer. |
70
+ | Housekeeping | A fresh install seeds one internal `session_cleanup` coordinator. Curriculum jobs are not seeded. Housekeeping never uses systemd. |
71
+
72
+ Details: [Runtime Source Protection](Runtime-Source-Protection.md),
73
+ [Mission Cancellation](Mission-Cancellation.md),
74
+ [Backup Retention](Backup-Retention.md), [Cron](Cron.md),
75
+ [Persistence](Persistence.md).
76
+
60
77
  ## Deployment and safe verification
61
78
 
62
79
  1. Inspect the intended checkout and preserve existing changes before upgrading.
@@ -7,8 +7,8 @@ external services and environment overrides can place evidence elsewhere.
7
7
 
8
8
  | Path | Owner | Format | Reset tool | Purpose |
9
9
  |---|---|---|---|---|
10
- | `pwn.yaml` | `PWN::Config` · `PWN::Plugins::Vault` | AES-encrypted YAML | `pwn-vault` | engines, keys, agent options |
11
- | `pwn.yaml.decryptor` | `PWN::Plugins::Vault` | key/IV | - | decrypts `pwn.yaml` (or set `PWN_DECRYPTOR_KEY`/`_IV`) |
10
+ | `pwn.yaml` | `PWN::Config` · `PWN::Plugins::Vault` | Legacy AES-CBC or authenticated YubiKey-derived AES-GCM | `pwn-vault` | engines, keys, agent options; see [hardware vaults](YubiKey-Vaults.md) |
11
+ | `pwn.yaml.decryptor` | `PWN::Plugins::Vault` | Legacy key/IV | - | decrypts legacy `pwn.yaml` (or set `PWN_DECRYPTOR_KEY`/`_IV`); absent for hardware-only vaults |
12
12
  | **`.schema`** | **`PWN::Migrate`** | JSON `{schema:,pwn_version:,at:}` | `pwn setup --migrate` | **schema stamp - drives the one-line drift warning on launch** |
13
13
  | `memory.json` | `PWN::Memory` | JSON array | `memory_clear` | facts · prefs · lessons · env - injected into every prompt |
14
14
  | `memory.idx` | `PWN::MemoryIndex` | JSON `{key → {sha,vec}}` | `PWN::MemoryIndex.reset` | local embedding index over `memory.json` - powers **relevance-ranked** MEMORY injection (incremental; only re-embeds changed entries) |
@@ -26,7 +26,7 @@ external services and environment overrides can place evidence elsewhere.
26
26
  | `extrospection/voice/*` | `Extrospection` | wav / txt | `rm -rf` | TTS/STT artifacts from `extro_voice` |
27
27
  | `sessions/*.jsonl` | `PWN::Sessions` | JSON-per-line | `sessions_delete` | full transcript per pwn-ai run (with per-step `step_reward` from `Reward.prm`) |
28
28
  | `open_goal.json` | `PWN::AI::Agent::OpenGoal` | JSON | delete the file | unfinished host-work request so `continue` / `resume` picks it up |
29
- | `cron/jobs.yml` | `PWN::Cron` | YAML | `cron_remove` | scheduled jobs. Fresh install seeds hygiene (`learning_consolidate_nightly` + `pwn_stores_lean_nightly` **on**) and curriculum practice/judge/train **off** |
29
+ | `cron/jobs.yml` | `PWN::Cron` | YAML | `cron_remove` | Scheduled jobs; fresh install seeds only the `session_cleanup` housekeeping coordinator. Curriculum schedules require explicit creation and are not recreated after deletion |
30
30
  | `cron/log/*.log` | `PWN::Cron` | text | rm | last_run output |
31
31
  | `agents.yml` | `PWN::AI::Agent::Swarm` | YAML | edit / `agent_spawn` | persona registry |
32
32
  | `swarm/<id>/bus.jsonl` | `Swarm` | JSON-per-line | rm -rf | append-only multi-agent chat |
@@ -34,7 +34,8 @@ external services and environment overrides can place evidence elsewhere.
34
34
  | `curriculum/` | `PWN::AI::Agent::Curriculum` | JSONL | rm -rf | self-play reproducers · train/gate A/B eval sets |
35
35
  | `curriculum_kpi.jsonl` | `PWN::AI::Agent::Curriculum.practice_kpi` | JSONL | rm | outer curriculum KPI snapshots (repeating-mistake trend) |
36
36
  | `finetune/*.jsonl` | `Learning.export_finetune` · `Reward.export_dpo` | ShareGPT / OpenAI / DPO JSONL | `rm` | supervised + preference datasets - feed to a LoRA over the local model |
37
- | `backup/<ts>/` | `PWN::Migrate` | tree copy | rm -rf | timestamped snapshot taken before every `--migrate --fix` |
37
+ | `workspaces/runtime/` | `PWN::Plugins::AISandbox` · `SessionCleanup.temporary` | files | operator-managed | Unspecified `pwn-ai` writes, `TMPDIR`, and registered disposable inputs such as Festival speech. Another path is writable only when the original request names it. `/opt/pwn` stays read-only |
38
+ | `backup/<generation>/` | `PWN::Backups` | shared generations | see [Backup Retention](Backup-Retention.md) | One store, seven generation folders total, for every recovery producer including migrate |
38
39
  | `quarantine/` | `PWN::Migrate` | quarantined originals | rm -rf | corrupt/incompatible state files moved aside so the owner re-seeds |
39
40
  | `pwn_history` | Pry / pwn-ai | text | operator-managed | Shared durable input history at `~/.pwn/pwn_history` |
40
41
 
@@ -69,8 +70,9 @@ for the full `PWN::Migrate` API.
69
70
  tar czf pwn-state-$(date +%F).tgz -C "$HOME" .pwn
70
71
  ```
71
72
 
72
- `pwn setup --migrate --fix` also writes one automatically to
73
- `~/.pwn/backup/<timestamp>/` before touching anything.
73
+ `pwn setup --migrate --fix` admits one generation through `PWN::Backups` under
74
+ `~/.pwn/backup/` before it changes state. That store holds seven generations
75
+ total, not a new unbounded tree per run. See [Backup Retention](Backup-Retention.md).
74
76
 
75
77
  ## Start fresh for a new engagement
76
78