@slicedearth/whoisleuth-cli 2.3.0 → 2.5.1

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 (418) hide show
  1. package/NOTICE +8 -2
  2. package/README.md +19 -15
  3. package/bin/whoisleuth.mjs +8 -1
  4. package/cli/archive-content-digest.mjs +14 -0
  5. package/cli/archive-inspect.mjs +20 -53
  6. package/cli/argument-values.mjs +55 -0
  7. package/cli/arguments.mjs +24 -408
  8. package/cli/artifact-validation/case-response.mjs +65 -44
  9. package/cli/artifact-validation/investigation-capsule.mjs +52 -31
  10. package/cli/artifact-validation/lookup-evidence.mjs +15 -9
  11. package/cli/artifact-validation/signed-review.mjs +136 -68
  12. package/cli/artifact-validation/structure-primitives.mjs +1 -105
  13. package/cli/artifact-verify.mjs +92 -24
  14. package/cli/assurance-arguments.mjs +56 -0
  15. package/cli/assurance-command-definitions.mjs +179 -0
  16. package/cli/assurance-command-runner.mjs +40 -5
  17. package/cli/bagit-folder.mjs +57 -0
  18. package/cli/bagit-review.mjs +23 -0
  19. package/cli/bounded-json.mjs +1 -1
  20. package/cli/bulk-checkpoint.mjs +1 -1
  21. package/cli/bulk-command-runner.mjs +4 -2
  22. package/cli/bulk-output.mjs +29 -3
  23. package/cli/bulk.mjs +2 -3
  24. package/cli/case-command.mjs +212 -0
  25. package/cli/case-pack.mjs +50 -32
  26. package/cli/ci-report.mjs +6 -3
  27. package/cli/collection-arguments.mjs +123 -0
  28. package/cli/collection-command-definitions.mjs +330 -0
  29. package/cli/collection-preflight.mjs +8 -1
  30. package/cli/command-argument-grammar.mjs +8 -206
  31. package/cli/command-definition.mjs +329 -0
  32. package/cli/command-reference.mjs +94 -1357
  33. package/cli/command-seeds.mjs +18 -0
  34. package/cli/compare.mjs +1 -1
  35. package/cli/completion.mjs +298 -79
  36. package/cli/config-profile.mjs +8 -6
  37. package/cli/ct-event-intake.mjs +16 -16
  38. package/cli/discover.mjs +5 -4
  39. package/cli/discovery-observation-snapshot.mjs +46 -18
  40. package/cli/discovery-scan-command-runner.mjs +5 -3
  41. package/cli/discovery-scan.mjs +7 -4
  42. package/cli/discovery-snapshot.mjs +2 -2
  43. package/cli/discovery-workflow.mjs +5 -5
  44. package/cli/domain-control-monitor.mjs +50 -20
  45. package/cli/domain-control-observations.mjs +151 -103
  46. package/cli/errors.mjs +37 -6
  47. package/cli/evidence-arguments.mjs +46 -0
  48. package/cli/evidence-command-definitions.mjs +69 -0
  49. package/cli/evidence-command-runner.mjs +18 -8
  50. package/cli/evidence-signing.mjs +5 -4
  51. package/cli/external-observation-mapping.mjs +9 -10
  52. package/cli/formatters/terminal-command-formats.mjs +6 -0
  53. package/cli/formatters/terminal-lookup.mjs +24 -46
  54. package/cli/formatters/terminal-shared.mjs +5 -3
  55. package/cli/formatters/terminal-technology.mjs +114 -0
  56. package/cli/history-arguments.mjs +39 -0
  57. package/cli/history-command-definitions.mjs +88 -0
  58. package/cli/indicator-set-command.mjs +33 -0
  59. package/cli/inline-command-families.mjs +6 -46
  60. package/cli/inline-command-runner.mjs +4 -31
  61. package/cli/input.mjs +2 -2
  62. package/cli/intake-command.mjs +107 -0
  63. package/cli/interactive-launcher.mjs +2 -160
  64. package/cli/interchange-report.mjs +8 -10
  65. package/cli/investigation-artifacts.mjs +176 -0
  66. package/cli/investigation-checkpoint.mjs +10 -0
  67. package/cli/investigation-folder.mjs +114 -0
  68. package/cli/investigation-manifest.mjs +1 -112
  69. package/cli/investigation-package-review.mjs +103 -0
  70. package/cli/investigation-plan.mjs +38 -147
  71. package/cli/investigation-recipes.mjs +177 -0
  72. package/cli/investigation-run.mjs +256 -35
  73. package/cli/local-document-checkpoint.mjs +170 -0
  74. package/cli/local-mmdb-review.mjs +197 -17
  75. package/cli/lookalike-calibration.mjs +1 -1
  76. package/cli/lookup-brief.mjs +2 -2
  77. package/cli/lookup-browser.mjs +5 -12
  78. package/cli/lookup-command-runner.mjs +33 -39
  79. package/cli/lookup-diff.mjs +47 -12
  80. package/cli/lookup-plan.mjs +16 -14
  81. package/cli/lookup-reconcile.mjs +15 -12
  82. package/cli/lookup-timeline.mjs +7 -6
  83. package/cli/mail-header-review.mjs +24 -247
  84. package/cli/mail-review.mjs +75 -21
  85. package/cli/manual.mjs +1 -0
  86. package/cli/network-arguments.mjs +51 -0
  87. package/cli/network-command-definitions.mjs +130 -0
  88. package/cli/network-command-runner.mjs +45 -31
  89. package/cli/offline-evidence-review.mjs +39 -6
  90. package/cli/output-file.mjs +31 -5
  91. package/cli/posture.mjs +1 -1
  92. package/cli/registry-doctor.mjs +3 -1
  93. package/cli/registry-fixture-scaffold.mjs +1 -1
  94. package/cli/registry-support.mjs +1 -1
  95. package/cli/retained-artifact-diff.mjs +10 -9
  96. package/cli/review-arguments.mjs +131 -0
  97. package/cli/review-command-definitions.mjs +273 -0
  98. package/cli/review-command-runner.mjs +104 -149
  99. package/cli/risk-calibration.mjs +8 -8
  100. package/cli/runner.mjs +74 -37
  101. package/cli/saved-lookup.mjs +2 -2
  102. package/cli/selected-input-worker.mjs +63 -0
  103. package/cli/sharing-review.mjs +8 -80
  104. package/cli/signer-trust.mjs +104 -0
  105. package/cli/source-reliability.mjs +75 -84
  106. package/cli/support-arguments.mjs +49 -0
  107. package/cli/support-command-definitions.mjs +209 -0
  108. package/cli/support-command-runner.mjs +2 -2
  109. package/cli/terminal-input.mjs +168 -0
  110. package/cli/workflow-arguments.mjs +86 -0
  111. package/cli/workflow-command-definitions.mjs +78 -0
  112. package/cli/workflow-command-runner.mjs +75 -13
  113. package/docs/cli-reference.md +103 -30
  114. package/docs/cli.md +510 -25
  115. package/docs/contextual-reviews.md +155 -0
  116. package/lib/abort.mjs +4 -1
  117. package/lib/analyst-taxonomy.mjs +2 -40
  118. package/lib/application-version.mjs +1 -1
  119. package/lib/availability.mjs +27 -754
  120. package/lib/bounded-contract-normalizers.mjs +4 -0
  121. package/lib/bounded-file.mjs +3 -1
  122. package/lib/bounded-html-document.mjs +125 -0
  123. package/lib/bounded-json.mjs +2 -290
  124. package/lib/browser-library-profile.mjs +99 -130
  125. package/lib/browser-library-worker.mjs +112 -0
  126. package/lib/candidate-provenance-bounds.mjs +2 -0
  127. package/lib/classify.mjs +1 -1
  128. package/lib/client-behavior-profile.mjs +3 -3
  129. package/lib/ct-query.mjs +2 -27
  130. package/lib/ct-response-bounds.mjs +2 -9
  131. package/lib/ct-search.mjs +89 -46
  132. package/lib/dmarc-discovery.mjs +62 -0
  133. package/lib/dns-convergence-review.mjs +21 -8
  134. package/lib/dns-delegation-health.mjs +36 -22
  135. package/lib/dns-inheritance-review.mjs +207 -0
  136. package/lib/dns-intelligence.mjs +44 -29
  137. package/lib/dns-mx.mjs +1 -1
  138. package/lib/dns-query-coverage.mjs +40 -0
  139. package/lib/dnssec-evidence-validation.mjs +11 -3
  140. package/lib/domain-assurance.mjs +1 -1
  141. package/lib/domain-change-packet.mjs +18 -14
  142. package/lib/domain-change-review.mjs +40 -16
  143. package/lib/domain-control-flight-recorder.mjs +301 -124
  144. package/lib/domain-control-manifest.mjs +126 -58
  145. package/lib/domain-posture-analysis.mjs +41 -11
  146. package/lib/domain-posture-parsers.mjs +88 -6
  147. package/lib/domain-posture.mjs +120 -32
  148. package/lib/domain-registration.mjs +392 -0
  149. package/lib/evidence-export-privacy.mjs +568 -0
  150. package/lib/evidence-export.mjs +12 -593
  151. package/lib/evidence-report.mjs +7 -2
  152. package/lib/external-intelligence-risk.mjs +2 -124
  153. package/lib/favicon.mjs +117 -41
  154. package/lib/generated/cisa-kev-catalog.mjs +50 -4
  155. package/lib/generated/registrar-standing-catalogue.mjs +18 -10
  156. package/lib/generated/retire-browser-catalog.mjs +2742 -1034
  157. package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
  158. package/lib/html-signals.mjs +87 -85
  159. package/lib/http-evidence-bounds.mjs +2 -7
  160. package/lib/http-intelligence.mjs +52 -27
  161. package/lib/idn-confusable-policy.mjs +6 -4
  162. package/lib/idn-confusables.mjs +1 -1
  163. package/lib/interchange-fidelity-registry.mjs +10 -0
  164. package/lib/lookup-cache.mjs +34 -11
  165. package/lib/lookup-child-profile-contract.mjs +24 -15
  166. package/lib/lookup-contract-primitives.mjs +1 -1
  167. package/lib/lookup-diagnostics.mjs +2 -1
  168. package/lib/lookup-network-evidence-bounds.mjs +1 -10
  169. package/lib/lookup-progress.mjs +79 -13
  170. package/lib/lookup-response-contract.mjs +1077 -0
  171. package/lib/lookup-response.mjs +11 -24
  172. package/lib/lookup-source-progress.mjs +24 -35
  173. package/lib/lookup.mjs +49 -28
  174. package/lib/observed-network-context.mjs +43 -27
  175. package/lib/opportunity-scoring.mjs +2 -107
  176. package/lib/outbound-identity.mjs +1 -1
  177. package/lib/outbound-request-bounds.mjs +4 -1
  178. package/lib/page-fingerprints.mjs +87 -134
  179. package/lib/page-role-profile.mjs +12 -8
  180. package/lib/perceptual-hash-comparison.mjs +2 -34
  181. package/lib/perceptual-hash.mjs +79 -24
  182. package/lib/portable-generator.mjs +2 -29
  183. package/lib/project-metadata.mjs +2 -8
  184. package/lib/rdap-bootstrap.mjs +72 -22
  185. package/lib/rdap-client.mjs +26 -14
  186. package/lib/rdap-normalization.mjs +33 -18
  187. package/lib/rdap-search-workbench.mjs +1 -1
  188. package/lib/rdap-transport.mjs +47 -20
  189. package/lib/rdap-types.mjs +2 -1
  190. package/lib/rdap.mjs +18 -3
  191. package/lib/registrable-domain.mjs +2 -36
  192. package/lib/registrar-standing-contract.mjs +2 -1
  193. package/lib/registrar-standing.mjs +9 -13
  194. package/lib/registry-capabilities.mjs +7 -3
  195. package/lib/registry-capability-catalogue.mjs +29 -20
  196. package/lib/registry-dates.mjs +2 -190
  197. package/lib/registry-insights.mjs +56 -18
  198. package/lib/response-policy.mjs +86 -48
  199. package/lib/risk-calibration-summary.mjs +2 -338
  200. package/lib/risk-scoring.mjs +2 -345
  201. package/lib/rpki-evidence.mjs +8 -5
  202. package/lib/safe-fetch.mjs +54 -47
  203. package/lib/scoring-evidence-quality.mjs +2 -107
  204. package/lib/security-txt.mjs +10 -7
  205. package/lib/semantic-version.mjs +2 -54
  206. package/lib/service-binding-dns.mjs +35 -0
  207. package/lib/smtp-transport-review.mjs +75 -34
  208. package/lib/sslbl-certificates.generated.mjs +141 -138
  209. package/lib/static-html-analysis.mjs +117 -146
  210. package/lib/structured-data-identity.mjs +2 -1
  211. package/lib/technology-markup.mjs +100 -0
  212. package/lib/threat-intelligence-manifests.mjs +11 -139
  213. package/lib/threat-intelligence-runtime.mjs +24 -52
  214. package/lib/threat-intelligence-targets.mjs +6 -34
  215. package/lib/threat-intelligence-types.mjs +2 -36
  216. package/lib/typosquat-generator.mjs +29 -16
  217. package/lib/urlhaus-intelligence.mjs +8 -3
  218. package/lib/urlscan-intelligence.mjs +1 -1
  219. package/lib/website-enrichment.mjs +388 -0
  220. package/lib/website-security-posture.mjs +30 -6
  221. package/lib/website-technology.mjs +111 -71
  222. package/lib/whois-authority.mjs +55 -5
  223. package/lib/whois-chain.mjs +9 -2
  224. package/lib/whois-common-formats.mjs +5 -5
  225. package/lib/whois-contracts.mjs +2 -1
  226. package/lib/whois-dialects.mjs +2 -2
  227. package/lib/whois-normalization.mjs +3 -1
  228. package/lib/whois-parser-support.mjs +8 -1
  229. package/lib/whois-parser.mjs +5 -3
  230. package/lib/whois-transport.mjs +23 -7
  231. package/package.json +14 -14
  232. package/packages/analysis/analyst-taxonomy.mjs +40 -0
  233. package/packages/analysis/bounded-json.mjs +330 -0
  234. package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
  235. package/packages/analysis/cli-argument-grammar.mjs +216 -0
  236. package/packages/analysis/ct-query.mjs +27 -0
  237. package/packages/analysis/ct-response-bounds.mjs +9 -0
  238. package/packages/analysis/external-intelligence-risk.mjs +124 -0
  239. package/packages/analysis/http-evidence-bounds.mjs +8 -0
  240. package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
  241. package/packages/analysis/opportunity-scoring.mjs +107 -0
  242. package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
  243. package/packages/analysis/portable-generator.mjs +29 -0
  244. package/packages/analysis/project-metadata.mjs +8 -0
  245. package/packages/analysis/registrable-domain.mjs +56 -0
  246. package/packages/analysis/risk-calibration-summary.mjs +352 -0
  247. package/packages/analysis/risk-scoring.mjs +345 -0
  248. package/packages/analysis/scoring-evidence-quality.mjs +107 -0
  249. package/packages/analysis/semantic-version.mjs +54 -0
  250. package/packages/analysis/threat-intelligence-types.mjs +75 -0
  251. package/packages/cases/case-attachment-model.mjs +83 -0
  252. package/packages/cases/case-evidence-links.mjs +135 -0
  253. package/packages/cases/case-evidence-model.mjs +229 -69
  254. package/packages/cases/case-evidence-quality.mjs +5 -0
  255. package/packages/cases/case-export-input.mjs +55 -0
  256. package/packages/cases/case-incident-context.mjs +52 -0
  257. package/packages/cases/case-markdown.mjs +11 -0
  258. package/packages/cases/case-migration-model.mjs +98 -48
  259. package/packages/cases/case-model.mjs +1 -0
  260. package/packages/cases/case-recheck-model.mjs +149 -0
  261. package/packages/cases/case-record-contracts.mjs +1 -1
  262. package/packages/cases/case-record-core.mjs +4 -3
  263. package/packages/cases/case-record-operations.mjs +123 -133
  264. package/packages/cases/case-record-projection.mjs +30 -20
  265. package/packages/cases/case-record-version-input.mjs +19 -0
  266. package/packages/cases/case-report.mjs +114 -67
  267. package/packages/cases/case-requested-evidence.mjs +146 -0
  268. package/packages/cases/case-response-actions.mjs +756 -0
  269. package/packages/cases/case-response-model.mjs +49 -1129
  270. package/packages/cases/case-response-outcomes.mjs +338 -0
  271. package/packages/cases/case-response-packet-render.mjs +157 -0
  272. package/packages/cases/case-response-packet-types.mjs +1 -0
  273. package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
  274. package/packages/cases/case-response-packet.mjs +58 -593
  275. package/packages/cases/case-response-records.mjs +93 -0
  276. package/packages/cases/case-response-review-inputs.mjs +382 -0
  277. package/packages/cases/case-response-values.mjs +85 -0
  278. package/packages/cases/case-review-package.mjs +45 -0
  279. package/packages/cases/case-selection.mjs +46 -0
  280. package/packages/cases/case-storage-model.mjs +26 -1
  281. package/packages/cases/case-workflow-metadata.mjs +225 -0
  282. package/packages/cases/case-workflow-migration.mjs +43 -0
  283. package/packages/cases/http-summary.mjs +6 -1
  284. package/packages/cases/response-route-freshness.mjs +16 -0
  285. package/packages/comparison/capture-context.mjs +61 -0
  286. package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
  287. package/packages/comparison/favicon-similarity.mjs +1 -1
  288. package/packages/comparison/page-similarity.mjs +3 -2
  289. package/packages/comparison/relationship-evidence.mjs +103 -14
  290. package/packages/comparison/relationship-provenance.mjs +99 -0
  291. package/packages/contracts/analyst-interchange.mjs +234 -0
  292. package/packages/contracts/analyst-review-state-contract.mjs +11 -0
  293. package/packages/contracts/analyst-review-state.mjs +5 -4
  294. package/packages/contracts/case-portability.mjs +370 -114
  295. package/packages/contracts/case-views-contract.mjs +12 -0
  296. package/packages/contracts/case-views.mjs +26 -0
  297. package/packages/contracts/cli-command-semantics.mjs +3 -0
  298. package/packages/contracts/cli-grammar.mjs +6 -0
  299. package/packages/contracts/cli-lookup-brief.mjs +2 -0
  300. package/packages/contracts/cli-lookup.mjs +0 -1
  301. package/packages/contracts/context-review.mjs +24 -0
  302. package/packages/contracts/document-intake.mjs +11 -0
  303. package/packages/contracts/domain-control-flight-recorder.mjs +98 -44
  304. package/packages/contracts/domain-control-manifest.mjs +128 -74
  305. package/packages/contracts/domain-control-monitor.mjs +59 -45
  306. package/packages/contracts/domain-control-review.mjs +200 -121
  307. package/packages/contracts/domain-name.mjs +21 -0
  308. package/packages/contracts/external-observation-interchange.mjs +2 -1
  309. package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
  310. package/packages/contracts/har-review.mjs +2 -0
  311. package/packages/contracts/http-url.mjs +2 -0
  312. package/packages/contracts/identity-events.mjs +4 -0
  313. package/packages/contracts/investigation-package-limits.mjs +24 -0
  314. package/packages/contracts/investigation-portability.mjs +56 -27
  315. package/packages/contracts/investigation-run.mjs +18 -0
  316. package/packages/contracts/ip-address.mjs +44 -0
  317. package/packages/contracts/lookup-evidence.mjs +35 -0
  318. package/packages/contracts/mail-authentication.mjs +5 -0
  319. package/packages/contracts/message-intake.mjs +31 -0
  320. package/packages/contracts/offline-comparison.mjs +4 -2
  321. package/packages/contracts/page-fingerprints.mjs +15 -0
  322. package/packages/contracts/passive-mail.mjs +10 -0
  323. package/packages/contracts/risk-calibration.mjs +0 -1
  324. package/packages/contracts/schema-compatibility.mjs +1 -1
  325. package/packages/contracts/schema-lifecycle.mjs +83 -153
  326. package/packages/contracts/selected-file-limits.mjs +6 -0
  327. package/packages/contracts/vulnerability-identifiers.mjs +5 -0
  328. package/packages/contracts/web-capture.mjs +10 -0
  329. package/packages/contracts/workspace-portability.mjs +219 -238
  330. package/packages/evidence/artifact-integrity.mjs +8 -2
  331. package/packages/evidence/artifact-structure.mjs +105 -0
  332. package/packages/evidence/collection-quality.mjs +67 -0
  333. package/packages/evidence/decision-fact.mjs +4 -1
  334. package/packages/evidence/dns-query-outcome.mjs +72 -0
  335. package/packages/evidence/domain-control-runtime.mjs +155 -27
  336. package/packages/evidence/domain-posture-context.mjs +79 -0
  337. package/packages/evidence/email-recipient.mjs +18 -0
  338. package/packages/evidence/image-regions.mjs +56 -0
  339. package/packages/evidence/latest-observations.mjs +28 -0
  340. package/packages/evidence/lookup-target.mjs +108 -0
  341. package/packages/evidence/observation.mjs +18 -1
  342. package/packages/evidence/passphrase-encryption.mjs +44 -0
  343. package/packages/evidence/png-pixels.mjs +117 -0
  344. package/packages/evidence/public-address-policy.mjs +83 -0
  345. package/packages/evidence/record-identity.mjs +72 -0
  346. package/packages/evidence/registry-dates.mjs +190 -0
  347. package/packages/evidence/retained-file.mjs +69 -0
  348. package/packages/evidence/review-age.mjs +14 -0
  349. package/packages/interchange/bagit.mjs +342 -0
  350. package/packages/interchange/bounded-zip-extraction.mjs +278 -0
  351. package/packages/interchange/crc32.mjs +17 -0
  352. package/packages/interchange/defensive-indicator-export.mjs +264 -0
  353. package/packages/interchange/external-findings-import.mjs +93 -61
  354. package/packages/interchange/managed-indicator-export.mjs +35 -0
  355. package/packages/interchange/managed-indicator-set.mjs +210 -0
  356. package/packages/interchange/misp-indicator-export.mjs +93 -0
  357. package/packages/interchange/sharing-policy.mjs +56 -0
  358. package/packages/interchange/stix-indicator-export.mjs +129 -0
  359. package/packages/interchange/web-capture-import.mjs +389 -0
  360. package/packages/investigation/acquisition-decision-packet.mjs +1 -1
  361. package/packages/investigation/bulk-mail-exposure.mjs +9 -14
  362. package/packages/investigation/capture-coverage.mjs +57 -0
  363. package/packages/investigation/connector-provenance-review.mjs +100 -0
  364. package/packages/investigation/context-review.mjs +35 -0
  365. package/packages/investigation/document-intake.mjs +81 -0
  366. package/packages/investigation/docx-intake.mjs +133 -0
  367. package/packages/investigation/domain-history-review.mjs +100 -0
  368. package/packages/investigation/domain-transition-review.mjs +47 -0
  369. package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
  370. package/packages/investigation/har-intake.mjs +68 -0
  371. package/packages/investigation/identity-event-intake.mjs +131 -0
  372. package/packages/investigation/identity-incident-review.mjs +39 -0
  373. package/packages/investigation/incident-sequence-review.mjs +58 -0
  374. package/packages/investigation/intake-report.mjs +19 -0
  375. package/packages/investigation/investigation-bagit.mjs +14 -0
  376. package/packages/investigation/investigation-capsule.mjs +33 -5
  377. package/packages/investigation/investigation-manifest.mjs +200 -0
  378. package/packages/investigation/investigation-package-crypto.mjs +98 -0
  379. package/packages/investigation/investigation-package.mjs +200 -0
  380. package/packages/investigation/link-intake.mjs +179 -0
  381. package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
  382. package/packages/investigation/lookup-asset-graph.mjs +161 -141
  383. package/packages/investigation/lookup-claim-readiness.mjs +18 -0
  384. package/packages/investigation/lookup-investigation-brief.mjs +3 -3
  385. package/packages/investigation/lookup-task-guidance.mjs +58 -0
  386. package/packages/investigation/mail-authentication-review.mjs +222 -0
  387. package/packages/investigation/mail-header-identity.mjs +209 -0
  388. package/packages/investigation/message-intake.mjs +195 -0
  389. package/packages/investigation/page-behaviour.mjs +113 -0
  390. package/packages/investigation/pdf-intake.mjs +155 -0
  391. package/packages/investigation/platform-continuity-review.mjs +60 -0
  392. package/packages/investigation/qr-intake.mjs +56 -0
  393. package/packages/investigation/requested-action-hints.mjs +11 -0
  394. package/packages/investigation/selected-input-review.mjs +17 -0
  395. package/packages/investigation/storefront-review.mjs +70 -0
  396. package/packages/monitoring/analyst-review-state.mjs +38 -90
  397. package/packages/workspace/brand-profile-model.mjs +160 -22
  398. package/packages/workspace/bulk-columns.mjs +17 -0
  399. package/packages/workspace/bulk-review-model.mjs +39 -31
  400. package/packages/workspace/bulk-session-model.mjs +112 -27
  401. package/packages/workspace/bulk-sort.mjs +1 -0
  402. package/packages/workspace/campaign-model.mjs +1 -1
  403. package/packages/workspace/case-views.mjs +102 -0
  404. package/packages/workspace/detection-rule-model.mjs +1 -1
  405. package/packages/workspace/hostile-input.mjs +29 -8
  406. package/packages/workspace/investigation-guide.mjs +2 -2
  407. package/packages/workspace/investigation-template-model.mjs +45 -6
  408. package/packages/workspace/page-baseline.mjs +20 -17
  409. package/packages/workspace/relationship-observation-model.mjs +54 -35
  410. package/packages/workspace/shortlist-model.mjs +14 -5
  411. package/packages/workspace/watchlist-history.mjs +30 -14
  412. package/packages/workspace/watchlist-store.mjs +24 -6
  413. package/packages/workspace/website-snapshot-model.mjs +98 -33
  414. package/packages/workspace/workspace-archive-crypto.mjs +41 -61
  415. package/packages/workspace/workspace-archive.mjs +90 -25
  416. package/third-party-notices.txt +577 -17
  417. package/lib/hostname.mjs +0 -38
  418. package/packages/investigation/bulk-review-export.mjs +0 -63
