@slicedearth/whoisleuth-cli 1.39.1 → 1.45.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 (90) hide show
  1. package/README.md +23 -4
  2. package/bin/whoisleuth.mjs +31 -9
  3. package/cli/archive-inspect.mjs +37 -2
  4. package/cli/arguments.mjs +361 -256
  5. package/cli/artifact-verify.mjs +35 -1
  6. package/cli/bulk-checkpoint.mjs +72 -39
  7. package/cli/bulk-command-runner.mjs +34 -4
  8. package/cli/bulk.mjs +18 -7
  9. package/cli/case-pack.mjs +136 -0
  10. package/cli/ci-report.mjs +73 -0
  11. package/cli/collection-preflight.mjs +59 -0
  12. package/cli/command-reference.mjs +395 -0
  13. package/cli/completion.mjs +67 -13
  14. package/cli/config-profile.mjs +183 -0
  15. package/cli/ct-event-intake.mjs +159 -0
  16. package/cli/discovery-observation-snapshot.mjs +8 -14
  17. package/cli/discovery-scan-command-runner.mjs +29 -2
  18. package/cli/discovery-snapshot.mjs +8 -15
  19. package/cli/domain-control-monitor.mjs +104 -0
  20. package/cli/domain-control-observations.mjs +235 -0
  21. package/cli/errors.mjs +7 -1
  22. package/cli/evidence-command-arguments.mjs +11 -0
  23. package/cli/evidence-command-runner.mjs +1 -0
  24. package/cli/external-observation-mapping.mjs +162 -0
  25. package/cli/fail-policy.mjs +64 -0
  26. package/cli/formatters/html.mjs +1 -1
  27. package/cli/formatters/json.mjs +12 -4
  28. package/cli/formatters/terminal.mjs +1 -1
  29. package/cli/http.mjs +2 -1
  30. package/cli/investigation-manifest.mjs +113 -0
  31. package/cli/investigation-run.mjs +112 -0
  32. package/cli/lookup-brief.mjs +116 -0
  33. package/cli/lookup-command-runner.mjs +8 -1
  34. package/cli/lookup-diff.mjs +17 -19
  35. package/cli/lookup-reconcile.mjs +1 -1
  36. package/cli/mail-review.mjs +5 -5
  37. package/cli/manual.mjs +1 -1
  38. package/cli/network-command-runner.mjs +4 -1
  39. package/cli/offline-evidence-review.mjs +105 -8
  40. package/cli/open-asset-model-bridge.mjs +169 -0
  41. package/cli/registry-cohort.mjs +121 -0
  42. package/cli/registry-doctor.mjs +106 -11
  43. package/cli/registry-fixture-scaffold.mjs +74 -0
  44. package/cli/runner.mjs +388 -315
  45. package/docs/cli-reference.md +266 -21
  46. package/docs/cli.md +43 -7
  47. package/frontend/src/lib/analysis/acquisition-due-diligence.js +2 -2
  48. package/frontend/src/lib/analysis/brand-profile-model.js +50 -6
  49. package/frontend/src/lib/analysis/case-evidence-model.js +1 -1
  50. package/frontend/src/lib/analysis/case-record-core.js +1 -1
  51. package/frontend/src/lib/analysis/case-record-operations.js +1 -1
  52. package/frontend/src/lib/analysis/case-report.js +573 -0
  53. package/frontend/src/lib/analysis/case-response-model.js +30 -1
  54. package/frontend/src/lib/analysis/detection-rule-model.js +36 -0
  55. package/frontend/src/lib/analysis/external-findings-import.js +320 -0
  56. package/frontend/src/lib/analysis/lookup-asset-graph.js +8 -8
  57. package/frontend/src/lib/analysis/lookup-decision-support.js +9 -2
  58. package/frontend/src/lib/analysis/lookup-presentation.js +14 -30
  59. package/frontend/src/lib/analysis/lookup-source-refresh.js +36 -63
  60. package/frontend/src/lib/analysis/scoring.js +1 -1
  61. package/frontend/src/lib/analysis/utils.js +1 -1
  62. package/frontend/src/lib/analysis/visualization-models.js +2 -3
  63. package/frontend/src/lib/analysis/workspace-archive-crypto.js +1 -4
  64. package/frontend/src/lib/analysis/workspace-archive.js +5 -7
  65. package/lib/abusech-auth.mjs +1 -1
  66. package/lib/availability.mjs +1 -1
  67. package/lib/bounded-contract-normalizers.mjs +33 -0
  68. package/lib/bounded-file.mjs +69 -0
  69. package/lib/bounded-json-response.mjs +157 -0
  70. package/lib/browser-library-profile.mjs +167 -38
  71. package/lib/dns-convergence-review.mjs +183 -0
  72. package/lib/dns-delegation-health.mjs +196 -3
  73. package/lib/domain-change-packet.mjs +133 -0
  74. package/lib/domain-change-review.mjs +307 -0
  75. package/lib/domain-control-flight-recorder.mjs +268 -0
  76. package/lib/domain-portfolio-review.mjs +151 -0
  77. package/lib/evidence-export.mjs +1 -1
  78. package/lib/html-signals.mjs +5 -11
  79. package/lib/lookup-cache.mjs +5 -1
  80. package/lib/nameserver-preflight-review.mjs +159 -0
  81. package/lib/page-language-signals.mjs +124 -0
  82. package/lib/rdap-transport.mjs +6 -6
  83. package/lib/risk-scoring.mjs +42 -1
  84. package/lib/safe-fetch.mjs +37 -32
  85. package/lib/static-html-analysis.mjs +29 -1
  86. package/lib/threat-intelligence-runtime.mjs +1 -2
  87. package/lib/trust-store-comparison.mjs +146 -0
  88. package/lib/website-technology.mjs +22 -12
  89. package/lib/zone-intent-review.mjs +534 -0
  90. package/package.json +1 -1
