@slicedearth/whoisleuth-cli 1.34.0 → 1.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +19 -2
  2. package/bin/whoisleuth.mjs +41 -24
  3. package/cli/arguments.mjs +326 -3
  4. package/cli/artifact-verify.mjs +60 -14
  5. package/cli/bulk.mjs +1 -2
  6. package/cli/compare.mjs +1 -1
  7. package/cli/completion.mjs +36 -2
  8. package/cli/doctor.mjs +11 -5
  9. package/cli/evidence-command-arguments.mjs +1 -1
  10. package/cli/evidence-command-runner.mjs +2 -2
  11. package/cli/evidence-signing.mjs +4 -4
  12. package/cli/export-evidence.mjs +9 -2
  13. package/cli/formatters/html.mjs +1 -1
  14. package/cli/formatters/json.mjs +13 -1
  15. package/cli/formatters/terminal.mjs +14 -4
  16. package/cli/investigation-plan.mjs +112 -0
  17. package/cli/local-mmdb-review.mjs +76 -0
  18. package/cli/lookalike-calibration.mjs +121 -0
  19. package/cli/lookup-command-runner.mjs +4 -1
  20. package/cli/lookup-plan.mjs +3 -3
  21. package/cli/lookup-reconcile.mjs +141 -0
  22. package/cli/lookup-timeline.mjs +1 -1
  23. package/cli/mail-review.mjs +48 -3
  24. package/cli/offline-evidence-review.mjs +159 -0
  25. package/cli/registry-doctor.mjs +125 -0
  26. package/cli/registry-support.mjs +1 -4
  27. package/cli/risk-calibration.mjs +117 -9
  28. package/cli/runner.mjs +342 -15
  29. package/cli/saved-lookup.mjs +2 -2
  30. package/cli/sharing-review.mjs +202 -0
  31. package/cli/source-reliability.mjs +1 -1
  32. package/docs/cli-reference.md +1110 -0
  33. package/docs/cli.md +224 -847
  34. package/frontend/src/lib/analysis/acquisition-decision-packet.js +1 -1
  35. package/frontend/src/lib/analysis/acquisition-due-diligence.js +6 -6
  36. package/frontend/src/lib/analysis/bulk-domain-comparison.js +12 -12
  37. package/frontend/src/lib/analysis/bulk-mail-exposure.js +1 -1
  38. package/frontend/src/lib/analysis/bulk-retry-plan.js +3 -10
  39. package/frontend/src/lib/analysis/bulk-session-model.js +7 -2
  40. package/frontend/src/lib/analysis/bulk-source-coverage.js +76 -0
  41. package/frontend/src/lib/analysis/case-evidence-model.js +26 -4
  42. package/frontend/src/lib/analysis/case-migration-model.js +6 -1
  43. package/frontend/src/lib/analysis/case-record-contracts.js +4 -2
  44. package/frontend/src/lib/analysis/case-record-core.js +5 -1
  45. package/frontend/src/lib/analysis/case-record-operations.js +6 -1
  46. package/frontend/src/lib/analysis/case-response-packet.js +7 -7
  47. package/frontend/src/lib/analysis/evidence-coverage-ledger.js +328 -0
  48. package/frontend/src/lib/analysis/http-summary.js +2 -1
  49. package/frontend/src/lib/analysis/investigation-capsule.js +105 -0
  50. package/frontend/src/lib/analysis/lookup-asset-graph.js +806 -0
  51. package/frontend/src/lib/analysis/lookup-decision-support.js +480 -0
  52. package/frontend/src/lib/analysis/lookup-investigation-brief.js +153 -0
  53. package/frontend/src/lib/analysis/lookup-presentation.js +74 -0
  54. package/frontend/src/lib/analysis/lookup-response.js +2 -0
  55. package/frontend/src/lib/analysis/lookup-source-refresh.js +304 -0
  56. package/frontend/src/lib/analysis/lookup-summary-model.js +398 -0
  57. package/frontend/src/lib/analysis/page-similarity.js +1 -1
  58. package/frontend/src/lib/analysis/relationship-evidence.js +3 -3
  59. package/frontend/src/lib/analysis/relationship-observation-model.js +4 -4
  60. package/frontend/src/lib/analysis/scoring.js +8 -88
  61. package/frontend/src/lib/analysis/shortlist-model.js +8 -4
  62. package/frontend/src/lib/analysis/utils.js +7 -40
  63. package/frontend/src/lib/analysis/visualization-bounds.js +26 -0
  64. package/frontend/src/lib/analysis/visualization-force-graph.js +352 -0
  65. package/frontend/src/lib/analysis/visualization-matrix.js +88 -0
  66. package/frontend/src/lib/analysis/visualization-models.js +370 -0
  67. package/frontend/src/lib/analysis/visualization-time-series.js +144 -0
  68. package/frontend/src/lib/analysis/website-snapshot-model.js +57 -3
  69. package/frontend/src/lib/analysis/workspace-archive-crypto.js +1 -1
  70. package/frontend/src/lib/analysis/workspace-archive.js +2 -2
  71. package/lib/analyst-taxonomy.mjs +40 -0
  72. package/lib/availability.mjs +30 -10
  73. package/lib/browser-library-profile.mjs +15 -2
  74. package/lib/bulk-limits.mjs +13 -0
  75. package/lib/certificate-extension-profile.mjs +71 -0
  76. package/lib/credential-surface-profile.mjs +1 -1
  77. package/lib/ct-response-bounds.mjs +9 -0
  78. package/lib/ct-search.mjs +66 -12
  79. package/lib/dns-delegation-health.mjs +56 -5
  80. package/lib/dnssec-evidence-validation.mjs +235 -0
  81. package/lib/domain-assurance.mjs +368 -0
  82. package/lib/domain-control-manifest.mjs +382 -0
  83. package/lib/domain-posture.mjs +11 -10
  84. package/lib/encrypted-dns-contract.mjs +81 -0
  85. package/lib/evidence-export.mjs +17 -3
  86. package/lib/evidence-report-markdown.mjs +1 -1
  87. package/lib/evidence-report.mjs +4 -1
  88. package/lib/favicon.mjs +3 -8
  89. package/lib/generated/cisa-kev-catalog.mjs +1668 -0
  90. package/lib/html-signals.mjs +4 -0
  91. package/lib/http-evidence-bounds.mjs +4 -0
  92. package/lib/http-intelligence.mjs +2 -1
  93. package/lib/ip-prefix.mjs +87 -0
  94. package/lib/local-geoip-evidence.mjs +89 -0
  95. package/lib/lookup-response-contract.mjs +346 -0
  96. package/lib/observed-network-context.mjs +1 -1
  97. package/lib/opportunity-scoring.mjs +107 -0
  98. package/lib/outbound-identity.mjs +9 -0
  99. package/lib/outbound-request-bounds.mjs +9 -0
  100. package/lib/page-fingerprints.mjs +1 -1
  101. package/lib/perceptual-hash-comparison.mjs +34 -0
  102. package/lib/perceptual-hash.mjs +3 -31
  103. package/lib/project-metadata.mjs +8 -0
  104. package/lib/provider-policy-admission.mjs +49 -0
  105. package/lib/rdap-capabilities.mjs +6 -6
  106. package/lib/rdap-search-workbench.mjs +247 -0
  107. package/lib/rdap.mjs +7 -2
  108. package/lib/registry-access.mjs +1 -1
  109. package/lib/registry-capabilities.mjs +31 -1
  110. package/lib/registry-capability-catalogue.mjs +15 -14
  111. package/lib/registry-insights.mjs +6 -6
  112. package/lib/response-policy.mjs +81 -6
  113. package/lib/risk-scoring.mjs +206 -83
  114. package/lib/rpki-evidence.mjs +105 -0
  115. package/lib/safe-fetch.mjs +2 -1
  116. package/lib/scoring-evidence-quality.mjs +107 -0
  117. package/lib/security-txt.mjs +3 -3
  118. package/lib/semantic-version.mjs +54 -0
  119. package/lib/static-html-analysis.mjs +27 -0
  120. package/lib/threat-intelligence-manifests.mjs +1 -1
  121. package/lib/threatfox-intelligence.mjs +11 -6
  122. package/lib/tls-intelligence.mjs +13 -3
  123. package/lib/tlsa-evidence.mjs +194 -0
  124. package/lib/urlhaus-intelligence.mjs +11 -6
  125. package/lib/urlscan-intelligence.mjs +10 -5
  126. package/lib/website-security-posture.mjs +16 -9
  127. package/lib/website-technology.mjs +1 -1
  128. package/lib/whois-chain.mjs +14 -152
  129. package/lib/whois.mjs +2 -2
  130. package/package.json +6 -2
  131. package/third-party-notices.txt +911 -19
