@slicedearth/whoisleuth-cli 1.47.4 → 2.0.0

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 (235) hide show
  1. package/README.md +74 -177
  2. package/SECURITY.md +6 -3
  3. package/bin/whoisleuth.mjs +2 -1
  4. package/cli/archive-inspect.mjs +28 -10
  5. package/cli/arguments.mjs +204 -174
  6. package/cli/artifact-structure.mjs +837 -163
  7. package/cli/artifact-verify.mjs +90 -61
  8. package/cli/bounded-json.mjs +1 -1
  9. package/cli/bulk-checkpoint.mjs +33 -20
  10. package/cli/bulk.mjs +9 -2
  11. package/cli/case-pack.mjs +180 -576
  12. package/cli/ci-report.mjs +6 -1
  13. package/cli/collection-preflight.mjs +2 -2
  14. package/cli/command-catalogue.mjs +12 -3
  15. package/cli/command-reference.mjs +910 -92
  16. package/cli/completion.mjs +504 -226
  17. package/cli/config-profile.mjs +13 -3
  18. package/cli/discover.mjs +7 -1
  19. package/cli/discovery-observation-snapshot.mjs +24 -28
  20. package/cli/discovery-scan-command-runner.mjs +9 -9
  21. package/cli/discovery-scan.mjs +17 -3
  22. package/cli/discovery-snapshot.mjs +15 -7
  23. package/cli/domain-control-monitor.mjs +203 -30
  24. package/cli/domain-control-observations.mjs +15 -21
  25. package/cli/errors.mjs +8 -3
  26. package/cli/evidence-signing.mjs +10 -20
  27. package/cli/external-observation-mapping.mjs +3 -3
  28. package/cli/formatters/json.mjs +20 -32
  29. package/cli/formatters/terminal.mjs +583 -22
  30. package/cli/input.mjs +7 -2
  31. package/cli/interactive-launcher.mjs +226 -0
  32. package/cli/interchange-report.mjs +19 -19
  33. package/cli/investigation-manifest.mjs +1 -1
  34. package/cli/investigation-plan.mjs +167 -16
  35. package/cli/investigation-run.mjs +9 -1
  36. package/cli/lookalike-calibration.mjs +3 -1
  37. package/cli/lookup-brief.mjs +13 -8
  38. package/cli/lookup-browser.mjs +844 -0
  39. package/cli/lookup-command-runner.mjs +94 -31
  40. package/cli/lookup-diff.mjs +2 -2
  41. package/cli/lookup-plan.mjs +1 -0
  42. package/cli/mail-review.mjs +206 -94
  43. package/cli/manual.mjs +5 -5
  44. package/cli/offline-evidence-review.mjs +18 -7
  45. package/cli/open-asset-model-bridge.mjs +1 -1
  46. package/cli/output-file.mjs +70 -14
  47. package/cli/page-compare.mjs +11 -6
  48. package/cli/progress-events.mjs +2 -1
  49. package/cli/progress.mjs +3 -3
  50. package/cli/registry-cohort.mjs +9 -51
  51. package/cli/registry-doctor.mjs +67 -28
  52. package/cli/retained-artifact-diff.mjs +7 -8
  53. package/cli/risk-calibration.mjs +85 -38
  54. package/cli/runner.mjs +189 -75
  55. package/cli/saved-lookup.mjs +424 -57
  56. package/cli/sharing-review.mjs +6 -1
  57. package/cli/source-reliability.mjs +75 -36
  58. package/cli/terminal-presentation.mjs +43 -18
  59. package/docs/cli-reference.md +125 -1708
  60. package/docs/cli.md +86 -254
  61. package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
  62. package/frontend/src/lib/analysis/domain-control-records.js +3 -88
  63. package/lib/availability.mjs +58 -40
  64. package/lib/bounded-contract-normalizers.mjs +2 -4
  65. package/lib/bounded-file.mjs +78 -4
  66. package/lib/bounded-json.mjs +51 -13
  67. package/lib/browser-library-profile.mjs +1 -1
  68. package/lib/classify.mjs +56 -5
  69. package/lib/client-behavior-profile.mjs +1 -1
  70. package/lib/credential-surface-profile.mjs +1 -1
  71. package/lib/cryptographic-assurance.mjs +35 -17
  72. package/lib/ct-query.mjs +3 -3
  73. package/lib/ct-search.mjs +37 -21
  74. package/lib/distributed-operation-budget.mjs +4 -2
  75. package/lib/dns-delegation-health.mjs +9 -6
  76. package/lib/dns-intelligence.mjs +1 -1
  77. package/lib/dnssec-chain-validation.mjs +2 -3
  78. package/lib/dnssec-evidence-validation.mjs +13 -7
  79. package/lib/domain-assurance.mjs +12 -12
  80. package/lib/domain-change-packet.mjs +1 -1
  81. package/lib/domain-control-flight-recorder.mjs +442 -78
  82. package/lib/domain-control-manifest.mjs +314 -59
  83. package/lib/domain-posture-analysis.mjs +5 -2
  84. package/lib/encrypted-dns-contract.mjs +2 -3
  85. package/lib/evidence-export.mjs +518 -36
  86. package/lib/external-intelligence-risk.mjs +26 -10
  87. package/lib/feature-policy.mjs +1 -1
  88. package/lib/homepage-metadata-contract.mjs +290 -0
  89. package/lib/hostname.mjs +1 -1
  90. package/lib/html-signals.mjs +89 -5
  91. package/lib/http-intelligence.mjs +331 -2
  92. package/lib/interchange-fidelity-registry.mjs +28 -26
  93. package/lib/lookup-cache.mjs +6 -4
  94. package/lib/lookup.mjs +25 -2
  95. package/lib/observed-network-context.mjs +1 -1
  96. package/lib/page-fingerprints.mjs +2 -2
  97. package/lib/page-role-profile.mjs +1 -1
  98. package/lib/portable-generator.mjs +8 -0
  99. package/lib/provider-policy-admission.mjs +3 -1
  100. package/lib/rdap-bootstrap.mjs +76 -16
  101. package/lib/rdap-client.mjs +20 -12
  102. package/lib/rdap-normalization.mjs +306 -75
  103. package/lib/rdap-transport.mjs +2 -2
  104. package/lib/rdap-validation.mjs +65 -14
  105. package/lib/rdap.mjs +2 -2
  106. package/lib/registrable-domain.mjs +36 -0
  107. package/lib/registry-insights.mjs +7 -0
  108. package/lib/response-policy.mjs +27 -4
  109. package/lib/risk-calibration-summary.mjs +35 -33
  110. package/lib/risk-scoring.mjs +4 -3
  111. package/lib/safe-fetch.mjs +62 -40
  112. package/lib/security-txt.mjs +1 -1
  113. package/lib/smtp-transport-review.mjs +8 -5
  114. package/lib/sslbl-intelligence.mjs +6 -8
  115. package/lib/static-html-analysis.mjs +301 -4
  116. package/lib/structured-data-identity.mjs +51 -19
  117. package/lib/threat-intelligence-fixtures.mjs +2 -4
  118. package/lib/threat-intelligence-manifests.mjs +2 -7
  119. package/lib/threat-intelligence-runtime.mjs +3 -5
  120. package/lib/threat-intelligence-types.mjs +3 -0
  121. package/lib/threatfox-intelligence.mjs +7 -3
  122. package/lib/tls-intelligence.mjs +4 -4
  123. package/lib/urlhaus-intelligence.mjs +4 -2
  124. package/lib/urlscan-intelligence.mjs +6 -5
  125. package/lib/website-security-posture.mjs +1 -1
  126. package/lib/website-technology.mjs +1 -1
  127. package/lib/whois-authority.mjs +43 -8
  128. package/lib/whois-chain.mjs +25 -3
  129. package/lib/whois-normalization.mjs +3 -1
  130. package/lib/whois-transport.mjs +12 -3
  131. package/lib/zone-intent-review.mjs +1 -1
  132. package/package.json +12 -4
  133. package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
  134. package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
  135. package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
  136. package/packages/cases/case-migration-model.mjs +476 -0
  137. package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
  138. package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
  139. package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
  140. package/packages/cases/case-record-model.mjs +9 -0
  141. package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
  142. package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
  143. package/packages/cases/case-response-model.mjs +1604 -0
  144. package/packages/cases/case-response-packet.mjs +1312 -0
  145. package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
  146. package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
  147. package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
  148. package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
  149. package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
  150. package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
  151. package/packages/comparison/favicon-similarity.mjs +78 -0
  152. package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
  153. package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
  154. package/packages/contracts/analyst-review-state-contract.mjs +61 -0
  155. package/packages/contracts/analyst-review-state.mjs +50 -0
  156. package/packages/contracts/case-portability.mjs +1310 -0
  157. package/packages/contracts/cli-command-catalogue.mjs +2 -0
  158. package/packages/contracts/cli-lookup.mjs +394 -0
  159. package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
  160. package/packages/contracts/domain-control-manifest.mjs +1027 -0
  161. package/packages/contracts/domain-control-monitor.mjs +310 -0
  162. package/packages/contracts/domain-control-review.mjs +828 -0
  163. package/packages/contracts/domain-name.mjs +4 -0
  164. package/packages/contracts/external-observation-interchange.mjs +224 -0
  165. package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
  166. package/packages/contracts/investigation-portability.mjs +331 -0
  167. package/packages/contracts/offline-comparison.mjs +160 -0
  168. package/packages/contracts/risk-calibration.mjs +1288 -0
  169. package/packages/contracts/schema-compatibility.mjs +71 -0
  170. package/packages/contracts/schema-lifecycle.mjs +1702 -0
  171. package/packages/contracts/sslbl-snapshot.mjs +17 -0
  172. package/packages/contracts/workspace-portability.mjs +1347 -0
  173. package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
  174. package/packages/evidence/decision-fact.mjs +735 -0
  175. package/packages/evidence/domain-control-runtime.mjs +547 -0
  176. package/packages/evidence/domain-name.mjs +41 -0
  177. package/packages/evidence/observation.mjs +384 -0
  178. package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
  179. package/packages/interchange/retained-text.mjs +8 -0
  180. package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
  181. package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
  182. package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
  183. package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
  184. package/packages/investigation/bulk-review-policy.mjs +1 -0
  185. package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
  186. package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
  187. package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
  188. package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
  189. package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
  190. package/packages/investigation/lookup-investigation-brief.mjs +172 -0
  191. package/packages/monitoring/analyst-review-state.mjs +680 -0
  192. package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
  193. package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
  194. package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
  195. package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
  196. package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
  197. package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
  198. package/packages/workspace/hostile-input.mjs +172 -0
  199. package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
  200. package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
  201. package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
  202. package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
  203. package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
  204. package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
  205. package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
  206. package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
  207. package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
  208. package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
  209. package/third-party-notices.txt +14 -14
  210. package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
  211. package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
  212. package/frontend/src/lib/analysis/case-migration-model.js +0 -305
  213. package/frontend/src/lib/analysis/case-record-model.js +0 -9
  214. package/frontend/src/lib/analysis/case-response-model.js +0 -728
  215. package/frontend/src/lib/analysis/case-response-packet.js +0 -583
  216. package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
  217. package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
  218. package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
  219. package/frontend/src/lib/analysis/lookup-response.js +0 -2
  220. package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
  221. package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
  222. package/frontend/src/lib/analysis/scoring.js +0 -55
  223. package/frontend/src/lib/analysis/utils.js +0 -255
  224. package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
  225. package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
  226. package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
  227. package/frontend/src/lib/analysis/visualization-models.js +0 -338
  228. package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
  229. package/lib/bounded-json-response.mjs +0 -170
  230. package/lib/lookup-response-contract.mjs +0 -1034
  231. package/lib/observation.mjs +0 -115
  232. /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
  233. /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
  234. /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
  235. /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
@@ -1,25 +1,18 @@
1
1
  # WHOISleuth CLI reference
2
2
 