@@ -69,6 +69,8 @@ node bin/whoisleuth.mts http example.com --json
69
69
  node bin/whoisleuth.mts tls example.com --json
70
70
  node bin/whoisleuth.mts registry-support example.uk --json
71
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
72
74
  node bin/whoisleuth.mts risk-calibrate calibration.json --json
73
75
  node bin/whoisleuth.mts lookalike-calibrate reviewed-candidates.json --json
74
76
  node bin/whoisleuth.mts verify-artifact workspace.json --json
@@ -78,16 +80,26 @@ node bin/whoisleuth.mts source-report lookup.json --json
78
80
  node bin/whoisleuth.mts inspect-archive workspace.json --json
79
81
  node bin/whoisleuth.mts inspect-archive workspace.json --search example.invalid --json
80
82
  node bin/whoisleuth.mts sign-artifact response-packet.json --private-key-file analyst-private.pem > response-packet.signed.json
83
+ node bin/whoisleuth.mts ct-intake certificate-events.json --json > external-findings.json
84
+ node bin/whoisleuth.mts map-observations observation-mapping.json --json > external-findings.json
85
+ node bin/whoisleuth.mts oam-export external-findings.json --json > asset-bridge.json
86
+ node bin/whoisleuth.mts manifest lookup.json comparison.json --workflow 'domain review' --json > investigation-manifest.json
81
87
  node bin/whoisleuth.mts verify-signature response-packet.signed.json --public-key-file analyst-public.pem
82
88
  node bin/whoisleuth.mts lookup example.com --deep --json > lookup.json
83
89
  node bin/whoisleuth.mts compare lookup.json --json
84
90
  node bin/whoisleuth.mts page-compare official.json candidate.json --json
85
91
  node bin/whoisleuth.mts mail-review bulk.json --json
86
92
  node bin/whoisleuth.mts review-evidence dnssec-evidence.json --json
93
+ node bin/whoisleuth.mts review-evidence trust-store-comparison.json --json
87
94
  node bin/whoisleuth.mts domain-control domain-control-input.json --json
88
95
  node bin/whoisleuth.mts assurance domain-assurance-input.json --json
96
+ node bin/whoisleuth.mts change-packet domain-change-packet-input.json --json
89
97
  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
90
98
  node bin/whoisleuth.mts workflow-plan domain-triage example.test --json
99
+ node bin/whoisleuth.mts workflow-run domain-triage example.test --approve-network --json --output run.json
100
+ node bin/whoisleuth.mts brief lookup.json
101
+ node bin/whoisleuth.mts case-pack cases.json --audience trusted --reviewed --json
102
+ node bin/whoisleuth.mts monitor-once control-manifest.json --previous prior-control.json --junit
91
103
  node bin/whoisleuth.mts diff first-lookup.json second-lookup.json --json
92
104
  node bin/whoisleuth.mts reconcile office.json mobile.json external.json --json
93
105
  node bin/whoisleuth.mts timeline first-observation.json second-observation.json latest-observation.json --json
@@ -193,9 +205,11 @@ Commands that query RDAP, WHOIS, DNS, HTTP, TLS, or Certificate Transparency do
193
205
  so directly from the machine running the CLI. They do not use the hosted login,
194
206
  hosted session, or deployment usage controls; upstream providers can see and
195
207
  rate-limit the local machine's network address. Offline `discover`, `compare`,
196
- `page-compare`, `mail-review`, `review-evidence`, `domain-control`, `assurance`,
197
- `sharing-review`, `workflow-plan`, `diff`, `reconcile`, `timeline`,
198
- `risk-calibrate`, `lookalike-calibrate`, `registry-doctor`, `verify-artifact`, `source-report`, `export`,
208
+ `page-compare`, `mail-review`, `review-evidence`, `domain-control`, `assurance`, `change-packet`,
209
+ `sharing-review`, `workflow-plan`, `brief`, `case-pack`, `registry-cohort`,
210
+ `registry-scaffold`, `diff`, `reconcile`, `timeline`,
211
+ `risk-calibrate`, `lookalike-calibrate`, `registry-doctor`, `verify-artifact`, `source-report`,
212
+ `map-observations`, `oam-export`, `export`,
199
213
  `commands`, `completion`, and `manual` operations make no network requests. Commands write
200
214
  to stdout unless the analyst deliberately selects a local output file.
201
215
 
@@ -218,6 +232,23 @@ An existing destination is refused. `--force` is valid only with `--output` and
218
232
  allows an intentional atomic replacement. A failed or cancelled command does
219
233
  not publish a partial output file. Output is capped at 32 MiB.
220
234
 