package/docs/cli.md CHANGED
@@ -1,17 +1,11 @@
1
- # WHOISleuth CLI
1
+ # WHOISleuth CLI guide
2
2
 
3
- The first-party command-line interface runs the same local classification and
4
- lookup modules as the Express and serverless adapters. It does not call the
5
- hosted WHOISleuth deployment.
6
-
7
- `whoisleuth --help` displays the copyright, AGPL-3.0-only licence, and official
8
- source location. It groups commands by investigation, discovery, saved-evidence,
9
- integrity, and calibration workflows. `whoisleuth <command> --help` displays the
10
- command's purpose, focused invocation, example, and collection boundary without
11
- printing the full command list. `whoisleuth commands --json` exposes those same
12
- contracts as a stable local catalogue for wrappers and terminal tooling.
13
- Packaged copies include `LICENSE`, `NOTICE`, and
14
- `TRADEMARKS.md` alongside the CLI documentation.
3
+ The first-party CLI runs locally and uses the same bounded classification,
4
+ collection, and analysis modules as the web application. It does not call the
5
+ hosted WHOISleuth deployment. Use this guide for installation, first commands,
6
+ collection boundaries, output, and automation behaviour. The
7
+ [complete CLI reference](cli-reference.md) documents every command family and
8
+ artefact contract.
15
9
 
16
10
  ## Installation
17
11
 
@@ -24,866 +18,249 @@ npm install --global --ignore-scripts @slicedearth/whoisleuth-cli
24
18
  whoisleuth doctor
25
19
  ```
26
20
 
27
- Update and verify an existing global installation with:
21
+ Update and verify an existing installation with:
28
22
 
29
23
  ```bash
30
24
  npm install --global --ignore-scripts @slicedearth/whoisleuth-cli@latest
31
25
  whoisleuth --version
26
+ whoisleuth doctor
32
27
  ```
33
28
 
34
- The scoped package and application share one semantic version. The package runs
35
- locally and does not call the hosted WHOISleuth deployment. Networked commands
36
- contact only the sources described by their collection profile; offline review,
37
- comparison, verification, completion, and manual commands remain local.
29
+ The scoped package and application share one semantic version. The package
30
+ includes `LICENSE`, `NOTICE`, `TRADEMARKS.md`, this guide, and the complete
31
+ reference. It requires no lifecycle scripts, so the examples disable them.
32
+ An unqualified `npx whoisleuth` is not this project.
38
33
 
39
- ## Commands
34
+ From a repository checkout, replace `whoisleuth` with
35
+ `node bin/whoisleuth.mts`. Maintainers can assemble, install, and smoke-test the
36
+ exact compiled package closure without publishing it:
40
37
 
41
38
  ```bash
42
- node bin/whoisleuth.mts lookup example.com
43
- node bin/whoisleuth.mts lookup AS13335 --json
44
- printf 'example.com\n' | node bin/whoisleuth.mts lookup --json
45
- node bin/whoisleuth.mts lookup example.com --deep
46
- node bin/whoisleuth.mts lookup example.com --deep --plan --json
47
- node bin/whoisleuth.mts lookup example.com --deep --summary
48
- node bin/whoisleuth.mts lookup example.com --deep --verbose
49
- node bin/whoisleuth.mts lookup example.com --deep --markdown --output lookup.md
50
- node bin/whoisleuth.mts lookup example.com --deep --json --strict-exit --events
51
- cat domains.txt | node bin/whoisleuth.mts bulk --jsonl
52
- node bin/whoisleuth.mts bulk domains.txt --csv --registered-only
53
- node bin/whoisleuth.mts bulk domains.txt --domains --inconclusive-only
54
- node bin/whoisleuth.mts bulk domains.txt --queries --errors-only
55
- node bin/whoisleuth.mts bulk domains.txt --concurrency 4
56
- node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json
57
- node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json --resume
58
- node bin/whoisleuth.mts ct-search 'example brand' --json
59
- node bin/whoisleuth.mts discover example.com --preset common --jsonl
60
- node bin/whoisleuth.mts discover example.test --dictionary private-terms.txt --snapshot discovery-state.json --json
61
- node bin/whoisleuth.mts discover-scan example.test --scan-limit 50 --checkpoint candidate-scan.json --json
62
- node bin/whoisleuth.mts discover-scan example.test --deep --scan-limit 20 --resolver 192.0.2.53 --observation-snapshot observed-candidates.json --csv
63
- node bin/whoisleuth.mts posture example.com --selectors selector1 --retired-selectors selector0 --mail-profile defensive-no-mail --json
64
- node bin/whoisleuth.mts http example.com --json
65
- node bin/whoisleuth.mts tls example.com --json
66
- node bin/whoisleuth.mts registry-support example.uk --json
67
- node bin/whoisleuth.mts risk-calibrate calibration.json --json
68
- node bin/whoisleuth.mts verify-artifact workspace.json --json
69
- node bin/whoisleuth.mts verify-artifact lookup.json --json
70
- node bin/whoisleuth.mts verify-artifact workspace-encrypted.json --passphrase-file passphrase.txt --json
71
- node bin/whoisleuth.mts source-report lookup.json --json
72
- node bin/whoisleuth.mts inspect-archive workspace.json --json
73
- node bin/whoisleuth.mts inspect-archive workspace.json --search example.invalid --json
74
- node bin/whoisleuth.mts sign-artifact response-packet.json --private-key-file analyst-private.pem > response-packet.signed.json
75
- node bin/whoisleuth.mts verify-signature response-packet.signed.json --public-key-file analyst-public.pem
76
- node bin/whoisleuth.mts lookup example.com --deep --json > lookup.json
77
- node bin/whoisleuth.mts compare lookup.json --json
78
- node bin/whoisleuth.mts page-compare official.json candidate.json --json
79
- node bin/whoisleuth.mts mail-review bulk.json --json
80
- node bin/whoisleuth.mts diff first-lookup.json second-lookup.json --json
81
- node bin/whoisleuth.mts timeline first-observation.json second-observation.json latest-observation.json --json
82
- node bin/whoisleuth.mts export lookup.json > evidence.json
83
- node bin/whoisleuth.mts export lookup.json --markdown > evidence.md
84
- node bin/whoisleuth.mts export lookup.json --html > evidence.html
85
- node bin/whoisleuth.mts doctor
86
- node bin/whoisleuth.mts doctor --network --json
87
- node bin/whoisleuth.mts commands --json
88
- node bin/whoisleuth.mts completion zsh
89
- node bin/whoisleuth.mts completion powershell
90
- node bin/whoisleuth.mts manual | man -l -
39
+ npm run cli:package:check
91
40
  ```
92
41
 
93
- These examples run from a checked-out repository; replace
94
- `node bin/whoisleuth.mts` with `whoisleuth` after installation. The root
95
- application package remains private. A separate scoped CLI archive is assembled
96
- from the exact executable dependency closure, installed in a temporary
97
- directory, and smoke tested with:
42
+ ## First commands
43
+
44
+ Inspect the installed capabilities before collection:
98
45
 
99
46
  ```bash