package/docs/cli.md CHANGED
@@ -7,6 +7,11 @@ collection boundaries and output. Installed `whoisleuth --help`, focused
7
7
  version. The [CLI reference](cli-reference.md) covers durable command and
8
8
  artefact contracts.
9
9
 
10
+ The [website command reference](https://www.whoisleuth.com/cli) starts with common
11
+ tasks, then offers searchable commands with examples, option explanations and
12
+ defaults. Command-section links can be bookmarked. Its option guidance and
13
+ installed help derive from the same command definitions.
14
+
10
15
  The generated [privacy and data-flow catalogue](https://github.com/slicedearth/whoisleuth/blob/main/docs/privacy-data-flow-catalogue.md)
11
16
  lists the network, recipient, retention and export boundary for every command.
12
17
 
@@ -35,6 +40,10 @@ dependency lifecycle scripts. From a repository checkout, replace `whoisleuth`
35
40
  with `node bin/whoisleuth.mts`; maintainers can verify the exact package closure
36
41
  with `npm run cli:package:check`.
37
42
 
43
+ Rendered-page collection uses the separate [optional capture companion](https://github.com/slicedearth/whoisleuth/tree/main/packages/web-capture#install-a-local-candidate),
44
+ not the main CLI. It can be installed from a verified local archive; browser
45
+ installation and each authorised capture remain explicit actions.
46
+
38
47
  ## First commands
39
48
 
40
49
  Inspect the installed command set and plan one Lookup before collecting:
@@ -56,6 +65,11 @@ whoisleuth lookup example.test --deep --browse
56
65
  whoisleuth lookup example.test --deep --markdown --output lookup.md
57
66
  ```
58
67
 
68
+ Pasted URLs normally select only their hostname. To collect one particular
69
+ page, use `whoisleuth lookup 'https://portal.example.test/review' --deep --exact-url`.
70
+ This sends the path and query to the website, without the fragment. Review
71
+ retained paths and page-derived text before sharing the result.
72
+
59
73
  To continue a reviewed terminal Lookup in the browser, save the completed
60
74
  private document from the interactive view:
61
75
 
@@ -65,13 +79,22 @@ whoisleuth lookup example.test --deep --browse --save-lookup lookup.json
65
79
 
66
80
  In Console Lookup, open **Replay exported evidence**, select the file, verify
67
81
  its digest and source states, then create or update a browser-local Case. The
68
- file is not uploaded. Case classification, exact incident links, response
69
- actions and packet preparation remain deliberate browser steps.
82
+ file is not uploaded. Case classification, exact incident links and response
83
+ actions remain deliberate analyst steps. Ordinary Case files also support the
84
+ offline CLI workflow below.
85
+ When several incident Cases share a domain, select the intended Case before
86
+ retaining replay evidence. Case packs preserve each current Case ID separately;
87
+ trusted and public packs exclude analyst-entered incident titles.
88
+ Every audience retains tags, decision summaries and rationale as well as domain
89
+ evidence and Case, pin and decision IDs. Public does not mean anonymous.
90
+ Browser Lookup evidence can be verified or packaged in the CLI. `brief`,
91
+ `export`, `compare`, `registry-doctor` and `source-report` instead require a
92
+ saved CLI Lookup; they do not reconstruct it from a browser export.
70
93
 
71
94
  Process selected local input:
72
95
 
73
96
  ```bash
74
- whoisleuth bulk domains.txt --csv
97
+ whoisleuth bulk domains.txt --csv-with-metadata
75
98
  whoisleuth discover example.test --preset common --jsonl
76
99
  whoisleuth verify-artifact lookup.json --json --strict-exit
77
100
  whoisleuth compare lookup.json --json
@@ -83,17 +106,15 @@ One strict domain, IP address or ASN can occupy command position as Lookup
83
106
  shorthand. URL-like or ambiguous input requires the explicit `lookup` command.
84
107
  Only `bulk` accepts multiple targets.
85
108
 
86
- ## Command groups
109
+ `discover` accepts a brand label or registrable domain and supports multi-part
110
+ public suffixes in `--tlds` (for example, `co.uk,com.au`). It preserves the full
111
+ suffix and does not collect candidate evidence; use `discover-scan` only after
112
+ reviewing its separate network plan. Subdomains and URLs are not generation seeds.
87
113
 
88
- The installed registry groups commands under the same analyst vocabulary as the
89
- application:
114
+ ## Command groups
90
115
 
91
- | Group | Common commands |
92
- | --- | --- |
93
- | Investigate | `lookup`, `bulk`, `discover`, `ct-search`, `posture`, `http`, `tls`, `compare`, `mail-headers`, `brief` |
94
- | Respond | `case-pack`, `change-packet`, `sharing-review`, `export` (local handoff from browser-created Cases) |
95
- | Assure | `dnssec-validate`, `mail-transport`, `domain-control`, `assurance`, `workflow-plan`, `diff`, `inspect-archive`, `verify-artifact` |
96
- | Utilities | `doctor`, `commands`, `completion`, `manual`, `registry-scaffold` |
116
+ The installed catalogue groups commands under Investigate, Respond, Assure and
117
+ Utilities, with each command's inputs, output and network boundary.
97
118
 
98
119
  Filter the canonical index without running the selected commands:
99
120
 
@@ -104,12 +125,14 @@ whoisleuth commands --json
104
125
  whoisleuth manual | man -l -
105
126
  ```
106
127
 
107
- `registry-scaffold` has a separate fixture bootstrap contract: its `--profile`
108
- selects one fixed fixture profile and it rejects shared `--config` profiles.
109
- It creates sanitised local fixtures and does not contact a registry.
110
-
111
128
  ## Collection boundaries
112
129
 
130
+ For an explicit inherited-policy and direct parent-delegation review, use
131
+ `whoisleuth posture example.test --include-inherited-dns --json`. Without that
132
+ option, posture collection keeps its existing exact-name scope. The extra
133
+ results distinguish recursive policy publication from sampled parent referrals;
134
+ they do not establish name existence, message authentication or DNSSEC validity.
135
+
113
136
  | Mode | Behaviour |
114
137
  | --- | --- |
115
138
  | Fast Lookup | Registration-first triage without WHOIS or deeper website and TLS collection. |
@@ -123,6 +146,93 @@ their focused help. They do not use the hosted login or hosted usage controls.
123
146
  `lookup --plan` lists planned source families and disclosure targets before
124
147
  collection. `doctor` is offline unless `--network` is selected.
125
148
 
149
+ Deep Lookup reports IPv4 and IPv6 results separately, including empty answers,
150
+ NODATA, resolver-reported missing names and query failures. These outcomes remain
151
+ distinct in evidence exports; none alone establishes domain removal.
152
+
153
+ Plans are target-specific: Fast IP and ASN plans contain RDAP only; Deep IP
154
+ adds WHOIS and eligible reverse DNS, while Deep ASN adds WHOIS. Domain-only web,
155
+ TLS and selected intelligence do not apply to address or ASN targets.
156
+ Plans do not probe source health or measure remaining quota, cost, result fan-out
157
+ or exact request counts. CLI configuration profiles repeat supported settings;
158
+ optional selections made on the website do not transfer to a CLI command.
159
+ For completed website results, see [Source quality](https://github.com/slicedearth/whoisleuth/blob/main/docs/application-guide.md#lookup).
160
+
161
+ ### Message and link intake
162
+
163
+ Use `intake email message.eml --json` for MIME email and nested messages,
164
+ `intake calendar invitation.ics`, `intake qr selected.png`, or `intake text`
165
+ with text on standard input. These commands never open a destination or execute
166
+ supplied content. Output compares displayed and actual hosts, retains reported
167
+ header authentication and interprets supplied authorisation parameters without
168
+ retaining URL tokens. The source hash identifies the original bytes.
169
+ Defanged schemes and hosts are accepted, including `hxxps[:]//host[.]example`.
170
+ Path, query and fragment characters keep their supplied meaning; text scanning
171
+ removes unmatched surrounding prose brackets, while explicit links remain exact.
172
+
173
+ Add a repeatable `--reported-action`, such as `entered_device_code` or
174
+ `granted_consent`, to include account-recovery guidance based on an explicitly
175
+ reported action. The report does not infer a stolen session from domain evidence.
176
+ `--strict-exit` returns 4 for a partial review. QR review supports still PNGs;
177
+ no decoded result is not proof that a symbol is absent.
178
+ Excluded links and unsupported QR payloads make the review partial, with
179
+ category counts rather than private payload text. Bare QR hostnames are not
180
+ automatically treated as URLs.
181
+
182
+ ### Local IP-location database review
183
+
184
+ `review-evidence query.json --mmdb selected.mmdb --json --strict-exit` reads
185
+ one analyst-selected database locally. It never downloads, updates or transmits
186
+ the database. A current query uses this input:
187
+
188
+ ```json
189
+ {
190
+ "schema": "whoisleuth.local-mmdb-query",
191
+ "version": 2,
192
+ "address": "192.0.2.1",
193
+ "sourceLabel": "Selected local database",
194
+ "databaseVersion": "Analyst-declared edition",
195
+ "license": "Analyst-declared licence",
196
+ "freshnessPolicy": {
197
+ "maxAgeDays": 90,
198
+ "rationale": "Quarterly research review; recheck before operational use"
199
+ }
200
+ }
201
+ ```
202
+
203
+ Replace the reserved example address with the public IP under review. Reserved
204
+ and private addresses are unavailable in current reviews. Select and justify an
205
+ age limit appropriate to the task; the example is not a default or an accuracy
206
+ guarantee. The policy requires positive whole days and a plain-text rationale
207
+ of at most 240 characters. The command's review time is compared with the
208
+ intrinsic build time; a future build, stale database, unsupported format/type,
209
+ miss or unusable record cannot supply current location context. Malformed
210
+ fields remain unknown, with partial coverage when other useful fields survive.
211
+
212
+ Current output retains SHA-256 of the exact read bytes, byte length, intrinsic
213
+ database type, build time, binary format and IP version. Analyst source, version
214
+ and licence labels remain separate claims. Only coarse network, country, region,
215
+ city and ASN attribution can be returned; no raw record or local path is included.
216
+ The existing version-1 review envelope contains the self-described
217
+ `whoisleuth.local-mmdb-review` version-1 result. Reject future result versions
218
+ rather than treating them as current evidence.
219
+
220
+ The file must remain a regular, stable file of at most 512 MiB. After the bounded
221
+ read and digest, one disposable worker limits synchronous parsing and lookup to
222
+ ten seconds, with bounded heap/stack and an 8 KiB result. This is not a ten-second
223
+ whole-command or filesystem deadline. Default exit 0 means a report was produced;
224
+ `--strict-exit` returns 4 for unavailable or partial current reviews. Version-1
225
+ queries retain their historical JSON shape and exit behaviour, without intrinsic
226
+ metadata or freshness admission; terminal output identifies that limitation.
227
+
228
+ ### Contextual reviews
229
+
230
+ `review-evidence` also accepts [contextual review inputs](contextual-reviews.md)
231
+ for source-qualified incident sequences, retained domain history, platform-object continuity, authorised storefront
232
+ comparison and connector provenance. These workflows are offline; partial
233
+ context reviews return 4 when `--strict-exit` is selected. Browser-generated
234
+ reusable inputs use the same validators.
235
+
126
236
  ### Message-header review
127
237
 
128
238
  `mail-headers` parses only the bounded header block from a selected message file
@@ -132,14 +242,362 @@ reported order. It makes no request and does not retain address local parts,
132
242
  display names, subject, body, attachments, or raw header values in its output.
133
243
  Authentication states are header claims, not an independent DNS or
134
244
  cryptographic validation, and alignment differences can be legitimate.
245
+ Both `mail-headers` and `intake email` preserve individual authentication header
246
+ positions and service identifiers. Evaluate the receiver boundary yourself; to
247
+ record trust in one exact header, add `--trusted-auth-header 1:3` (message part 1,
248
+ header 3). Repeat for separate headers. This annotates the supplied claim without
249
+ validating it or extending trust to matching service names or nested messages.
250
+
251
+ `intake pdf selected.pdf`, `intake docx selected.docx` and `intake har selected.har`
252
+ use the same offline review as the browser. Document results include part hashes,
253
+ page references and extraction coverage. Encrypted documents require a separately
254
+ decrypted copy; no password is requested. HAR results preserve file order and
255
+ reported timings, with unavailable values distinct from zero. Neither document
256
+ resources nor recorded requests are fetched. Add `--strict-exit` for exit 4 when
257
+ coverage is partial, and `--json` for the minimised report.
258
+
259
+ `intake identity events.json --json` reads an Entra sign-in `value` array or an
260
+ Okta System Log array. To record a scoped comparison, wrap the selected provider
261
+ records in this input (timestamps require an explicit timezone):
262
+
263
+ ```json
264
+ {
265
+ "schema": "whoisleuth.identity-events.input",
266
+ "version": 1,
267
+ "provider": "entra",
268
+ "events": [],
269
+ "match": {
270
+ "applicationId": "11111111-1111-4111-8111-111111111111",
271
+ "tenantId": null,
272
+ "actorLabel": null,
273
+ "startedAt": "2026-01-01T00:00:00Z",
274
+ "endedAt": "2026-01-01T01:00:00Z"
275
+ }
276
+ }
277
+ ```
278
+
279
+ Replace `events` with the selected records. Actor labels apply only within that
280
+ file; missing fields stay unavailable. The source hash identifies the complete
281
+ selected file, including an envelope when supplied. No provider API is contacted.
135
282
 
136
283
  ## Output and automation
137
284
 
285
+ ### Local Case files
286
+
287
+ Create a working file, inspect its Case IDs, then append a note:
288
+
289
+ ```sh
290
+ whoisleuth case open --domain example.test --title "Review the selected form" --output cases.json
291
+ whoisleuth case show cases.json
292
+ whoisleuth case note cases.json --note-file note.txt --output cases.json --force
293
+ ```
294
+
295
+ Use `--case-id` when the file contains several Cases. `open` reuses the selected
296
+ Case; `--new-incident --title "Another incident"` creates a distinct ID for the
297
+ same domain. It does not open a browser or collect anything.
298
+
299
+ `pin`, `link`, `withdraw-link`, `assess` and `recheck` read a selected JSON file with `--input`:
300
+
301
+ ```sh
302
+ whoisleuth case pin cases.json --input pin.json --output cases.json --force
303
+ whoisleuth case link cases.json --input relationship.json --output cases.json --force
304
+ whoisleuth case assess cases.json --input assessment.json --output cases.json --force
305
+ whoisleuth case recheck cases.json --input recheck.json --output cases.json --force
306
+ ```
307
+
308
+ For `link`, use `fromPinId`, `toPinId`, `kind` (`derived_from` or
309
+ `shared_source`) and `basis`. `withdraw-link` takes the relationship `id` and
310
+ `reason`; its original declaration remains in the file. `case show` lists the
311
+ identities and `--json` includes the full history.
312
+
313
+ A pin describes the supplied observation, not a new collection. For example:
314
+
315
+ ```json
316
+ {
317
+ "label": "Selected page observation",
318
+ "value": "A form was retained in the supplied capture.",
319
+ "source": "Analyst supplied capture",
320
+ "observedAt": "2026-09-01T12:00:00.000Z",
321
+ "completeness": "complete",
322
+ "sourceState": "complete",
323
+ "observationHostname": "example.test"
324
+ }
325
+ ```
326
+
327
+ Supply an observation time only when known; use `null` otherwise. Describe
328
+ partial or unavailable evidence honestly. IDs and creation times are allocated
329
+ locally. Unknown fields, truncated values and unsupported formats are rejected.
330
+
331
+ An assessment includes a reviewed disposition and reason, a summary, a rationale,
332
+ and evidence relationships. Replace `selected-pin-id` with an ID from `case show`:
333
+
334
+ ```json
335
+ {
336
+ "disposition": "suspicious",
337
+ "reviewReasonCode": "other_reviewed",
338
+ "summary": "Review the apparent credential request",
339
+ "rationale": "The supplied observation needs independent corroboration.",
340
+ "evidence": [{ "pinId": "selected-pin-id", "stance": "supports" }]
341
+ }
342
+ ```
343
+
344
+ Each fact selects one retained `pinId` or a new `pin` object. Stances are
345
+ `supports`, `contradicts` or `unresolved`; at least one must support the decision.
346
+ Counterevidence and unresolved evidence remain linked, not discarded. Assessment
347
+ summary and rationale use single-line text; note files can contain paragraphs.
348
+
349
+ A recheck input contains `state`, `observedAt`, `completeness`, `source` and
350
+ `comparisonSummary`, with optional limitations and follow-up time. For example,
351
+ use `state: "unavailable"` and `completeness: "partial"` when a supplied capture
352
+ did not complete. `not_reproduced` requires a saved recheck question from the
353
+ Case's console workflow, its exact `recheck` context, the target
354
+ `observationHostname`, a complete later observation where a baseline exists,
355
+ and `conditionsMatch: "comparable"`. It never means global removal or takedown.
356
+
357
+ Mutations require an explicit output path and write the complete current Case
358
+ export. They never prune old evidence to fit. Input formatting may use up to
359
+ 16 MiB; the canonical store remains limited to 4 MiB and 500 Cases. `show --json`
360
+ can export a selected subset to a **different** path. Ordinary Case imports also
361
+ allow 16 MiB for formatting and export metadata; the stored-data limit remains
362
+ 4 MiB. Case packs retain their separate 25-Case selection and 4-MiB source-file
363
+ limits, and include generated reports within the bounded import allowance.
364
+
365
+ `show` reports the exact file digest. Add `--expect-file-digest sha256:<digest>`
366
+ to reject a file changed since that review. Source/output locks and atomic
367
+ replacement also detect changes during execution. After an interrupted process,
368
+ inspect its adjacent `.workflow.lock` before deliberately removing it. Validation
369
+ or conflict failures leave the Case file unchanged and return 2. Filesystem
370
+ publication failures return 3.
371
+
372
+ Working files contain private analyst content and file references, not attachment
373
+ bytes. Use `case-pack` for a reviewed audience projection and a separate evidence
374
+ package for selected original files. Nothing is uploaded or reported automatically.
375
+
376
+ ### Portable evidence files
377
+
378
+ Package selected files without changing their bytes, then verify the ZIP offline:
379
+
380
+ ```sh
381
+ whoisleuth manifest evidence.json screenshot.png --workflow "Evidence review" \
382
+ --package --output evidence.zip
383
+ whoisleuth verify-artifact evidence.zip --package --json --strict-exit
384
+ whoisleuth manifest evidence.json screenshot.png --workflow "Evidence review" \
385
+ --folder ./evidence-project
386
+ whoisleuth verify-artifact --folder ./evidence-project --json --strict-exit
387
+ ```
388
+
389
+ Packages admit the 128-file, 64-MiB original selection plus one bounded Case
390
+ export, with a separate manifest allowance. Original-file limits remain unchanged.
391
+ `verify-artifact` uses the same exact ordinary Case reader as `case show`,
392
+ without repairing content. Package verification also reports original-reference
393
+ completeness for each Case file; `--strict-exit` returns 4 when a referenced original is missing.
394
+ An ordinary Case JSON file alone provides structural validity, not a checksum.
395
+ They use generated entry names, not original paths. Ordinary ZIPs and folders
396
+ are private and unencrypted; packaging does not redact selected files. The report distinguishes
397
+ file identity, supported source formats, opaque content, exact capsule/source
398
+ links and capture-manifest attachment matches. Include the capture manifest and
399
+ its screenshot/DOM-digest files together to check their declared bytes; original
400
+ filenames are not needed to establish a match. It does not import files or establish source truth, signature trust or a
401
+ trusted timestamp. Unsupported or rejected entries produce a partial report;
402
+ `--strict-exit` returns 4. Without `--package` or `--folder`, `manifest` produces a
403
+ standalone JSON manifest; exact public version-2 manifests remain readable.
404
+
405
+ For [BagIt 1.0](https://www.rfc-editor.org/rfc/rfc8493.html) interchange, use
406
+ `--bagit` with `--package` or `--folder` on both commands:
407
+
408
+ ```sh
409
+ whoisleuth manifest evidence.json screenshot.png --workflow "Evidence review" \
410
+ --bagit --package --output bag.zip
411
+ whoisleuth verify-artifact bag.zip --bagit --package --json --strict-exit
412
+ whoisleuth manifest evidence.json --workflow "Evidence review" --bagit --folder ./bag
413
+ whoisleuth verify-artifact --bagit --folder ./bag --json --strict-exit
414
+ ```
415
+
416
+ BagIt output contains unchanged files under `data/`, SHA-512 payload and tag
417
+ manifests, and the existing source-declaration manifest as a tag file. It is
418
+ unencrypted and cannot be combined with `--passphrase-file`. Verification also
419
+ accepts SHA-256. It checks every manifest, reports absent files and unsupported
420
+ algorithms, and never downloads `fetch.txt` locations. A valid result establishes
421
+ declared byte integrity, not source-format validity, authenticity or factual
422
+ accuracy. Unchecked tags remain explicit; reported entry names are generated.
423
+
424
+ The bounded reader accepts UTF-8 tags, safe relative paths up to 1,024 UTF-8
425
+ bytes and 16 levels, and the same payload byte/file allowances as evidence
426
+ packages. Tags have a separate allowance of 32 files, 512 KiB each and 2 MiB
427
+ combined. Symbolic links, special files, path collisions and unsupported ZIP
428
+ features are refused. BagIt itself does not impose these implementation limits.
429
+
430
+ For an encrypted ordinary package, supply a local passphrase file:
431
+
432
+ ```sh
433
+ whoisleuth manifest evidence.json screenshot.png --workflow "Evidence review" \
434
+ --package --passphrase-file ./package-passphrase.txt --output evidence.wlep
435
+ whoisleuth verify-artifact evidence.wlep --package \
436
+ --passphrase-file ./package-passphrase.txt --json --strict-exit
437
+ ```
438
+
439
+ The file contains one UTF-8 line of at least 12 characters, at most 1,024 bytes.
440
+ Keep it private and separate from the package. Neither command uploads it or
441
+ includes it in its report. Version-1 encrypted packages protect the entire ZIP,
442
+ including its manifest. Unlocking authenticates the container before validating
443
+ file identities. The report's package digest identifies the decrypted ZIP;
444
+ its input byte count identifies the encrypted file. Wrong passphrases, corrupted
445
+ containers and unsupported versions exit 3 without a verification report.
446
+ Encryption does not establish authorship, factual accuracy or redaction, and
447
+ does not apply to folder exports or separately downloaded JSON backups.
448
+
449
+ Folders contain `manifest.json` and generated `artifacts/artifact-N` files.
450
+ Creation requires a new destination and never replaces an existing folder.
451
+ Failed writes can leave partial private output; inspect or remove it before
452
+ choosing another destination. Verification rejects symbolic links, unexpected
453
+ trees and unlisted files. Its package digest and byte count describe the
454
+ canonical stored-ZIP representation, not filesystem metadata. These are
455
+ selected evidence exports, not complete workspace backups.
456
+
457
+ ### Resuming a fixed workflow
458
+
459
+ Inspect a paused run without executing any step or rewriting its checkpoint:
460
+
461
+ ```sh
462
+ whoisleuth workflow-run domain-triage example.test --resume run.json --preview --json
463
+ ```
464
+
465
+ The preview separates validated complete and partial outputs, failed steps that
466
+ can be retried, unresolved inputs, pending upstream outputs and fresh approval
467
+ requirements. It does not open selected input files. Preview output goes to
468
+ stdout; approval, interactive and output-file flags cannot be combined with it.
469
+
470
+ All recipes listed by `workflow-plan --list` can run through `workflow-run`.
471
+ Planning remains offline. Execution emits a checkpoint; terminal output shows
472
+ retained steps, output identities, missing inputs and the next required action.
473
+ A partial collection
474
+ pauses for review; resuming keeps that observation and does not collect it
475
+ again. Later steps can finish without making the earlier evidence complete:
476
+ the run still exits with code 4. Validation, usage and export failures remain
477
+ failures and are retried on resume. Step diagnostics stay on stderr, separate
478
+ from checkpoint JSON. New network steps still need `--approve-network`.
479
+
480
+ New runs connect compatible earlier outputs for domain triage, registry review,
481
+ historical comparison and handoff linting. For a complete domain-triage hand-off:
482
+
483
+ ```sh
484
+ whoisleuth workflow-run domain-triage example.test --approve-network \
485
+ --json --output run.json
486
+ ```
487
+
488
+ Bindings accept Lookup outputs for export, diff, timeline, comparison, source
489
+ reports and briefs; evidence exports for verification; and Case-pack output
490
+ for sharing review. Remaining placeholders use `--select` in order;
491
+ for example, `--select diff=previous.json` keeps the current observation as the
492
+ second input. Supply every input for an uncompleted step to use files instead,
493
+ or override a connection with `--use-artifact <step-id>:<input-number>=<earlier-step-id>`.
494
+ Input numbers start at 1 and refer to the fixed recipe's placeholders.
495
+ `diff` compares saved observations of the same or different domains; `timeline`
496
+ orders observations of one domain. Candidate and domain-control intent inputs
497
+ remain analyst selections. No external file is inferred or extracted automatically.
498
+ `reconcile` lists each observation time and declared observer/vantage, keeping
499
+ disagreement and unavailable evidence separate. Distinct labels do not verify
500
+ independent collection; timing and shared caches can affect the comparison.
501
+
502
+ `domain-control-flight-recorder` retains the last comparable complete observation
503
+ across failed or partial collections. A changed recovery records the interval
504
+ between complete source observations; it does not assign the change to the
505
+ recovery time. An approved window must contain that entire interval. Historical
506
+ report versions remain readable without acquiring this newer qualification.
507
+
508
+ For example, registry review can reuse its collection without extracting files:
509
+
510
+ ```sh
511
+ whoisleuth workflow-run registry-disagreement example.test --approve-network \
512
+ --json --output registry-run.json
513
+ ```
514
+
515
+ The evidence-handoff recipe pauses before any human-review declaration. Review
516
+ the selected material and the listed declarations, then use
517
+ `--confirm-review package` or `--confirm-review lint` for that particular step.
518
+ Confirmation applies only to the current invocation and is not inferred from
519
+ the checkpoint. A completed recipe means its commands ran, not that an
520
+ investigation is resolved or sharing is authorised.
521
+
522
+ Optional `--interactive` asks for missing paths or values on terminal stderr,
523
+ keeping checkpoint JSON on stdout or in the selected output file. A blank answer
524
+ pauses. Prompts do not grant network approval or confirm review. Redirected input
525
+ and unattended runs use `--select` instead. Resuming a checkpoint preserves its
526
+ recorded connections; new defaults are not applied to old checkpoints.
527
+
528
+ Checkpoint version 3 reads versions 1 and 2. Older installations reject version
529
+ 3. Checkpoints retain exact output schemas, content digests and input bindings;
530
+ changed identities or completed inputs are rejected. Digests identify content,
531
+ not source authenticity, truth or freshness. The checkpoint can contain
532
+ selected local paths and evidence; review it before sharing.
533
+
534
+ Use `--json --output state.json` for a resumable file, and add `--force` when
535
+ replacing it. File output holds private adjacent `.workflow.lock` files for
536
+ the selected resume and output paths until publication. Another writer is
537
+ refused; changes to either file during execution prevent replacement. State
538
+ files must be regular files without symbolic or additional hard links.
539
+
540
+ Locks contain only a local process ID and are removed after the run. Following
541
+ an interrupted process, confirm that its owner has stopped before removing its
542
+ abandoned lock. Locks have no automatic expiry. Shell redirection and external
543
+ editors do not participate in this cooperative file-ownership protocol.
544
+
545
+ ### Managed indicator revisions
546
+
547
+ `indicator-set` uses the same file-backed revision model as Bulk. It performs
548
+ no collection or submission. A plan names the set, gives a review `basis` and
549
+ UTC `expiresAt`, and supplies `rows` plus an explicit `selectedDomains` list.
550
+ Rows use the normal Bulk eligibility rules, including available profile
551
+ context, a Suspicious or Confirmed abuse disposition and retained risk evidence.
552
+ Supply `officialDomains`, `allowlistedDomains` and `commonInfrastructureDomains`
553
+ where applicable; excluded candidates never become additions.
554
+
555
+ Example plan shape (fictional input; replace the evidence and expiry):
556
+
557
+ ```json
558
+ {
559
+ "name": "Reviewed candidates",
560
+ "basis": "Reviewed the selected page observation",
561
+ "expiresAt": "2026-10-23T00:00:00.000Z",
562
+ "selectedDomains": ["candidate.example.test"],
563
+ "rows": [{
564
+ "domain": "candidate.example.test", "availability": "registered",
565
+ "analystDisposition": "suspicious", "risk": 80,
566
+ "profileContext": { "sourceState": "ready" }
567
+ }]
568
+ }
569
+ ```
570
+
571
+ ```sh
572
+ whoisleuth indicator-set revise indicator-plan.json --json --output indicators-r1.json
573
+ whoisleuth indicator-set inspect indicators-r1.json
574
+ whoisleuth verify-artifact indicators-r1.json --strict-exit
575
+ whoisleuth indicator-set stix indicators-r1.json --output indicators-r1.stix.json
576
+ whoisleuth indicator-set misp indicators-r1.json --output indicators-r1.misp.json
577
+ ```
578
+
579
+ Later plans embed the last manifest as `previous` and explicitly list `renewIds`
580
+ or `withdrawIds`. New domains still require reviewed rows and selection.
581
+ `reintroduceDomains` permits a previously withdrawn domain to receive a new
582
+ identity; it never revives the old one. Missing domains stay unchanged.
583
+ Renewal updates the review window and note, not the original observation.
584
+
585
+ Keep previous manifests and use separate output paths. File ownership checks
586
+ reject concurrent source/destination changes and refuse replacing the input,
587
+ even with `--force`. STIX and MISP operations always emit JSON. MISP expiry is
588
+ a review deadline, not automatic deletion; exports require separate recipient
589
+ review. Digests verify content, not authorship or an unavailable earlier chain.
590
+
591
+ ### Formats and exit behaviour
592
+
138
593
  Terminal text is the default. Commands expose JSON, JSONL, CSV, Markdown, HTML
139
594
  or domain-only output only where declared by the installed registry. Redirected
140
595
  and machine output contains no ANSI or transient progress text.
141
596
 
142
597
  - `--summary` and `--verbose` change presentation, not collection.
598
+ Verbose Lookup includes retained technology signals and library advisory
599
+ identifiers. Their source times are separate; ages are measured at output
600
+ generation, not at a later replay.
143
601
  - `lookup --browse` provides an interactive terminal view; press `?` for keys.
144
602
  - `--events` writes versioned lifecycle events to stderr.
145
603
  - `--strict-exit` and `--fail-on` expose selected evidence states to automation.
@@ -151,17 +609,44 @@ concurrency and observer labels. They cannot add targets, enable Deep
151
609
  collection, choose output paths or approve network work. Explicit command
152
610
  options override profile defaults.
153
611
 
612
+ `source-report` summarises collector-owned health envelopes in saved Lookup
613
+ and Bulk output. Raw publications and extension fields are not interpreted as
614
+ diagnostics, and missing health metadata remains unmeasured.
615
+
616
+ `mail-review` keeps failed input targets, incomplete DNS coverage and original
617
+ collection times visible. Source-output and review-generation times do not
618
+ stand in for missing observation times. `domain-change` and
619
+ `domain-change-packet` retain authority, resolver and certificate observation
620
+ times; previously published version-2 and version-3 packets remain verifiable.
621
+ Current DNS convergence and domain-change inputs use version 2. Each snapshot
622
+ declares `queries`, for example
623
+ `[{"owner":"example.test","type":"MX","state":"observed"}]`.
624
+ An `observed` query with no matching records means an empty answer; `partial`
625
+ and `unavailable` queries do not. Omitted queries are not queried. Version-1
626
+ inputs remain readable but establish query scope only for supplied records.
627
+ An expected empty set alone cannot establish an observed empty answer.
628
+
629
+ Domain-control review version 3 keeps renewal reminders separate from observed
630
+ configuration: `due` and `not_due` describe the chosen review date, not DNS or
631
+ registration changes. Incomplete evidence remains `partial` even when a reminder
632
+ is due. Terminal and JUnit summaries report due reminders separately. Previous
633
+ review and monitor checkpoints retain their original versioned interpretation.
634
+
154
635
  ## Exit codes
155
636
 
156
- | Code | Meaning |
157
- | ---: | --- |
158
- | 0 | The command completed. |
159
- | 2 | Invalid command, option or input. |
160
- | 3 | Collection, lookup or comparison failed. |
161
- | 4 | The result was partial or a selected evidence policy was not met. |
162
- | 70 | Internal CLI bootstrap failure. |
163
- | 130 | The analyst cancelled the command. |
164
- | 143 | The process received SIGTERM. |
637
+ See the [exit-code reference](cli-reference.md#files-output-and-automation).
638
+ A completed command can still contain partial sources; use the selected
639
+ command's strict-exit policy when automation requires complete evidence.
640
+
641
+ For scripts, use `whoisleuth verify-artifact evidence.json --json --strict-exit`.
642
+ An incomplete verification returns **4** with that option. Without it, **0**
643
+ means a report was produced, not that the artefact passed verification; inspect
644
+ the report's state and checks.
645
+ Unencrypted workspace archives retain mutable root metadata outside their
646
+ section digests, so they return 4 under `--strict-exit` even when all supported
647
+ section checks pass. Structure-only exports can pass their narrower contract;
648
+ strict exit is not a ranking of cryptographic assurance. `interchange-report`
649
+ separately describes import/export support and is not the full verifier inventory.
165
650
 
166
651
  ## Command details
167
652