235
+ `--config <file> --profile <name>` reads a strict version-1 local configuration
236
+ document of at most 64 KiB. Without `--config`, the path is
237
+ `$XDG_CONFIG_HOME/whoisleuth/config.json`. A profile may contain no more than
238
+ 16 safe defaults drawn from `--no-color`, `--summary`, `--verbose`, `--fast`,
239
+ `--concurrency`, `--observer`, and `--vantage`. It cannot supply a target,
240
+ Deep mode, output path, network approval, failure policy, credential, or
241
+ arbitrary argument. Explicit command options override profile defaults from
242
+ the same group.
243
+
244
+ Lookup, Bulk, and `monitor-once` can emit bounded JUnit XML. Owned-domain
245
+ Posture can emit SARIF only when `--owned-domain` confirms the passive target
246
+ boundary. The reports contain categorical source or posture findings rather
247
+ than raw evidence. Supported network commands accept an explicit comma-
248
+ separated `--fail-on` policy from `source-failure`, `inconclusive`, `danger`,
249
+ and `material-drift`; a match returns the partial-result exit code after the
250
+ normal output is emitted.
251
+
221
252
  `lookup --markdown` and `lookup --html` build the existing normalised evidence
222
253
  report directly after one completed domain lookup. They do not start a second
223
254
  collection. These report formats reject IP and ASN inputs before collection;
@@ -299,7 +330,8 @@ delivery observed by the caller.
299
330
 
300
331
  Pressing Ctrl-C asks an in-flight Lookup or Bulk run to stop, suppresses any
301
332
  partial final result, flushes an enabled Bulk checkpoint, and exits with code
302
- 130. Already started network operations remain subject to their existing hard
333
+ 130. SIGTERM follows the same graceful cleanup path and exits with code 143.
334
+ Already started network operations remain subject to their existing hard
303
335
  timeouts while the shared in-process API unwinds; the executable terminates
304
336
  after CLI cleanup. A second Ctrl-C removes any known unpublished temporary
305
337
  output file on a best-effort basis before exiting immediately.
@@ -307,11 +339,23 @@ output file on a best-effort basis before exiting immediately.
307
339
  Bulk can persist compact progress with `--checkpoint <file>`. A new checkpoint
308
340
  is private, bounded to 16 MiB, versioned, and tied to the exact ordered input
309
341
  and scan mode by SHA-256. It stores only compact per-item results, never full
310
- Lookup responses. Reusing the same path is refused unless `--resume` is also
342
+ Lookup responses. Checkpoint schema 2 records a nullable per-row observation
343
+ time. Schema 1 remains readable, but its resumed rows keep observation time
344
+ unknown rather than inheriting the resume time. Reusing the same path is
345
+ refused unless `--resume` is also
311
346
  specified. Resume revalidates the complete untrusted document and skips only
312
347
  the exact completed rows; changed input, mode, schema, digest, or malformed
313
348
  results fail closed.
314
349
 
350
+ Bulk JSON, JSONL, and item documents use schema version 3. Each row includes
351
+ `observedAt` and a `collectionOrigin` of `current_run` or
352
+ `resumed_checkpoint`; the top-level `generatedAt` remains the report creation
353
+ time. HTTP JSON uses schema version 2 and adds the explicit `assessment` field.
354
+ The legacy `activityStatus` field remains for compatibility, but is `null`
355
+ when collection is inconclusive rather than labelling the target unreachable.
356
+ Consumers should use `assessment`; older version-1 `unreachable` values remain
357
+ historical inconclusive labels, not proof that a target cannot be reached.
358
+
315
359
  If checkpoint persistence fails after collection, WHOISleuth still emits the
316
360
  completed final result, reports the checkpoint limitation, and exits with code
317
361
  4. The last successfully published checkpoint remains available, but it may
@@ -362,13 +406,14 @@ machine access is not evidence that a domain is unregistered or safe.
362
406
  | 4 | A bounded operation completed partially, such as Bulk item failures or failed explicit Doctor network checks. |
363
407
  | 70 | Unexpected CLI bootstrap failure. |
364
408
  | 130 | The analyst cancelled the command. No partial final result was emitted. |
409
+ | 143 | The process received SIGTERM. No partial final result was emitted. |
365
410
 
366
- This release supports `lookup`, `bulk`, `ct-search`, `discover`, `discover-scan`, `posture`,
367
- `http`, `tls`, `registry-support`, `registry-doctor`, `risk-calibrate`,
411
+ This release supports `lookup`, `bulk`, `ct-search`, `ct-intake`, `map-observations`, `oam-export`, `discover`, `discover-scan`, `posture`,
412
+ `http`, `tls`, `registry-support`, `registry-doctor`, `registry-cohort`, `registry-scaffold`, `risk-calibrate`,
368
413
  `lookalike-calibrate`, `verify-artifact`,
369
- `inspect-archive`, `sign-artifact`, `verify-signature`, `source-report`,
370
- `compare`, `page-compare`, `mail-review`, `review-evidence`, `domain-control`,
371
- `assurance`, `sharing-review`, `workflow-plan`, `diff`, `reconcile`, `timeline`,
414
+ `inspect-archive`, `manifest`, `sign-artifact`, `verify-signature`, `source-report`,
415
+ `compare`, `page-compare`, `mail-review`, `review-evidence`, `brief`, `case-pack`, `domain-control`,
416
+ `monitor-once`, `assurance`, `change-packet`, `sharing-review`, `workflow-plan`, `workflow-run`, `diff`, `reconcile`, `timeline`,
372
417
  `export`, `doctor`, `commands`, `completion`, and `manual`. Additional command families
373
418
  are added as separate bounded increments rather than exposing incomplete
374
419
  aliases.