100
- npm run cli:package:check
47
+ whoisleuth --help
48
+ whoisleuth commands
49
+ whoisleuth doctor
50
+ whoisleuth lookup example.com --plan --json
51
+ ```
52
+
53
+ Run a fast lookup or deliberately request the deeper profile:
54
+
55
+ ```bash
56
+ whoisleuth lookup example.com
57
+ whoisleuth lookup example.com --deep
58
+ whoisleuth lookup example.com --deep --summary
59
+ whoisleuth lookup example.com --deep --markdown --output lookup.md
101
60
  ```
102
61
 
103
- The check includes only the executable, reachable TypeScript modules, CLI
104
- guide, licence, notices, and trademark terms. It excludes application routes,
105
- deployment adapters, tests, and development tools. Direct runtime dependencies
106
- are pinned to the exact versions exercised by the reviewed repository lockfile.
107
- The package and its dependencies require no lifecycle scripts, so the install
108
- examples disable them explicitly.
109
- Its generated manifest stays private. Release-candidate assembly is a separate
110
- explicit operation and never publishes from a developer checkout. Use the
111
- scoped package name; an unqualified `npx whoisleuth` is not this project.
112
-
113
- Lookup defaults to the conservative fast profile. `--deep` must be requested
114
- explicitly and can add bounded WHOIS, DNS, website, TLS, registrar RDAP, and
115
- observed-network IP RDAP work for a domain. The browser Console's optional
116
- security.txt and external intelligence selections are not CLI flags; the CLI
117
- does not run those actions implicitly.
118
-
119
- For a directly entered public IP address, `lookup --deep` can also run one
120
- bounded reverse-DNS query. Terminal output shows the explicit source state and
121
- up to five normalized PTR names; JSON retains up to eight. PTR names are
122
- operator-published routing context, not proof of hosting control or ownership.
123
- Fast lookups do not run this query.
124
-
125
- `lookup --plan` performs classification and prints a versioned preflight without
126
- starting collection. It lists the normalized target, selected fast or deep
127
- mode, planned source families, conditional operations, and the data each family
128
- may receive. The plan cannot predict live feature configuration, cache state,
129
- redirects, referrals, source availability, or an exact request count. It can be
130
- combined with `--json`, but not with report, detail, strict-exit, progress-event,
131
- or quiet options.
132
-
133
- Only one query is accepted by `lookup`. Multiple-input processing belongs to
134
- the explicit `bulk` command rather than being silently inferred by `lookup`.
135
- Standard input is capped at 4 KiB and must contain one non-empty line.
136
-
137
- `registry-support` is an offline catalogue view. It accepts one domain, bare
138
- suffix, or leading-dot suffix as an argument or on stdin and makes no network
139
- request. Its output is described below.
140
-
141
- `doctor` is offline by default. It checks the Node runtime, platform, terminal
142
- capabilities, and availability of offline command families. `doctor --network`
143
- adds independent bounded public-DNS, HTTPS/RDAP bootstrap, and WHOIS port 43
144
- checks against fixed IANA diagnostic infrastructure. The three probes run
145
- within the same per-probe ceiling so one stalled transport does not block the
146
- others. The report retains only state, HTTP status, and a bounded explanation,
147
- not resolved addresses or response content. A failed optional network check
148
- returns the partial-failure exit code rather than claiming that all CLI
149
- collection is unavailable.
150
-
151
- `completion bash`, `completion zsh`, `completion fish`, and `completion
152
- powershell` print static shell completion scripts to stdout. The command does
153
- not edit a shell profile or make a network request. Review the generated script
154
- before sourcing it or placing it in the relevant shell completion directory.
155
- `manual` prints a generated roff manual page and likewise changes no local
156
- configuration.
157
-
158
- `commands` prints the installed command catalogue without running collection or
159
- reading evidence. `commands --json` emits version 1 of the
160
- `whoisleuth.cli.command-catalogue` schema, including each command's usage,
161
- example, declared collection mode, collection scope, and boundary. This is the
162
- supported discovery contract for local wrappers; it is not a permission grant
163
- or a substitute for focused help.
164
-
165
- Focused command help and the generated manual classify every operation as
166
- offline or networked and state the relevant target, input, and concurrency
167
- ceiling. These labels describe observable behavior rather than granting
168
- authorization. The packaged `DISCLOSURE` defines the intended defensive scope;
169
- operators remain responsible for law, provider terms, and authorization.
170
-
171
- ## Deployment boundary
172
-
173
- The CLI runs locally. The serverless deployment publishes only `frontend/build`
174
- and packages functions only from `netlify/functions`, so `bin/` and `cli/` are
175
- not part of the hosted static site or function bundle. The application root and
176
- ordinary package-check candidate remain private. Explicitly reviewed CLI
177
- release candidates can be published under the scoped public package described
178
- in the installation section.
179
-
180
- Commands that query RDAP, WHOIS, DNS, HTTP, TLS, or Certificate Transparency do
181
- so directly from the machine running the CLI. They do not use the hosted login,
182
- hosted session, or deployment usage controls; upstream providers can see and
183
- rate-limit the local machine's network address. Offline `discover`, `compare`,
184
- `page-compare`, `mail-review`, `diff`, `timeline`, `risk-calibrate`, `verify-artifact`, `source-report`, `export`,
185
- `commands`, `completion`, and `manual` operations make no network requests. Commands write
186
- to stdout unless the analyst deliberately selects a local output file.
187
-
188
- ## Output
189
-
190
- Human-readable terminal output is the default. On an interactive terminal it
191
- uses restrained semantic colour, groups Lookup evidence by purpose, wraps
192
- prose to the detected terminal width, and shows transient progress on stderr
193
- for slower collection. State remains explicit in text, so colour is never the
194
- only distinction. Redirected terminal output, JSON, and JSONL contain no ANSI
195
- sequences or progress text. `--no-color` and a non-empty conventional
196
- `NO_COLOR` environment variable disable colour; `WHOISLEUTH_NO_PROGRESS=1`
197
- disables the transient indicator. Command examples remain one copyable line
198
- even when prose is wrapped for a narrow terminal.
199
-
200
- Every command accepts `--output <file>` as a safer alternative to shell
201
- redirection. WHOISleuth buffers bounded output, creates a private temporary file
202
- beside the destination, syncs it, and publishes it atomically with mode `0600`.
203
- An existing destination is refused. `--force` is valid only with `--output` and
204
- allows an intentional atomic replacement. A failed or cancelled command does
205
- not publish a partial output file. Output is capped at 32 MiB.
206
-
207
- `lookup --markdown` and `lookup --html` build the existing normalized evidence
208
- report directly after one completed domain lookup. They do not start a second
209
- collection. These report formats reject IP and ASN inputs before collection;
210
- JSON and terminal output remain available for those query types.
211
-
212
- Lookup terminal output supports three detail levels. The standard view keeps
213
- the normal bounded evidence summary. `--summary` keeps the source states and
214
- key findings while omitting endpoints and supporting detail. `--verbose` adds
215
- the document generation time and the existing bounded per-source collection
216
- timings. These switches are presentation-only and cannot be combined with
217
- `--json`; they do not change collection or the result document.
218
-
219
- `--json` writes one versioned document to standard output:
220
-
221
- ```json
222
- {
223
- "schema": "whoisleuth.cli.lookup",
224
- "version": 1,
225
- "generatedAt": "2026-07-14T00:00:00.000Z",
226
- "mode": "fast",
227
- "query": "example.com",
228
- "type": "domain"
229
- }
62
+ Process an explicit bounded list, generate lookalike candidates, or inspect a
63
+ saved result without sending it anywhere:
64
+
65
+ ```bash
66
+ whoisleuth bulk domains.txt --csv
67
+ whoisleuth discover example.com --preset common --jsonl
68
+ whoisleuth verify-artifact lookup.json --json
69
+ whoisleuth compare lookup.json --json
70
+ ```
71
+
72
+ Only one query is accepted by `lookup`; multi-input processing belongs to
73
+ `bulk`. Standard input to `lookup` is capped at 4 KiB and must contain one
74
+ non-empty line.
75
+
76
+ ## Collection profiles
77
+
78
+ Commands are classified as either networked or offline in focused help, the
79
+ generated manual, and `commands --json`.
80
+
81
+ | Profile | Boundary |
82
+ | --- | --- |
83
+ | Fast Lookup | Conservative default for domain triage. It does not run WHOIS or deep website and TLS collection. |
84
+ | Deep Lookup | Explicitly requested bounded RDAP, WHOIS, DNS, website, TLS, registrar-RDAP, and observed-network work where applicable. |
85
+ | Compact deep Bulk | Uses the shared Lookup orchestration but omits full-only response fields and collectors. |
86
+ | Offline review | Reads only the named bounded artefact or analyst-authored input and makes no network request. |
87
+
88
+ Networked commands run from the local machine and contact the sources declared
89
+ by their profile. They do not use the hosted login or hosted usage controls;
90
+ upstream services can see and rate-limit the local network address. The CLI
91
+ does not implicitly enable the browser Console's optional external intelligence
92
+ actions.
93
+
94
+ `lookup --plan` performs classification and emits a versioned preflight without
95
+ starting collection. It lists planned source families and disclosure targets,
96
+ but cannot predict cache state, live configuration, redirects, referrals,
97
+ source availability, or exact request count.
98
+
99
+ `doctor` is offline by default. `doctor --network` adds three independent,
100
+ bounded checks against fixed diagnostic infrastructure and retains only state,
101
+ HTTP status, and a bounded explanation.
102
+
103
+ ## Command map
104
+
105
+ | Goal | Commands |
106
+ | --- | --- |
107
+ | Investigate a target | `lookup`, `http`, `tls`, `posture`, `registry-support` |
108
+ | Review many targets | `bulk`, `discover`, `discover-scan`, `ct-search` |
109
+ | Compare saved evidence | `compare`, `page-compare`, `diff`, `reconcile`, `timeline`, `mail-review` |
110
+ | Review supplied evidence | `review-evidence`, `registry-doctor`, `source-report`, `sharing-review` |
111
+ | Plan and assure changes | `domain-control`, `assurance`, `workflow-plan` |
112
+ | Verify and package evidence | `verify-artifact`, `inspect-archive`, `sign-artifact`, `verify-signature`, `export` |
113
+ | Calibrate offline | `risk-calibrate`, `lookalike-calibrate` |
114
+ | Operate the CLI | `doctor`, `commands`, `completion`, `manual` |
115
+
116
+ Use focused help for the exact arguments and collection ceiling:
117
+
118
+ ```bash
119
+ whoisleuth lookup --help
120
+ whoisleuth sharing-review --help
121
+ whoisleuth commands --json
122
+ whoisleuth manual | man -l -
230
123
  ```