3
- This complete reference documents every supported command contract. For a
4
- short installation and first-use path, start with the [CLI guide](cli.md).
3
+ The WHOISleuth CLI runs locally. Installed `whoisleuth <command> --help`,
4
+ `whoisleuth commands` and `whoisleuth manual` are the exact grammar, option and
5
+ command authorities for that installed version. The public
6
+ [CLI reference](https://whoisleuth.com/cli) provides a searchable view generated
7
+ from the same registry.
5
8
 
6
- The first-party command-line interface runs the same local classification and
7
- lookup modules as the Express and serverless adapters. It does not call the
8
- hosted WHOISleuth deployment.
9
-
10
- `whoisleuth --help` displays the copyright, AGPL-3.0-only licence, and official
11
- source location. It groups commands by investigation, discovery, saved-evidence,
12
- integrity, and calibration workflows. `whoisleuth <command> --help` displays the
13
- command's purpose, focused invocation, example, and collection boundary without
14
- printing the full command list. `whoisleuth commands --json` exposes those same
15
- contracts as a stable local catalogue for wrappers and terminal tooling.
16
- Packaged copies include `LICENSE`, `NOTICE`, and
17
- `TRADEMARKS.md` alongside the CLI documentation.
9
+ This page records the common interfaces and the boundaries that matter across
10
+ commands. The generated [privacy/data-flow catalogue](https://github.com/slicedearth/whoisleuth/blob/main/docs/privacy-data-flow-catalogue.md)
11
+ contains the exhaustive recipient, retention and export metadata.
18
12
 
19
13
  ## Installation
20
14
 
21
- Public releases require Node.js 24 or later and install the `whoisleuth`
22
- command:
15
+ Public releases require Node.js 24 or later:
23
16
 
24
17
  ```bash
25
18
  npm exec --yes --ignore-scripts --package=@slicedearth/whoisleuth-cli -- whoisleuth --help
@@ -27,1703 +20,127 @@ npm install --global --ignore-scripts @slicedearth/whoisleuth-cli
27
20
  whoisleuth doctor
28
21
  ```
29
22
 
30
- Update and verify an existing global installation with:
31
-
32
- ```bash
33
- npm install --global --ignore-scripts @slicedearth/whoisleuth-cli@latest
34
- whoisleuth --version
35
- ```
23
+ The scoped package and application share one semantic version. The package
24
+ requires no dependency lifecycle scripts and does not call the hosted
25
+ WHOISleuth deployment. From a repository checkout, replace `whoisleuth` with
26
+ `node bin/whoisleuth.mts`.
36
27
 
37
- The scoped package and application share one semantic version. The package runs
38
- locally and does not call the hosted WHOISleuth deployment. Networked commands
39
- contact only the sources described by their collection profile; offline review,
40
- comparison, verification, completion, and manual commands remain local.
41
-
42
- ## Commands
28
+ ## Find and inspect commands
43
29
 
44
30
  ```bash
45
- node bin/whoisleuth.mts lookup example.com
46
- node bin/whoisleuth.mts lookup AS13335 --json
47
- printf 'example.com\n' | node bin/whoisleuth.mts lookup --json
48
- node bin/whoisleuth.mts lookup example.com --deep
49
- node bin/whoisleuth.mts lookup example.com --deep --plan --json
50
- node bin/whoisleuth.mts lookup example.com --deep --summary
51
- node bin/whoisleuth.mts lookup example.com --deep --verbose
52
- node bin/whoisleuth.mts lookup example.com --deep --markdown --output lookup.md
53
- node bin/whoisleuth.mts lookup example.com --deep --json --strict-exit --events
54
- node bin/whoisleuth.mts lookup example.test --deep --observer workstation-a --vantage office-egress --json > office.json
55
- cat domains.txt | node bin/whoisleuth.mts bulk --jsonl
56
- node bin/whoisleuth.mts bulk domains.txt --csv --registered-only
57
- node bin/whoisleuth.mts bulk domains.txt --domains --inconclusive-only
58
- node bin/whoisleuth.mts bulk domains.txt --queries --errors-only
59
- node bin/whoisleuth.mts bulk domains.txt --concurrency 4
60
- node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json
61
- node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json --resume
62
- node bin/whoisleuth.mts ct-search 'example brand' --json
63
- node bin/whoisleuth.mts discover example.com --preset common --jsonl
64
- node bin/whoisleuth.mts discover example.test --dictionary private-terms.txt --snapshot discovery-state.json --json
65
- node bin/whoisleuth.mts discover-scan example.test --scan-limit 50 --checkpoint candidate-scan.json --json
66
- node bin/whoisleuth.mts discover-scan example.test --deep --scan-limit 20 --resolver <public-resolver-ip> --observation-snapshot observed-candidates.json --csv
67
- node bin/whoisleuth.mts posture example.com --selectors selector1 --retired-selectors selector0 --mail-profile defensive-no-mail --json
68
- node bin/whoisleuth.mts http example.com --json
69
- node bin/whoisleuth.mts tls example.com --json
70
- node bin/whoisleuth.mts registry-support example.uk --json
71
- node bin/whoisleuth.mts registry-doctor lookup.json --json
72
- node bin/whoisleuth.mts registry-cohort saved-lookups.jsonl --json
73
- node bin/whoisleuth.mts registry-scaffold --profile example-profile --suffix test --scenario registered
74
- node bin/whoisleuth.mts risk-calibrate calibration.json --json
75
- node bin/whoisleuth.mts risk-calibrate calibration.json --summary-json
76
- node bin/whoisleuth.mts lookalike-calibrate reviewed-candidates.json --json
77
- node bin/whoisleuth.mts verify-artifact workspace.json --json
78
- node bin/whoisleuth.mts verify-artifact lookup.json --json --strict-exit
79
- node bin/whoisleuth.mts verify-artifact workspace-encrypted.json --passphrase-file passphrase.txt --json
80
- node bin/whoisleuth.mts source-report lookup.json --json
81
- node bin/whoisleuth.mts inspect-archive workspace.json --json
82
- node bin/whoisleuth.mts inspect-archive workspace.json --search example.invalid --json
83
- node bin/whoisleuth.mts sign-artifact response-packet.json --private-key-file analyst-private.pem > response-packet.signed.json
84
- node bin/whoisleuth.mts ct-intake certificate-events.json --json > external-findings.json
85
- node bin/whoisleuth.mts map-observations observation-mapping.json --json > external-findings.json
86
- node bin/whoisleuth.mts oam-export external-findings.json --json > asset-bridge.json
87
- node bin/whoisleuth.mts manifest lookup.json comparison.json --workflow 'domain review' --json > investigation-manifest.json
88
- node bin/whoisleuth.mts verify-signature response-packet.signed.json --public-key-file analyst-public.pem
89
- node bin/whoisleuth.mts lookup example.com --deep --json > lookup.json
90
- node bin/whoisleuth.mts compare lookup.json --json
91
- node bin/whoisleuth.mts page-compare official.json candidate.json --json
92
- node bin/whoisleuth.mts mail-review bulk.json --json
93
- node bin/whoisleuth.mts dnssec-validate example.test --resolver "$PUBLIC_RESOLVER_IP" --trust-anchor anchor.json --owned-or-authorized --json
94
- node bin/whoisleuth.mts mail-transport selected-mx.json --resolver "$PUBLIC_RESOLVER_IP" --trust-anchor anchor.json --owned-or-authorized --active-probe --json
95
- node bin/whoisleuth.mts review-evidence dnssec-evidence.json --json
96
- node bin/whoisleuth.mts review-evidence trust-store-comparison.json --json
97
- node bin/whoisleuth.mts domain-control domain-control-input.json --json
98
- node bin/whoisleuth.mts assurance domain-assurance-input.json --json
99
- node bin/whoisleuth.mts change-packet domain-change-packet-input.json --json
100
- node bin/whoisleuth.mts sharing-review response-packet.json --marking amber --recipient-scope organization --purpose 'Reviewed incident handoff' --human-reviewed --personal-data-reviewed --redactions-confirmed --json
101
- node bin/whoisleuth.mts workflow-plan domain-triage example.test --json
102
- node bin/whoisleuth.mts workflow-run domain-triage example.test --approve-network --json --output run.json
103
- node bin/whoisleuth.mts brief lookup.json
104
- node bin/whoisleuth.mts case-pack cases.json --audience trusted --reviewed --json
105
- node bin/whoisleuth.mts monitor-once control-manifest.json --previous prior-control.json --junit
106
- node bin/whoisleuth.mts diff first-lookup.json second-lookup.json --json
107
- node bin/whoisleuth.mts reconcile office.json mobile.json external.json --json
108
- node bin/whoisleuth.mts timeline first-observation.json second-observation.json latest-observation.json --json
109
- node bin/whoisleuth.mts export lookup.json > evidence.json
110
- node bin/whoisleuth.mts export lookup.json --markdown > evidence.md
111
- node bin/whoisleuth.mts export lookup.json --html > evidence.html
112
- node bin/whoisleuth.mts doctor
113
- node bin/whoisleuth.mts doctor --network --json
114
- node bin/whoisleuth.mts commands --json
115
- node bin/whoisleuth.mts completion zsh
116
- node bin/whoisleuth.mts completion powershell
117
- node bin/whoisleuth.mts manual | man -l -
118
- ```
119
-
120
- These examples run from a checked-out repository; replace
121
- `node bin/whoisleuth.mts` with `whoisleuth` after installation. The root
122
- application package remains private. A separate scoped CLI archive is assembled
123
- from the exact executable dependency closure, installed in a temporary
124
- directory, and smoke tested with:
125
-
126
- ```bash
127
- npm run cli:package:check
128
- ```
129
-
130
- The check includes only the executable, reachable TypeScript modules, CLI
131
- guide, licence, notices, and trademark terms. It excludes application routes,
132
- deployment adapters, tests, and development tools. Direct runtime dependencies
133
- are pinned to the exact versions exercised by the reviewed repository lockfile.
134
- The package and its dependencies require no lifecycle scripts, so the install
135
- examples disable them explicitly.
136
- Its generated manifest stays private. Release-candidate assembly is a separate
137
- explicit operation and never publishes from a developer checkout. Use the
138
- scoped package name; an unqualified `npx whoisleuth` is not this project.
139
-
140
- Lookup defaults to the conservative fast profile. `--deep` must be requested
141
- explicitly and can add bounded WHOIS, DNS, website, TLS, registrar RDAP, and
142
- observed-network IP RDAP work for a domain. The browser Console's optional
143
- security.txt and external intelligence selections are not CLI flags; the CLI
144
- does not run those actions implicitly.
145
-
146
- For a directly entered public IP address, `lookup --deep` can also run one
147
- bounded reverse-DNS query. Terminal output shows the explicit source state and
148
- up to five normalised PTR names; JSON retains up to eight. PTR names are
149
- operator-published routing context, not proof of hosting control or ownership.
150
- Fast lookups do not run this query.
151
-
152
- `lookup --plan` performs classification and prints a versioned preflight without
153
- starting collection. It lists the normalised target, selected fast or deep
154
- mode, planned source families, conditional operations, and the data each family
155
- may receive. The plan cannot predict live feature configuration, cache state,
156
- redirects, referrals, source availability, or an exact request count. It can be
157
- combined with `--json`, but not with report, detail, strict-exit, progress-event,
158
- or quiet options.
159
-
160
- Only one query is accepted by `lookup`. Multiple-input processing belongs to
161
- the explicit `bulk` command rather than being silently inferred by `lookup`.
162
- Standard input is capped at 4 KiB and must contain one non-empty line.
163
-
164
- `registry-support` is an offline catalogue view. It accepts one domain, bare
165
- suffix, or leading-dot suffix as an argument or on stdin and makes no network
166
- request. Its output is described below.
167
-
168
- `doctor` is offline by default. It checks the Node runtime, platform, terminal
169
- capabilities, and availability of offline command families. `doctor --network`
170
- adds independent bounded public-DNS, HTTPS/RDAP bootstrap, and WHOIS port 43
171
- checks against fixed IANA diagnostic infrastructure. The three probes run
172
- within the same per-probe ceiling so one stalled transport does not block the
173
- others. The report retains only state, HTTP status, and a bounded explanation,
174
- not resolved addresses or response content. A failed optional network check
175
- returns the partial-failure exit code rather than claiming that all CLI
176
- collection is unavailable.
177
-
178
- `completion bash`, `completion zsh`, `completion fish`, and `completion
179
- powershell` print static shell completion scripts to stdout. The command does
180
- not edit a shell profile or make a network request. Review the generated script
181
- before sourcing it or placing it in the relevant shell completion directory.
182
- `manual` prints a generated roff manual page and likewise changes no local
183
- configuration.
184
-
185
- `commands` prints the installed command catalogue without running collection or
186
- reading evidence. `commands --json` emits version 1 of the
187
- `whoisleuth.cli.command-catalogue` schema, including each command's usage,
188
- example, declared collection mode, collection scope, and boundary. This is the
189
- supported discovery contract for local wrappers; it is not a permission grant
190
- or a substitute for focused help.
191
-
192
- Focused command help and the generated manual classify every operation as
193
- offline or networked and state the relevant target, input, and concurrency
194
- ceiling. These labels describe observable behaviour rather than granting
195
- authorisation. The packaged `DISCLOSURE` defines the intended defensive scope;
196
- operators remain responsible for law, provider terms, and authorisation.
197
-
198
- ## Deployment boundary
199
-
200
- The CLI runs locally. The serverless deployment publishes only `frontend/build`
201
- and packages functions only from `netlify/functions`, so `bin/` and `cli/` are
202
- not part of the hosted static site or function bundle. The application root and
203
- ordinary package-check candidate remain private. Explicitly reviewed CLI
204
- release candidates can be published under the scoped public package described
205
- in the installation section.
206
-
207
- Commands that query RDAP, WHOIS, DNS, HTTP, TLS, SMTP, or Certificate Transparency do
208
- so directly from the machine running the CLI. They do not use the hosted login,
209
- hosted session, or deployment usage controls; upstream providers can see and
210
- rate-limit the local machine's network address. Offline `discover`, `compare`,
211
- `page-compare`, `mail-review`, `review-evidence`, `domain-control`, `assurance`, `change-packet`,
212
- `sharing-review`, `workflow-plan`, `brief`, `case-pack`, `registry-cohort`,
213
- `registry-scaffold`, `diff`, `reconcile`, `timeline`,
214
- `risk-calibrate`, `lookalike-calibrate`, `registry-doctor`, `verify-artifact`, `source-report`,
215
- `map-observations`, `oam-export`, `export`,
216
- `commands`, `completion`, and `manual` operations make no network requests. Commands write
217
- to stdout unless the analyst deliberately selects a local output file.
218
-
219
- Path operands are opened through one bounded regular-file boundary. Named
220
- pipes, devices and directories are refused without waiting for a writer;
221
- symbolic links to regular analyst-selected files remain supported. Standard
222
- input is the only intentional streaming input and observes the same command
223
- byte limit and cancellation signal.
224
-
225
- ## Output
226
-
227
- Human-readable terminal output is the default. On an interactive terminal it
228
- uses restrained semantic colour, groups Lookup evidence by purpose, wraps
229
- prose to the detected terminal width, and shows transient progress on stderr
230
- for slower collection. State remains explicit in text, so colour is never the
231
- only distinction. Redirected terminal output, JSON, and JSONL contain no ANSI
232
- sequences or progress text. `--no-color` and a non-empty conventional
233
- `NO_COLOR` environment variable disable colour; `WHOISLEUTH_NO_PROGRESS=1`
234
- disables the transient indicator. Command examples remain one copyable line
235
- even when prose is wrapped for a narrow terminal.
236
-
237
- Every command accepts `--output <file>` as a safer alternative to shell
238
- redirection. WHOISleuth buffers bounded output, creates a private temporary file
239
- beside the destination, syncs it, and publishes it atomically with mode `0600`.
240
- An existing destination is refused. `--force` is valid only with `--output` and
241
- allows an intentional atomic replacement. A failed or cancelled command does
242
- not publish a partial output file. Output is capped at 32 MiB.
243
-
244
- `--config <file> --profile <name>` reads a strict version-1 local configuration
245
- document of at most 64 KiB. Without `--config`, the path is
246
- `$XDG_CONFIG_HOME/whoisleuth/config.json`. A profile may contain no more than
247
- 16 safe defaults drawn from `--no-color`, `--summary`, `--verbose`, `--fast`,
248
- `--concurrency`, `--observer`, and `--vantage`. It cannot supply a target,
249
- Deep mode, output path, network approval, failure policy, credential, or
250
- arbitrary argument. Explicit command options override profile defaults from
251
- the same group.
252
-
253
- Lookup, Bulk, and `monitor-once` can emit bounded JUnit XML. Owned-domain
254
- Posture can emit SARIF only when `--owned-domain` confirms the passive target
255
- boundary. The reports contain categorical source or posture findings rather
256
- than raw evidence. Supported network commands accept an explicit comma-
257
- separated `--fail-on` policy from `source-failure`, `inconclusive`, `danger`,
258
- and `material-drift`; a match returns the partial-result exit code after the
259
- normal output is emitted.
260
-
261
- `lookup --markdown` and `lookup --html` build the existing normalised evidence
262
- report directly after one completed domain lookup. They do not start a second
263
- collection. These report formats reject IP and ASN inputs before collection;
264
- JSON and terminal output remain available for those query types. Both readable
265
- formats include a fixed local generator footer by default. `--no-attribution`
266
- removes that presentation-only footer without changing evidence-source
267
- attribution, generator metadata in JSON, or collection behaviour.
268
-
269
- Lookup terminal output supports three detail levels. The standard view keeps
270
- the normal bounded evidence summary. `--summary` keeps the source states and
271
- key findings while omitting endpoints and supporting detail. `--verbose` adds
272
- the document generation time and the existing bounded per-source collection
273
- timings. These switches are presentation-only and cannot be combined with
274
- `--json`; they do not change collection or the result document.
275
-
276
- `--json` writes one versioned document to standard output:
277
-
278
- ```json
279
- {
280
- "schema": "whoisleuth.cli.lookup",
281
- "version": 1,
282
- "generatedAt": "2026-07-14T00:00:00.000Z",
283
- "mode": "fast",
284
- "query": "example.com",
285
- "type": "domain"
286
- }
31
+ whoisleuth --help
32
+ whoisleuth commands --common
33
+ whoisleuth commands --group investigate --mode network
34
+ whoisleuth lookup --help
35
+ whoisleuth lookup example.test --deep --plan --json
287
36
  ```
288
37
 
289
- The complete document also carries the normalised `rdap`, `whois`,
290
- `availability`, and `diagnostics` sections returned by the shared lookup
291
- orchestrator. Machine output goes to stdout. Usage and lookup errors go to
292
- stderr, so redirected JSON is not mixed with diagnostics.
293
-
294
- Human-readable domain lookup output separately shows the registrar RDAP status
295
- and endpoint whenever the shared lookup diagnostics represent that follow-up.
296
- Deep mode may report success, unsupported, not found, or an explicit failure;
297
- fast mode reports the existing skipped state. IP, ASN, and lookup responses
298
- without registrar diagnostics remain unchanged. These source states are
299
- provenance only and do not decide availability or imply safety.
300
-
301
- When a deep domain response includes registry interpretation version 1, the
302
- terminal summary also shows the lifecycle label, separately attributed RDAP and
303
- WHOIS disclosure states, reconciliation state, and complete, partial, and
304
- unavailable publication counts. It does not print published contact routes.
305
- The JSON document retains the bounded interpretation and its limitations.
306
-
307
- A deep domain lookup can also show the status, selected public address, and
308
- registered network name from the bounded observed network context. It uses the
309
- same address and IP RDAP source represented in the JSON document. This is
310
- point-in-time edge or network-registration context, not proof of the origin
311
- host, hosting control, ownership, intent, or maliciousness. Fast and compact
312
- commands do not run the enrichment.
313
-
314
- The deep terminal summary also reports the website activity state, page title,
315
- DNS, HTTP, and TLS source states, bounded publisher-declared structured
316
- identity entities, up to six bounded technology indicators, a browser-library
317
- profile count, and the four passive security-posture counts.
318
- These are concise projections of the same evidence already present in the
319
- lookup response. The browser-library line counts apparent components and
320
- catalogue advisory matches without retaining script references or raw
321
- signatures. These summaries make no extra request, omit raw evidence
322
- descriptions, and do not turn a technology or advisory match, or a missing
323
- posture signal, into proof of exploitability, hosting control, ownership, or
324
- maliciousness. Use `--json` when the full bounded evidence, limitations, and
325
- source diagnostics are required.
326
-
327
- `lookup --strict-exit` is an opt-in automation policy. It still emits the
328
- ordinary complete output document, but returns exit code 4 when a requested
329
- diagnostic source is partial, unavailable, rate-limited, timed out, or failed.
330
- The default exit contract remains unchanged, so inconclusive source health does
331
- not silently break existing scripts.
332
-
333
- `lookup --events` and `bulk --events` write versioned
334
- `whoisleuth.cli.progress` version 1 JSONL lifecycle events to stderr while the
335
- ordinary final result stays on stdout. Events contain only the command,
336
- sequence, time, source state or Bulk item index, categorical failure reasons,
337
- and final exit status. They do not contain a target, query, endpoint, file
338
- path, error detail, or evidence value. Human progress and error text are
339
- suppressed while events are active. `--events` cannot be combined with
340
- `--output`, because the final event must describe the same completed stdout
341
- delivery observed by the caller.
342
-
343
- Pressing Ctrl-C asks an in-flight Lookup or Bulk run to stop, suppresses any
344
- partial final result, flushes an enabled Bulk checkpoint, and exits with code
345
- 130. SIGTERM follows the same graceful cleanup path and exits with code 143.
346
- Already started network operations remain subject to their existing hard
347
- timeouts while the shared in-process API unwinds; the executable terminates
348
- after CLI cleanup. A second Ctrl-C removes any known unpublished temporary
349
- output file on a best-effort basis before exiting immediately.
350
-
351
- Bulk can persist compact progress with `--checkpoint <file>`. A new checkpoint
352
- is private, bounded to 16 MiB, versioned, and tied to the exact ordered input
353
- and scan mode by SHA-256. It stores only compact per-item results, never full
354
- Lookup responses. Checkpoint schema 2 records a nullable per-row observation
355
- time. Schema 1 remains readable, but its resumed rows keep observation time
356
- unknown rather than inheriting the resume time. Reusing the same path is
357
- refused unless `--resume` is also
358
- specified. Resume revalidates the complete untrusted document and skips only
359
- the exact completed rows; changed input, mode, schema, digest, or malformed
360
- results fail closed.
361
-
362
- Bulk JSON, JSONL, and item documents use schema version 3. Each row includes
363
- `observedAt` and a `collectionOrigin` of `current_run` or
364
- `resumed_checkpoint`; the top-level `generatedAt` remains the report creation
365
- time. HTTP JSON uses schema version 2 and adds the explicit `assessment` field.
366
- The legacy `activityStatus` field remains for compatibility, but is `null`
367
- when collection is inconclusive rather than labelling the target unreachable.
368
- Consumers should use `assessment`; older version-1 `unreachable` values remain
369
- historical inconclusive labels, not proof that a target cannot be reached.
370
-
371
- If checkpoint persistence fails after collection, WHOISleuth still emits the
372
- completed final result, reports the checkpoint limitation, and exits with code
373
- 4. The last successfully published checkpoint remains available, but it may
374
- not contain the final completed items.
375
-
376
- `diff <left.json> <right.json>` compares two compatible retained artefacts
377
- entirely offline. Saved Lookup documents keep their version-1 lookup-diff
378
- output. Portable Bulk-session exports and domain-portfolio reviews emit a
379
- `whoisleuth.cli.comparison-ledger` version-1 document with a value-free bounded
380
- index and at most 256 exact detail rows. Bulk exports containing more than one
381
- session require `--left-session <id>` or `--right-session <id>` as applicable.
382
- The command keeps missing, unavailable, equal, added, and different evidence
383
- separate, treats later portfolio omission as incomplete rather than removal,
384
- and retains no input path. It makes no ownership, safety, or maliciousness
385
- inference.
386
-
387
- `lookup --observer <label> --vantage <label>` can add two bounded analyst labels
388
- to a saved Lookup document. The labels identify the person or process that ran
389
- the observation and the collection context chosen by that analyst; they are
390
- not verified identities, network measurements, or proof of independent
391
- collection. They do not change which sources are queried.
392
-
393
- `reconcile <observation.json> <observation.json> [...]` accepts from 2 to 5
394
- saved Lookup documents for the same domain and compares their bounded fields
395
- offline. It keeps agreement, disagreement, and non-comparable evidence
396
- separate, retains observation times and optional analyst labels, and copies no
397
- filenames or raw registration payloads. Distinct complete label pairs are
398
- reported as labelled collection contexts only; majority agreement is not
399
- treated as truth or authority.
400
-
401
- `timeline <observation.json> <observation.json> [...]` accepts from 2 to 20
402
- saved Lookup documents for one domain, orders them by their validated
403
- observation times, and compares each adjacent pair offline. The 32 MiB aggregate
404
- input ceiling is enforced while files are read and again by the timeline
405
- builder. Output retains normalised field states and observation times but no
406
- input filenames or raw registry payloads. A difference can reflect a domain
407
- change or changed collection conditions, so the command makes no current-state,
408
- ownership, intent, safety, or maliciousness conclusion.
409
-
410
- When diagnostics version 5, 6, 7, or 8 reports a documented registry collection
411
- constraint, terminal output also shows the suffix, WHOIS and RDAP access
412
- profiles, and the bounded limitation. This is static access-policy context: it
413
- does not make another request, and restricted, unpublished, or unavailable
414
- machine access is not evidence that a domain is unregistered or safe.
415
-
416
- ## Exit codes
38
+ The command index groups work under Investigate, Respond, Assure and Utilities.
39
+ Filters combine by intersection. `commands --json` emits the versioned local
40
+ catalogue without reading evidence or running the selected commands.
41
+
42
+ An eligible domain, reserved documentation domain, IP address or ASN can occupy
43
+ command position as Lookup shorthand. Use explicit `lookup` where input needs
44
+ supported URL-like normalisation. Credentials, paths, queries, fragments, ports
45
+ and unsupported special-use targets are rejected by the shorthand.
46
+
47
+ `registry-scaffold` has a separate fixture contract: its `--profile` selects one
48
+ fixed fixture profile and shared `--config` profiles are rejected. It produces
49
+ sanitised local fixture material and makes no registry request.
50
+
51
+ ## Requests and authorisation
52
+
53
+ Offline commands read only supplied arguments, files, standard input and
54
+ installed catalogues. Networked commands contact the sources named by their
55
+ focused help; those sources can observe and rate-limit the operator's network
56
+ address. The CLI does not inherit hosted login, provider configuration or
57
+ hosted operation limits.
58
+
59
+ `lookup --plan` classifies a target and lists intended source families and
60
+ disclosures without collecting. Fast is the Lookup default. Deep adds the
61
+ applicable registration, DNS, HTTP, TLS, page, technology and network context.
62
+ Optional browser providers are not implicit CLI actions.
63
+
64
+ `dnssec-validate` and `mail-transport` are isolated authorised actions. Both
65
+ require a selected literal public resolver, a local trust-anchor document and
66
+ `--owned-or-authorized`; mail transport also requires `--active-probe`. Mail
67
+ transport handles at most three selected MX hosts sequentially, sends `EHLO`
68
+ and uses `STARTTLS` only when advertised. It does not send mail, authenticate,
69
+ test relay, enumerate recipients or retry automatically. DNSSEC, TLSA/DANE,
70
+ PKIX, STARTTLS and SMTP transport remain separate evidence states.
71
+
72
+ The repository-only rendered-capture package is outside hosted and distributable
73
+ collection. It executes remote page JavaScript only for an explicitly
74
+ authorised route set, and each admitted resource operator receives the exact
75
+ requested URL. Captures and screenshots remain local until the operator deletes
76
+ them.
77
+
78
+ ## Files, output and automation
79
+
80
+ File inputs are bounded before parsing; directories, devices and named pipes are
81
+ refused. Output goes to the terminal unless a command supports `--output`.
82
+ Private output is written atomically and an existing path is refused unless
83
+ replacement is explicit.
84
+
85
+ Terminal text is the default. JSON, JSONL, CSV, Markdown, HTML and domain-only
86
+ formats are available only where the installed command declares them.
87
+ Redirected and machine output contains no ANSI or progress text. Diagnostics
88
+ and optional target-free `--events` output use standard error.
89
+
90
+ `--fail-on` and `--strict-exit` expose selected evidence states to automation
91
+ without changing the result document. Review focused help for the policies a
92
+ command supports.
417
93
 
418
94
  | Code | Meaning |
419
95
  | ---: | --- |
420
- | 0 | Command completed. Individual sources may still be partial or inconclusive; inspect diagnostics. |
421
- | 2 | Invalid command, option, input, or stdin shape. |
422
- | 3 | The requested lookup, collection, or comparison operation could not run. |
423
- | 4 | A bounded operation completed partially, such as Bulk item failures or failed explicit Doctor network checks. |
424
- | 70 | Unexpected CLI bootstrap failure. |
425
- | 130 | The analyst cancelled the command. No partial final result was emitted. |
426
- | 143 | The process received SIGTERM. No partial final result was emitted. |
427
-
428
- This release supports `lookup`, `bulk`, `ct-search`, `ct-intake`, `map-observations`, `oam-export`, `discover`, `discover-scan`, `posture`,
429
- `http`, `tls`, `dnssec-validate`, `mail-transport`, `registry-support`, `registry-doctor`, `registry-cohort`, `registry-scaffold`, `risk-calibrate`,
430
- `lookalike-calibrate`, `verify-artifact`,
431
- `inspect-archive`, `manifest`, `sign-artifact`, `verify-signature`, `source-report`,
432
- `compare`, `page-compare`, `mail-review`, `review-evidence`, `brief`, `case-pack`, `domain-control`,
433
- `monitor-once`, `assurance`, `change-packet`, `sharing-review`, `workflow-plan`, `workflow-run`, `diff`, `reconcile`, `timeline`,
434
- `export`, `doctor`, `commands`, `completion`, and `manual`. Additional command families
435
- are added as separate bounded increments rather than exposing incomplete
436
- aliases.
437
-
438
- ## Registry capability coverage
439
-
440
- `registry-support` projects the embedded, versioned registry compatibility
441
- catalogue for one domain or suffix. It distinguishes an explicit
442
- fixture-backed or access-documented suffix profile from the generic IANA
443
- discovery profile and reports the RDAP and WHOIS discovery/access paths,
444
- WHOIS query and parser profiles, fixture scenarios,
445
- verification references, documentation references, an optional reviewed
446
- official browser-lookup URL, and the catalogue's
447
- limitation. The same document includes a dated, official-source aggregate of
448
- generic, generic-restricted, sponsored, and infrastructure RDAP coverage. That
449
- snapshot is separate from the suffix profile and from live reachability.
450
-
451
- Terminal output is bounded and control-safe. `--json` emits version 4 of the
452
- `whoisleuth.cli.registry-support` schema. Unknown but syntactically valid
453
- suffixes retain the generic `discovery_only` profile; malformed input exits
454
- with code 2. The command never probes a registry or tests current reachability.
455
- An official lookup URL is emitted only for an individually reviewed HTTPS
456
- destination. The CLI does not open it or append the investigated domain.
457
- Coverage is context only and cannot decide registration, availability,
458
- ownership, safety, or maliciousness.
459
-
460
- `registry-doctor [lookup.json]` compares one saved domain Lookup with the same
461
- reviewed local capability profile. It reports whether RDAP and WHOIS collection
462
- states align with allowed, permission-required, or unsupported access, counts
463
- bounded normalised publication fields, and reports publication-quality context
464
- for object identity, base conformance, redaction metadata, required self links,
465
- and event consistency. A missing identifier or metadata field is a publication
466
- omission, not a failed lookup. The command is offline, makes no retry, and
467
- cannot establish current registry reachability.
468
-
469
- `registry-cohort [lookups-or-reports.json|jsonl]` accepts either 1 to 500 saved
470
- Lookups or an unmixed collection of retained `whoisleuth.cli.registry-cohort`
471
- version-1/version-2 reports. It emits version 2 with target-free suffix and
472
- capability-profile timelines, an explicit sample window, retained-report count,
473
- and at most 50 visible points per cohort. A version-1 report becomes one
474
- retained point. A cohort with fewer than five samples remains insufficient,
475
- and overlapping retained samples are never summed or promoted to independent
476
- consistency. Domains, queries, filenames, and raw registry evidence are not
477
- retained.
478
-
479
- `registry-scaffold --profile <id> --suffix <suffix> --scenario <state>` emits
480
- one bounded, sanitised synthetic fixture scaffold from the installed registry
481
- catalogue. The three supported states are `registered`, `not_found`, and
482
- `inconclusive`. It does not fetch a live response, include a real target, or
483
- alter the embedded catalogue.
484
-
485
- The offline RDAP supplied-evidence review implements the request shape defined
486
- by RFC 9536 for `resources`, `domains`, `nameservers`, and `entities`. It also
487
- validates a supplied server's `reverse_search_properties_mapping` response
488
- before preparing a request. Only registered IANA property paths and exact
489
- supported resource/property pairs are accepted. Preparing the request neither
490
- proves that a particular server supports it nor sends a reverse search; active
491
- execution remains gated on a reviewed server policy, privacy boundary, and
492
- deterministic fixtures.
493
-
494
- The separate maintenance command `npm run rdap-extensions:drift` compares the
495
- pinned official RDAP extension fixture with the reviewed local interpretation
496
- catalogue entirely offline. `--live` performs one bounded manual fetch from the
497
- fixed official CSV URL and reports added, removed, renamed, status-changed,
498
- unrecognized, and local-only identifiers. `--json` emits
499
- `whoisleuth.rdap-extension-drift-audit` version 1. The manual live fetch keeps one
500
- 10-second deadline armed through the capped 128 KiB body read and fails closed if
501
- the official CSV is truncated. Drift exits with status 1 and
502
- requires specification and fixture review; the command never enables an
503
- extension, changes authority or availability logic, or issues reverse search.
504
-
505
- `npm run service-dependencies:audit` validates the passive service-dependency
506
- catalogue entirely offline. It checks the catalogue digest, duplicate
507
- identifiers and target suffixes, evidence classes, source and licence
508
- treatment, source dates, and a fixed 180-day review age. `--json` emits
509
- `whoisleuth.service-dependency-signature-audit` version 1. A stale or changed
510
- signature requires manual provider and benign-fixture review; the command
511
- never resolves a target, checks an account, or tests claimability.
512
-
513
- ## Offline Risk calibration
514
-
515
- `risk-calibrate` replays a versioned analyst-labelled fixture dataset through
516
- the exact Risk model shared with the web application. It does not perform a
517
- lookup, contact the hosted deployment, persist the dataset, alter model weights,
518
- choose a threshold, or tune the model automatically. The report compares fixed
519
- thresholds from 40 through 90 and identifies 70 as the current review band. It
520
- also reports F1, balanced accuracy, Wilson 95% intervals, deliberately small
521
- scan-depth and review-reason strata, and a current-versus-previous model replay.
522
- Strata with fewer than 20 included labels are marked insufficient rather than
523
- presented as reliable performance estimates. Report version 3 distinguishes
524
- the ordinary `detailed` output from the aggregate `summary` projection.
525
-
526
- `--json` emits the bounded detailed report, including record identifiers,
527
- domains, analyst dispositions, scores and factor summaries. Use
528
- `--summary-json` for the target-free aggregate accepted by Monitor's local
529
- calibration explorer. That projection retains only dataset and model versions,
530
- aggregate class and score-band counts, fixed-threshold confusion metrics and
531
- Wilson intervals, bounded strata, model-comparison counts, and an explicit
532
- zero-target privacy declaration. It contains no record array, identifier,
533
- domain, evidence field, factor, note or provider payload. Both forms remain
534
- offline and are written only when the analyst deliberately selects an output.
535
-
536
- The JSON report derives a bounded `interoperabilityTags` list from the
537
- analyst-owned disposition and review reason. The mapping uses reviewed MISP
538
- taxonomy terms only where the meaning is unambiguous. Tags do not change a
539
- score, disposition, or case, and no MISP service is contacted.
540
-
541
- Input is capped at 2 MiB and 500 records. Each record requires a unique bounded
542
- ID, an ASCII DNS hostname, one existing analyst disposition, and a strict
543
- whitelist of current scoring inputs. Unknown fields are discarded; arrays,
544
- strings, ages, booleans, provider records, and external findings are revalidated
545
- and bounded before scoring. The accepted dataset envelope is:
546
-
547
- ```json
548
- {
549
- "schema": "whoisleuth.risk-calibration-dataset",
550
- "version": 2,
551
- "records": [
552
- {
553
- "id": "fixture-1",
554
- "domain": "login.example.test",
555
- "analystDisposition": "confirmed_abuse",
556
- "reviewReasonCode": "confirmed_credential_abuse",
557
- "evidence": {
558
- "availability": "registered",
559
- "mutationTypes": ["dictionary"],
560
- "hasPasswordField": true
561
- }
562
- }
563
- ]
564
- }
565
- ```
566
-
567
- `confirmed_abuse` is treated as a positive calibration label; `false_positive`
568
- and `expected` are negative labels. `unreviewed`, `suspicious`, and
569
- `closed_no_action` remain contextual and are excluded from threshold quality
570
- metrics, as are records for which Risk is not applicable. Terminal output caps
571
- its record list at 100 while `--json` retains the complete bounded detailed report,
572
- factor breakdowns, score bands, confidence intervals, bounded strata, model
573
- comparison counts, and per-threshold confusion metrics. Analyst
574
- dispositions and heuristic scores are review context: neither proves
575
- maliciousness or safety.
576
-
577
- The authenticated Monitor workspace can deliberately create the accepted
578
- dataset from explicitly selected cases. Selection is available only when a case
579
- has a reviewed disposition and retained normalised evidence. That local export
580
- contains the case ID, domain, disposition, optional reviewed reason code, and a
581
- bounded whitelist of current scoring inputs. A matched review-language signal
582
- is exported as a fixed indicator, not as captured page text. It excludes notes,
583
- tags, assertions, actions, contacts, raw
584
- source data, provider payloads, and stored Risk scores. Before download, a local
585
- review step shows the selected, included, and excluded counts, the domains and
586
- dispositions that will be written, and the excluded evidence classes. The CLI
587
- still validates every exported record and never trains, tunes, or changes the
588
- Risk model.
589
-
590
- ## Lookalike review-yield calibration
591
-
592
- `lookalike-calibrate [dataset.json]` summarises reviewed candidate outcomes by
593
- mutation family without retaining domains, candidate identifiers, analyst
594
- notes, or evidence values in its output. Input uses
595
- `whoisleuth.lookalike-calibration-input` version 1, is capped at 2 MiB and 5,000
596
- unique opaque records, and requires one reviewed disposition plus from 1 to 12
597
- bounded mutation-family identifiers per record.
598
-
599
- The report keeps exact disposition counts, review-lead rate, false-positive
600
- rate, and a per-family sample state. Fewer than 20 reviewed observations remains
601
- `insufficient`. The command is diagnostic only: it does not tune candidate
602
- generation, filtering, ranking, or Risk, and a useful historical yield does not
603
- establish that a current candidate is malicious or safe.
604
-
605
- ## Offline artefact verification
606
-
607
- `verify-artifact` validates a supported local artefact without printing its
608
- evidence contents. Input is capped at 15 MiB. It currently recognises ordinary
609
- and encrypted workspace archives, case-response packets, acquisition-decision
610
- exports, supported Lookup-evidence exports, Lookup claim passports, domain-comparison exports, Bulk mail-exposure exports, and Bulk review
611
- manifests. Reviewed CLI case packs are checked against their canonical digest,
612
- bounded browser-importable case collection, audience, and review marker. The
613
- command verifies both the embedded projections and whole-capsule digest of an
614
- investigation capsule version 2, including its metadata and linked
615
- source-contract digests, and reports `verified`. The non-embedded Lookup
616
- evidence must still be retained separately. Legacy capsule version 1 covers
617
- only the embedded brief, graph, and optional analyst-record projections, so it
618
- remains `integrity_valid` rather than whole-file `verified`. It also validates
619
- the bounded versioned structure of saved CLI Lookup JSON and current
620
- Lookup-evidence schemas 25 and 26. Because neither saved Lookup documents nor full
621
- Lookup-evidence exports embed a checksum or signature, those results are
622
- reported as `structure_valid` with content integrity explicitly unchecked.
623
- Lookup-evidence verification closes the supported export envelopes and source
624
- wrappers, enforces the same 5 MiB, 20,000-entry, and 24-level limits as browser
625
- replay, and does not claim that
626
- an observation is accurate, current, or unchanged. Exact retained-file identity
627
- requires a verified investigation manifest and matching `--manifest-entry`;
628
- the evidence file can contain a bounded privacy-projected registry RDAP
629
- publication, normalised WHOIS values, and bounded contacts, so review it before
630
- sharing. Request and response headers, cookies, session and credential fields,
631
- and URL credentials, queries, and fragments are excluded. Saved Lookup validation retains its
632
- stricter 8 MiB document ceiling.
633
-
634
- For an ordinary workspace archive, the command validates the versioned
635
- structure, section byte counts, record counts, and per-section checksums. For a
636
- supported signed review artefact, it validates the declared SHA-256 digest. An
637
- encrypted archive without `--passphrase-file` can establish only that the
638
- envelope is structurally valid; it reports `envelope_valid` and explicitly
639
- leaves authenticated encryption and inner checksums unchecked.
640
- Checksum validity is reported separately from importability. A checksum-valid
641
- archive can therefore list unsupported sections when it contains a section
642
- schema this version cannot restore, or skipped and pruned records when a
643
- supported section cannot be restored completely; inspect those counts before
644
- importing.
645
-
646
- Supplying a separate passphrase file, capped at 1 KiB, allows authenticated
647
- decryption followed by the ordinary workspace checks. The passphrase is never
648
- accepted as a command-line value, printed, or retained. Verification detects
649
- changes against the artefact's declared contract; it does not authenticate the
650
- analyst or establish that an observation was accurate or remains current.
651
-
652
- `--manifest <manifest.json> --manifest-entry <artifact-N>` additionally verifies
653
- the selected investigation manifest before comparing its declared entry with
654
- the supplied artefact. `identity_verified` requires the exact UTF-8 byte length
655
- and raw digest as well as canonical digest, schema, and version. A
656
- `canonical_match_only` result means semantic JSON identity survived but the
657
- retained bytes differ, so it is a partial result under `--strict-exit`.
658
- `mismatch` likewise remains separate from the artefact's own structure or
659
- integrity result. Manifest paths and artefact contents are not copied into the
660
- verification report.
661
-
662
- Verification reports use `whoisleuth.offline-artifact-verification` version 3
663
- and do not expose an always-true validity shortcut. The explicit state and
664
- checks distinguish structure, whole-file integrity, projection integrity, and
665
- authenticated encryption. `--strict-exit` returns exit 0 for `verified` and
666
- `structure_valid` when no manifest identity is requested or exact identity is
667
- verified. It returns partial-failure exit 4 for `integrity_valid`,
668
- `envelope_valid`, canonical-only identity, or identity mismatch; ordinary
669
- verification errors retain their existing error codes. Raw JSON is scanned
670
- before parsing to reject duplicate object keys and excessive nesting or
671
- key/value counts.
672
-
673
- Current integrity-bearing review outputs use `sorted-json-v2`, whose object-key
674
- order is a locale-independent JavaScript code-unit order. Readers select the
675
- canonical bytes from an exact artifact-version and canonicalization pair,
676
- reject mixed or missing current declarations, and validate bounded structure
677
- before any legacy hashing. Historical outputs keep their original
678
- `sorted-json-v1` bytes and are accepted only through their legacy version.
679
- Version 1 used the runtime's ambient `localeCompare` ordering, so historical
680
- files containing non-ASCII object-key extensions require a compatible locale
681
- and ICU ordering to verify; it is not a portable canonicalization guarantee.
682
- The fixed keys emitted by the supported legacy builders are ASCII, but only
683
- version 2 provides the cross-locale portability contract.
684
-
685
- The current/legacy integrity pairs are: acquisition decision 2/1, Lookup claim
686
- passport 1 (sorted-json-v2 only),
687
- domain-comparison export 4/3 with nested comparison 3, Bulk mail-exposure
688
- export 2/1 with nested report 1, Bulk review manifest 2/1, domain-control
689
- manifest 2/1, domain-change packet 2/1 with input 1, investigation manifest
690
- 2/1, case-response packet 6/5, investigation capsule 2/1, CLI case-pack packet
691
- 2/1 with Case schema 12, and signed evidence package 2/1. Current outputs
692
- declare `sorted-json-v2`; legacy declarations remain explicit only where their
693
- historical contract included one.
694
-
695
- ## Interchange fidelity report
696
-
697
- `interchange-report [artifact.json]` explains what one recognised portable
698
- artefact preserves, excludes, and supports in browser and CLI workflows. It
699
- recognises supported Lookup-evidence exports, Lookup claim passports, domain-control passports, Brand Profile exports from versions 2
700
- through 6, workspace archives from versions 1 through 5, encrypted workspace
701
- envelopes, reviewed CLI case packs, and the retired one-way desired-baseline
702
- export. The retired export is identified as unsupported rather than silently
703
- treated as a current passport.
704
-
705
- Output is metadata-only: registered schema and version, verification state,
706
- bounded counts, browser import/export support, CLI read/write/verify support,
707
- semantic fidelity, and fixed preserved or excluded field-group identifiers.
708
- For workspace archives, checksum validity and complete browser importability are
709
- reported separately. A checksum-valid archive with a newer inner section stays
710
- verified for integrity while its fidelity is withheld and importability is
711
- reported as partial. The same partial state applies when browser preview would
712
- block a section, skip a malformed record, or prune data to a collection bound.
713
- It never echoes a target, contact, note, evidence value, passphrase, arbitrary
714
- unknown schema string, or encrypted content. A separate `--passphrase-file`
715
- can authenticate an encrypted workspace; without it, only the envelope is
716
- reported as valid and fidelity is withheld. Each registered interchange family
717
- declares its exact assurance requirement: browser-importable structure is
718
- sufficient for Brand Profile normalisation and the current bounded
719
- Lookup-evidence contract, while checksummed formats
720
- require whole-file integrity and encrypted workspaces additionally require
721
- authenticated decryption. “Semantic exact after normalisation” does not mean byte
722
- identity, current evidence, or a successful round trip through an unrelated
723
- format.
724
-
725
- For case packs, the fixed fidelity metadata explicitly lists public-audience
726
- Brand Profile references as excluded and trusted/internal references as
727
- preserved. The report never prints an identifier or other stored value.
728
-
729
- ## Privacy-safe source reliability report
730
-
731
- `source-report` summarises source states, durations, truncation, and rate-limit
732
- counts from one version-1 CLI Lookup, Bulk, or Bulk-item document, a JSON array
733
- of up to 100 such documents, or an array containing only previously generated
734
- source-reliability reports. Lookup documents and reports cannot be mixed.
735
- Merged reports preserve exact state and sample totals and add bounded ranges
736
- for each input report's median and p95 duration, a bounded source timeline,
737
- fast/deep/unknown cohorts, and the earliest and latest valid report generation
738
- times. Exact duplicate reports are rejected so one saved report is not
739
- accidentally counted twice. Per-source failure, partial, truncation, and
740
- rate-limit rates remain separate. The command does not pretend that aggregate
741
- timing distributions can reconstruct the original target-level samples. Input
742
- is capped at 12 MiB and traversal, source, duration, and output counts are
743
- bounded.
744
-
745
- The report retains only fixed source identifiers and aggregate operational
746
- counts. It does not output targets, queries, endpoints, source limitations, raw
747
- evidence, or provider payloads. Observation-envelope duration and the
748
- potentially overlapping Lookup timing are reported separately and never added
749
- together. It is a local engineering diagnostic, not analytics, a source-quality
750
- score, or evidence that a collector is currently healthy.
751
-
752
- ## Workspace archive inspection
753
-
754
- `inspect-archive` verifies an ordinary or encrypted workspace archive and then
755
- reports its versioned sections, counts, byte sizes, and checksum states without
756
- printing stored records. An encrypted archive requires
757
- `--passphrase-file`; passphrases are never accepted on the command line.
758
-
759
- An optional `--search` is exact and case-insensitive across a fixed allowlist
760
- of target fields. Default matches disclose only the section, field name, and a
761
- SHA-256 digest. `--reveal` must be selected explicitly to print the matched
762
- value. Notes, contacts, raw evidence, arbitrary object fields, and partial text
763
- search are excluded. Unicode domain searches are canonicalized to their
764
- DNS-safe ASCII form before exact comparison. `--require-match` makes a
765
- no-match result fail for automation and is valid only with `--search`.
766
- Traversal, input size, matches, depth, arrays, and strings are bounded.
767
-
768
- Version 2 of the inspection report adds a content digest over the ordered
769
- section identifiers, schema versions, record counts, and normalised section
770
- content. It deliberately excludes JSON formatting and each section envelope's
771
- export time, so two separately exported archives with the same retained
772
- workspace content have the same identity. `--expect-content-digest <sha256:digest>`
773
- fails when that identity differs, which supports deliberate transfer checks
774
- without printing records. Matching digests detect equivalent retained content;
775
- they do not authenticate who created it or establish that its evidence is
776
- accurate or current. The report separately identifies the source archive
777
- version and the installed reader version.
778
-
779
- ## Optional evidence-package signing
780
-
781
- `sign-artifact` signs a locally verified case-response packet or supported
782
- review manifest with an externally managed Ed25519 private key:
783
-
784
- ```bash
785
- node bin/whoisleuth.mts sign-artifact packet.json --private-key-file analyst-private.pem > packet.signed.json
786
- node bin/whoisleuth.mts verify-signature packet.signed.json --public-key-file analyst-public.pem
787
- ```
788
-
789
- New signed packages use version 2 with `sorted-json-v2` canonicalisation and
790
- embed the public key, key identifier, canonical UTC signature time, and
791
- original artefact. Signing requires verified structure plus a verified
792
- applicable integrity contract; a recomputed digest around an empty or malformed
793
- document is rejected. Signature verification reports cryptographic status and
794
- embedded-artifact assurance separately in
795
- `whoisleuth.evidence-signature-verification` version 2. A result can therefore
796
- remain `signature_valid` while embedded-artifact assurance is `not_verified`;
797
- the signature authenticates those exact bytes without upgrading their
798
- structure or inner integrity. Signer trust is separately reported as
799
- `trusted_key` or `embedded_key_only`. Without a separately supplied trusted public key,
800
- verification establishes only that the package is internally self-consistent.
801
- Historical version-1 packages remain verifiable with their explicit
802
- `sorted-json-v1` signature declaration when their original locale-dependent
803
- ordering is reproduced; version 2 is the portable signature contract.
804
- WHOISleuth does not generate, store, recover, rotate, publish, or establish
805
- trust in signing keys.
806
-
807
- ## Reproducible investigation manifest
808
-
809
- `manifest <artefact.json> [...] --workflow <label>` reads from 1 to 16 ordered
810
- JSON artefacts and emits `whoisleuth.investigation-manifest` version 2. Each
811
- entry records its sequence, bounded schema and version metadata, exact UTF-8
812
- content digest, `sorted-json-v2` canonical JSON digest, and byte count. Version
813
- 1 manifests remain verifiable with their historical digest bytes. The manifest
814
- also records the installed CLI version and can include an externally calculated
815
- `--configuration-digest sha256:...` without copying configuration values.
816
-
817
- Source paths, filenames, command-line arguments, and artefact contents are not
818
- retained. Individual artefacts are capped at 15 MiB and the ordered set at
819
- 32 MiB. The manifest carries its own canonical integrity digest, works with
820
- `verify-artifact`, and can be passed to `sign-artifact`. Reproduction still
821
- requires the exact digested artefacts, compatible source services, and any
822
- separately held configuration; matching digests do not establish observation
823
- accuracy or freshness.
824
-
825
- ## Bulk lookup
826
-
827
- `bulk` accepts a newline-delimited file or stdin. It preserves input order,
828
- removes case-insensitive duplicates, and returns a result for every retained
829
- query. Input is capped at 1 MiB. Fast mode is capped at 500 queries with four
830
- workers by default. Deep mode is capped at 50 queries with two workers by
831
- default. Explicit concurrency cannot exceed eight in fast mode or three in
832
- deep mode.
833
-
834
- Bulk uses the shared compact lookup response, so it does not retain raw RDAP
835
- objects or WHOIS response bodies. `--json` returns one bounded collection;
836
- `--jsonl` emits one self-contained versioned item per line. `--domains` emits
837
- only successfully normalised domain names, while `--queries` preserves the
838
- selected bounded input queries and is suitable for an exact retry queue.
839
- Version 2 adds a
840
- bounded `dnsSummary` projection for observed A, AAAA, NS, and MX records plus
841
- null MX, SPF, and DMARC state. `--csv` writes fixed columns for automation,
842
- including the DNS summaries and explicit outcome.
843
-
844
- `--registered-only`, `--inconclusive-only`, and `--errors-only` are mutually
845
- exclusive output filters. The first retains registered, for-sale, and expiring
846
- authority-aware states. The second retains unknown authority states and failed
847
- rows. The third retains only rows whose lookup operation failed, which is useful
848
- for producing an exact retry queue without treating an inconclusive successful
849
- lookup as an operational error. Filtering does not change collection and never
850
- converts an unavailable or failed source into an unregistered result. Machine
851
- documents record collected and emitted counts. A mixture of successful and
852
- failed queries exits with code 4 while preserving every collected result before
853
- output filtering. For example, `bulk domains.txt --queries --errors-only`
854
- produces only the exact failed inputs while retaining the partial-failure exit.
855
-
856
- ## Certificate Transparency search
857
-
858
- `ct-search` accepts one keyword as an argument or on stdin and calls the same
859
- bounded Certificate Transparency module as the web application. It contacts
860
- the upstream public log search service directly from the local machine; it
861
- does not call the hosted WHOISleuth deployment. Quote a multi-word keyword so
862
- the shell passes it as one argument.
863
-
864
- Terminal output summarises certificate rows, observed hostnames, canonical
865
- registrable-domain matches, observation times, bounded certificate-issuance
866
- groups, and completeness. An issuance group contains names observed together
867
- in one public certificate record; a cross-domain group is a review lead, not
868
- an attribution or ownership finding. Terminal output shows at most 100 matches
869
- and five hostnames per match, with explicit omission notes. `--json` returns
870
- the complete bounded structured result in the versioned
871
- `whoisleuth.cli.ct-search` schema. The certificate-group cap is reported
872
- separately from the registrable-domain result cap. CT observations do not prove
873
- that a website is active or malicious.
874
-
875
- ## Local certificate event intake
876
-
877
- `ct-intake [events.json]` reads `whoisleuth.ct-event-batch` version 1 entirely
878
- offline. Each source-qualified event supplies a log identifier, observation
879
- time, certificate SHA-256 digest, one or more DNS names, completeness, and
880
- optional issuer, expiry, and limitations. Unknown fields, malformed names,
881
- unsupported completeness values, and oversized batches are rejected.
882
-
883
- JSON output uses the existing `whoisleuth.external-findings` version 4 contract
884
- and can be deliberately imported into the browser Console. Wildcard names are
885
- normalised to their base DNS name, exact event/domain duplicates are removed,
886
- and deterministic output is capped at 100 findings, 25 domains, and 20 findings
887
- per domain to match the browser import boundary. Omission is stated in each
888
- retained finding when the supplied batch exceeds those limits. The command
889
- makes a deterministic event identifier and preserves the supplied log id,
890
- certificate digest, issuer, expiry, original DNS-name count, and whether the
891
- bounded import retained the complete supplied name set. This metadata lets the
892
- browser replay reviewed issuer and certificate-name expectations without
893
- mistaking an omitted name for a mismatch. The command
894
- makes no request and treats every certificate event as a review lead, not proof
895
- that the certificate was served, requested, or controlled by the named domain.
896
-
897
- ## Declarative observation interchange
898
-
899
- `map-observations [mapping.json]` reads
900
- `whoisleuth.external-observation-mapping` version 1 and applies one strict,
901
- declarative profile to at most 500 local records. The profile selects bounded
902
- dotted field paths for the domain, summary, observation time, optional
903
- reference, and optional completeness. It fixes one source, evidence class, and
904
- finding category for the batch. Profiles cannot contain scripts, templates,
905
- computed expressions, network endpoints, or executable callbacks. Prototype
906
- paths and unknown fields are rejected.
907
-
908
- The resulting JSON is the existing browser-compatible
909
- `whoisleuth.external-findings` version 4 contract. It retains source identity,
910
- observation time, completeness, and limitations, while stating that field
911
- semantics were not independently verified. Deterministic output is capped at
912
- 100 findings, 25 domains, and 20 findings per domain. The command makes no
913
- request and never interprets an omitted field as a negative observation.
914
-
915
- `oam-export [external-findings.json]` projects that strict findings document
916
- into `whoisleuth.open-asset-model-bridge` version 1. It uses the documented
917
- [OWASP Open Asset Model](https://github.com/owasp-amass/open-asset-model)
918
- vocabulary for bounded `FQDN`, `IPAddress`, and `TLSCertificate` assets plus
919
- `dns_record` and `san_dns_name` relations. Only typed A, AAAA, and SHA-256
920
- certificate observations create infrastructure assets or relations. Other
921
- findings remain represented by their FQDN asset.
922
-
923
- This bridge wrapper is not a claim that every receiving implementation accepts
924
- one universal native serialisation. Receivers must validate the projection
925
- against their supported model version. WHOISleuth preserves source and
926
- completeness as discovery metadata, does not invent a confidence score, and
927
- does not treat an edge as proof of ownership, control, coordination, intent,
928
- safety, or maliciousness.
929
-
930
- ## Lookalike discovery
931
-
932
- `discover` runs the same pure, bounded lookalike generator as the Discover
933
- tool without making network requests. It accepts a brand label or a
934
- domain with one suffix label. The default TLD set is `com,net,org`; replace it
935
- with `--tlds com,net` when narrower coverage is wanted.
936
-
937
- Generation presets are `common`, `impersonation`, and `all` (the default).
938
- Keyboard-aware mutations support `qwerty` (the default), `azerty`, and
939
- `qwertz`; `--keyboard all` uses their bounded combined neighbour set, including
940
- adjacent number-row keys. Supply a UTF-8 text file with
941
- `--dictionary terms.txt` to add up to 100 local terms when
942
- using the `impersonation` or `all` preset. Dictionary input is capped at 4,096
943
- bytes and individual terms at 32 characters. Machine output records accepted
944
- and rejected term counts but never copies the private terms into its metadata.
945
-
946
- Use `--families <ids>` instead of `--preset` to run an exact comma-separated
947
- selection. Supported IDs are `character_addition`, `character_omission`,
948
- `character_duplication`, `character_transposition`, `pluralization`,
949
- `tld_embedding`, `www_prefix`, `hyphenation`, `separator_omission`,
950
- `word_reordering`, `keyboard_substitution`, `keyboard_insertion`, `vowel_swap`,
951
- `bitsquatting`, `ascii_homoglyph`, `unicode_homoglyph`,
952
- `unicode_whole_label`, `unicode_homoglyph_depth_2`, `dictionary`,
953
- `dictionary_token_replacement`, `tld_typo`, and `tld_substitution`. Machine
954
- output records the normalised custom selection. A custom dictionary file
955
- requires `dictionary` or `dictionary_token_replacement` in that selection.
956
-
957
- `--domains` writes the unique candidate names only. `--snapshot <file>` keeps
958
- one private, versioned local observation of the normalised seed, generation
959
- configuration, custom-dictionary digest, and candidate set. The first run
960
- creates the snapshot; later runs report added and removed candidates before
961
- atomically replacing it. The snapshot never stores private dictionary terms.
962
- Use the same command from a local scheduler such as cron or a system timer to
963
- perform repeatable discovery runs. WHOISleuth does not install a scheduler,
964
- run a background daemon, or infer that an added candidate is registered,
965
- active, available, or malicious.
966
-
967
- Dotted subdomain permutations are intentionally excluded because the lookup
968
- pipeline validates a hostname's registrable parent. The CLI does not present
969
- authoritative parent registration evidence as proof that a generated hostname
970
- exists.
971
-
972
- Terminal output is capped at 200 candidates with an explicit notice. Versioned
973
- JSON and JSONL use `whoisleuth.cli.discover` schema version 2 and retain the
974
- complete bounded candidate set and mutation provenance. Internationalised
975
- candidates show the DNS-safe ASCII form and readable Unicode form together in
976
- terminal output. The Impersonation and All presets can add up to six
977
- deterministic whole-label Unicode candidates when every letter has a reviewed
978
- same-script replacement. The command generates candidates only. It does not
979
- claim that a domain is registered, active, or malicious.
980
-
981
- `unicode_homoglyph_depth_2` is an explicit `--families` option and is never
982
- enabled by a preset. It replaces exactly two ASCII letters with characters
983
- from the same reviewed script, ranks curated mappings first, and returns at
984
- most 256 candidates. JSON and terminal output report generated candidates,
985
- cross-script or invalid combinations omitted by policy, and lower-ranked
986
- label variants omitted by the family or overall generation budget.
987
-
988
- ## Supervised candidate scan
989
-
990
- `discover-scan` composes the same bounded generator with the compact Lookup
991
- collector. Unlike `discover`, it is an explicitly networked command. It selects
992
- the first deterministic candidates up to `--scan-limit`, processes fixed
993
- chunks sequentially, and retains each registration and DNS source state. Fast
994
- mode is the default and accepts at most 500 candidates. Explicit `--deep` mode
995
- is capped at 50 candidates and concurrency 3 because it can add WHOIS, DNS,
996
- HTTP, TLS, page, and technology work for every selected candidate.
997
-
998
- Use `--chunk-size 1-100`, `--concurrency 1-8`, and the existing private
999
- `--checkpoint <file> [--resume]` boundary to control a run. The checkpoint is
1000
- tied to the exact ordered candidate set and scan mode. A new generation order,
1001
- limit, or mode requires another checkpoint. `--resolver` accepts at most three
1002
- literal IPv4 or IPv6 recursive-resolver addresses and applies them only to DNS
1003
- observations in this local run. Resolver hostnames and automatic third-party
1004
- resolver selection are not supported. The selected resolvers can observe the
1005
- queried candidates and may apply their own logging and retention policies.
1006
-
1007
- An optional newline-delimited `--allowlist` accepts at most 500 domains and 64
1008
- KiB. Matching candidates remain in evidence and are labelled `suppressed` in
1009
- the review queue; the full list and unmatched entries are not copied into
1010
- output, while each matching scanned candidate necessarily exposes that match.
1011
- Allowlisting never deletes a result or asserts that it is safe. Output filters
1012
- select registered, inconclusive, acquisition-review, or
1013
- suppressed rows without changing whole-run counts or source health. JSON,
1014
- JSONL, CSV, and domain-list forms retain mutation provenance and explicit
1015
- review lanes. The queue provides next manual actions, not automatic lookup,
1016
- acquisition, blocking, submission, or enforcement.
1017
-
1018
- Deep compact results can group exact shared addresses, nameservers, and mail
1019
- servers observed across at least two candidates. These bounded relationships
1020
- are pivot leads only. Common hosting, delivery, registrar, and mail services
1021
- can produce the same relationship without common ownership or control.
1022
-
1023
- `--observation-snapshot <file>` keeps a private versioned local projection of
1024
- registration state, confidence, and bounded A, AAAA, NS, MX, null-MX, SPF, and
1025
- DMARC summaries for the exact candidate set, mode, and resolver selection.
1026
- Version 2 records registration and DNS observation times and latest component
1027
- states separately; version 1 is read and normalised on the next write. A later
1028
- identical run reports material field changes only for components collected
1029
- with enough completeness to support the comparison. If registration or DNS is
1030
- partial or unavailable, the prior usable evidence for that component is
1031
- preserved and the attempt is reported as unavailable rather than converting
1032
- missing data into a removal. Raw registry
1033
- records, contacts, page content, and request details are excluded. Snapshot
1034
- differences are review prompts, not proof of ownership, control, intent,
1035
- safety, or maliciousness.
1036
-
1037
- ## Domain posture audit
1038
-
1039
- `posture` runs the standard-profile form of the owned-domain DNS and
1040
- email-security audit used by Brand Profiles. It queries registry status and
1041
- DNSSEC evidence, nameserver delegation, SPF, DMARC, MX, CAA, MTA-STS, TLS-RPT,
1042
- and BIMI directly from the local machine. Literal SPF include and redirect
1043
- branches are expanded within fixed depth, policy-query, DNS-term, void-answer,
1044
- cycle, and time bounds. External DMARC reporting authorisation and external
1045
- nameserver, mail, SPF, and reporting dependencies are separately reported.
1046
- The MTA-STS policy request uses the fixed HTTPS policy host and well-known path;
1047
- HTTP redirects are not followed, as required by RFC 8461, and a redirect leaves
1048
- that evidence unavailable rather than borrowing a policy body from another URL.
1049
- Supply up to ten known active and retired DKIM selectors in total with
1050
- `--selectors selector1,selector2` and `--retired-selectors selector0`;
1051
- selectors cannot be reliably discovered from DNS, so no-selector output
1052
- reports DKIM as not checked. Use `--mail-profile standard`,
1053
- `--mail-profile defensive-no-mail`, or `--mail-profile parked` to make the
1054
- intended mail posture explicit rather than inferring it from DNS.
1055
-
1056
- Terminal output shows each pass, review, action, or informational result and
1057
- caps displayed records at five per check with an explicit omission notice.
1058
- Versioned JSON retains the complete bounded report, including SPF traversal,
1059
- DMARC authorisation, and dependency provenance. Warnings and dangers are
1060
- findings rather than command failures; transient resolver or policy-fetch
1061
- failures remain informational and should be retried before changing DNS.
1062
-
1063
- ## HTTP intelligence
1064
-
1065
- `http` runs the same bounded safe homepage probe used by a deep lookup. It
1066
- tries HTTPS before HTTP, follows only validated public-network redirects, caps
1067
- each attempt at six seconds, and captures at most 300,000 response bytes for
1068
- hashing and metadata. A non-success HTTP response still proves that a web
1069
- service answered; failure of both schemes remains inconclusive rather than
1070
- evidence that no website exists.
1071
-
1072
- The command reports normalised redirect, response, content, selected security-
1073
- header, body-hash, completeness, and attempt provenance. Captured homepage text
1074
- is never written to terminal or JSON output. Query strings are removed by the
1075
- shared HTTP evidence normalizer, and terminal values are additionally bounded
1076
- and control-safe.
1077
-
1078
- ## TLS intelligence
1079
-
1080
- `tls` runs the same bounded one-connection TLS collector used by deep lookups.
1081
- It resolves the hostname once through the public-address guard, validates every
1082
- answer, connects directly to the first validated address on port 443, and keeps
1083
- the original hostname for SNI and certificate identity checking. Resolution
1084
- and handshake work share a five-second deadline.
1085
-
1086
- The report retains the negotiated protocol, ALPN and cipher, runtime trust and
1087
- hostname checks, certificate validity, bounded subject and issuer names, SANs,
1088
- SAN class counts, signature algorithm and OID, extended-key-usage purposes,
1089
- classified Authority Information Access presence counts, SHA-256 certificate
1090
- and public-key fingerprints, a bounded certificate chain, and neutral
1091
- findings. AIA responder and issuer locations are classified as HTTP, HTTPS, or
1092
- other and then discarded; they are not retained or followed. The collector
1093
- stores no certificate bytes, session material, or application data and does
1094
- not enumerate supported protocol or cipher suites.
1095
- A failed collection is inconclusive rather than proof that no TLS service
1096
- exists.
1097
-
1098
- ## Registry-source comparison
1099
-
1100
- `compare` reads one version-1 `whoisleuth.cli.lookup` domain document from a
1101
- file or stdin and emits a version-3 `whoisleuth.cli.compare` document. Its
1102
- primary comparison reconciles normalised registry RDAP and WHOIS fields. When
1103
- the saved deep lookup also represents the optional registrar RDAP follow-up,
1104
- an additional sibling comparison reconciles the portable registry and
1105
- registrar RDAP publication fields. The command does not repeat the lookup,
1106
- contact the hosted deployment, or treat a conflict as a command failure. Use a
1107
- deep lookup when multiple sources are wanted; a fast lookup deliberately
1108
- records WHOIS as skipped, and comparison preserves that state as unavailable
1109
- rather than misreporting RDAP-only publication.
1110
-
1111
- Input is capped at 8 MiB and must retain the lookup schema, mode, source
1112
- diagnostics, and normalised parsed source sections. Per-value, list, and event
1113
- bounds are revalidated because saved JSON is treated as untrusted input. A
1114
- successful registrar source must include a normalised object and agree with
1115
- its diagnostic status. Raw RDAP JSON, registrar contacts and source-specific
1116
- handles, WHOIS response bodies, availability evidence, and unrelated lookup
1117
- fields are not copied into the comparison result.
1118
-
1119
- When the saved Lookup contains valid version-5, version-6, or version-7, context-only
1120
- registry-access diagnostics, version 3 retains their bounded suffix, access profiles, and
1121
- limitation. Terminal output labels the same collection context explicitly.
1122
- It explains source reachability only and cannot decide registration,
1123
- availability, ownership, safety, or maliciousness. Older documents and saved
1124
- lookups without that context do not gain a registry-access field.
1125
-
1126
- Terminal and versioned JSON output cover domain identity, registry object ID,
1127
- registrar, registrar IANA ID, lifecycle dates, DNSSEC, statuses, and name
1128
- servers. The shared comparison model normalises harmless case, punctuation,
1129
- ordering, and date-precision differences while distinguishing conflicts,
1130
- one-source publication, redaction, incomplete sources, and unavailable
1131
- sources. This is source reconciliation, not an availability, ownership, or
1132
- maliciousness decision.
1133
-
1134
- ## Static page comparison
1135
-
1136
- `page-compare <left.json> <right.json>` reads two different version-1 saved
1137
- Deep domain Lookup documents and compares the already-retained static page
1138
- identity, exact and perceptual favicon evidence, technology identifiers, TLS
1139
- issuer label, and TLS public-key fingerprint. DOM structure is compared first
1140
- by its exact bounded tag-sequence digest, then, when both captures provide it,
1141
- by a parse5-tokenized structural SimHash. It makes no request and requires
1142
- complete supported static page-identity observations on both sides.
1143
-
1144
- Each page component remains independent. Exact, similar, overlapping,
1145
- different, unavailable, and partial evidence are not collapsed into a score.
1146
- Version 3 also withholds technology-set equality and disjointness whenever
1147
- either saved technology observation is partial or truncated; the retained
1148
- identifiers remain visible only as incomplete evidence.
1149
- Matching components are investigative relationships rather than proof of
1150
- copying, common ownership, control, intent, safety, or maliciousness. Static
1151
- comparison does not execute JavaScript; use the optional local rendered
1152
- capture package only when that additional active behaviour is authorised.
1153
-
1154
- ## Passive mail exposure review
1155
-
1156
- `mail-review [bulk.json|bulk.jsonl]` reads version-2 Bulk output locally and
1157
- summarises MX, null MX, SPF, DMARC, and mail-provider relationships. It keeps
1158
- authenticated mail, authentication gaps, incomplete authentication evidence,
1159
- no explicit MX, null MX, and incomplete DNS evidence as separate states.
1160
- Shared-provider relationships are based only on the registrable domain of an
1161
- observed MX hostname and do not establish shared ownership or control.
1162
-
1163
- The command makes no DNS or SMTP request and retains no source path. It does
1164
- not test message acceptance, relay behaviour, mailbox existence, catch-all
1165
- behaviour, SMTP banners, or whether a mail server is rogue, safe, or malicious.
1166
-
1167
- An analyst may add a bounded `tlsaEvidence` object to a version-2 Bulk item for
1168
- offline DANE review. It must name the exact `_25._tcp.<mx-host>` service, and
1169
- that host must occur in the same item's retained MX evidence. A certificate
1170
- association match is complete only when `dnssecState` is `validated`. For an
1171
- SMTP relay service on `_25._tcp`, PKIX-TA usage 0 and PKIX-EE usage 1 are
1172
- reported as unsupported and cannot complete SMTP DANE assurance because RFC
1173
- 7672 leaves their SMTP treatment undefined. Usage 2 likewise
1174
- cannot complete DANE-TA assurance because this offline action does not construct
1175
- or validate a path from the observed leaf to the supplied trust-anchor material.
1176
- For other service ports, usage 0 compares against the bounded
1177
- `authorityMaterials` array and remains path-binding limited, while usage 1
1178
- compares against the leaf and still requires independently validated PKIX.
1179
- Usage 3 compares against the supplied leaf certificate or SPKI. Mismatched
1180
- service names, missing certificate roles, unvalidated prerequisites, malformed
1181
- material, and truncation remain invalid, partial, or untrusted rather than
1182
- becoming a DANE match.
1183
-
1184
- ## Isolated cryptographic and mail transport review
1185
-
1186
- These two network actions are disconnected from Lookup, Bulk, monitoring, and
1187
- fixed recipes. They run only through their dedicated commands, require
1188
- `--owned-or-authorized` on every invocation, use one caller-selected literal
1189
- public resolver address, and require a local version-1
1190
- `whoisleuth.dnssec-trust-anchor` JSON file. The resolver receives the DNS
1191
- questions and can apply its own logging and retention. Resolver hostnames,
1192
- private or reserved addresses, automatic resolver selection, and a non-default
1193
- DNS port are refused.
1194
-
1195
- Queries set RD, CD, and EDNS DO so the selected recursive resolver returns
1196
- DNSSEC material for local validation; the resolver's AD bit is recorded only
1197
- as response metadata and never substitutes for local cryptographic checks.
1198
-
1199
- `dnssec-validate <domain> --resolver <public-IP> --trust-anchor <anchor.json>
1200
- --owned-or-authorized` performs an isolated DNS-over-TCP validation from the
1201
- supplied anchor. It validates supported DS, DNSKEY, and RRSIG relationships,
1202
- and supported authenticated NSEC or exact NSEC3 denial evidence. NSEC3 opt-out
1203
- spans and unknown flags remain explicitly unsupported. The action
1204
- is capped at 32 DNS queries, eight delegations, 512 KiB of DNS
1205
- response bytes, 2.5 seconds per query, and 15 seconds total, with no retry.
1206
- Authenticated RRsets are capped at 32 records and 16 matching signatures, and
1207
- denial review accepts at most 16 NSEC3 records per response.
1208
- Canonical RRsets are deduplicated and ordered by canonical RDATA before the
1209
- fixed owner, type, class, TTL, and length fields are constructed. NSEC Next
1210
- Domain Name case is preserved as required for its canonical RDATA, while the
1211
- RRSIG Signer's Name in signed data is canonical lowercase. A positive RRset
1212
- whose RRSIG label count indicates wildcard expansion remains `unsupported`
1213
- unless authenticated denial evidence proves the expansion; this version does
1214
- not infer that proof from the signature alone.
1215
- Supported signature algorithms are 5, 7, 8, 10, 13, 14, 15, and 16; supported
1216
- DS digests are 1, 2, and 4; NSEC3 iteration counts above 500 remain
1217
- unsupported. The output is `whoisleuth.dnssec-chain-validation` version 1 and
1218
- keeps `secure`, `insecure`, `bogus`, `indeterminate`, `timed_out`,
1219
- `unsupported`, and `invalid` separate. Resolver transport failures do not
1220
- become cryptographic failures. The report retains the target, selected
1221
- resolver, trust-anchor source and review time, validated zones, bounded counts,
1222
- state, failure stage, and limitations. DNS wire responses, DNSKEY public keys,
1223
- signatures, and transaction identifiers are not retained. `secure` describes
1224
- the supported chain at that observation time; it is not an ownership,
1225
- availability, safety, or maliciousness conclusion.
1226
-
1227
- The trust-anchor document contains exactly `schema`, `version`, `zone`,
1228
- `source`, `reviewedAt`, and one to eight `dsRecords`. Each DS entry contains
1229
- `keyTag`, `algorithm`, `digestType`, and a correctly sized hexadecimal
1230
- `digest`. The file is capped at 64 KiB. WHOISleuth does not download, refresh,
1231
- select, or vouch for an anchor; maintaining its source and review time is the
1232
- operator's responsibility.
1233
-
1234
- `mail-transport [input.json] --resolver <public-IP> --trust-anchor
1235
- <anchor.json> --owned-or-authorized --active-probe` additionally requires a
1236
- separate active-probe acknowledgement on every run. Its version-1
1237
- `whoisleuth.mail-transport.input` names one domain, one to three distinct
1238
- selected MX hostnames, and optional bounded `policyContext.mtaSts` and
1239
- `policyContext.tlsRpt` observations. Policy entries retain a supplied state,
1240
- source, observation time, and `complete`, `partial`, or `unavailable`
1241
- completeness; the command does not fetch either policy.
1242
-
1243
- Selected hosts are processed sequentially. Each host is resolved through the
1244
- chosen resolver, every returned connection candidate is checked as a public
1245
- IP address, at most 16 candidates and four aliases are accepted per resolution,
1246
- and the selected address must still occur in a fresh resolution immediately
1247
- before one direct pinned port-25 connection. The client accepts only bounded
1248
- printable-ASCII SMTP reply framing, sends exactly `EHLO whoisleuth.invalid`, and sends
1249
- `STARTTLS` only when advertised. It never sends `AUTH`, `MAIL FROM`, `RCPT TO`,
1250
- `DATA`, `VRFY`, `EXPN`, a message, or a retry. Per endpoint, SMTP response data
1251
- is capped at 16 KiB, 64 lines, 1,000 bytes per line, and 32 retained capability
1252
- names; one leaf certificate is accepted up to 256 KiB and the fixed endpoint
1253
- timeout is eight seconds. Expiry aborts and destroys the connection. The entire
1254
- sequential run is capped at 30 seconds and shares a
1255
- 32-query, 512-KiB DNS budget.
1256
-
1257
- Address provenance advances only through `selected`, `revalidated`, and
1258
- `connected`; failures retain the strongest completed stage and never describe a
1259
- DNS candidate as connection evidence. Successful fresh public-address checking
1260
- uses `resolution.state=public_revalidated`. The separate
1261
- `addressAuthentication.state` is `not_evaluated` after an address candidate is
1262
- retained because the A or AAAA RRset and any CNAME chain are not
1263
- cryptographically validated by this action; it is `unavailable` when no
1264
- candidate was retained.
1265
-
1266
- TLSA records for `_25._tcp.<selected-mx>` are compared only after their RRset
1267
- or denial evidence validates through that endpoint's separately reported
1268
- DNSSEC context. PKIX authorisation, certificate hostname identity,
1269
- DNSSEC-chain validation, TLSA publication, DANE comparison, STARTTLS, SMTP
1270
- transport, MTA-STS context, and TLS-RPT context retain separate states;
1271
- missing or incomplete evidence in one family is never filled from another.
1272
- If a DANE-TA TLSA usage 2 association is published, active STARTTLS review
1273
- retains only the observed leaf certificate and leaves that comparison partial
1274
- because this action does not construct or validate a certificate path to a
1275
- TLSA trust anchor.
1276
- The `whoisleuth.cli.mail-transport-review` version-1 output retains selected MX
1277
- names, the bounded address value, family, highest proven address stage, and
1278
- separate address-authentication state, greeting status and SHA-256 digest, capability
1279
- names, certificate and SPKI SHA-256 digests, protocol and cipher labels,
1280
- bounded certificate authorization or identity error text, source times, counts,
1281
- completeness, and limitations. Raw greeting or reply
1282
- text, certificate bytes, TLS session material, and DNS wire responses are not
1283
- retained. Only address values whose stage is `connected` can form an exact
1284
- shared-address lead. Address, greeting-digest, and certificate-digest matches
1285
- are labelled review leads only; they do not establish a rogue endpoint, common
1286
- ownership, coordination, intent, safety, or maliciousness. Neither action
1287
- changes Risk, Opportunity, registration availability, or any stored browser
1288
- record.
1289
-
1290
- ## Offline supplied-evidence review
1291
-
1292
- `review-evidence [evidence.json]` accepts one version-1 JSON input and performs
1293
- only deterministic local parsing, comparison, or request planning. Input is
1294
- capped at 16 MiB. The command never retrieves DNS, opens SMTP, contacts an
1295
- RDAP server, submits a resolver query, or looks up an address in a hosted
1296
- GeoIP service.
1297
-
1298
- Supported input schemas are:
1299
-
1300
- - `whoisleuth.dnssec-evidence-input`: compares bounded DS and DNSKEY records,
1301
- recalculates DNSKEY key tags and supported DS digests, and reviews supplied
1302
- RRSIG validity windows at `observedAt`. `consistent` describes only the
1303
- supplied DS/DNSKEY relationship. It does not authenticate a chain to a root
1304
- trust anchor, validate RRSIG cryptography, or prove that a missing record is
1305
- absent.
1306
- - `whoisleuth.tlsa-evidence-input`: binds TLSA records to one explicit
1307
- `_port._transport.hostname` service and compares supplied leaf or authority
1308
- certificate/SPKI bytes. `matched` requires independently validated DNSSEC;
1309
- PKIX-TA and PKIX-EE usages also require an independently validated PKIX path.
1310
- PKIX-TA usage 0 retains per-record authority comparisons but cannot complete
1311
- PKIX-TA assurance because supplied authority material is not proven to belong
1312
- to that validated path. DANE-TA usage 2 likewise cannot complete DANE-TA
1313
- assurance because this offline action does not construct or validate a
1314
- leaf-to-anchor certificate path. The command does not retrieve the certificate
1315
- or negotiate STARTTLS.
1316
- - `whoisleuth.rdap-search-input`: normalises a supplied RDAP search-help
1317
- response and prepares an exact supported reverse-search request without
1318
- sending it. If the document also contains a previously obtained `response`,
1319
- the command checks its bounded property mappings against the reviewed
1320
- registration and reports omitted or unrecognised mappings without treating
1321
- the response as authoritative or complete.
1322
- - `whoisleuth.rpki-route-input`: compares a route prefix and origin ASN with a
1323
- bounded analyst-supplied VRP set.
1324
- - `whoisleuth.cryptographic-assurance.input`: accepts separately wrapped,
1325
- source-qualified DNSSEC, route-origin, and DANE/TLSA evidence and emits three
1326
- independent cards. Every card retains its own authority, source, observation
1327
- time, state, completeness, result, and limitations. An omitted family remains
1328
- `unavailable`; the version-1 `whoisleuth.cryptographic-assurance.review`
1329
- contains `combinedState: null` and never emits a scalar score or combined
1330
- verdict.
1331
- - `whoisleuth.local-geoip-query`: queries an analyst-supplied bounded prefix
1332
- database whose source, version, and licence metadata remain in the result.
1333
- - `whoisleuth.local-mmdb-query`: with an explicit `--mmdb <database-file>`,
1334
- queries one analyst-supplied MaxMind DB file locally. The query document must
1335
- carry `address`, `sourceLabel`, `databaseVersion`, and `license`. WHOISleuth
1336
- does not bundle, download, update, license, transmit, or retain the database.
1337
- The nested result uses the same address, network, country, region, city, ASN,
1338
- source, and limitation fields as the bounded JSON-prefix path. This mode is
1339
- CLI-only because a hosted or browser deployment cannot open an analyst's
1340
- local file; its JSON result remains portable to website-compatible evidence
1341
- archives and reports.
1342
- - `whoisleuth.encrypted-dns-plan-input`: validates an explicitly reviewed
1343
- encrypted-DNS provider contract and prepares a bounded query plan. It does
1344
- not execute the plan.
1345
- - `whoisleuth.zone-intent.input`: parses either a deliberately supported,
1346
- bounded BIND master-file subset or normalised DNS records and compares it
1347
- with one separately attributed observation. TXT values are reduced to
1348
- SHA-256 digests, unsupported directives and records remain rejected, and an
1349
- incomplete observation never becomes a missing-record conclusion. The
1350
- review does not contact a resolver or apply a DNS change.
1351
- - `whoisleuth.domain-portfolio.input`: reviews up to 500 analyst-supplied
1352
- domain inventory rows for exact registrar, DNS, mail, certificate and
1353
- recovery concentration, renewal-review gaps and internal recovery-domain
1354
- dependencies. Account labels and provider assignments remain analyst
1355
- assertions, and the review makes no provider or account request.
1356
- - `whoisleuth.domain-change.input`: compares bounded, separately labelled
1357
- authority and resolver observations; reviews CDS, CDNSKEY and CSYNC
1358
- publication consistency; inventories supplied SRV services and ACME
1359
- dependencies; and records TLS key continuity, stapling, embedded timestamp,
1360
- and HSTS preload context. TXT values are retained only as SHA-256 digests.
1361
- The result is an offline review gate, not a DNSSEC validation, preload-list
1362
- lookup, certificate-authority request, or permission to apply a change.
1363
- - `whoisleuth.dns-convergence.input`: compares the latest supplied observation
1364
- from at least two labelled resolvers or network vantages. It preserves
1365
- converged, divergent, unexpected, incomplete, and insufficient record-set
1366
- states, can compare against optional expected values, and projects a simple
1367
- observation-time plus TTL cache horizon. It does not query DNS or decide
1368
- which divergent value is authoritative, and supplied TTLs do not reveal
1369
- actual cache age or resolver refresh policy.
1370
- - `whoisleuth.nameserver-preflight.input`: reviews an intended nameserver set
1371
- against separately attributed direct-service observations before a
1372
- delegation change. It checks authoritative service, the served NS set, SOA
1373
- evidence, public addresses and in-bailiwick glue readiness without querying
1374
- DNS, opening a socket or changing registry configuration.
1375
- - `whoisleuth.trust-store-comparison.input`: compares an already-observed,
1376
- bounded certificate-chain SHA-256 fingerprint set with up to 16
1377
- analyst-supplied trust-store snapshots. Each snapshot carries its own name,
1378
- version, source, review time, completeness, and no more than 2,048 anchor
1379
- fingerprints. Exact intersections, complete non-observations, and
1380
- inconclusive inputs remain separate. A match does not build or validate a
1381
- certificate path, and non-observation does not mean a client would reject
1382
- the chain because served chains commonly omit their root.
1383
-
1384
- The common output is `whoisleuth.cli.offline-evidence-review` version 1. It
1385
- retains the nested result's explicit state and limitations. A locally
1386
- consistent relationship is not converted into a claim about current
1387
- publication, ownership, safety, or maliciousness.
1388
-
1389
- `review-evidence --strict-exit` is an opt-in local automation boundary. It
1390
- returns the partial-failure exit code when an input family exposes an explicit
1391
- failed `gate`, or when a zone-intent comparison is incomplete or contains a
1392
- difference, missing value or unexpected value. Other evidence families retain
1393
- their ordinary exit behaviour; the flag does not invent a pass/fail policy for
1394
- them. JSON is still emitted before the exit status is selected.
1395
-
1396
- ## Domain control manifests
1397
-
1398
- `domain-control [input.json]` builds or reviews a bounded desired-state
1399
- manifest without making a request or changing configuration. A manifest input
1400
- uses `whoisleuth.domain-control-manifest-input` version 1, includes an expiry,
1401
- and may record expected nameservers, DS, MX, CAA, TLS issuer, TLS SPKI
1402
- fingerprint, registrar-lock preference, and a renewal-review date for up to 100
1403
- domains. Empty fields mean unconfigured; they do not require a record to be
1404
- absent.
1405
-
1406
- The emitted `whoisleuth.domain-control-manifest` version 2 is normalised and
1407
- protected by a `sorted-json-v2` SHA-256 digest. Version 1 remains readable with
1408
- its historical canonicalization contract. Either version can also be passed to
1409
- `sign-artifact` for an optional local Ed25519 signature. Integrity and
1410
- signature verification detect changes and authenticate possession of the
1411
- selected key; neither establishes that the desired state is correct. Manifest
1412
- input and verification reject unknown fields, and verification rejects
1413
- non-canonical content rather than returning a shape that differs from the
1414
- normalised contract covered by the digest.
1415
-
1416
- The same version-two manifest is the browser Console's domain-control passport
1417
- format; its browser and CLI input envelope remains version 1. A deliberately
1418
- selected passport can move the supported desired-state
1419
- subset between Brand Profiles and the CLI without copying profile identity,
1420
- notes, observations, change windows, suppressions, or other browser-only
1421
- planning. Browser import verifies the digest and presents a field-level,
1422
- non-destructive preview. A manifest remains an analyst-authored expectation in
1423
- either runtime; interoperability does not make it observed evidence.
1424
-
1425
- A review input uses `whoisleuth.domain-control-review-input` version 1 and
1426
- contains one manifest plus separately attributed observations. Only a complete
1427
- `observed` field may produce `drift`. Partial, unavailable, unsupported, and
1428
- missing observations remain inconclusive, and unrelated observations are
1429
- counted but ignored. The review performs no DNS, RDAP, HTTP, TLS, SMTP, or
1430
- registrar request.
1431
-
1432
- The saved-Lookup review envelope uses
1433
- `whoisleuth.cli.domain-control-review-input` version 1 and accepts from 1 to
1434
- 100 saved Lookup documents. The raw envelope is scanned before parsing for
1435
- duplicate keys, the prototype-sensitive `__proto__` key, and a 50-level
1436
- envelope depth that preserves each saved Lookup's standalone 48-level limit.
1437
- Its aggregate scanner permits up to 5,050,000 keys and 10,100,000 values so
1438
- the envelope does not silently narrow the per-document version-1 contract;
1439
- no individual object or array may contain more than 10,000 items, and the
1440
- command's byte ceiling remains authoritative. Each embedded Lookup is
1441
- then independently revalidated against its ordinary saved-document bounds.
1442
-
1443
- ## Domain assurance review
1444
-
1445
- `assurance [input.json]` reviews one bounded analyst-authored workflow entirely
1446
- offline. Version 1 inputs remain supported. Version 2 adds optional
1447
- `customChecks` to retirement inputs and remains the current normalised output
1448
- version. The input uses `whoisleuth.domain-assurance.input` and one of three
1449
- fixed kinds:
1450
-
1451
- - `planned-change` records a bounded window, evidence-backed milestones,
1452
- rollback criteria, and post-change checks;
1453
- - `recovery-dependencies` reviews up to 100 domains across registrar, DNS,
1454
- mail, certificate, and recovery providers and highlights exact provider
1455
- concentration; and
1456
- - `retirement` checks the fixed decommissioning controls without treating an
1457
- unchecked item as complete. Version 2 can add up to 20 analyst-defined checks,
1458
- each with a bounded identifier, label, expected boolean, and true, false, or
1459
- null observed value.
1460
-
1461
- Provider labels, readiness confirmations, evidence references, and expected
1462
- states remain analyst assertions. The output preserves incomplete and
1463
- needs-review states and gives every negative state an explicit review reason.
1464
- The versioned input rejects unknown fields and contradictory evidence attached
1465
- to an unfinished check. It stores no credentials, makes no request, and changes
1466
- no registrar, DNS, mail, certificate, or recovery configuration.
1467
-
1468
- ## Domain change assurance packet
1469
-
1470
- `change-packet [input.json]` combines one pre-change domain review, one
1471
- post-change domain review, and one planned-change assurance input for the same
1472
- domain. The input remains `whoisleuth.domain-change-packet.input` version 1. The
1473
- command revalidates each nested contract, summarises changed authoritative
1474
- record sets, and emits `whoisleuth.domain-change-packet` version 2 with a
1475
- `sorted-json-v2` SHA-256 integrity digest. Historical packet version 1 remains
1476
- verifiable with its original digest bytes.
1477
-
1478
- The packet is ready only when both supplied observation reviews and the change
1479
- plan pass their own bounded gates. Partial or unavailable evidence remains a
1480
- review reason. Packet assembly performs no collection or configuration change,
1481
- and digest validity does not authenticate the analyst. A reviewed packet can be
1482
- passed to `sign-artifact` when the operator has an independently managed
1483
- Ed25519 signing-key lifecycle.
1484
-
1485
- ## Pre-sharing review
1486
-
1487
- `sharing-review [artifact.json]` performs a redacted local preflight before an
1488
- analyst deliberately shares an artefact. The analyst must select a TLP 2.0
1489
- marking, a recipient scope, and a bounded purpose. Optional confirmations record
1490
- human review, personal-data review, and redaction review. The command validates
1491
- supported artefact integrity where possible, detects stricter embedded TLP
1492
- markings, and reports only key-category counts plus bounded, control-free schema
1493
- and integer version metadata. It emits no content values and copies no raw
1494
- evidence.
1495
-
1496
- An imported stricter marking cannot be downgraded by the requested marking, and
1497
- recipient scope is checked against the effective marking. Evading this local
1498
- preflight remains possible, so the result is a review aid rather than access
1499
- control, legal advice, or recipient authorisation.
1500
-
1501
- ## Fixed investigation plans
1502
-
1503
- `workflow-plan <recipe> <domain|brand>` turns one of four fixed recipes into a
1504
- versioned `whoisleuth.cli.investigation-plan` document. Supported recipes are
1505
- `domain-triage`, `lookalike-review`, `owned-domain-review`, and
1506
- `historical-comparison`. Each step names an existing CLI command, carries its
1507
- arguments as a JSON array, declares whether it is offline or networked, and
1508
- marks network-disclosure or analyst-selection approval gates.
1509
-
1510
- The command is plan-only. It does not execute a step, construct a shell script,
1511
- interpret a placeholder as a file path, read an artefact, make a request,
1512
- change a case, or submit evidence. Analysts deliberately run selected commands
1513
- after reviewing their collection boundaries. This provides repeatable
1514
- domain-specific workflows without an arbitrary automation or plugin surface.
1515
-
1516
- ## Local handoffs and one-shot monitoring
1517
-
1518
- `brief [lookup.json]` reads one saved Lookup and emits version 2 of the bounded
1519
- `whoisleuth.cli.lookup-brief` contract. It separates retained facts,
1520
- contradictions, incomplete source states, and manual actions. Each action
1521
- states its evidence basis and expected outcome; it does not run the action or
1522
- create a case.
1523
-
1524
- `case-pack [cases.json] --audience <internal|trusted|public> --reviewed` reads a
1525
- browser case export containing at most 25 normalised cases, applies the selected
1526
- redaction boundary, and emits a browser-importable case collection with a
1527
- canonical SHA-256 digest and redaction manifest. It refuses an invalid,
1528
- duplicate, larger, or generated package above the browser's 2 MiB import limit
1529
- rather than silently omitting records or evidence. Trusted output removes notes,
1530
- recipient values, and manual-pivot targets. Public output additionally removes
1531
- actions, analyst assertions, named investigation branches, and every exact
1532
- Brand Profile identifier from both the top-level cases and embedded Case report
1533
- v8 projections. Its redaction manifest records the bounded
1534
- `brandProfileReferencesOmitted` count, which cannot exceed 200. Trusted and
1535
- internal output preserve the exact references and record zero omissions.
1536
- Trusted output retains branch references while redacting action recipients.
1537
- The review flag records an explicit choice; it does not prove recipient
1538
- authorisation, profile ownership, attribution, or factual correctness.
1539
-
1540
- `verify-artifact` recognises the complete case-pack envelope before browser
1541
- import. It checks the canonical digest, supported case-export schema, bounded
1542
- case count, report count, audience and explicit review marker without printing
1543
- case contents. For Case schema 12 it validates the opaque-reference syntax,
1544
- requires public cases and reports to contain no references, and requires
1545
- trusted or internal report references to match their top-level case. The
1546
- case-pack envelope is version 2 with `sorted-json-v2` integrity; a valid legacy
1547
- version-1 pack carrying a
1548
- pre-v12 Case collection remains accepted without inventing the new field or
1549
- redaction count. Current builders reject a schema-12 raw case unless its
1550
- complete bounded Case projection, safe canonical identity, and unique
1551
- at-most-eight reference list survive normalisation exactly; over-bound notes,
1552
- actions, and other fields are not silently truncated. Legacy builder input
1553
- remains lenient for later fields. Before digest calculation, the verifier binds
1554
- each report's supported schema version, canonical Case identifier, domain, and
1555
- audience-sensitive projection to its top-level case. It rejects re-signed
1556
- public notes, actions, assertions, branches, recipients, manual-trail targets,
1557
- or Brand Profile references, and the corresponding trusted-field leaks,
1558
- including copies at unexpected nested paths. It also enforces the exact
1559
- epoch-specific audience exclusion list, source-case count, and omission-count
1560
- rules. Current Case schema 12 requires report v8, schema 11 requires report v7,
1561
- and schemas through 10 accept supported reports through v6 without inventing
1562
- later branch or Brand-reference fields. For every audience and Case schema 2
1563
- through 11, retained Case and report nodes must have the same object, array,
1564
- null, or scalar kind as their bounded normalised projection, and every nested
1565
- key must belong to that versioned projection. Historical omissions remain
1566
- valid, while re-signed scalar substitutions and undeclared private fields do
1567
- not. These checks
1568
- also govern offline verification and interchange-fidelity classification. This
1569
- detects a changed or internally inconsistent hand-off file but does not
1570
- authenticate the analyst, recipient, source observations, or review decision.
1571
-
1572
- `monitor-once [manifest.json]` performs one bounded Deep control review for at
1573
- most 20 manifest domains with concurrency capped at three. An optional
1574
- `--previous` checkpoint adds local first/last-observed comparison. It is not a
1575
- daemon or scheduler, retains compact normalised observations rather than raw
1576
- payloads, and keeps failed collection incomplete. JSON, JUnit, and explicit
1577
- failure policies are supported.
1578
-
1579
- `workflow-run <recipe> <subject>` executes only concrete steps from the four
1580
- installed fixed recipes. Network steps require `--approve-network` for that
1581
- invocation. Analyst-selection placeholders always pause. A bounded `--resume`
1582
- state must match the exact recipe and subject, cannot add commands or shell
1583
- syntax, and is not evidence that earlier results remain current. Retained
1584
- successful steps must form an exact contiguous prefix of the installed recipe,
1585
- including command, ordered arguments, mode and output schema. A final failed
1586
- step is retried rather than trusted as completed.
1587
-
1588
- The `domain-triage` plan deliberately pairs `export` with `verify-artifact`.
1589
- A fresh current Lookup-evidence export must pass the verifier as
1590
- `structure_valid`; this establishes bounded contract compatibility, not a
1591
- checksum, signature, source truth, or observation currency.
1592
-
1593
- ## Optional local rendered capture
1594
-
1595
- `packages/web-capture` is a private repo-local Playwright package, not part of
1596
- the distributable core CLI or hosted application. It requires an explicit
1597
- authorisation flag, one domain-hosted HTTP(S) target, and one new output
1598
- directory. IP-literal targets are rejected so successful manifests remain
1599
- compatible with the domain-only Cases importer:
1600
-
1601
- ```bash
1602
- npm run capture:local -- https://example.test --output-dir ./capture-example --authorize-rendered-capture
1603
- npm run capture:compare -- ./official/manifest.json ./candidate/manifest.json --json
1604
- ```
1605
-
1606
- It writes a fixed 1024x768 PNG, screenshot SHA-256 and perceptual dHash, a DOM
1607
- digest containing hashes and bounded element counts rather than markup or page
1608
- text, and a version-2 `whoisleuth.web-capture-manifest` compatible with the
1609
- Cases importer. Version-1 manifests remain importable.
1610
-
1611
- Rendered capture executes page JavaScript. Each admitted resource operator
1612
- receives the exact requested URL, including path and query, and ordinary
1613
- allowlisted request headers; structured manifest and digest fields retain only
1614
- the target hostname, final HTTP(S) origin, admitted public resource hostnames,
1615
- and one control-sanitised page title of up to 300 characters, not those paths or queries. The screenshot necessarily
1616
- preserves visible rendered content and may include page text or a page-reflected
1617
- path or query until the operator deletes it. It caps HTTP(S) requests and request
1618
- hostnames, blocks downloads, service workers, dedicated and shared workers, WebSockets, WebRTC, WebTransport,
1619
- non-read methods, credentials, non-default ports, and private or reserved
1620
- addresses, and retains no request path, query, headers, bodies, cookies,
1621
- credentials, DOM markup, or body text in the structured manifest or digest
1622
- beyond that bounded title. Every browser request is intercepted, its hostname is resolved again,
1623
- and the bounded response is fetched through WHOISleuth's public-address-pinned
1624
- transport. Individual responses are capped at 4 MiB and all fetched response
1625
- bodies together are capped at 24 MiB. Redirect destinations pass through the
1626
- same interception and validation before collection continues. Use a disposable
1627
- network-restricted environment for untrusted targets because rendered page code
1628
- still executes inside the isolated browser process.
1629
-
1630
- The separate offline comparison command accepts two selected version-2 local
1631
- capture manifests. It first verifies each referenced screenshot and DOM digest
1632
- against its declared size, SHA-256, screenshot dimensions, and perceptual hash;
1633
- it also validates bounded source and limitation metadata and requires the DOM,
1634
- manifest, and source collection times to agree. It then
1635
- compares screenshot distance, exact equality of the bounded preorder
1636
- element-tag sequence and body text-node sequence, bounded element counts, page
1637
- identity, technologies, and request-domain sets. The tag sequence omits nesting
1638
- and attributes. The version-1 DOM-digest schema retains the `visibleText` field
1639
- name for compatibility, but it includes bounded body text nodes that CSS or
1640
- non-rendered containers may hide and is not a visibility claim.
1641
- Version-2 comparison output reports only whether the two bounded page titles
1642
- match; it does not copy either title. It does not recrawl either target, reveal the input paths, or collapse the
1643
- independent components into a similarity or maliciousness score. A missing
1644
- perceptual hash remains unavailable rather than becoming a visual difference.
1645
-
1646
- ## Lookup evidence export
1647
-
1648
- `export` converts one version-1 `whoisleuth.cli.lookup` domain document from a
1649
- file or stdin into the same versioned `whoisleuth.lookup-evidence` JSON package
1650
- produced by the web Lookup tool. It performs no lookup and writes only to
1651
- stdout, so use ordinary shell redirection when a file is wanted. Pretty JSON is
1652
- the default; `--compact` emits one compact JSON line for pipeline use, while
1653
- `--markdown` produces a readable source-attributed summary and `--html`
1654
- produces a self-contained printable report. Compact, Markdown, and HTML output
1655
- are mutually exclusive. Markdown and HTML include the fixed generator footer
1656
- by default; `--no-attribution` removes only that footer and is rejected for
1657
- JSON output.
1658
-
1659
- The current schema-26 JSON output can be passed directly to
1660
- `verify-artifact`. The verifier reports `structure_valid` because this format
1661
- has no embedded checksum or signature. Use a verified investigation manifest
1662
- and exact manifest-entry identity when byte-for-byte retention matters, and
1663
- review the bounded privacy-projected registry RDAP publication, normalised
1664
- WHOIS values, and bounded contacts before sharing. `interchange-report`
1665
- recognises the current format and describes the
1666
- browser replay as lossy by design because replay renders bounded normalised
1667
- facts rather than raw payloads. Schema 25 remains readable when its retained
1668
- historical wrappers differ from retained diagnostics in the ways emitted by
1669
- the former producer. The diagnostics remain authoritative, unavailable wrapper
1670
- data is suppressed during replay, and other contradictory legacy shapes fail
1671
- closed rather than becoming positive observations.
1672
-
1673
- The saved input is capped at 8 MiB and scanned before parsing for duplicate
1674
- object keys, the prototype-sensitive `__proto__` key, more than 48 nesting levels, more than 50,000
1675
- keys, more than 100,000 values, or more than 10,000 items in one container. It is then revalidated using the same schema, source-status,
1676
- parsed-data, scalar, list, and event boundaries as `compare`. Portable
1677
- projection stops at 24 levels and 20,000 entries before recursive copying, and
1678
- the completed package must pass the same portable-tree assertion used by the
1679
- browser replay and serializer.
1680
- The export retains query context, explicitly projected source diagnostics,
1681
- normalised registry data, a bounded privacy-projected registry RDAP
1682
- publication, availability analysis, bounded WHOIS referral-hop
1683
- metadata without response bodies, and the shared
1684
- registry-source comparison. Version 23 can add the exact local
1685
- SSLBL comparison already represented by a deep full Lookup. Version 21 adds the bounded registry
1686
- lifecycle, disclosure, publication-quality, reconciliation, and abuse-routing
1687
- interpretation derived from the already-collected sources. Registrar RDAP raw
1688
- data, contacts, entities, links, notices, and source-specific handles remain
1689
- excluded. Schema version 20 replaces selected security-policy values in
1690
- retained HTTP evidence
1691
- with presence-only markers and can include fixed response-policy findings from
1692
- an already-represented deep lookup. Version 19 additionally retains the bounded credential-surface projection
1693
- when the saved deep lookup represents it. Version 18 retains the bounded structured identity projection
1694
- when the saved deep lookup represents it. Version 17 retains the normalised portable-field comparison, explicit
1695
- source-health states, a strict bounded projection of observed network
1696
- registration, an optional normalised security.txt disclosure source, HTTPS
1697
- service-binding publications, and the passive browser-library profile when the
1698
- saved deep lookup represents them. Raw IP RDAP payloads, security.txt response
1699
- bodies, script references, matched script content, and contact entities remain
1700
- excluded.
1701
-
1702
- Markdown output summarises query context, assessment state, registry sources,
1703
- source reconciliation, network observations, and collection diagnostics. It
1704
- escapes all upstream text as untrusted content, bounds displayed values and
1705
- lists, and deliberately omits raw RDAP JSON and full WHOIS response bodies.
1706
- Use JSON when the complete evidence package or machine processing is required.
1707
- The browser Console reuses this bounded Markdown renderer for domain results
1708
- after first reducing the typed Lookup response to the known normalised fields
1709
- needed by the report. It separately offers bounded IP and ASN summaries from
1710
- their normalised RDAP and source-health fields; IP reports can also include
1711
- bounded reverse-DNS context already collected by Lookup. The browser action
1712
- does not change the CLI input or output contract.
1713
- When a version-5, version-6, or version-7 lookup records a documented registry-access constraint, both
1714
- Markdown and HTML include that context in collection diagnostics without
1715
- changing its non-authoritative interpretation. Both formats also include the
1716
- selected address and bounded network registration when present, together with
1717
- the edge-versus-origin limitation.
1718
-
1719
- HTML uses the same bounded summary model as Markdown. The generated document
1720
- contains inline styling for screen and print, but no scripts, forms, active
1721
- links, or external resources. A restrictive embedded Content Security Policy
1722
- provides defence in depth when the local file is opened in a browser. All
1723
- registry values are HTML-escaped and displayed as text.
1724
-
1725
- This is a deliberately rich evidence package. Raw registry sources can contain
1726
- publicly published contact data, and deep availability evidence can contain
1727
- bounded website, DNS, mail, page-identity, and TLS observations. Review and
1728
- secure the output before sharing it. The CLI does not add browser-only IDN
1729
- profile analysis, so that optional evidence field is `null`.
96
+ | 0 | The command completed; individual sources can still be partial. |
97
+ | 2 | The command, option or input was invalid. |
98
+ | 3 | Collection, lookup or comparison failed. |
99
+ | 4 | The result was partial or a selected evidence policy was not met. |
100
+ | 70 | The CLI could not complete bootstrap. |
101
+ | 130 | The analyst cancelled; no partial final result was emitted. |
102
+ | 143 | SIGTERM stopped the process; no partial final result was emitted. |
103
+
104
+ ## Portable documents and compatibility
105
+
106
+ Readers accept only their declared public and current versions. Unknown,
107
+ unreleased historical and future schemas fail before partial interpretation;
108
+ an invalid import is not treated as an empty document.
109
+
110
+ `inspect-archive` reads current workspace archive v6 and exact public v5. It
111
+ reports section metadata and digest-only search results unless `--reveal` is
112
+ explicitly selected. It never searches notes, contacts or arbitrary raw fields.
113
+
114
+ `export` reads supported saved Lookup v1 or v2 and writes current Lookup
115
+ evidence schema 27. Exact public schema 26 remains readable and can contain
116
+ public contact fields; current schema 27 excludes raw registration payloads,
117
+ expanded contacts, credentials, complete query-bearing URLs and provider
118
+ payloads.
119
+
120
+ `verify-artifact` checks a recognised structure and its applicable integrity
121
+ contract. `interchange-report` describes retained and omitted fields.
122
+ `sign-artifact` and `verify-signature` keep artefact validity, signature
123
+ validity and signer trust separate. None of these checks establishes that
124
+ evidence is accurate, current, safe to share or attributable to a person.
125
+
126
+ ## Command-family boundaries
127
+
128
+ - Evidence collection commands retain separate source states. A failed or
129
+ missing supporting source is not converted into absence, availability or a
130
+ favourable score.
131
+ - Discover and Certificate Transparency commands produce review candidates.
132
+ Generation, publication and shared infrastructure do not establish ownership,
133
+ control, activity, intent or maliciousness.
134
+ - Bulk applies one declared collection contract per target. Fast accepts up to
135
+ 500 targets and Deep up to 50; each target remains a separate request.
136
+ - Respond commands prepare local Cases, packets and sharing reviews. They do not
137
+ submit, publish, notify or grant recipient authorisation.
138
+ - Assurance, comparison and calibration commands describe supplied or retained
139
+ evidence. They do not tune the running model, change infrastructure or turn an
140
+ analyst label into observed truth.
141
+ - `workflow-plan` lists fixed installed recipes without executing them.
142
+ `workflow-run` executes only installed steps, requires approval for network
143
+ work and pauses for analyst-selection placeholders.
144
+
145
+ Use the installed focused help for positional inputs, exact ceilings, options,
146
+ network effects, outputs and command-specific exit behaviour.