@@ -395,10 +440,24 @@ ownership, safety, or maliciousness.
395
440
  `registry-doctor [lookup.json]` compares one saved domain Lookup with the same
396
441
  reviewed local capability profile. It reports whether RDAP and WHOIS collection
397
442
  states align with allowed, permission-required, or unsupported access, counts
398
- bounded normalised publication fields, and reports whether a registry object
399
- identifier was observed. A missing identifier is a publication omission, not
400
- a failed lookup. The command is offline, makes no retry, and cannot establish
401
- current registry reachability.
443
+ bounded normalised publication fields, and reports publication-quality context
444
+ for object identity, base conformance, redaction metadata, required self links,
445
+ and event consistency. A missing identifier or metadata field is a publication
446
+ omission, not a failed lookup. The command is offline, makes no retry, and
447
+ cannot establish current registry reachability.
448
+
449
+ `registry-cohort [lookups.json|lookups.jsonl]` aggregates from 1 to 500 saved
450
+ Lookups into suffix and capability-profile cohorts. It retains only target-free
451
+ counts for source alignment and publication-quality states. A cohort with
452
+ fewer than five samples is labelled insufficient rather than treated as a
453
+ quality result, and repeated observations from one environment are not assumed
454
+ representative.
455
+
456
+ `registry-scaffold --profile <id> --suffix <suffix> --scenario <state>` emits
457
+ one bounded, sanitised synthetic fixture scaffold from the installed registry
458
+ catalogue. The three supported states are `registered`, `not_found`, and
459
+ `inconclusive`. It does not fetch a live response, include a real target, or
460
+ alter the embedded catalogue.
402
461
 
403
462
  The offline RDAP supplied-evidence review implements the request shape defined
404
463
  by RFC 9536 for `resources`, `domains`, `nameservers`, and `entities`. It also
@@ -513,7 +572,9 @@ establish that a current candidate is malicious or safe.
513
572
  evidence contents. Input is capped at 15 MiB. It currently recognises ordinary
514
573
  and encrypted workspace archives, case-response packets, acquisition-decision
515
574
  exports, domain-comparison exports, Bulk mail-exposure exports, and Bulk review
516
- manifests. It also verifies the internal digests of a complete investigation
575
+ manifests. Reviewed CLI case packs are checked against their canonical digest,
576
+ bounded browser-importable case collection, audience, and review marker. The
577
+ command also verifies the internal digests of a complete investigation
517
578
  capsule, including its evidence, brief, graph, and optional analyst-record
518
579
  projections, and that capsule can be passed to `sign-artifact`. It also validates the bounded versioned structure of saved CLI
519
580
  Lookup JSON. Because saved Lookup documents do not embed a checksum or
@@ -573,6 +634,17 @@ DNS-safe ASCII form before exact comparison. `--require-match` makes a
573
634
  no-match result fail for automation and is valid only with `--search`.
574
635
  Traversal, input size, matches, depth, arrays, and strings are bounded.
575
636
 
637
+ Version 2 of the inspection report adds a content digest over the ordered
638
+ section identifiers, schema versions, record counts, and normalised section
639
+ content. It deliberately excludes JSON formatting and each section envelope's
640
+ export time, so two separately exported archives with the same retained
641
+ workspace content have the same identity. `--expect-content-digest <sha256:digest>`
642
+ fails when that identity differs, which supports deliberate transfer checks
643
+ without printing records. Matching digests detect equivalent retained content;
644
+ they do not authenticate who created it or establish that its evidence is
645
+ accurate or current. The report separately identifies the source archive
646
+ version and the installed reader version.
647
+
576
648
  ## Optional evidence-package signing
577
649
 
578
650
  `sign-artifact` signs a locally verified case-response packet or supported
@@ -592,6 +664,23 @@ verification establishes only that the package is internally self-consistent.
592
664
  WHOISleuth does not generate, store, recover, rotate, publish, or establish
593
665
  trust in signing keys.
594
666
 
667
+ ## Reproducible investigation manifest
668
+
669
+ `manifest <artefact.json> [...] --workflow <label>` reads from 1 to 16 ordered
670
+ JSON artefacts and emits `whoisleuth.investigation-manifest` version 1. Each
671
+ entry records its sequence, bounded schema and version metadata, exact UTF-8
672
+ content digest, canonical JSON digest, and byte count. The manifest also records
673
+ the installed CLI version and can include an externally calculated
674
+ `--configuration-digest sha256:...` without copying configuration values.
675
+
676
+ Source paths, filenames, command-line arguments, and artefact contents are not
677
+ retained. Individual artefacts are capped at 15 MiB and the ordered set at
678
+ 32 MiB. The manifest carries its own canonical integrity digest, works with
679
+ `verify-artifact`, and can be passed to `sign-artifact`. Reproduction still
680
+ requires the exact digested artefacts, compatible source services, and any
681
+ separately held configuration; matching digests do not establish observation
682
+ accuracy or freshness.
683
+
595
684
  ## Bulk lookup
596
685
 
597
686
  `bulk` accepts a newline-delimited file or stdin. It preserves input order,
@@ -642,6 +731,56 @@ the complete bounded structured result in the versioned
642
731
  separately from the registrable-domain result cap. CT observations do not prove
643
732
  that a website is active or malicious.
644
733
 