231
124
 
232
- The complete document also carries the normalized `rdap`, `whois`,
233
- `availability`, and `diagnostics` sections returned by the shared lookup
234
- orchestrator. Machine output goes to stdout. Usage and lookup errors go to
235
- stderr, so redirected JSON is not mixed with diagnostics.
236
-
237
- Human-readable domain lookup output separately shows the registrar RDAP status
238
- and endpoint whenever the shared lookup diagnostics represent that follow-up.
239
- Deep mode may report success, unsupported, not found, or an explicit failure;
240
- fast mode reports the existing skipped state. IP, ASN, and lookup responses
241
- without registrar diagnostics remain unchanged. These source states are
242
- provenance only and do not decide availability or imply safety.
243
-
244
- When a deep domain response includes registry interpretation version 1, the
245
- terminal summary also shows the lifecycle label, separately attributed RDAP and
246
- WHOIS disclosure states, reconciliation state, and complete, partial, and
247
- unavailable publication counts. It does not print published contact routes.
248
- The JSON document retains the bounded interpretation and its limitations.
249
-
250
- A deep domain lookup can also show the status, selected public address, and
251
- registered network name from the bounded observed network context. It uses the
252
- same address and IP RDAP source represented in the JSON document. This is
253
- point-in-time edge or network-registration context, not proof of the origin
254
- host, hosting control, ownership, intent, or maliciousness. Fast and compact
255
- commands do not run the enrichment.
256
-
257
- The deep terminal summary also reports the website activity state, page title,
258
- DNS, HTTP, and TLS source states, bounded publisher-declared structured
259
- identity entities, up to six bounded technology indicators, a browser-library
260
- profile count, and the four passive security-posture counts.
261
- These are concise projections of the same evidence already present in the
262
- lookup response. The browser-library line counts apparent components and
263
- catalogue advisory matches without retaining script references or raw
264
- signatures. These summaries make no extra request, omit raw evidence
265
- descriptions, and do not turn a technology or advisory match, or a missing
266
- posture signal, into proof of exploitability, hosting control, ownership, or
267
- maliciousness. Use `--json` when the full bounded evidence, limitations, and
268
- source diagnostics are required.
269
-
270
- `lookup --strict-exit` is an opt-in automation policy. It still emits the
271
- ordinary complete output document, but returns exit code 4 when a requested
272
- diagnostic source is partial, unavailable, rate-limited, timed out, or failed.
273
- The default exit contract remains unchanged, so inconclusive source health does
274
- not silently break existing scripts.
275
-
276
- `lookup --events` and `bulk --events` write versioned
277
- `whoisleuth.cli.progress` version 1 JSONL lifecycle events to stderr while the
278
- ordinary final result stays on stdout. Events contain only the command,
279
- sequence, time, source state or Bulk item index, categorical failure reasons,
280
- and final exit status. They do not contain a target, query, endpoint, file
281
- path, error detail, or evidence value. Human progress and error text are
282
- suppressed while events are active. `--events` cannot be combined with
283
- `--output`, because the final event must describe the same completed stdout
284
- delivery observed by the caller.
285
-
286
- Pressing Ctrl-C asks an in-flight Lookup or Bulk run to stop, suppresses any
287
- partial final result, flushes an enabled Bulk checkpoint, and exits with code
288
- 130. Already started network operations remain subject to their existing hard
289
- timeouts while the shared in-process API unwinds; the executable terminates
290
- after CLI cleanup. A second Ctrl-C removes any known unpublished temporary
291
- output file on a best-effort basis before exiting immediately.
292
-
293
- Bulk can persist compact progress with `--checkpoint <file>`. A new checkpoint
294
- is private, bounded to 16 MiB, versioned, and tied to the exact ordered input
295
- and scan mode by SHA-256. It stores only compact per-item results, never full
296
- Lookup responses. Reusing the same path is refused unless `--resume` is also
297
- specified. Resume revalidates the complete untrusted document and skips only
298
- the exact completed rows; changed input, mode, schema, digest, or malformed
299
- results fail closed.
300
-
301
- If checkpoint persistence fails after collection, WHOISleuth still emits the
302
- completed final result, reports the checkpoint limitation, and exits with code
303
- 4. The last successfully published checkpoint remains available, but it may
304
- not contain the final completed items.
305
-
306
- `diff <left.json> <right.json>` compares two different saved domain Lookup
307
- documents entirely offline. It reuses the bounded Bulk comparison model for
308
- registration, DNS, page identity, mail, certificate, and relationship fields,
309
- keeps missing and unavailable evidence distinct, and makes no ownership or
310
- maliciousness inference. The output records both original observation times.
311
-
312
- `timeline <observation.json> <observation.json> [...]` accepts from 2 to 20
313
- saved Lookup documents for one domain, orders them by their validated
314
- observation times, and compares each adjacent pair offline. The 32 MiB aggregate
315
- input ceiling is enforced while files are read and again by the timeline
316
- builder. Output retains normalized field states and observation times but no
317
- input filenames or raw registry payloads. A difference can reflect a domain
318
- change or changed collection conditions, so the command makes no current-state,
319
- ownership, intent, safety, or maliciousness conclusion.
320
-
321
- When diagnostics version 5, 6, 7, or 8 reports a documented registry collection
322
- constraint, terminal output also shows the suffix, WHOIS and RDAP access
323
- profiles, and the bounded limitation. This is static access-policy context: it
324
- does not make another request, and restricted, unpublished, or unavailable
325
- machine access is not evidence that a domain is unregistered or safe.
125
+ ## Output and automation
126
+
127
+ Human-readable terminal output is the default. Interactive terminals use
128
+ restrained semantic colour and transient progress on stderr. Redirected output,
129
+ JSON, and JSONL contain no ANSI sequences or progress text. State remains
130
+ explicit in text, so colour is never the only distinction.
131
+
132
+ - `--json` and `--jsonl` provide bounded machine-readable documents.
133
+ - `--summary` and `--verbose` change human detail without changing collection.
134
+ - `--no-color` or `NO_COLOR` disables colour.
135
+ - `WHOISLEUTH_NO_PROGRESS=1` disables the transient progress indicator.
136
+ - `--events` keeps the final document on stdout and writes versioned JSONL
137
+ lifecycle events to stderr.
138
+ - `--strict-exit` treats failures in explicitly requested source families as a
139
+ partial failure while keeping skipped or unsupported sources neutral.
140
+
141
+ Every command accepts `--output <file>`. WHOISleuth buffers bounded output,
142
+ creates a private temporary file beside the destination, syncs it, and publishes
143
+ it atomically with mode `0600`. Existing destinations are refused unless
144
+ `--force` is deliberately combined with `--output`. Failed or cancelled
145
+ commands do not publish partial files. Output is capped at 32 MiB.
326
146
 
327
147
  ## Exit codes
328
148
 
329
149
  | Code | Meaning |
330
150
  | ---: | --- |
331
- | 0 | Command completed. Individual sources may still be partial or inconclusive; inspect diagnostics. |
151
+ | 0 | Command completed. Individual sources can still be partial or inconclusive; inspect diagnostics. |
332
152
  | 2 | Invalid command, option, input, or stdin shape. |
333
- | 3 | The requested lookup, collection, or comparison operation could not run. |
334
- | 4 | A bounded operation completed partially, such as Bulk item failures or failed explicit Doctor network checks. |
153
+ | 3 | The requested lookup, collection, or comparison could not run. |
154
+ | 4 | A bounded operation completed partially. |
335
155
  | 70 | Unexpected CLI bootstrap failure. |