734
+ ## Local certificate event intake
735
+
736
+ `ct-intake [events.json]` reads `whoisleuth.ct-event-batch` version 1 entirely
737
+ offline. Each source-qualified event supplies a log identifier, observation
738
+ time, certificate SHA-256 digest, one or more DNS names, completeness, and
739
+ optional issuer, expiry, and limitations. Unknown fields, malformed names,
740
+ unsupported completeness values, and oversized batches are rejected.
741
+
742
+ JSON output uses the existing `whoisleuth.external-findings` version 3 contract
743
+ and can be deliberately imported into the browser Console. Wildcard names are
744
+ normalised to their base DNS name, exact event/domain duplicates are removed,
745
+ and deterministic output is capped at 100 findings, 25 domains, and 20 findings
746
+ per domain to match the browser import boundary. Omission is stated in each
747
+ retained finding when the supplied batch exceeds those limits. The command
748
+ makes no request and treats every certificate event as a review lead, not proof
749
+ that the certificate was served, requested, or controlled by the named domain.
750
+
751
+ ## Declarative observation interchange
752
+
753
+ `map-observations [mapping.json]` reads
754
+ `whoisleuth.external-observation-mapping` version 1 and applies one strict,
755
+ declarative profile to at most 500 local records. The profile selects bounded
756
+ dotted field paths for the domain, summary, observation time, optional
757
+ reference, and optional completeness. It fixes one source, evidence class, and
758
+ finding category for the batch. Profiles cannot contain scripts, templates,
759
+ computed expressions, network endpoints, or executable callbacks. Prototype
760
+ paths and unknown fields are rejected.
761
+
762
+ The resulting JSON is the existing browser-compatible
763
+ `whoisleuth.external-findings` version 3 contract. It retains source identity,
764
+ observation time, completeness, and limitations, while stating that field
765
+ semantics were not independently verified. Deterministic output is capped at
766
+ 100 findings, 25 domains, and 20 findings per domain. The command makes no
767
+ request and never interprets an omitted field as a negative observation.
768
+
769
+ `oam-export [external-findings.json]` projects that strict findings document
770
+ into `whoisleuth.open-asset-model-bridge` version 1. It uses the documented
771
+ [OWASP Open Asset Model](https://github.com/owasp-amass/open-asset-model)
772
+ vocabulary for bounded `FQDN`, `IPAddress`, and `TLSCertificate` assets plus
773
+ `dns_record` and `san_dns_name` relations. Only typed A, AAAA, and SHA-256
774
+ certificate observations create infrastructure assets or relations. Other
775
+ findings remain represented by their FQDN asset.
776
+
777
+ This bridge wrapper is not a claim that every receiving implementation accepts
778
+ one universal native serialisation. Receivers must validate the projection
779
+ against their supported model version. WHOISleuth preserves source and
780
+ completeness as discovery metadata, does not invent a confidence score, and
781
+ does not treat an edge as proof of ownership, control, coordination, intent,
782
+ safety, or maliciousness.
783
+
645
784
  ## Lookalike discovery
646
785
 
647
786
  `discover` runs the same pure, bounded lookalike generator as the Discover
@@ -907,7 +1046,10 @@ Supported input schemas are:
907
1046
  The command does not retrieve the certificate or negotiate STARTTLS.
908
1047
  - `whoisleuth.rdap-search-input`: normalises a supplied RDAP search-help
909
1048
  response and prepares an exact supported reverse-search request without
910
- sending it.
1049
+ sending it. If the document also contains a previously obtained `response`,
1050
+ the command checks its bounded property mappings against the reviewed
1051
+ registration and reports omitted or unrecognised mappings without treating
1052
+ the response as authoritative or complete.
911
1053
  - `whoisleuth.rpki-route-input`: compares a route prefix and origin ASN with a
912
1054
  bounded analyst-supplied VRP set.
913
1055
  - `whoisleuth.local-geoip-query`: queries an analyst-supplied bounded prefix
@@ -924,12 +1066,57 @@ Supported input schemas are:
924
1066
  - `whoisleuth.encrypted-dns-plan-input`: validates an explicitly reviewed
925
1067
  encrypted-DNS provider contract and prepares a bounded query plan. It does
926
1068
  not execute the plan.
1069
+ - `whoisleuth.zone-intent.input`: parses either a deliberately supported,
1070
+ bounded BIND master-file subset or normalised DNS records and compares it
1071
+ with one separately attributed observation. TXT values are reduced to
1072
+ SHA-256 digests, unsupported directives and records remain rejected, and an
1073
+ incomplete observation never becomes a missing-record conclusion. The
1074
+ review does not contact a resolver or apply a DNS change.
1075
+ - `whoisleuth.domain-portfolio.input`: reviews up to 500 analyst-supplied
1076
+ domain inventory rows for exact registrar, DNS, mail, certificate and
1077
+ recovery concentration, renewal-review gaps and internal recovery-domain
1078
+ dependencies. Account labels and provider assignments remain analyst
1079
+ assertions, and the review makes no provider or account request.
1080
+ - `whoisleuth.domain-change.input`: compares bounded, separately labelled
1081
+ authority and resolver observations; reviews CDS, CDNSKEY and CSYNC
1082
+ publication consistency; inventories supplied SRV services and ACME
1083
+ dependencies; and records TLS key continuity, stapling, embedded timestamp,
1084
+ and HSTS preload context. TXT values are retained only as SHA-256 digests.
1085
+ The result is an offline review gate, not a DNSSEC validation, preload-list
1086
+ lookup, certificate-authority request, or permission to apply a change.
1087
+ - `whoisleuth.dns-convergence.input`: compares the latest supplied observation
1088
+ from at least two labelled resolvers or network vantages. It preserves
1089
+ converged, divergent, unexpected, incomplete, and insufficient record-set
1090
+ states, can compare against optional expected values, and projects a simple
1091
+ observation-time plus TTL cache horizon. It does not query DNS or decide
1092
+ which divergent value is authoritative, and supplied TTLs do not reveal
1093
+ actual cache age or resolver refresh policy.
1094
+ - `whoisleuth.nameserver-preflight.input`: reviews an intended nameserver set
1095
+ against separately attributed direct-service observations before a
1096
+ delegation change. It checks authoritative service, the served NS set, SOA
1097
+ evidence, public addresses and in-bailiwick glue readiness without querying
1098
+ DNS, opening a socket or changing registry configuration.
1099
+ - `whoisleuth.trust-store-comparison.input`: compares an already-observed,
1100
+ bounded certificate-chain SHA-256 fingerprint set with up to 16
1101
+ analyst-supplied trust-store snapshots. Each snapshot carries its own name,
1102
+ version, source, review time, completeness, and no more than 2,048 anchor
1103
+ fingerprints. Exact intersections, complete non-observations, and
1104
+ inconclusive inputs remain separate. A match does not build or validate a
1105
+ certificate path, and non-observation does not mean a client would reject
1106
+ the chain because served chains commonly omit their root.
927
1107
 
928
1108
  The common output is `whoisleuth.cli.offline-evidence-review` version 1. It
929
1109
  retains the nested result's explicit state and limitations. A locally
930
1110
  consistent relationship is not converted into a claim about current
931
1111
  publication, ownership, safety, or maliciousness.
932
1112
 
1113
+ `review-evidence --strict-exit` is an opt-in local automation boundary. It
1114
+ returns the partial-failure exit code when an input family exposes an explicit
1115
+ failed `gate`, or when a zone-intent comparison is incomplete or contains a
1116
+ difference, missing value or unexpected value. Other evidence families retain
1117
+ their ordinary exit behaviour; the flag does not invent a pass/fail policy for
1118
+ them. JSON is still emitted before the exit status is selected.
1119
+
933
1120
  ## Domain control manifests
934
1121
 
935
1122
  `domain-control [input.json]` builds or reviews a bounded desired-state
@@ -981,6 +1168,22 @@ The versioned input rejects unknown fields and contradictory evidence attached
981
1168
  to an unfinished check. It stores no credentials, makes no request, and changes
982
1169
  no registrar, DNS, mail, certificate, or recovery configuration.
983
1170
 
1171
+ ## Domain change assurance packet
1172
+
1173
+ `change-packet [input.json]` combines one pre-change domain review, one
1174
+ post-change domain review, and one planned-change assurance input for the same
1175
+ domain. The input uses `whoisleuth.domain-change-packet.input` version 1. The
1176
+ command revalidates each nested contract, summarises changed authoritative
1177
+ record sets, and emits `whoisleuth.domain-change-packet` version 1 with a
1178
+ canonical SHA-256 integrity digest.
1179
+
1180
+ The packet is ready only when both supplied observation reviews and the change
1181
+ plan pass their own bounded gates. Partial or unavailable evidence remains a
1182
+ review reason. Packet assembly performs no collection or configuration change,
1183
+ and digest validity does not authenticate the analyst. A reviewed packet can be
1184
+ passed to `sign-artifact` when the operator has an independently managed
1185
+ Ed25519 signing-key lifecycle.
1186
+
984
1187
  ## Pre-sharing review
985
1188
 
986
1189
  `sharing-review [artifact.json]` performs a redacted local preflight before an
@@ -1012,6 +1215,43 @@ change a case, or submit evidence. Analysts deliberately run selected commands
1012
1215
  after reviewing their collection boundaries. This provides repeatable
1013
1216
  domain-specific workflows without an arbitrary automation or plugin surface.
1014
1217
 
1218
+ ## Local handoffs and one-shot monitoring
1219
+
1220
+ `brief [lookup.json]` reads one saved Lookup and emits version 2 of the bounded
1221
+ `whoisleuth.cli.lookup-brief` contract. It separates retained facts,
1222
+ contradictions, incomplete source states, and manual actions. Each action
1223
+ states its evidence basis and expected outcome; it does not run the action or
1224
+ create a case.
1225
+
1226
+ `case-pack [cases.json] --audience <internal|trusted|public> --reviewed` reads a
1227
+ browser case export containing at most 25 normalised cases, applies the selected
1228
+ redaction boundary, and emits a browser-importable case collection with a
1229
+ canonical SHA-256 digest and redaction manifest. It refuses an invalid,
1230
+ duplicate, larger, or generated package above the browser's 2 MiB import limit
1231
+ rather than silently omitting records or evidence. Trusted output removes notes,
1232
+ recipient values, and manual-pivot targets. Public output additionally removes
1233
+ actions and analyst assertions. The review flag records an explicit choice; it
1234
+ does not prove recipient authorisation or factual correctness.
1235
+
1236
+ `verify-artifact` recognises the complete case-pack envelope before browser
1237
+ import. It checks the canonical digest, supported case-export schema, bounded
1238
+ case count, report count, audience and explicit review marker without printing
1239
+ case contents. This detects a changed hand-off file but does not authenticate
1240
+ the analyst, recipient, source observations, or review decision.
1241
+
1242
+ `monitor-once [manifest.json]` performs one bounded Deep control review for at
1243
+ most 20 manifest domains with concurrency capped at three. An optional
1244
+ `--previous` checkpoint adds local first/last-observed comparison. It is not a
1245
+ daemon or scheduler, retains compact normalised observations rather than raw
1246
+ payloads, and keeps failed collection incomplete. JSON, JUnit, and explicit
1247
+ failure policies are supported.
1248
+
1249
+ `workflow-run <recipe> <subject>` executes only concrete steps from the four
1250
+ installed fixed recipes. Network steps require `--approve-network` for that
1251
+ invocation. Analyst-selection placeholders always pause. A bounded `--resume`
1252
+ state must match the exact recipe and subject, cannot add commands or shell
1253
+ syntax, and is not evidence that earlier results remain current.
1254
+
1015
1255
  ## Optional local rendered capture
1016
1256
 
1017
1257
  `packages/web-capture` is a private repo-local Playwright package, not part of
@@ -1029,12 +1269,17 @@ text, and a version-2 `whoisleuth.web-capture-manifest` compatible with the
1029
1269
  Cases importer. Version-1 manifests remain importable.
1030
1270
 
1031
1271
  Rendered capture executes page JavaScript. It caps HTTP(S) requests and request
1032
- hostnames, blocks downloads, service workers, WebSockets, non-read methods,
1033
- credentials, non-default ports, and private or reserved addresses, and retains
1272
+ hostnames, blocks downloads, service workers, WebSockets, WebRTC, WebTransport,
1273
+ non-read methods, credentials, non-default ports, and private or reserved
1274
+ addresses, and retains
1034
1275
  no request path, query, headers, bodies, cookies, credentials, DOM markup, or
1035
- page text. The browser connection is not pinned to the address checked before
1036
- each hostname's first request, so DNS rebinding remains a residual risk. Use a
1037
- disposable network-restricted environment for untrusted targets.
1276
+ page text. Every browser request is intercepted, its hostname is resolved again,
1277
+ and the bounded response is fetched through WHOISleuth's public-address-pinned
1278
+ transport. Individual responses are capped at 4 MiB and all fetched response
1279
+ bodies together are capped at 24 MiB. Redirect destinations pass through the
1280
+ same interception and validation before collection continues. Use a disposable
1281
+ network-restricted environment for untrusted targets because rendered page code
1282
+ still executes inside the isolated browser process.
1038
1283
 
1039
1284
  The separate offline comparison command accepts two selected version-2 local
1040
1285
  capture manifests. It first verifies each referenced screenshot and DOM digest
package/docs/cli.md CHANGED
@@ -67,6 +67,8 @@ whoisleuth bulk domains.txt --csv
67
67
  whoisleuth discover example.com --preset common --jsonl
68
68
  whoisleuth verify-artifact lookup.json --json
69
69
  whoisleuth compare lookup.json --json
70
+ whoisleuth brief lookup.json
71
+ whoisleuth registry-doctor lookup.json --json
70
72
  ```
71
73
 
72
74
  Only one query is accepted by `lookup`; multi-input processing belongs to
@@ -105,13 +107,13 @@ HTTP status, and a bounded explanation.
105
107
  | Goal | Commands |
106
108
  | --- | --- |
107
109
  | Investigate a target | `lookup`, `http`, `tls`, `posture`, `registry-support` |
108
- | Review many targets | `bulk`, `discover`, `discover-scan`, `ct-search` |
110
+ | Review many targets | `bulk`, `discover`, `discover-scan`, `ct-search`, `ct-intake` |
109
111
  | 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` |
112
+ | Review supplied evidence | `brief`, `review-evidence`, `registry-doctor`, `registry-cohort`, `source-report`, `sharing-review`, `map-observations` |
113
+ | Plan and assure changes | `domain-control`, `monitor-once`, `assurance`, `change-packet`, `workflow-plan`, `workflow-run` |
114
+ | Verify and package evidence | `case-pack`, `manifest`, `oam-export`, `verify-artifact`, `inspect-archive`, `sign-artifact`, `verify-signature`, `export` |
113
115
  | Calibrate offline | `risk-calibrate`, `lookalike-calibrate` |
114
- | Operate the CLI | `doctor`, `commands`, `completion`, `manual` |
116
+ | Operate the CLI | `doctor`, `registry-scaffold`, `commands`, `completion`, `manual` |
115
117
 
116
118
  Use focused help for the exact arguments and collection ceiling:
117
119
 
@@ -137,6 +139,18 @@ explicit in text, so colour is never the only distinction.
137
139
  lifecycle events to stderr.
138
140
  - `--strict-exit` treats failures in explicitly requested source families as a
139
141
  partial failure while keeping skipped or unsupported sources neutral.
142
+ - `--fail-on` applies explicit `source-failure`, `inconclusive`, `danger`, or
143
+ `material-drift` automation policy where the selected command supports it.
144
+ - `--junit` emits bounded CI test cases for Lookup, Bulk, and one-shot control
145
+ reviews; owned-domain Posture can emit `--sarif` only with
146
+ `--owned-domain`.
147
+
148
+ `--config <file> --profile <name>` applies a strict version-1 local profile.
149
+ The default path is `$XDG_CONFIG_HOME/whoisleuth/config.json`. Profiles can set
150
+ only presentation mode, Fast collection, bounded concurrency, and observer or
151
+ vantage labels. They cannot add targets, enable Deep collection, select output
152
+ paths, approve network work, or set failure policy. Explicit command options
153
+ override defaults from the same option group.
140
154
 
141
155
  Every command accepts `--output <file>`. WHOISleuth buffers bounded output,
142
156
  creates a private temporary file beside the destination, syncs it, and publishes
@@ -154,6 +168,7 @@ commands do not publish partial files. Output is capped at 32 MiB.
154
168
  | 4 | A bounded operation completed partially. |
155
169
  | 70 | Unexpected CLI bootstrap failure. |
156
170
  | 130 | The analyst cancelled the command; no partial final result was emitted. |
171
+ | 143 | The process received SIGTERM; no partial final result was emitted. |
157
172
 
158
173
  ## Detailed command reference
159
174
 
@@ -182,12 +197,17 @@ full contracts into the packaged reference.
182
197
 
183
198
  ### Workspace archive inspection
184
199
 
185
- [Read the redacted archive summary and search contract.](cli-reference.md#workspace-archive-inspection)
200
+ [Read the redacted archive summary, stable content-digest, transfer-check, and
201
+ search contract.](cli-reference.md#workspace-archive-inspection)
186
202
 
187
203
  ### Optional evidence-package signing
188
204
 
189
205
  [Read the local signing and verification contract.](cli-reference.md#optional-evidence-package-signing)
190
206
 
207
+ ### Reproducible investigation manifest
208
+
209
+ [Read the ordered, path-free artefact digest contract.](cli-reference.md#reproducible-investigation-manifest)
210
+
191
211
  ### Bulk lookup
192
212
 
193
213
  [Read Bulk bounds, filters, formats, checkpoints, and cancellation behaviour.](cli-reference.md#bulk-lookup)
@@ -196,6 +216,14 @@ full contracts into the packaged reference.
196
216
 
197
217
  [Read the CT query and source-limit contract.](cli-reference.md#certificate-transparency-search)
198
218
 
219
+ ### Local certificate event intake
220
+
221
+ [Read the offline, source-qualified event import contract.](cli-reference.md#local-certificate-event-intake)
222
+
223
+ ### Declarative observation interchange
224
+
225
+ [Read the bounded field-mapping and Open Asset Model bridge contracts.](cli-reference.md#declarative-observation-interchange)
226
+
199
227
  ### Lookalike discovery
200
228
 
201
229
  [Read candidate generation, dictionaries, snapshots, and output behaviour.](cli-reference.md#lookalike-discovery)
@@ -230,7 +258,7 @@ full contracts into the packaged reference.
230
258
 
231
259
  ### Offline supplied-evidence review
232
260
 
233
- [Read the DNSSEC, DANE, and supplied-observation review contract.](cli-reference.md#offline-supplied-evidence-review)
261
+ [Read the DNSSEC, DANE, zone-intent, portfolio, domain-change, and supplied-observation review contract.](cli-reference.md#offline-supplied-evidence-review)
234
262
 
235
263
  ### Domain control manifests
236
264
 
@@ -240,6 +268,10 @@ full contracts into the packaged reference.
240
268
 
241
269
  [Read planned-change, recovery-dependency, and retirement contracts.](cli-reference.md#domain-assurance-review)
242
270
 
271
+ ### Domain change assurance packet
272
+
273
+ [Read the bounded pre-change, post-change, planning, and integrity contract.](cli-reference.md#domain-change-assurance-packet)
274
+
243
275
  ### Pre-sharing review
244
276
 
245
277
  [Read the local marking, scope, redaction, and integrity preflight.](cli-reference.md#pre-sharing-review)
@@ -248,6 +280,10 @@ full contracts into the packaged reference.
248
280
 
249
281
  [Read the plan-only recipe contract and approval gates.](cli-reference.md#fixed-investigation-plans)
250
282
 
283
+ ### Local handoffs and one-shot monitoring
284
+
285
+ [Read brief, case-package, fixed-recipe execution, and one-shot control-review contracts.](cli-reference.md#local-handoffs-and-one-shot-monitoring)
286
+
251
287
  ### Optional local rendered capture
252
288
 
253
289
  [Read the separate Playwright capture package boundary.](cli-reference.md#optional-local-rendered-capture)
@@ -205,7 +205,7 @@ function contactsItem(insights) {
205
205
  function sourceComplete(evidence) {
206
206
  return evidence.source === 'dns' && evidence.complete === true;
207
207
  }
208
- function transitionDependencies(availability, activation, dnsEvidence, dnsRecords, tlsEvidence) {
208
+ function transitionDependencies(_availability, activation, dnsEvidence, dnsRecords, tlsEvidence) {
209
209
  const dnsComplete = sourceComplete(dnsEvidence);
210
210
  const nameservers = strings(dnsRecords.ns);
211
211
  const addresses = [...strings(dnsRecords.a), ...strings(dnsRecords.aaaa)];
@@ -383,7 +383,7 @@ export function buildAcquisitionDueDiligence(input) {
383
383
  policyChecks,
384
384
  nextSteps,
385
385
  limitations: [
386
- 'This workspace organizes observed evidence; it does not value a domain or establish that it can be acquired.',
386
+ 'This workspace organises observed evidence; it does not value a domain or establish that it can be acquired.',
387
387
  'Expiry dates, EPP statuses, sale signals, and contact routes can change after collection.',
388
388
  'WHOISleuth does not assess legal rights, registry eligibility, registrar terms, escrow, tax, or payment risk.',
389
389
  ],