336
- | 130 | The analyst cancelled the command. No partial final result was emitted. |
337
-
338
- This release supports `lookup`, `bulk`, `ct-search`, `discover`, `discover-scan`, `posture`,
339
- `http`, `tls`, `registry-support`, `risk-calibrate`, `verify-artifact`,
340
- `inspect-archive`, `sign-artifact`, `verify-signature`, `source-report`,
341
- `compare`, `page-compare`, `mail-review`, `diff`, `timeline`, `export`, `doctor`, `commands`, `completion`, and `manual`. Additional command families
342
- are added as separate bounded increments rather than exposing incomplete
343
- aliases.
344
-
345
- ## Registry capability coverage
346
-
347
- `registry-support` projects the embedded, versioned registry compatibility
348
- catalogue for one domain or suffix. It distinguishes an explicit
349
- fixture-backed or access-documented suffix profile from the generic IANA
350
- discovery profile and reports the RDAP and WHOIS discovery/access paths,
351
- WHOIS query and parser profiles, any bounded fallback, fixture scenarios,
352
- verification references, documentation references, and the catalogue's
353
- limitation. The same document includes a dated, official-source aggregate of
354
- generic, generic-restricted, sponsored, and infrastructure RDAP coverage. That
355
- snapshot is separate from the suffix profile and from live reachability.
356
-
357
- Terminal output is bounded and control-safe. `--json` emits version 2 of the
358
- `whoisleuth.cli.registry-support` schema. Unknown but syntactically valid
359
- suffixes retain the generic `discovery_only` profile; malformed input exits
360
- with code 2. The command never probes a registry or tests current reachability.
361
- Coverage is context only and cannot decide registration, availability,
362
- ownership, safety, or maliciousness.
363
-
364
- The separate maintenance command `npm run rdap-extensions:drift` compares the
365
- pinned official RDAP extension fixture with the reviewed local interpretation
366
- catalogue entirely offline. `--live` performs one bounded manual fetch from the
367
- fixed official CSV URL and reports added, removed, renamed, status-changed,
368
- unrecognized, and local-only identifiers. `--json` emits
369
- `whoisleuth.rdap-extension-drift-audit` version 1. Drift exits with status 1 and
370
- requires specification and fixture review; the command never enables an
371
- extension, changes authority or availability logic, or issues reverse search.
372
-
373
- `npm run service-dependencies:audit` validates the passive service-dependency
374
- catalogue entirely offline. It checks the catalogue digest, duplicate
375
- identifiers and target suffixes, evidence classes, source and licence
376
- treatment, source dates, and a fixed 180-day review age. `--json` emits
377
- `whoisleuth.service-dependency-signature-audit` version 1. A stale or changed
378
- signature requires manual provider and benign-fixture review; the command
379
- never resolves a target, checks an account, or tests claimability.
380
-
381
- ## Offline Risk calibration
382
-
383
- `risk-calibrate` replays a versioned analyst-labelled fixture dataset through
384
- the exact Risk model shared with the web application. It does not perform a
385
- lookup, contact the hosted deployment, persist the dataset, alter model weights,
386
- choose a threshold, or tune the model automatically. The report compares fixed
387
- thresholds from 40 through 90 and identifies 70 as the current review band.
388
-
389
- Input is capped at 2 MiB and 500 records. Each record requires a unique bounded
390
- ID, an ASCII DNS hostname, one existing analyst disposition, and a strict
391
- whitelist of current scoring inputs. Unknown fields are discarded; arrays,
392
- strings, ages, booleans, provider records, and external findings are revalidated
393
- and bounded before scoring. The accepted dataset envelope is:
394
-
395
- ```json
396
- {
397
- "schema": "whoisleuth.risk-calibration-dataset",
398
- "version": 1,
399
- "records": [
400
- {
401
- "id": "fixture-1",
402
- "domain": "login.example.test",
403
- "analystDisposition": "confirmed_abuse",
404
- "evidence": {
405
- "availability": "registered",
406
- "mutationTypes": ["dictionary"],
407
- "hasPasswordField": true
408
- }
409
- }
410
- ]
411
- }
412
- ```
156
+ | 130 | The analyst cancelled the command; no partial final result was emitted. |
413
157
 
414
- `confirmed_abuse` is treated as a positive calibration label; `false_positive`
415
- and `expected` are negative labels. `unreviewed`, `suspicious`, and
416
- `closed_no_action` remain contextual and are excluded from threshold quality
417
- metrics, as are records for which Risk is not applicable. Terminal output caps
418
- its record list at 100 while `--json` retains the complete bounded report,
419
- factor breakdowns, score bands, and per-threshold confusion metrics. Analyst
420
- dispositions and heuristic scores are review context: neither proves
421
- maliciousness or safety.
422
-
423
- The authenticated Monitor workspace can deliberately create the accepted
424
- dataset from explicitly selected cases. Selection is available only when a case
425
- has a reviewed disposition and retained normalized evidence. That local export
426
- contains the case ID, domain, disposition, and a bounded whitelist of current
427
- scoring inputs. It excludes notes, tags, assertions, actions, contacts, raw
428
- source data, provider payloads, and stored Risk scores. Before download, a local
429
- review step shows the selected, included, and excluded counts, the domains and
430
- dispositions that will be written, and the excluded evidence classes. The CLI
431
- still validates every exported record and never trains, tunes, or changes the
432
- Risk model.
433
-
434
- ## Offline artifact verification
435
-
436
- `verify-artifact` validates a supported local artifact without printing its
437
- evidence contents. Input is capped at 15 MiB. It currently recognises ordinary
438
- and encrypted workspace archives, case-response packets, acquisition-decision
439
- exports, domain-comparison exports, Bulk mail-exposure exports, and Bulk review
440
- manifests. It also validates the bounded versioned structure of saved CLI
441
- Lookup JSON. Because saved Lookup documents do not embed a checksum or
442
- signature, that result is reported as `structure_valid` with content integrity
443
- explicitly unchecked. Saved Lookup validation retains its stricter 8 MiB
444
- document ceiling.
445
-
446
- For an ordinary workspace archive, the command validates the versioned
447
- structure, section byte counts, record counts, and per-section checksums. For a
448
- supported signed review artifact, it validates the declared SHA-256 digest. An
449
- encrypted archive without `--passphrase-file` can establish only that the
450
- envelope is structurally valid; it reports `envelope_valid` and explicitly
451
- leaves authenticated encryption and inner checksums unchecked.
452
-
453
- Supplying a separate passphrase file, capped at 1 KiB, allows authenticated
454
- decryption followed by the ordinary workspace checks. The passphrase is never
455
- accepted as a command-line value, printed, or retained. Verification detects
456
- changes against the artifact's declared contract; it does not authenticate the
457
- analyst or establish that an observation was accurate or remains current.
458
-
459
- ## Privacy-safe source reliability report
460
-
461
- `source-report` summarises source states, durations, truncation, and rate-limit
462
- counts from one version-1 CLI Lookup, Bulk, or Bulk-item document, a JSON array
463
- of up to 100 such documents, or an array containing only previously generated
464
- source-reliability reports. Lookup documents and reports cannot be mixed.
465
- Merged reports preserve exact state and sample totals and add bounded ranges
466
- for each input report's median and p95 duration, a bounded source timeline,
467
- fast/deep/unknown cohorts, and the earliest and latest valid report generation
468
- times. Exact duplicate reports are rejected so one saved report is not
469
- accidentally counted twice. Per-source failure, partial, truncation, and
470
- rate-limit rates remain separate. The command does not pretend that aggregate
471
- timing distributions can reconstruct the original target-level samples. Input
472
- is capped at 12 MiB and traversal, source, duration, and output counts are
473
- bounded.
474
-
475
- The report retains only fixed source identifiers and aggregate operational
476
- counts. It does not output targets, queries, endpoints, source limitations, raw
477
- evidence, or provider payloads. Observation-envelope duration and the
478
- potentially overlapping Lookup timing are reported separately and never added
479
- together. It is a local engineering diagnostic, not analytics, a source-quality
480
- score, or evidence that a collector is currently healthy.
481
-
482
- ## Workspace archive inspection
483
-
484
- `inspect-archive` verifies an ordinary or encrypted workspace archive and then
485
- reports its versioned sections, counts, byte sizes, and checksum states without
486
- printing stored records. An encrypted archive requires
487
- `--passphrase-file`; passphrases are never accepted on the command line.
488
-
489
- An optional `--search` is exact and case-insensitive across a fixed allowlist
490
- of target fields. Default matches disclose only the section, field name, and a
491
- SHA-256 digest. `--reveal` must be selected explicitly to print the matched
492
- value. Notes, contacts, raw evidence, arbitrary object fields, and partial text
493
- search are excluded. Unicode domain searches are canonicalized to their
494
- DNS-safe ASCII form before exact comparison. `--require-match` makes a
495
- no-match result fail for automation and is valid only with `--search`.
496
- Traversal, input size, matches, depth, arrays, and strings are bounded.
497
-
498
- ## Optional evidence-package signing
499
-
500
- `sign-artifact` signs a locally verified case-response packet or supported
501
- review manifest with an externally managed Ed25519 private key:
158
+ ## Detailed command reference
502
159
 
503
- ```bash
504
- node bin/whoisleuth.mts sign-artifact packet.json --private-key-file analyst-private.pem > packet.signed.json
505
- node bin/whoisleuth.mts verify-signature packet.signed.json --public-key-file analyst-public.pem
506
- ```
160
+ The headings below preserve the established guide anchors while moving their
161
+ full contracts into the packaged reference.
507
162
 
508
- The signed package embeds the public key, key identifier, signature time, and
509
- original artifact. Verification first checks the exact package field set and
510
- signature, then the artifact's own checksums or manifest. A result is reported
511
- as `signature_valid`; signer trust is separately reported as `trusted_key` or
512
- `embedded_key_only`. Without a separately supplied trusted public key,
513
- verification establishes only that the package is internally self-consistent.
514
- WHOISleuth does not generate, store, recover, rotate, publish, or establish
515
- trust in signing keys.
516
-
517
- ## Bulk lookup
518
-
519
- `bulk` accepts a newline-delimited file or stdin. It preserves input order,
520
- removes case-insensitive duplicates, and returns a result for every retained
521
- query. Input is capped at 1 MiB. Fast mode is capped at 500 queries with four
522
- workers by default. Deep mode is capped at 50 queries with two workers by
523
- default. Explicit concurrency cannot exceed eight in fast mode or three in
524
- deep mode.
525
-
526
- Bulk uses the shared compact lookup response, so it does not retain raw RDAP
527
- objects or WHOIS response bodies. `--json` returns one bounded collection;
528
- `--jsonl` emits one self-contained versioned item per line. `--domains` emits
529
- only successfully normalized domain names, while `--queries` preserves the
530
- selected bounded input queries and is suitable for an exact retry queue.
531
- Version 2 adds a
532
- bounded `dnsSummary` projection for observed A, AAAA, NS, and MX records plus
533
- null MX, SPF, and DMARC state. `--csv` writes fixed columns for automation,
534
- including the DNS summaries and explicit outcome.
535
-
536
- `--registered-only`, `--inconclusive-only`, and `--errors-only` are mutually
537
- exclusive output filters. The first retains registered, for-sale, and expiring
538
- authority-aware states. The second retains unknown authority states and failed
539
- rows. The third retains only rows whose lookup operation failed, which is useful
540
- for producing an exact retry queue without treating an inconclusive successful
541
- lookup as an operational error. Filtering does not change collection and never
542
- converts an unavailable or failed source into an unregistered result. Machine
543
- documents record collected and emitted counts. A mixture of successful and
544
- failed queries exits with code 4 while preserving every collected result before
545
- output filtering. For example, `bulk domains.txt --queries --errors-only`
546
- produces only the exact failed inputs while retaining the partial-failure exit.
547
-
548
- ## Certificate Transparency search
549
-
550
- `ct-search` accepts one keyword as an argument or on stdin and calls the same
551
- bounded Certificate Transparency module as the web application. It contacts
552
- the upstream public log search service directly from the local machine; it
553
- does not call the hosted WHOISleuth deployment. Quote a multi-word keyword so
554
- the shell passes it as one argument.
555
-
556
- Terminal output summarizes certificate rows, observed hostnames, canonical
557
- registrable-domain matches, observation times, and completeness. It shows at
558
- most 100 matches and five hostnames per match, with explicit omission notes.
559
- `--json` returns the complete bounded structured result in the versioned
560
- `whoisleuth.cli.ct-search` schema. CT observations do not prove that a website
561
- is active or malicious.
562
-
563
- ## Lookalike discovery
564
-
565
- `discover` runs the same pure, bounded lookalike generator as the Discover
566
- tool without making network requests. It accepts a brand label or a
567
- domain with one suffix label. The default TLD set is `com,net,org`; replace it
568
- with `--tlds com,net` when narrower coverage is wanted.
569
-
570
- Generation presets are `common`, `impersonation`, and `all` (the default).
571
- Keyboard-aware mutations support `qwerty` (the default), `azerty`, and
572
- `qwertz`; `--keyboard all` uses their bounded combined neighbour set, including
573
- adjacent number-row keys. Supply a UTF-8 text file with
574
- `--dictionary terms.txt` to add up to 100 local terms when
575
- using the `impersonation` or `all` preset. Dictionary input is capped at 4,096
576
- bytes and individual terms at 32 characters. Machine output records accepted
577
- and rejected term counts but never copies the private terms into its metadata.
578
-
579
- Use `--families <ids>` instead of `--preset` to run an exact comma-separated
580
- selection. Supported IDs are `character_addition`, `character_omission`,
581
- `character_duplication`, `character_transposition`, `pluralization`,
582
- `tld_embedding`, `www_prefix`, `hyphenation`, `separator_omission`,
583
- `word_reordering`, `keyboard_substitution`, `keyboard_insertion`, `vowel_swap`,
584
- `bitsquatting`, `ascii_homoglyph`, `unicode_homoglyph`,
585
- `unicode_whole_label`, `unicode_homoglyph_depth_2`, `dictionary`,
586
- `dictionary_token_replacement`, `tld_typo`, and `tld_substitution`. Machine
587
- output records the normalized custom selection. A custom dictionary file
588
- requires `dictionary` or `dictionary_token_replacement` in that selection.
589
-
590
- `--domains` writes the unique candidate names only. `--snapshot <file>` keeps
591
- one private, versioned local observation of the normalized seed, generation
592
- configuration, custom-dictionary digest, and candidate set. The first run
593
- creates the snapshot; later runs report added and removed candidates before
594
- atomically replacing it. The snapshot never stores private dictionary terms.
595
- Use the same command from a local scheduler such as cron or a system timer to
596
- perform repeatable discovery runs. WHOISleuth does not install a scheduler,
597
- run a background daemon, or infer that an added candidate is registered,
598
- active, available, or malicious.
599
-
600
- Dotted subdomain permutations are intentionally excluded because the lookup
601
- pipeline validates a hostname's registrable parent. The CLI does not present
602
- authoritative parent registration evidence as proof that a generated hostname
603
- exists.
604
-
605
- Terminal output is capped at 200 candidates with an explicit notice. Versioned
606
- JSON and JSONL use `whoisleuth.cli.discover` schema version 2 and retain the
607
- complete bounded candidate set and mutation provenance. Internationalised
608
- candidates show the DNS-safe ASCII form and readable Unicode form together in
609
- terminal output. The Impersonation and All presets can add up to six
610
- deterministic whole-label Unicode candidates when every letter has a reviewed
611
- same-script replacement. The command generates candidates only. It does not
612
- claim that a domain is registered, active, or malicious.
613
-
614
- `unicode_homoglyph_depth_2` is an explicit `--families` option and is never
615
- enabled by a preset. It replaces exactly two ASCII letters with characters
616
- from the same reviewed script, ranks curated mappings first, and returns at
617
- most 256 candidates. JSON and terminal output report generated candidates,
618
- cross-script or invalid combinations omitted by policy, and lower-ranked
619
- label variants omitted by the family or overall generation budget.
620
-
621
- ## Supervised candidate scan
622
-
623
- `discover-scan` composes the same bounded generator with the compact Lookup
624
- collector. Unlike `discover`, it is an explicitly networked command. It selects
625
- the first deterministic candidates up to `--scan-limit`, processes fixed
626
- chunks sequentially, and retains each registration and DNS source state. Fast
627
- mode is the default and accepts at most 500 candidates. Explicit `--deep` mode
628
- is capped at 50 candidates and concurrency 3 because it can add WHOIS, DNS,
629
- HTTP, TLS, page, and technology work for every selected candidate.
630
-
631
- Use `--chunk-size 1-100`, `--concurrency 1-8`, and the existing private
632
- `--checkpoint <file> [--resume]` boundary to control a run. The checkpoint is
633
- tied to the exact ordered candidate set and scan mode. A new generation order,
634
- limit, or mode requires another checkpoint. `--resolver` accepts at most three
635
- literal IPv4 or IPv6 recursive-resolver addresses and applies them only to DNS
636
- observations in this local run. Resolver hostnames and automatic third-party
637
- resolver selection are not supported. The selected resolvers can observe the
638
- queried candidates and may apply their own logging and retention policies.
639
-
640
- An optional newline-delimited `--allowlist` accepts at most 500 domains and 64
641
- KiB. Matching candidates remain in evidence and are labelled `suppressed` in
642
- the review queue; the full list and unmatched entries are not copied into
643
- output, while each matching scanned candidate necessarily exposes that match.
644
- Allowlisting never deletes a result or asserts that it is safe. Output filters
645
- select registered, inconclusive, acquisition-review, or
646
- suppressed rows without changing whole-run counts or source health. JSON,
647
- JSONL, CSV, and domain-list forms retain mutation provenance and explicit
648
- review lanes. The queue provides next manual actions, not automatic lookup,
649
- acquisition, blocking, submission, or enforcement.
650
-
651
- Deep compact results can group exact shared addresses, nameservers, and mail
652
- servers observed across at least two candidates. These bounded relationships
653
- are pivot leads only. Common hosting, delivery, registrar, and mail services
654
- can produce the same relationship without common ownership or control.
655
-
656
- `--observation-snapshot <file>` keeps a private versioned local projection of
657
- registration state, confidence, and bounded A, AAAA, NS, MX, null-MX, SPF, and
658
- DMARC summaries for the exact candidate set, mode, and resolver selection.
659
- Version 2 records registration and DNS observation times and latest component
660
- states separately; version 1 is read and normalized on the next write. A later
661
- identical run reports material field changes only for components collected
662
- with enough completeness to support the comparison. If registration or DNS is
663
- partial or unavailable, the prior usable evidence for that component is
664
- preserved and the attempt is reported as unavailable rather than converting
665
- missing data into a removal. Raw registry
666
- records, contacts, page content, and request details are excluded. Snapshot
667
- differences are review prompts, not proof of ownership, control, intent,
668
- safety, or maliciousness.
669
-
670
- ## Domain posture audit
671
-
672
- `posture` runs the standard-profile form of the owned-domain DNS and
673
- email-security audit used by Brand Profiles. It queries registry status and
674
- DNSSEC evidence, nameserver delegation, SPF, DMARC, MX, CAA, MTA-STS, TLS-RPT,
675
- and BIMI directly from the local machine. Literal SPF include and redirect
676
- branches are expanded within fixed depth, policy-query, DNS-term, void-answer,
677
- cycle, and time bounds. External DMARC reporting authorization and external
678
- nameserver, mail, SPF, and reporting dependencies are separately reported.
679
- Supply up to ten known active and retired DKIM selectors in total with
680
- `--selectors selector1,selector2` and `--retired-selectors selector0`;
681
- selectors cannot be reliably discovered from DNS, so no-selector output
682
- reports DKIM as not checked. Use `--mail-profile standard`,
683
- `--mail-profile defensive-no-mail`, or `--mail-profile parked` to make the
684
- intended mail posture explicit rather than inferring it from DNS.
685
-
686
- Terminal output shows each pass, review, action, or informational result and
687
- caps displayed records at five per check with an explicit omission notice.
688
- Versioned JSON retains the complete bounded report, including SPF traversal,
689
- DMARC authorization, and dependency provenance. Warnings and dangers are
690
- findings rather than command failures; transient resolver or policy-fetch
691
- failures remain informational and should be retried before changing DNS.
692
-
693
- ## HTTP intelligence
694
-
695
- `http` runs the same bounded safe homepage probe used by a deep lookup. It
696
- tries HTTPS before HTTP, follows only validated public-network redirects, caps
697
- each attempt at six seconds, and captures at most 300,000 response bytes for
698
- hashing and metadata. A non-success HTTP response still proves that a web
699
- service answered; failure of both schemes remains inconclusive rather than
700
- evidence that no website exists.
701
-
702
- The command reports normalized redirect, response, content, selected security-
703
- header, body-hash, completeness, and attempt provenance. Captured homepage text
704
- is never written to terminal or JSON output. Query strings are removed by the
705
- shared HTTP evidence normalizer, and terminal values are additionally bounded
706
- and control-safe.
707
-
708
- ## TLS intelligence
709
-
710
- `tls` runs the same bounded one-connection TLS collector used by deep lookups.
711
- It resolves the hostname once through the public-address guard, validates every
712
- answer, connects directly to the first validated address on port 443, and keeps
713
- the original hostname for SNI and certificate identity checking. Resolution
714
- and handshake work share a five-second deadline.
715
-
716
- The report retains the negotiated protocol, ALPN and cipher, runtime trust and
717
- hostname checks, certificate validity, bounded subject and issuer names, SANs,
718
- SAN class counts, signature algorithm and OID, extended-key-usage purposes,
719
- classified Authority Information Access presence counts, SHA-256 certificate
720
- and public-key fingerprints, a bounded certificate chain, and neutral
721
- findings. AIA responder and issuer locations are classified as HTTP, HTTPS, or
722
- other and then discarded; they are not retained or followed. The collector
723
- stores no certificate bytes, session material, or application data and does
724
- not enumerate supported protocol or cipher suites.
725
- A failed collection is inconclusive rather than proof that no TLS service
726
- exists.
727
-
728
- ## Registry-source comparison
729
-
730
- `compare` reads one version-1 `whoisleuth.cli.lookup` domain document from a
731
- file or stdin and emits a version-3 `whoisleuth.cli.compare` document. Its
732
- primary comparison reconciles normalized registry RDAP and WHOIS fields. When
733
- the saved deep lookup also represents the optional registrar RDAP follow-up,
734
- an additional sibling comparison reconciles the portable registry and
735
- registrar RDAP publication fields. The command does not repeat the lookup,
736
- contact the hosted deployment, or treat a conflict as a command failure. Use a
737
- deep lookup when multiple sources are wanted; a fast lookup deliberately
738
- records WHOIS as skipped, and comparison preserves that state as unavailable
739
- rather than misreporting RDAP-only publication.
740
-
741
- Input is capped at 8 MiB and must retain the lookup schema, mode, source
742
- diagnostics, and normalized parsed source sections. Per-value, list, and event
743
- bounds are revalidated because saved JSON is treated as untrusted input. A
744
- successful registrar source must include a normalized object and agree with
745
- its diagnostic status. Raw RDAP JSON, registrar contacts and source-specific
746
- handles, WHOIS response bodies, availability evidence, and unrelated lookup
747
- fields are not copied into the comparison result.
748
-
749
- When the saved Lookup contains valid version-5, version-6, or version-7, context-only
750
- registry-access diagnostics, version 3 retains their bounded suffix, access profiles, and
751
- limitation. Terminal output labels the same collection context explicitly.
752
- It explains source reachability only and cannot decide registration,
753
- availability, ownership, safety, or maliciousness. Older documents and saved
754
- lookups without that context do not gain a registry-access field.
755
-
756
- Terminal and versioned JSON output cover domain identity, registry object ID,
757
- registrar, registrar IANA ID, lifecycle dates, DNSSEC, statuses, and name
758
- servers. The shared comparison model normalizes harmless case, punctuation,
759
- ordering, and date-precision differences while distinguishing conflicts,
760
- one-source publication, redaction, incomplete sources, and unavailable
761
- sources. This is source reconciliation, not an availability, ownership, or
762
- maliciousness decision.
763
-
764
- ## Static page comparison
765
-
766
- `page-compare <left.json> <right.json>` reads two different version-1 saved
767
- Deep domain Lookup documents and compares the already-retained static page
768
- identity, exact and perceptual favicon evidence, technology identifiers, TLS
769
- issuer label, and TLS public-key fingerprint. DOM structure is compared first
770
- by its exact bounded tag-sequence digest, then, when both captures provide it,
771
- by a parse5-tokenized structural SimHash. It makes no request and requires
772
- complete supported static page-identity observations on both sides.
773
-
774
- Each page component remains independent. Exact, similar, overlapping,
775
- different, unavailable, and partial evidence are not collapsed into a score.
776
- Matching components are investigative relationships rather than proof of
777
- copying, common ownership, control, intent, safety, or maliciousness. Static
778
- comparison does not execute JavaScript; use the optional local rendered
779
- capture package only when that additional active behavior is authorised.
780
-
781
- ## Passive mail exposure review
782
-
783
- `mail-review [bulk.json|bulk.jsonl]` reads version-2 Bulk output locally and
784
- summarizes MX, null MX, SPF, DMARC, and mail-provider relationships. It keeps
785
- authenticated mail, authentication gaps, incomplete authentication evidence,
786
- no explicit MX, null MX, and incomplete DNS evidence as separate states.
787
- Shared-provider relationships are based only on the registrable domain of an
788
- observed MX hostname and do not establish shared ownership or control.
789
-
790
- The command makes no DNS or SMTP request and retains no source path. It does
791
- not test message acceptance, relay behavior, mailbox existence, catch-all
792
- behavior, SMTP banners, or whether a mail server is rogue, safe, or malicious.
793
-
794
- ## Optional local rendered capture
795
-
796
- `packages/web-capture` is a private repo-local Playwright package, not part of
797
- the distributable core CLI or hosted application. It requires an explicit
798
- authorization flag and one new output directory:
163
+ ### Registry capability coverage
799
164
 
800
- ```bash
801
- npm run capture:local -- https://example.test --output-dir ./capture-example --authorize-rendered-capture
802
- npm run capture:compare -- ./official/manifest.json ./candidate/manifest.json --json
803
- ```
165
+ [Read the complete registry capability reference.](cli-reference.md#registry-capability-coverage)
166
+
167
+ ### Offline Risk calibration
168
+
169
+ [Read the calibration dataset and replay contract.](cli-reference.md#offline-risk-calibration)
170
+
171
+ ### Lookalike review-yield calibration
172
+
173
+ [Read the reviewed candidate calibration contract.](cli-reference.md#lookalike-review-yield-calibration)
174
+
175
+ ### Offline artefact verification
176
+
177
+ [Read the archive, saved Lookup, and signed-artefact verification contract.](cli-reference.md#offline-artefact-verification)
178
+
179
+ ### Privacy-safe source reliability report
180
+
181
+ [Read the target-free source diagnostic contract.](cli-reference.md#privacy-safe-source-reliability-report)
182
+
183
+ ### Workspace archive inspection
184
+
185
+ [Read the redacted archive summary and search contract.](cli-reference.md#workspace-archive-inspection)
186
+
187
+ ### Optional evidence-package signing
188
+
189
+ [Read the local signing and verification contract.](cli-reference.md#optional-evidence-package-signing)
190
+
191
+ ### Bulk lookup
192
+
193
+ [Read Bulk bounds, filters, formats, checkpoints, and cancellation behaviour.](cli-reference.md#bulk-lookup)
194
+
195
+ ### Certificate Transparency search
196
+
197
+ [Read the CT query and source-limit contract.](cli-reference.md#certificate-transparency-search)
198
+
199
+ ### Lookalike discovery
200
+
201
+ [Read candidate generation, dictionaries, snapshots, and output behaviour.](cli-reference.md#lookalike-discovery)
202
+
203
+ ### Supervised candidate scan
204
+
205
+ [Read the bounded candidate collection workflow.](cli-reference.md#supervised-candidate-scan)
206
+
207
+ ### Domain posture audit
208
+
209
+ [Read the DNS, mail, and certificate posture contract.](cli-reference.md#domain-posture-audit)
210
+
211
+ ### HTTP intelligence
212
+
213
+ [Read the bounded HTTP collection contract.](cli-reference.md#http-intelligence)
214
+
215
+ ### TLS intelligence
216
+
217
+ [Read the bounded TLS collection contract.](cli-reference.md#tls-intelligence)
218
+
219
+ ### Registry-source comparison
220
+
221
+ [Read the normalised RDAP and WHOIS comparison contract.](cli-reference.md#registry-source-comparison)
222
+
223
+ ### Static page comparison
224
+
225
+ [Read saved page-identity comparison behaviour.](cli-reference.md#static-page-comparison)
226
+
227
+ ### Passive mail exposure review
228
+
229
+ [Read the saved-evidence mail review contract.](cli-reference.md#passive-mail-exposure-review)
230
+
231
+ ### Offline supplied-evidence review
232
+
233
+ [Read the DNSSEC, DANE, and supplied-observation review contract.](cli-reference.md#offline-supplied-evidence-review)
234
+
235
+ ### Domain control manifests
236
+
237
+ [Read desired-state manifest and drift-review behaviour.](cli-reference.md#domain-control-manifests)
238
+
239
+ ### Domain assurance review
240
+
241
+ [Read planned-change, recovery-dependency, and retirement contracts.](cli-reference.md#domain-assurance-review)
242
+
243
+ ### Pre-sharing review
244
+
245
+ [Read the local marking, scope, redaction, and integrity preflight.](cli-reference.md#pre-sharing-review)
246
+
247
+ ### Fixed investigation plans
248
+
249
+ [Read the plan-only recipe contract and approval gates.](cli-reference.md#fixed-investigation-plans)
250
+
251
+ ### Optional local rendered capture
252
+
253
+ [Read the separate Playwright capture package boundary.](cli-reference.md#optional-local-rendered-capture)
254
+
255
+ ### Lookup evidence export
256
+
257
+ [Read normalised JSON, Markdown, and HTML export behaviour.](cli-reference.md#lookup-evidence-export)
258
+
259
+ ## Safety and limitations
804
260
 
805
- It writes a fixed 1024x768 PNG, screenshot SHA-256 and perceptual dHash, a DOM
806
- digest containing hashes and bounded element counts rather than markup or page
807
- text, and a version-2 `whoisleuth.web-capture-manifest` compatible with the
808
- Cases importer. Version-1 manifests remain importable.
809
-
810
- Rendered capture executes page JavaScript. It caps HTTP(S) requests and request
811
- hostnames, blocks downloads, service workers, WebSockets, non-read methods,
812
- credentials, non-default ports, and private or reserved addresses, and retains
813
- no request path, query, headers, bodies, cookies, credentials, DOM markup, or
814
- page text. The browser connection is not pinned to the address checked before
815
- each hostname's first request, so DNS rebinding remains a residual risk. Use a
816
- disposable network-restricted environment for untrusted targets.
817
-
818
- The separate offline comparison command accepts two selected version-2 local
819
- capture manifests. It first verifies each referenced screenshot and DOM digest
820
- against its declared size, SHA-256, screenshot dimensions, and perceptual hash;
821
- it also validates bounded source and limitation metadata and requires the DOM,
822
- manifest, and source collection times to agree. It then
823
- compares screenshot distance, exact rendered DOM and visible-text digests,
824
- bounded element counts, page identity, technologies, and request-domain sets.
825
- It does not recrawl either target, reveal the input paths, or collapse the
826
- independent components into a similarity or maliciousness score. A missing
827
- perceptual hash remains unavailable rather than becoming a visual difference.
828
-
829
- ## Lookup evidence export
830
-
831
- `export` converts one version-1 `whoisleuth.cli.lookup` domain document from a
832
- file or stdin into the same versioned `whoisleuth.lookup-evidence` JSON package
833
- produced by the web Lookup tool. It performs no lookup and writes only to
834
- stdout, so use ordinary shell redirection when a file is wanted. Pretty JSON is
835
- the default; `--compact` emits one compact JSON line for pipeline use, while
836
- `--markdown` produces a readable source-attributed summary and `--html`
837
- produces a self-contained printable report. Compact, Markdown, and HTML output
838
- are mutually exclusive.
839
-
840
- The saved input is capped at 8 MiB and revalidated using the same schema,
841
- source-status, parsed-data, scalar, list, and event boundaries as `compare`.
842
- The export retains query context, source diagnostics, normalized registry data,
843
- raw registry RDAP JSON, the raw WHOIS referral chain, availability analysis,
844
- and the shared registry-source comparison. Version 23 can add the exact local
845
- SSLBL comparison already represented by a deep full Lookup. Version 21 adds the bounded registry
846
- lifecycle, disclosure, publication-quality, reconciliation, and abuse-routing
847
- interpretation derived from the already-collected sources. Registrar RDAP raw
848
- data, contacts, entities, links, notices, and source-specific handles remain
849
- excluded. Schema version 20 replaces selected security-policy values in
850
- retained HTTP evidence
851
- with presence-only markers and can include fixed response-policy findings from
852
- an already-represented deep lookup. Version 19 additionally retains the bounded credential-surface projection
853
- when the saved deep lookup represents it. Version 18 retains the bounded structured identity projection
854
- when the saved deep lookup represents it. Version 17 retains the normalized portable-field comparison, explicit
855
- source-health states, a strict bounded projection of observed network
856
- registration, an optional normalized security.txt disclosure source, HTTPS
857
- service-binding publications, and the passive browser-library profile when the
858
- saved deep lookup represents them. Raw IP RDAP payloads, security.txt response
859
- bodies, script references, matched script content, and contact entities remain
860
- excluded.
861
-
862
- Markdown output summarizes query context, assessment state, registry sources,
863
- source reconciliation, network observations, and collection diagnostics. It
864
- escapes all upstream text as untrusted content, bounds displayed values and
865
- lists, and deliberately omits raw RDAP JSON and full WHOIS response bodies.
866
- Use JSON when the complete evidence package or machine processing is required.
867
- The browser Console reuses this bounded Markdown renderer for domain results
868
- after first reducing the typed Lookup response to the known normalized fields
869
- needed by the report. It separately offers bounded IP and ASN summaries from
870
- their normalized RDAP and source-health fields; IP reports can also include
871
- bounded reverse-DNS context already collected by Lookup. The browser action
872
- does not change the CLI input or output contract.
873
- When a version-5, version-6, or version-7 lookup records a documented registry-access constraint, both
874
- Markdown and HTML include that context in collection diagnostics without
875
- changing its non-authoritative interpretation. Both formats also include the
876
- selected address and bounded network registration when present, together with
877
- the edge-versus-origin limitation.
878
-
879
- HTML uses the same bounded summary model as Markdown. The generated document
880
- contains inline styling for screen and print, but no scripts, forms, active
881
- links, or external resources. A restrictive embedded Content Security Policy
882
- provides defense in depth when the local file is opened in a browser. All
883
- registry values are HTML-escaped and displayed as text.
884
-
885
- This is a deliberately rich evidence package. Raw registry sources can contain
886
- publicly published contact data, and deep availability evidence can contain
887
- bounded website, DNS, mail, page-identity, and TLS observations. Review and
888
- secure the output before sharing it. The CLI does not add browser-only IDN
889
- profile analysis, so that optional evidence field is `null`.
261
+ WHOISleuth is a defensive investigation tool, not an authorisation mechanism.
262
+ Operators remain responsible for applicable law, registry and provider terms,
263
+ and permission to assess a target. Missing, unsupported, partial, or failed
264
+ evidence never establishes absence, availability, safety, ownership, or intent.
265
+ Offline analyst assertions remain separately attributed and are not converted
266
+ into observed facts.