@slicedearth/whoisleuth-cli 2.5.1 → 2.7.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 (170) hide show
  1. package/DISCLOSURE +9 -6
  2. package/NOTICE +3 -2
  3. package/README.md +60 -67
  4. package/cli/artifact-validation/case-response.mjs +6 -814
  5. package/cli/artifact-validation/lookup-evidence.mjs +30 -3
  6. package/cli/artifact-validation/signed-review.mjs +2 -51
  7. package/cli/artifact-verify.mjs +41 -3
  8. package/cli/bulk-command-runner.mjs +4 -6
  9. package/cli/case-command.mjs +92 -16
  10. package/cli/case-pack.mjs +14 -3
  11. package/cli/ci-report.mjs +17 -5
  12. package/cli/command-definition.mjs +7 -6
  13. package/cli/discovery-scan-command-runner.mjs +4 -6
  14. package/cli/doctor.mjs +37 -3
  15. package/cli/domain-feed.mjs +61 -0
  16. package/cli/formatters/html.mjs +3 -0
  17. package/cli/formatters/json.mjs +9 -14
  18. package/cli/formatters/terminal-lookup.mjs +24 -1
  19. package/cli/history-command-definitions.mjs +1 -1
  20. package/cli/indicator-set-command.mjs +1 -2
  21. package/cli/intake-command.mjs +43 -3
  22. package/cli/investigation-artifacts.mjs +2 -3
  23. package/cli/investigation-package-review.mjs +2 -0
  24. package/cli/investigation-plan.mjs +15 -0
  25. package/cli/local-mmdb-review.mjs +27 -4
  26. package/cli/lookup-brief.mjs +11 -5
  27. package/cli/lookup-command-runner.mjs +21 -13
  28. package/cli/offline-evidence-review.mjs +51 -5
  29. package/cli/review-arguments.mjs +3 -2
  30. package/cli/review-command-definitions.mjs +23 -17
  31. package/cli/review-command-runner.mjs +6 -3
  32. package/cli/risk-calibration.mjs +6 -4
  33. package/cli/saved-lookup.mjs +22 -7
  34. package/cli/selected-input-worker.mjs +12 -2
  35. package/cli/source-reliability.mjs +12 -10
  36. package/cli/support-arguments.mjs +1 -1
  37. package/cli/support-command-definitions.mjs +5 -4
  38. package/cli/support-command-runner.mjs +9 -7
  39. package/cli/watchlist-review.mjs +43 -0
  40. package/cli/workflow-arguments.mjs +14 -0
  41. package/cli/workflow-command-definitions.mjs +27 -1
  42. package/cli/workflow-command-runner.mjs +32 -0
  43. package/docs/cli-reference.md +49 -11
  44. package/docs/cli.md +200 -26
  45. package/docs/contextual-reviews.md +38 -5
  46. package/docs/infrastructure-observations.md +135 -0
  47. package/docs/offline-intake-context.md +213 -0
  48. package/lib/credential-form-attribution.mjs +136 -0
  49. package/lib/credential-surface-profile.mjs +6 -2
  50. package/lib/dmarc-discovery.mjs +4 -2
  51. package/lib/dns-delegation-health.mjs +23 -7
  52. package/lib/dns-inheritance-review.mjs +14 -1
  53. package/lib/dns-intelligence.mjs +18 -4
  54. package/lib/domain-posture-analysis.mjs +53 -2
  55. package/lib/domain-posture-parsers.mjs +9 -0
  56. package/lib/domain-posture.mjs +29 -10
  57. package/lib/evidence-export-privacy.mjs +5 -2
  58. package/lib/evidence-export.mjs +10 -4
  59. package/lib/evidence-report.mjs +3 -1
  60. package/lib/favicon.mjs +13 -4
  61. package/lib/generated/cisa-kev-catalog.mjs +17 -4
  62. package/lib/generated/registrar-standing-catalogue.mjs +7 -7
  63. package/lib/generated/retire-browser-catalog.mjs +178 -5
  64. package/lib/html-signals.mjs +6 -0
  65. package/lib/idn-confusables.mjs +5 -1
  66. package/lib/lookup-child-profile-contract.mjs +28 -11
  67. package/lib/lookup-collection-receipt.mjs +52 -0
  68. package/lib/lookup-diagnostics.mjs +2 -12
  69. package/lib/lookup-progress.mjs +29 -31
  70. package/lib/lookup-response-contract.mjs +10 -0
  71. package/lib/lookup-response.mjs +15 -14
  72. package/lib/lookup-source-progress.mjs +4 -2
  73. package/lib/lookup.mjs +33 -2
  74. package/lib/operation-budget.mjs +4 -0
  75. package/lib/rdap-capabilities.mjs +43 -9
  76. package/lib/registry-insights.mjs +17 -6
  77. package/lib/service-binding-dns.mjs +19 -2
  78. package/lib/sslbl-certificates.generated.mjs +142 -140
  79. package/lib/static-html-analysis.mjs +32 -4
  80. package/lib/technology-evidence-role.mjs +1 -51
  81. package/lib/tls-intelligence.mjs +30 -5
  82. package/lib/website-enrichment.mjs +39 -23
  83. package/lib/whois-authority.mjs +56 -1
  84. package/lib/whois-chain.mjs +14 -4
  85. package/lib/whois-common-formats.mjs +3 -1
  86. package/lib/whois-contacts.mjs +13 -2
  87. package/lib/whois-contracts.mjs +2 -0
  88. package/lib/whois-normalization.mjs +4 -4
  89. package/package.json +9 -7
  90. package/packages/analysis/bounded-json.mjs +12 -2
  91. package/packages/analysis/risk-scoring.mjs +12 -9
  92. package/packages/cases/case-attachment-model.mjs +36 -1
  93. package/packages/cases/case-checkpoint-receipts.mjs +68 -0
  94. package/packages/cases/case-containment-handoff.mjs +195 -0
  95. package/packages/cases/case-evidence-links.mjs +44 -0
  96. package/packages/cases/case-evidence-model.mjs +51 -7
  97. package/packages/cases/case-migration-model.mjs +9 -1
  98. package/packages/cases/case-object-coverage.mjs +119 -0
  99. package/packages/cases/case-packet-correction.mjs +106 -0
  100. package/packages/cases/case-recheck-model.mjs +35 -5
  101. package/packages/cases/case-record-operations.mjs +87 -9
  102. package/packages/cases/case-record-projection.mjs +43 -5
  103. package/packages/cases/case-record-version-input.mjs +23 -1
  104. package/packages/cases/case-report.mjs +35 -4
  105. package/packages/cases/case-requested-evidence.mjs +1 -1
  106. package/packages/cases/case-response-actions.mjs +57 -1
  107. package/packages/cases/case-response-model.mjs +7 -0
  108. package/packages/cases/case-response-object.mjs +119 -0
  109. package/packages/cases/case-response-outcomes.mjs +81 -14
  110. package/packages/cases/case-response-packet-render.mjs +22 -3
  111. package/packages/cases/case-response-packet-validation.mjs +880 -0
  112. package/packages/cases/case-response-packet-vocabulary.mjs +12 -1
  113. package/packages/cases/case-response-packet.mjs +32 -17
  114. package/packages/cases/case-response-review-inputs.mjs +66 -8
  115. package/packages/cases/case-type-presentation.mjs +10 -0
  116. package/packages/cases/case-workflow-metadata.mjs +1 -17
  117. package/packages/comparison/capture-context.mjs +3 -2
  118. package/packages/contracts/analyst-interchange.mjs +7 -5
  119. package/packages/contracts/analyst-review-state-contract.mjs +1 -1
  120. package/packages/contracts/candidate-watch-review.mjs +54 -0
  121. package/packages/contracts/case-portability.mjs +292 -43
  122. package/packages/contracts/cli-command-semantics.mjs +3 -0
  123. package/packages/contracts/cli-doctor.mjs +4 -0
  124. package/packages/contracts/cli-lookup.mjs +57 -46
  125. package/packages/contracts/context-review.mjs +2 -1
  126. package/packages/contracts/external-observation-interchange.mjs +96 -28
  127. package/packages/contracts/lookup-evidence.mjs +13 -2
  128. package/packages/contracts/message-intake.mjs +7 -1
  129. package/packages/contracts/registry-insights.mjs +6 -0
  130. package/packages/contracts/risk-calibration.mjs +44 -29
  131. package/packages/contracts/schema-lifecycle.mjs +3 -1
  132. package/packages/contracts/web-capture.mjs +1 -1
  133. package/packages/contracts/workspace-portability.mjs +86 -16
  134. package/packages/evidence/collection-quality.mjs +1 -1
  135. package/packages/evidence/credential-form-attribution.mjs +60 -0
  136. package/packages/evidence/dmarc-policy-context.mjs +80 -0
  137. package/packages/evidence/image-regions.mjs +15 -0
  138. package/packages/evidence/lookup-collection-receipt.mjs +140 -0
  139. package/packages/evidence/lookup-collection-review.mjs +74 -0
  140. package/packages/evidence/record-identity.mjs +60 -16
  141. package/packages/evidence/technology-evidence-role.mjs +51 -0
  142. package/packages/interchange/external-findings-import.mjs +40 -21
  143. package/packages/interchange/json-output.mjs +14 -0
  144. package/packages/interchange/managed-indicator-export.mjs +3 -2
  145. package/packages/interchange/managed-indicator-set.mjs +13 -0
  146. package/packages/investigation/acquisition-decision-packet.mjs +78 -0
  147. package/packages/investigation/bulk-mail-exposure.mjs +2 -2
  148. package/packages/investigation/context-review.mjs +3 -3
  149. package/packages/investigation/document-intake.mjs +9 -3
  150. package/packages/investigation/domain-history-review.mjs +50 -12
  151. package/packages/investigation/domain-transition-review.mjs +2 -0
  152. package/packages/investigation/infrastructure-observation.mjs +234 -0
  153. package/packages/investigation/intake-context.mjs +284 -0
  154. package/packages/investigation/intake-indicators.mjs +116 -0
  155. package/packages/investigation/intake-phones.mjs +155 -0
  156. package/packages/investigation/intake-report.mjs +2 -1
  157. package/packages/investigation/investigation-manifest.mjs +33 -7
  158. package/packages/investigation/link-intake.mjs +38 -8
  159. package/packages/investigation/message-intake.mjs +55 -15
  160. package/packages/monitoring/domain-feed.mjs +321 -0
  161. package/packages/workspace/brand-candidate-workflow.mjs +351 -0
  162. package/packages/workspace/brand-keyword-campaign.mjs +241 -0
  163. package/packages/workspace/brand-profile-model.mjs +46 -2
  164. package/packages/workspace/bulk-session-model.mjs +12 -0
  165. package/packages/workspace/candidate-watch-handoff.mjs +195 -0
  166. package/packages/workspace/detection-rule-model.mjs +10 -2
  167. package/packages/workspace/shortlist-model.mjs +10 -2
  168. package/packages/workspace/watchlist-history.mjs +31 -4
  169. package/packages/workspace/watchlist-store.mjs +136 -7
  170. package/third-party-notices.txt +30 -30
@@ -2,9 +2,12 @@ import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
2
2
  import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
3
3
  export const CLI_LOOKUP_SCHEMA = 'whoisleuth.cli.lookup';
4
4
  export const PUBLIC_CLI_LOOKUP_VERSION = 1;
5
- export const CLI_LOOKUP_VERSION = 2;
5
+ export const HOMEPAGE_CLI_LOOKUP_VERSION = 2;
6
+ export const COLLECTION_RECEIPT_CLI_LOOKUP_VERSION = 3;
7
+ export const CLI_LOOKUP_VERSION = COLLECTION_RECEIPT_CLI_LOOKUP_VERSION;
6
8
  export const SUPPORTED_CLI_LOOKUP_VERSIONS = Object.freeze([
7
9
  PUBLIC_CLI_LOOKUP_VERSION,
10
+ HOMEPAGE_CLI_LOOKUP_VERSION,
8
11
  CLI_LOOKUP_VERSION,
9
12
  ]);
10
13
  export const MAX_CLI_LOOKUP_BYTES = 8 * 1024 * 1024;
@@ -43,6 +46,7 @@ export const CLI_LOOKUP_OPTIONAL_ROOT_KEYS = Object.freeze([
43
46
  'sslbl',
44
47
  'threatIntelligence',
45
48
  'collectionContext',
49
+ 'collectionReceipt',
46
50
  ]);
47
51
  export const CLI_LOOKUP_DIAGNOSTIC_REQUIRED_KEYS = Object.freeze([
48
52
  'rdap',
@@ -60,7 +64,7 @@ export const CLI_LOOKUP_COMPATIBILITY = defineSchemaCompatibility({
60
64
  writeSemantics: 'normalized_rewrite',
61
65
  byteBudget: MAX_CLI_LOOKUP_BYTES,
62
66
  owner: 'packages/contracts/cli-lookup.mts',
63
- note: 'Saved Lookup documents preserve version 1 domain, IP and ASN compatibility while version 2 can additionally retain bounded Deep homepage metadata and optional official registrar-standing context.',
67
+ note: 'Saved Lookup documents preserve versions 1 and 2; version 3 may retain a bounded collection receipt for the completed operation. Homepage metadata and registrar-standing context remain supported from version 2.',
64
68
  });
65
69
  export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
66
70
  id: 'cli-lookup',
@@ -87,11 +91,11 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
87
91
  {
88
92
  compatibilityId: CLI_LOOKUP_COMPATIBILITY.id,
89
93
  schema: CLI_LOOKUP_SCHEMA,
90
- version: CLI_LOOKUP_VERSION,
94
+ version: HOMEPAGE_CLI_LOOKUP_VERSION,
91
95
  role: 'document',
92
- lifecycle: 'current',
96
+ lifecycle: 'legacy',
93
97
  readable: true,
94
- emitted: true,
98
+ emitted: false,
95
99
  exactKeys: false,
96
100
  extensionPolicy: 'preserve_bounded',
97
101
  futureVersionBehaviour: 'reject',
@@ -100,8 +104,29 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
100
104
  byteBudget: MAX_CLI_LOOKUP_BYTES,
101
105
  fixtureIds: ['cli-lookup-v2', 'cli-lookup-asn-v2'],
102
106
  },
107
+ {
108
+ compatibilityId: CLI_LOOKUP_COMPATIBILITY.id,
109
+ schema: CLI_LOOKUP_SCHEMA,
110
+ version: CLI_LOOKUP_VERSION,
111
+ role: 'document', lifecycle: 'current', readable: true, emitted: true,
112
+ exactKeys: false, extensionPolicy: 'preserve_bounded', futureVersionBehaviour: 'reject',
113
+ migrationTarget: null, canonicalisation: null, byteBudget: MAX_CLI_LOOKUP_BYTES,
114
+ fixtureIds: ['cli-lookup-v3', 'cli-lookup-asn-v3'],
115
+ },
103
116
  ],
104
117
  fixtures: [
118
+ {
119
+ id: 'cli-lookup-v3', path: 'test/fixtures/cli-lookup-v3.json',
120
+ bytes: 7151, sha256: 'ee06201deb99ef1cdc5e79dc026a733fb14494593ee7ffdff3035b1fbc19d61e', contentDigestSha256: null,
121
+ schema: CLI_LOOKUP_SCHEMA, version: CLI_LOOKUP_VERSION, role: 'current',
122
+ expectation: 'accepted_exact', expectedOutputFixtureId: null, scope: 'repository',
123
+ },
124
+ {
125
+ id: 'cli-lookup-asn-v3', path: 'test/fixtures/cli-lookup-asn-v3.json',
126
+ bytes: 3506, sha256: 'c0a3dfe9d9c13d3f03079c6c76b33b353a6029b9a9893cbdb743b3f4d864c74e', contentDigestSha256: null,
127
+ schema: CLI_LOOKUP_SCHEMA, version: CLI_LOOKUP_VERSION, role: 'current',
128
+ expectation: 'accepted_exact', expectedOutputFixtureId: null, scope: 'repository',
129
+ },
105
130
  {
106
131
  id: 'cli-lookup-v1',
107
132
  path: 'test/fixtures/cli-lookup-v1.json',
@@ -122,8 +147,8 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
122
147
  sha256: 'd98356ede18ddd12fde8cf10b0cfa43c42f6c9a09f1951e0605e1ec912892778',
123
148
  contentDigestSha256: null,
124
149
  schema: CLI_LOOKUP_SCHEMA,
125
- version: CLI_LOOKUP_VERSION,
126
- role: 'current',
150
+ version: HOMEPAGE_CLI_LOOKUP_VERSION,
151
+ role: 'historical',
127
152
  expectation: 'accepted_exact',
128
153
  expectedOutputFixtureId: null,
129
154
  scope: 'repository',
@@ -148,8 +173,8 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
148
173
  sha256: 'a3ee74df02050d6efd935784734c8c4cfd5fcb9833393de68935f5040faf2856',
149
174
  contentDigestSha256: null,
150
175
  schema: CLI_LOOKUP_SCHEMA,
151
- version: CLI_LOOKUP_VERSION,
152
- role: 'current',
176
+ version: HOMEPAGE_CLI_LOOKUP_VERSION,
177
+ role: 'historical',
153
178
  expectation: 'accepted_exact',
154
179
  expectedOutputFixtureId: null,
155
180
  scope: 'repository',
@@ -158,26 +183,12 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
158
183
  metadata: {
159
184
  enforcement: 'declarative_only',
160
185
  shapes: [
161
- {
162
- id: 'cli-lookup.document.v1',
163
- schema: CLI_LOOKUP_SCHEMA,
164
- versions: [PUBLIC_CLI_LOOKUP_VERSION],
165
- objects: [
166
- { path: '$', requiredKeys: CLI_LOOKUP_REQUIRED_ROOT_KEYS, optionalKeys: CLI_LOOKUP_OPTIONAL_ROOT_KEYS, unknownKeys: 'preserve_bounded' },
167
- { path: '$.diagnostics', requiredKeys: CLI_LOOKUP_DIAGNOSTIC_REQUIRED_KEYS, optionalKeys: [], unknownKeys: 'preserve_bounded' },
168
- { path: '$.diagnostics.rdap', requiredKeys: ['status'], optionalKeys: [], unknownKeys: 'preserve_bounded' },
169
- { path: '$.diagnostics.whois', requiredKeys: ['status'], optionalKeys: [], unknownKeys: 'preserve_bounded' },
170
- ],
171
- fixedArrays: [],
172
- normalisation: 'preserve_bounded_document',
173
- target: null,
174
- },
175
- {
176
- id: 'cli-lookup.document.v2',
186
+ ...SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => ({
187
+ id: `cli-lookup.document.v${version}`,
177
188
  schema: CLI_LOOKUP_SCHEMA,
178
- versions: [CLI_LOOKUP_VERSION],
189
+ versions: [version],
179
190
  objects: [
180
- { path: '$', requiredKeys: CLI_LOOKUP_REQUIRED_ROOT_KEYS, optionalKeys: CLI_LOOKUP_OPTIONAL_ROOT_KEYS, unknownKeys: 'preserve_bounded' },
191
+ { path: '$', requiredKeys: CLI_LOOKUP_REQUIRED_ROOT_KEYS, optionalKeys: CLI_LOOKUP_OPTIONAL_ROOT_KEYS.filter(key => version >= COLLECTION_RECEIPT_CLI_LOOKUP_VERSION || key !== 'collectionReceipt'), unknownKeys: 'preserve_bounded' },
181
192
  { path: '$.diagnostics', requiredKeys: CLI_LOOKUP_DIAGNOSTIC_REQUIRED_KEYS, optionalKeys: [], unknownKeys: 'preserve_bounded' },
182
193
  { path: '$.diagnostics.rdap', requiredKeys: ['status'], optionalKeys: [], unknownKeys: 'preserve_bounded' },
183
194
  { path: '$.diagnostics.whois', requiredKeys: ['status'], optionalKeys: [], unknownKeys: 'preserve_bounded' },
@@ -185,7 +196,7 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
185
196
  fixedArrays: [],
186
197
  normalisation: 'preserve_bounded_document',
187
198
  target: null,
188
- },
199
+ })),
189
200
  ],
190
201
  boundProfiles: [
191
202
  {
@@ -214,9 +225,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
214
225
  ],
215
226
  serialisationProfiles: [
216
227
  {
217
- id: 'cli-lookup.json.v1-v2',
228
+ id: 'cli-lookup.json.v1-v3',
218
229
  schema: CLI_LOOKUP_SCHEMA,
219
- versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION],
230
+ versions: SUPPORTED_CLI_LOOKUP_VERSIONS,
220
231
  mediaType: 'application/json',
221
232
  encoding: 'utf-8',
222
233
  bom: false,
@@ -234,7 +245,7 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
234
245
  id: 'cli-lookup.sensitive-collection.v1',
235
246
  classification: 'analyst_authored_sensitive',
236
247
  projection: 'full_manifest',
237
- includedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'source-timings', 'normalised-evidence', 'registrar-standing-official-projection', 'collection-context'],
248
+ includedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'source-timings', 'normalised-evidence', 'registrar-standing-official-projection', 'collection-context', 'collection-receipt'],
238
249
  excludedCategories: ['credentials', 'cookies', 'sessions', 'authorisation-material'],
239
250
  notePolicy: 'not_applicable',
240
251
  retention: 'operator_controlled_output',
@@ -245,7 +256,7 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
245
256
  id: 'cli-lookup.sensitive-file.v1',
246
257
  classification: 'analyst_authored_sensitive',
247
258
  projection: 'full_manifest',
248
- includedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'source-timings', 'normalised-evidence', 'registrar-standing-official-projection', 'collection-context'],
259
+ includedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'source-timings', 'normalised-evidence', 'registrar-standing-official-projection', 'collection-context', 'collection-receipt'],
249
260
  excludedCategories: ['credentials', 'cookies', 'sessions', 'authorisation-material'],
250
261
  notePolicy: 'not_applicable',
251
262
  retention: 'deliberate_local_file',
@@ -257,7 +268,7 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
257
268
  classification: 'metadata_only',
258
269
  projection: 'metadata_only',
259
270
  includedCategories: ['schema-and-version', 'source-state-counts', 'bounded-timings', 'integrity-state'],
260
- excludedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'normalised-evidence', 'collection-context'],
271
+ excludedCategories: ['target-identifiers', 'query-and-mode', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'normalised-evidence', 'collection-context', 'collection-receipt'],
261
272
  notePolicy: 'not_applicable',
262
273
  retention: 'transient_report',
263
274
  network: 'none',
@@ -268,7 +279,7 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
268
279
  classification: 'metadata_only',
269
280
  projection: 'metadata_only',
270
281
  includedCategories: ['schema-and-version', 'source-state-counts', 'bounded-timings', 'truncation-and-rate-limit-counts', 'aggregate-lookup-mode-counts'],
271
- excludedCategories: ['target-identifiers', 'query-values', 'per-document-linkage', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'normalised-evidence', 'collection-context'],
282
+ excludedCategories: ['target-identifiers', 'query-values', 'per-document-linkage', 'raw-rdap-publications', 'raw-whois-responses', 'published-contacts', 'source-endpoints', 'normalised-evidence', 'collection-context', 'collection-receipt'],
272
283
  notePolicy: 'not_applicable',
273
284
  retention: 'transient_report',
274
285
  network: 'none',
@@ -292,10 +303,10 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
292
303
  operation: 'collect-and-serialise-fast-or-deep',
293
304
  acceptedContracts: [],
294
305
  emittedContract: { schema: CLI_LOOKUP_SCHEMA, version: CLI_LOOKUP_VERSION },
295
- shapeIds: ['cli-lookup.document.v2'],
306
+ shapeIds: [`cli-lookup.document.v${CLI_LOOKUP_VERSION}`],
296
307
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
297
308
  hookIds: ['cli-lookup.cli.build', 'cli-lookup.cli.serialise'],
298
- serialisationProfileId: 'cli-lookup.json.v1-v2',
309
+ serialisationProfileId: 'cli-lookup.json.v1-v3',
299
310
  privacyProfileId: 'cli-lookup.sensitive-collection.v1',
300
311
  expiryPolicyId: 'cli-lookup.expiry.not-applicable.v1',
301
312
  requestMode: 'explicit_bounded_passive_fast_or_deep',
@@ -307,9 +318,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
307
318
  id: 'cli-lookup.cli-read-all',
308
319
  plane: 'cli',
309
320
  operation: 'read-all-query-types',
310
- acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION], mode: 'direct' }],
321
+ acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: SUPPORTED_CLI_LOOKUP_VERSIONS, mode: 'direct' }],
311
322
  emittedContract: null,
312
- shapeIds: ['cli-lookup.document.v1', 'cli-lookup.document.v2'],
323
+ shapeIds: SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => `cli-lookup.document.v${version}`),
313
324
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
314
325
  hookIds: ['cli-lookup.cli.parse-all'],
315
326
  serialisationProfileId: null,
@@ -324,9 +335,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
324
335
  id: 'cli-lookup.cli-read-domain',
325
336
  plane: 'cli',
326
337
  operation: 'read-domain-only',
327
- acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION], mode: 'direct', discriminator: { path: '$.type', values: ['domain'] } }],
338
+ acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: SUPPORTED_CLI_LOOKUP_VERSIONS, mode: 'direct', discriminator: { path: '$.type', values: ['domain'] } }],
328
339
  emittedContract: null,
329
- shapeIds: ['cli-lookup.document.v1', 'cli-lookup.document.v2'],
340
+ shapeIds: SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => `cli-lookup.document.v${version}`),
330
341
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
331
342
  hookIds: ['cli-lookup.cli.parse-domain'],
332
343
  serialisationProfileId: null,
@@ -341,9 +352,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
341
352
  id: 'cli-lookup.cli-source-report',
342
353
  plane: 'cli',
343
354
  operation: 'build-source-reliability-report',
344
- acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION], mode: 'direct' }],
355
+ acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: SUPPORTED_CLI_LOOKUP_VERSIONS, mode: 'direct' }],
345
356
  emittedContract: null,
346
- shapeIds: ['cli-lookup.document.v1', 'cli-lookup.document.v2'],
357
+ shapeIds: SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => `cli-lookup.document.v${version}`),
347
358
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
348
359
  hookIds: ['cli-lookup.cli.normalise', 'cli-lookup.cli.source-report'],
349
360
  serialisationProfileId: null,
@@ -358,9 +369,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
358
369
  id: 'cli-lookup.cli-offline-verify',
359
370
  plane: 'cli',
360
371
  operation: 'verify-structure',
361
- acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION], mode: 'direct' }],
372
+ acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: SUPPORTED_CLI_LOOKUP_VERSIONS, mode: 'direct' }],
362
373
  emittedContract: null,
363
- shapeIds: ['cli-lookup.document.v1', 'cli-lookup.document.v2'],
374
+ shapeIds: SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => `cli-lookup.document.v${version}`),
364
375
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
365
376
  hookIds: ['cli-lookup.cli.parse-all', 'cli-lookup.cli.offline-verify'],
366
377
  serialisationProfileId: null,
@@ -375,9 +386,9 @@ export const CLI_LOOKUP_SCHEMA_LIFECYCLE = defineSchemaLifecycleFamily({
375
386
  id: 'cli-lookup.cli-sharing-review',
376
387
  plane: 'cli',
377
388
  operation: 'build-redacted-sharing-review',
378
- acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: [PUBLIC_CLI_LOOKUP_VERSION, CLI_LOOKUP_VERSION], mode: 'direct' }],
389
+ acceptedContracts: [{ schema: CLI_LOOKUP_SCHEMA, versions: SUPPORTED_CLI_LOOKUP_VERSIONS, mode: 'direct' }],
379
390
  emittedContract: null,
380
- shapeIds: ['cli-lookup.document.v1', 'cli-lookup.document.v2'],
391
+ shapeIds: SUPPORTED_CLI_LOOKUP_VERSIONS.map(version => `cli-lookup.document.v${version}`),
381
392
  boundProfileIds: ['cli-lookup.document-bounds.v1'],
382
393
  hookIds: ['cli-lookup.cli.parse-all', 'cli-lookup.cli.sharing-review'],
383
394
  serialisationProfileId: null,
@@ -2,7 +2,8 @@
2
2
  export const CONTEXT_REVIEW_SCHEMA = 'whoisleuth.context-review';
3
3
  export const CONTEXT_REVIEW_VERSION = 1;
4
4
  export const DOMAIN_HISTORY_INPUT_SCHEMA = 'whoisleuth.domain-history.input';
5
- export const DOMAIN_HISTORY_INPUT_VERSION = 1;
5
+ export const DOMAIN_HISTORY_INPUT_VERSION = 2;
6
+ export const DOMAIN_HISTORY_LEGACY_INPUT_VERSION = 1;
6
7
  export const PLATFORM_CONTINUITY_INPUT_SCHEMA = 'whoisleuth.platform-continuity.input';
7
8
  export const PLATFORM_CONTINUITY_INPUT_VERSION = 1;
8
9
  export const STOREFRONT_INPUT_SCHEMA = 'whoisleuth.storefront-review.input';
@@ -2,8 +2,8 @@ import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
2
2
  import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
3
3
  export const EXTERNAL_OBSERVATION_INTERCHANGE_CONTRACT_OWNER = 'packages/contracts/external-observation-interchange.mts';
4
4
  export const EXTERNAL_FINDINGS_SCHEMA = 'whoisleuth.external-findings';
5
- export const EXTERNAL_FINDINGS_VERSION = 4;
6
- export const SUPPORTED_EXTERNAL_FINDINGS_VERSIONS = Object.freeze([EXTERNAL_FINDINGS_VERSION]);
5
+ export const EXTERNAL_FINDINGS_VERSION = 5;
6
+ export const SUPPORTED_EXTERNAL_FINDINGS_VERSIONS = Object.freeze([4, 5]);
7
7
  export const MAX_EXTERNAL_FINDINGS_IMPORT_BYTES = 384 * 1024;
8
8
  export const MAX_EXTERNAL_FINDINGS = 100;
9
9
  export const MAX_EXTERNAL_FINDINGS_PER_DOMAIN = 20;
@@ -17,6 +17,19 @@ export const DNS_OBSERVATION_ROWS_SCHEMA = 'whoisleuth.dns-observation-rows';
17
17
  export const CERTIFICATE_OBSERVATION_ROWS_SCHEMA = 'whoisleuth.certificate-observation-rows';
18
18
  export const SUPPORTED_OBSERVATION_ROWS_VERSION = 1;
19
19
  export const MAX_CONVERSION_INPUT_ROWS = MAX_EXTERNAL_FINDINGS * 4;
20
+ export const INFRASTRUCTURE_OBSERVATION_SCHEMA = 'whoisleuth.infrastructure-observation';
21
+ export const INFRASTRUCTURE_OBSERVATION_VERSION = 1;
22
+ export const MAX_INFRASTRUCTURE_OBSERVATION_BYTES = 128 * 1024;
23
+ export const MAX_INFRASTRUCTURE_HOSTS = 128;
24
+ export const MAX_INFRASTRUCTURE_DNS_ROWS = 512;
25
+ export const MAX_INFRASTRUCTURE_CERTIFICATES = 32;
26
+ export const MAX_INFRASTRUCTURE_ROLES = 128;
27
+ export const MAX_INFRASTRUCTURE_SOURCES = 16;
28
+ export const MAX_INFRASTRUCTURE_LIMITATIONS = 12;
29
+ export const INFRASTRUCTURE_DNS_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR'];
30
+ export const INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA = 'whoisleuth.infrastructure-comparison.input';
31
+ export const INFRASTRUCTURE_COMPARISON_INPUT_VERSION = 1;
32
+ export const MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES = 2 * MAX_INFRASTRUCTURE_OBSERVATION_BYTES + 1_024;
20
33
  export const EXTERNAL_OBSERVATION_INTERCHANGE_COMPATIBILITY_FACADES = Object.freeze([
21
34
  ['frontend/src/lib/analysis/external-findings-import.ts', 'packages/interchange/external-findings-import.mts'],
22
35
  ['frontend/src/lib/analysis/external-findings-converters.ts', 'packages/interchange/external-findings-converters.mts'],
@@ -24,9 +37,9 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_COMPATIBILITY_FACADES = Object.fre
24
37
  export const EXTERNAL_FINDINGS_COMPATIBILITY = defineSchemaCompatibility({
25
38
  id: 'export.external-findings', kind: 'export', schema: EXTERNAL_FINDINGS_SCHEMA,
26
39
  currentVersion: EXTERNAL_FINDINGS_VERSION, supportedVersions: SUPPORTED_EXTERNAL_FINDINGS_VERSIONS,
27
- acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'non_destructive_merge',
40
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'none', writeSemantics: 'non_destructive_merge',
28
41
  byteBudget: MAX_EXTERNAL_FINDINGS_IMPORT_BYTES, owner: EXTERNAL_OBSERVATION_INTERCHANGE_CONTRACT_OWNER,
29
- note: 'Strict local findings import. Version 4 can retain bounded certificate event identity and name-completeness metadata while analyst assertions remain a separate case workflow.',
42
+ note: 'Strict local findings import. Historical version 4 is preserved exactly; version 5 adds an optional exact source-qualified infrastructure snapshot. No collection is initiated.',
30
43
  });
31
44
  export const EXTERNAL_FINDING_ROWS_COMPATIBILITY = defineSchemaCompatibility({
32
45
  id: 'import.external-finding-rows', kind: 'export', schema: EXTERNAL_FINDING_ROWS_SCHEMA,
@@ -62,17 +75,26 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_COMPATIBILITY = Object.freeze([
62
75
  DOMAIN_OBSERVATION_ROWS_COMPATIBILITY,
63
76
  DNS_OBSERVATION_ROWS_COMPATIBILITY,
64
77
  CERTIFICATE_OBSERVATION_ROWS_COMPATIBILITY,
78
+ defineSchemaCompatibility({ id: 'export.infrastructure-observation', kind: 'export', schema: INFRASTRUCTURE_OBSERVATION_SCHEMA, currentVersion: 1, supportedVersions: [1], acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only', writeSemantics: 'read_only', byteBudget: MAX_INFRASTRUCTURE_OBSERVATION_BYTES, owner: EXTERNAL_OBSERVATION_INTERCHANGE_CONTRACT_OWNER, note: 'Exact source-qualified selected infrastructure evidence retained through existing Case pins. Wildcards, DNS outcomes and independent provider roles remain distinct.' }),
79
+ defineSchemaCompatibility({ id: 'cli.infrastructure-comparison-input', kind: 'cli_document', schema: INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, currentVersion: 1, supportedVersions: [1], acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only', writeSemantics: 'read_only', byteBudget: MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES, owner: EXTERNAL_OBSERVATION_INTERCHANGE_CONTRACT_OWNER, note: 'An explicit earlier/later snapshot pair reviewed offline without collection, persistence or current absence claims.' }),
65
80
  ]);
66
81
  const EXTERNAL_INTERCHANGE_FIXTURES = Object.freeze([
67
- { id: 'external-findings-v4', path: 'test/fixtures/external-observation-interchange/external-findings-v4.json', bytes: 634, sha256: 'ceab916e1dcf99704c94e1b0cd82f5a061f138ae6a07ce8335273080c73b7624', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
68
- { id: 'external-finding-rows-v1', path: 'test/fixtures/external-observation-interchange/external-finding-rows-v1.json', bytes: 435, sha256: '09048325d801fdc837134580fd887cacde28744c9f87a43afc388c96117a4659', schema: EXTERNAL_FINDING_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'external-finding-rows-v1-output' },
69
- { id: 'external-finding-rows-v1-output', path: 'test/fixtures/external-observation-interchange/external-finding-rows-v1-output.json', bytes: 581, sha256: '7f84a9f6177d3245acd6fc21275276694fbe9e8cd94922b1e6997a73afa3149a', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
70
- { id: 'domain-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/domain-observation-rows-v1.json', bytes: 390, sha256: '2f95ab21e57e1a6aaab03cea75a4d58fd9e1234ba87f5268a899b23103359474', schema: DOMAIN_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'domain-observation-rows-v1-output' },
71
- { id: 'domain-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/domain-observation-rows-v1-output.json', bytes: 1_027, sha256: 'bba949308e9bb5b58bf5611d6dec23f074979522627631948cffc2f0056ac817', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
72
- { id: 'dns-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/dns-observation-rows-v1.json', bytes: 369, sha256: 'afb1808a8a81339a1801d337259c19c77ed957583b4ae06579b38ea6a8f68ed1', schema: DNS_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'dns-observation-rows-v1-output' },
73
- { id: 'dns-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/dns-observation-rows-v1-output.json', bytes: 988, sha256: '4c9a49b96181d2907eb95319d15975758823994fe029c051486f1cb6869221a0', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
74
- { id: 'certificate-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/certificate-observation-rows-v1.json', bytes: 515, sha256: '68363a5fe074b08a386dbc95a0993a2933ebcf05ba2f96148df632842d1c5869', schema: CERTIFICATE_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'certificate-observation-rows-v1-output' },
75
- { id: 'certificate-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/certificate-observation-rows-v1-output.json', bytes: 1_244, sha256: '6c484601b872f10bfdb7d9519d4b216f14ef277e7a429e4cc131154ad0e07652', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
82
+ { id: 'external-findings-v4', path: 'test/fixtures/external-observation-interchange/external-findings-v4.json', bytes: 634, sha256: 'ceab916e1dcf99704c94e1b0cd82f5a061f138ae6a07ce8335273080c73b7624', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
83
+ { id: 'external-finding-rows-v1', path: 'test/fixtures/external-observation-interchange/external-finding-rows-v1.json', bytes: 435, sha256: '09048325d801fdc837134580fd887cacde28744c9f87a43afc388c96117a4659', schema: EXTERNAL_FINDING_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'external-finding-rows-v1-output-v5' },
84
+ { id: 'external-finding-rows-v1-output', path: 'test/fixtures/external-observation-interchange/external-finding-rows-v1-output.json', bytes: 581, sha256: '7f84a9f6177d3245acd6fc21275276694fbe9e8cd94922b1e6997a73afa3149a', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
85
+ { id: 'domain-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/domain-observation-rows-v1.json', bytes: 390, sha256: '2f95ab21e57e1a6aaab03cea75a4d58fd9e1234ba87f5268a899b23103359474', schema: DOMAIN_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'domain-observation-rows-v1-output-v5' },
86
+ { id: 'domain-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/domain-observation-rows-v1-output.json', bytes: 1_027, sha256: 'bba949308e9bb5b58bf5611d6dec23f074979522627631948cffc2f0056ac817', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
87
+ { id: 'dns-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/dns-observation-rows-v1.json', bytes: 369, sha256: 'afb1808a8a81339a1801d337259c19c77ed957583b4ae06579b38ea6a8f68ed1', schema: DNS_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'dns-observation-rows-v1-output-v5' },
88
+ { id: 'dns-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/dns-observation-rows-v1-output.json', bytes: 988, sha256: '4c9a49b96181d2907eb95319d15975758823994fe029c051486f1cb6869221a0', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
89
+ { id: 'certificate-observation-rows-v1', path: 'test/fixtures/external-observation-interchange/certificate-observation-rows-v1.json', bytes: 515, sha256: '68363a5fe074b08a386dbc95a0993a2933ebcf05ba2f96148df632842d1c5869', schema: CERTIFICATE_OBSERVATION_ROWS_SCHEMA, version: 1, role: 'input', expectation: 'normalises_to_current_output', expectedOutputFixtureId: 'certificate-observation-rows-v1-output-v5' },
90
+ { id: 'certificate-observation-rows-v1-output', path: 'test/fixtures/external-observation-interchange/certificate-observation-rows-v1-output.json', bytes: 1_244, sha256: '6c484601b872f10bfdb7d9519d4b216f14ef277e7a429e4cc131154ad0e07652', schema: EXTERNAL_FINDINGS_SCHEMA, version: 4, role: 'historical', expectation: 'accepted_exact', expectedOutputFixtureId: null },
91
+ { id: 'external-finding-rows-v1-output-v5', path: 'test/fixtures/external-observation-interchange/external-finding-rows-v1-output-v5.json', bytes: 442, sha256: '9996be2b9d832fca9aa523a1a424148ef762483ab3923d35ca73e83cd16a8195', schema: EXTERNAL_FINDINGS_SCHEMA, version: 5, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
92
+ { id: 'domain-observation-rows-v1-output-v5', path: 'test/fixtures/external-observation-interchange/domain-observation-rows-v1-output-v5.json', bytes: 771, sha256: 'd375353576b5990e835d53c14bf7fa5adb5205191d3969478a69ec68b5e1291b', schema: EXTERNAL_FINDINGS_SCHEMA, version: 5, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
93
+ { id: 'dns-observation-rows-v1-output-v5', path: 'test/fixtures/external-observation-interchange/dns-observation-rows-v1-output-v5.json', bytes: 732, sha256: '532ff6ac9e9255f14105eb0a0286434fadb94f6d7397f0fb96007ebb6b7d1522', schema: EXTERNAL_FINDINGS_SCHEMA, version: 5, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
94
+ { id: 'certificate-observation-rows-v1-output-v5', path: 'test/fixtures/external-observation-interchange/certificate-observation-rows-v1-output-v5.json', bytes: 988, sha256: '09904df103f83f64616556e653fa14c6f2f1dc392b1f75a85fd3099680840e37', schema: EXTERNAL_FINDINGS_SCHEMA, version: 5, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
95
+ { id: 'external-findings-v5', path: 'test/fixtures/external-observation-interchange/external-findings-v5.json', bytes: 3871, sha256: 'debd062c2d951d22c06c29bb076529fa69a2769b8bae7269ce3884550fb55ba3', schema: EXTERNAL_FINDINGS_SCHEMA, version: 5, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
96
+ { id: 'infrastructure-comparison-input-v1', path: 'test/fixtures/infrastructure-observations/infrastructure-comparison-input-v1.json', bytes: 6284, sha256: 'ef0022faf29586774bd1b2de1f95eb3839d382e424c7b3d446e4dc1058437104', schema: INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, version: 1, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
97
+ { id: 'infrastructure-observation-v1', path: 'test/fixtures/infrastructure-observations/infrastructure-observation-v1.json', bytes: 3430, sha256: '1b76e43e5d5c889654faf12114217994c5cbd3a378685e7f9b9d18ebdde801f7', schema: INFRASTRUCTURE_OBSERVATION_SCHEMA, version: 1, role: 'current', expectation: 'accepted_exact', expectedOutputFixtureId: null },
76
98
  ].map((fixture) => Object.freeze({
77
99
  ...fixture,
78
100
  contentDigestSha256: null,
@@ -84,6 +106,7 @@ function externalFixtureIds(schema, version) {
84
106
  .map((fixture) => fixture.id));
85
107
  }
86
108
  const EXTERNAL_INTERCHANGE_CONTRACTS = Object.freeze([
109
+ ...[['export.infrastructure-observation', INFRASTRUCTURE_OBSERVATION_SCHEMA, true, MAX_INFRASTRUCTURE_OBSERVATION_BYTES], ['cli.infrastructure-comparison-input', INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, false, MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES]].map(([compatibilityId, schema, emitted, byteBudget]) => Object.freeze({ compatibilityId, schema, version: 1, role: 'document', lifecycle: 'current', readable: true, emitted, exactKeys: true, extensionPolicy: 'reject', futureVersionBehaviour: 'reject', migrationTarget: null, canonicalisation: null, byteBudget, fixtureIds: externalFixtureIds(schema, 1) })),
87
110
  ...SUPPORTED_EXTERNAL_FINDINGS_VERSIONS.map((version) => Object.freeze({
88
111
  compatibilityId: EXTERNAL_FINDINGS_COMPATIBILITY.id,
89
112
  schema: EXTERNAL_FINDINGS_SCHEMA,
@@ -95,7 +118,7 @@ const EXTERNAL_INTERCHANGE_CONTRACTS = Object.freeze([
95
118
  exactKeys: true,
96
119
  extensionPolicy: 'reject',
97
120
  futureVersionBehaviour: 'reject',
98
- migrationTarget: version === EXTERNAL_FINDINGS_VERSION ? null : { schema: EXTERNAL_FINDINGS_SCHEMA, version: EXTERNAL_FINDINGS_VERSION },
121
+ migrationTarget: null,
99
122
  canonicalisation: null,
100
123
  byteBudget: MAX_EXTERNAL_FINDINGS_IMPORT_BYTES,
101
124
  fixtureIds: externalFixtureIds(EXTERNAL_FINDINGS_SCHEMA, version),
@@ -151,18 +174,38 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLif
151
174
  metadata: {
152
175
  enforcement: 'declarative_only',
153
176
  shapes: [
154
- externalShape('external-interchange.findings.v4', EXTERNAL_FINDINGS_SCHEMA, [...SUPPORTED_EXTERNAL_FINDINGS_VERSIONS], ['schema', 'schemaVersion', 'source', 'findings'], 'preserve_document'),
177
+ externalShape('external-interchange.findings.v4', EXTERNAL_FINDINGS_SCHEMA, [4], ['schema', 'schemaVersion', 'source', 'findings'], 'preserve_document'),
178
+ externalShape('external-interchange.findings.v5', EXTERNAL_FINDINGS_SCHEMA, [5], ['schema', 'schemaVersion', 'source', 'findings'], 'preserve_document'),
179
+ externalShape('external-interchange.infrastructure.v1', INFRASTRUCTURE_OBSERVATION_SCHEMA, [1], ['schema', 'version', 'id', 'target', 'observedAt', 'mode', 'scope', 'coverage', 'sources', 'dns', 'certificates', 'roles', 'limitations'], 'preserve_document'),
180
+ externalShape('external-interchange.infrastructure-comparison.v1', INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, [1], ['schema', 'version', 'earlier', 'later'], 'preserve_document'),
155
181
  externalShape('external-interchange.finding-rows.v1', EXTERNAL_FINDING_ROWS_SCHEMA, [1], ['schema', 'schemaVersion', 'source', 'rows'], 'input_to_current'),
156
182
  externalShape('external-interchange.domain-rows.v1', DOMAIN_OBSERVATION_ROWS_SCHEMA, [1], ['schema', 'schemaVersion', 'source', 'observations'], 'input_to_current'),
157
183
  externalShape('external-interchange.dns-rows.v1', DNS_OBSERVATION_ROWS_SCHEMA, [1], ['schema', 'schemaVersion', 'source', 'observations'], 'input_to_current'),
158
184
  externalShape('external-interchange.certificate-rows.v1', CERTIFICATE_OBSERVATION_ROWS_SCHEMA, [1], ['schema', 'schemaVersion', 'source', 'observations'], 'input_to_current'),
159
185
  ],
160
- boundProfiles: [{ id: 'external-interchange.document.bounds', bounds: [
186
+ boundProfiles: [{ id: 'external-interchange.infrastructure.bounds', bounds: [
187
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_INFRASTRUCTURE_OBSERVATION_BYTES, handling: 'reject' },
188
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_INFRASTRUCTURE_OBSERVATION_BYTES, handling: 'reject' },
189
+ { id: 'hostnames', path: 'scope.hostnames', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: MAX_INFRASTRUCTURE_HOSTS, handling: 'reject' },
190
+ { id: 'dns-rows', path: 'dns', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: MAX_INFRASTRUCTURE_DNS_ROWS, handling: 'reject' },
191
+ { id: 'certificate-rows', path: 'certificates', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: MAX_INFRASTRUCTURE_CERTIFICATES, handling: 'reject' },
192
+ { id: 'role-rows', path: 'roles', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: MAX_INFRASTRUCTURE_ROLES, handling: 'reject' },
193
+ { id: 'sources', path: 'sources', phase: 'pre_accumulation', unit: 'items', minimum: 1, maximum: MAX_INFRASTRUCTURE_SOURCES, handling: 'reject' },
194
+ { id: 'dns-types', path: 'scope.dnsTypes', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: INFRASTRUCTURE_DNS_TYPES.length, handling: 'reject' },
195
+ { id: 'limitations', path: 'limitations', phase: 'pre_accumulation', unit: 'items', minimum: 0, maximum: MAX_INFRASTRUCTURE_LIMITATIONS, handling: 'reject' },
196
+ ] }, { id: 'external-interchange.infrastructure-comparison.bounds', bounds: [
197
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES, handling: 'reject' },
198
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES, handling: 'reject' },
199
+ ] }, { id: 'external-interchange.document.bounds', bounds: [
161
200
  { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_EXTERNAL_FINDINGS_IMPORT_BYTES, handling: 'reject' },
162
201
  { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_EXTERNAL_FINDINGS_IMPORT_BYTES, handling: 'reject' },
163
202
  { id: 'rows', path: '$', phase: 'pre_accumulation', unit: 'items', minimum: 1, maximum: MAX_CONVERSION_INPUT_ROWS, handling: 'reject' },
164
203
  ] }],
165
204
  hooks: [
205
+ { id: 'external-interchange.infrastructure.read', role: 'normaliser', runtime: 'shared', module: 'packages/investigation/infrastructure-observation.mts', exportName: 'readInfrastructureObservation' },
206
+ { id: 'external-interchange.infrastructure.serialise', role: 'serialiser', runtime: 'shared', module: 'packages/investigation/infrastructure-observation.mts', exportName: 'serialiseInfrastructureObservation' },
207
+ { id: 'external-interchange.infrastructure.convert', role: 'normaliser', runtime: 'shared', module: 'packages/interchange/external-findings-converters.mts', exportName: 'convertInfrastructureObservation' },
208
+ { id: 'external-interchange.infrastructure.compare', role: 'normaliser', runtime: 'cli', module: 'cli/offline-evidence-review.mts', exportName: 'buildOfflineEvidenceReview' },
166
209
  { id: 'external-interchange.findings.normalise', role: 'normaliser', runtime: 'shared', module: 'packages/interchange/external-findings-import.mts', exportName: 'parseExternalFindingsDocument' },
167
210
  { id: 'external-interchange.findings.serialise', role: 'serialiser', runtime: 'shared', module: 'packages/interchange/external-findings-import.mts', exportName: 'serializeExternalFindingsDocument' },
168
211
  { id: 'external-interchange.finding-rows.convert', role: 'normaliser', runtime: 'shared', module: 'packages/interchange/external-findings-converters.mts', exportName: 'convertExternalFindingRows' },
@@ -170,10 +213,15 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLif
170
213
  { id: 'external-interchange.findings.merge', role: 'merger', runtime: 'shared', module: 'packages/interchange/external-findings-import.mts', exportName: 'mergeExternalFindingsIntoCases' },
171
214
  ],
172
215
  serialisationProfiles: [{
173
- id: 'external-interchange.findings.json.v4', schema: EXTERNAL_FINDINGS_SCHEMA,
174
- versions: [...SUPPORTED_EXTERNAL_FINDINGS_VERSIONS], mediaType: 'application/json', encoding: 'utf-8', bom: false,
175
- indentSpaces: 2, terminalLf: true, propertyOrder: 'normalised_fixed', canonicalisation: null, integrity: 'none',
216
+ id: 'external-interchange.findings.json.v5', schema: EXTERNAL_FINDINGS_SCHEMA,
217
+ versions: [5], mediaType: 'application/json', encoding: 'utf-8', bom: false,
218
+ indentSpaces: 0, terminalLf: true, propertyOrder: 'normalised_fixed', canonicalisation: null, integrity: 'none',
176
219
  serializerHookId: 'external-interchange.findings.serialise', verifierHookIds: [],
220
+ }, {
221
+ id: 'external-interchange.infrastructure.json.v1', schema: INFRASTRUCTURE_OBSERVATION_SCHEMA,
222
+ versions: [1], mediaType: 'application/json', encoding: 'utf-8', bom: false,
223
+ indentSpaces: 0, terminalLf: true, propertyOrder: 'normalised_fixed', canonicalisation: null, integrity: 'none',
224
+ serializerHookId: 'external-interchange.infrastructure.serialise', verifierHookIds: [],
177
225
  }],
178
226
  privacyProfiles: [
179
227
  { id: 'external-interchange.privacy.transient', classification: 'analyst_authored_sensitive', projection: 'browser_import', includedCategories: ['source-attribution', 'bounded-observations', 'completeness', 'limitations'], excludedCategories: ['raw-upstream-payloads', 'expanded-contacts', 'credentials', 'cookies', 'query-bearing-urls'], notePolicy: 'discarded', retention: 'transient_report', network: 'none', sharingReview: 'required' },
@@ -181,22 +229,42 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLif
181
229
  ],
182
230
  expiryProfiles: [{ id: 'external-interchange.expiry.not-applicable', field: null, anchor: null, handling: 'not_applicable', phase: 'not_applicable', maximumLifetimeDays: null }],
183
231
  consumerEdges: [
232
+ {
233
+ id: 'external-interchange.consumer.infrastructure-read', plane: 'shared', operation: 'read-and-export-exact-infrastructure-snapshot',
234
+ acceptedContracts: [{ schema: INFRASTRUCTURE_OBSERVATION_SCHEMA, versions: [1], mode: 'direct' }], emittedContract: { schema: INFRASTRUCTURE_OBSERVATION_SCHEMA, version: 1 },
235
+ shapeIds: ['external-interchange.infrastructure.v1'], boundProfileIds: ['external-interchange.infrastructure.bounds'], hookIds: ['external-interchange.infrastructure.read', 'external-interchange.infrastructure.serialise'], serialisationProfileId: 'external-interchange.infrastructure.json.v1', privacyProfileId: 'external-interchange.privacy.transient', retentionEffect: 'transient_report', ...externalConsumerCommon,
236
+ },
237
+ {
238
+ id: 'external-interchange.consumer.infrastructure-convert', plane: 'shared', operation: 'convert-exact-snapshot-to-one-finding',
239
+ acceptedContracts: [{ schema: INFRASTRUCTURE_OBSERVATION_SCHEMA, versions: [1], mode: 'direct' }], emittedContract: { schema: EXTERNAL_FINDINGS_SCHEMA, version: 5 },
240
+ shapeIds: ['external-interchange.infrastructure.v1', 'external-interchange.findings.v5'], boundProfileIds: ['external-interchange.infrastructure.bounds', 'external-interchange.document.bounds'], hookIds: ['external-interchange.infrastructure.convert', 'external-interchange.findings.serialise'], serialisationProfileId: 'external-interchange.findings.json.v5', privacyProfileId: 'external-interchange.privacy.transient', retentionEffect: 'transient_report', ...externalConsumerCommon,
241
+ },
242
+ {
243
+ id: 'external-interchange.consumer.infrastructure-compare', plane: 'cli', operation: 'offline-infrastructure-comparison',
244
+ acceptedContracts: [{ schema: INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, versions: [1], mode: 'direct' }], emittedContract: null,
245
+ shapeIds: ['external-interchange.infrastructure-comparison.v1'], boundProfileIds: ['external-interchange.infrastructure-comparison.bounds'], hookIds: ['external-interchange.infrastructure.compare'], serialisationProfileId: null, privacyProfileId: 'external-interchange.privacy.transient', retentionEffect: 'transient_report', ...externalConsumerCommon,
246
+ },
247
+ {
248
+ id: 'external-interchange.consumer.findings-v4-read', plane: 'shared', operation: 'preserve-historical-findings',
249
+ acceptedContracts: [{ schema: EXTERNAL_FINDINGS_SCHEMA, versions: [4], mode: 'direct' }], emittedContract: null,
250
+ shapeIds: ['external-interchange.findings.v4'], boundProfileIds: ['external-interchange.document.bounds'], hookIds: ['external-interchange.findings.normalise'], serialisationProfileId: null, privacyProfileId: 'external-interchange.privacy.transient', retentionEffect: 'transient_report', ...externalConsumerCommon,
251
+ },
184
252
  {
185
253
  id: 'external-interchange.consumer.findings-normalise', plane: 'shared', operation: 'normalise-strict-findings',
186
- acceptedContracts: [{ schema: EXTERNAL_FINDINGS_SCHEMA, versions: [...SUPPORTED_EXTERNAL_FINDINGS_VERSIONS], mode: 'direct' }],
254
+ acceptedContracts: [{ schema: EXTERNAL_FINDINGS_SCHEMA, versions: [5], mode: 'direct' }],
187
255
  emittedContract: { schema: EXTERNAL_FINDINGS_SCHEMA, version: EXTERNAL_FINDINGS_VERSION },
188
- shapeIds: ['external-interchange.findings.v4'], boundProfileIds: ['external-interchange.document.bounds'],
256
+ shapeIds: ['external-interchange.findings.v5'], boundProfileIds: ['external-interchange.document.bounds'],
189
257
  hookIds: ['external-interchange.findings.normalise', 'external-interchange.findings.serialise'],
190
- serialisationProfileId: 'external-interchange.findings.json.v4', privacyProfileId: 'external-interchange.privacy.transient',
258
+ serialisationProfileId: 'external-interchange.findings.json.v5', privacyProfileId: 'external-interchange.privacy.transient',
191
259
  retentionEffect: 'transient_report', ...externalConsumerCommon,
192
260
  },
193
261
  {
194
262
  id: 'external-interchange.consumer.finding-rows', plane: 'shared', operation: 'convert-finding-rows',
195
263
  acceptedContracts: [{ schema: EXTERNAL_FINDING_ROWS_SCHEMA, versions: [1], mode: 'direct' }],
196
264
  emittedContract: { schema: EXTERNAL_FINDINGS_SCHEMA, version: EXTERNAL_FINDINGS_VERSION },
197
- shapeIds: ['external-interchange.finding-rows.v1', 'external-interchange.findings.v4'], boundProfileIds: ['external-interchange.document.bounds'],
265
+ shapeIds: ['external-interchange.finding-rows.v1', 'external-interchange.findings.v5'], boundProfileIds: ['external-interchange.document.bounds'],
198
266
  hookIds: ['external-interchange.finding-rows.convert', 'external-interchange.findings.serialise'],
199
- serialisationProfileId: 'external-interchange.findings.json.v4', privacyProfileId: 'external-interchange.privacy.transient',
267
+ serialisationProfileId: 'external-interchange.findings.json.v5', privacyProfileId: 'external-interchange.privacy.transient',
200
268
  retentionEffect: 'transient_report', ...externalConsumerCommon,
201
269
  },
202
270
  {
@@ -207,15 +275,15 @@ export const EXTERNAL_OBSERVATION_INTERCHANGE_LIFECYCLE_FAMILY = defineSchemaLif
207
275
  { schema: CERTIFICATE_OBSERVATION_ROWS_SCHEMA, versions: [1], mode: 'direct' },
208
276
  ],
209
277
  emittedContract: { schema: EXTERNAL_FINDINGS_SCHEMA, version: EXTERNAL_FINDINGS_VERSION },
210
- shapeIds: ['external-interchange.domain-rows.v1', 'external-interchange.dns-rows.v1', 'external-interchange.certificate-rows.v1', 'external-interchange.findings.v4'],
278
+ shapeIds: ['external-interchange.domain-rows.v1', 'external-interchange.dns-rows.v1', 'external-interchange.certificate-rows.v1', 'external-interchange.findings.v5'],
211
279
  boundProfileIds: ['external-interchange.document.bounds'], hookIds: ['external-interchange.observation-rows.convert', 'external-interchange.findings.serialise'],
212
- serialisationProfileId: 'external-interchange.findings.json.v4', privacyProfileId: 'external-interchange.privacy.transient',
280
+ serialisationProfileId: 'external-interchange.findings.json.v5', privacyProfileId: 'external-interchange.privacy.transient',
213
281
  retentionEffect: 'transient_report', ...externalConsumerCommon,
214
282
  },
215
283
  {
216
284
  id: 'external-interchange.consumer.case-merge', plane: 'shared', operation: 'non-destructive-case-merge',
217
- acceptedContracts: [{ schema: EXTERNAL_FINDINGS_SCHEMA, versions: [EXTERNAL_FINDINGS_VERSION], mode: 'direct' }], emittedContract: null,
218
- shapeIds: ['external-interchange.findings.v4'], boundProfileIds: ['external-interchange.document.bounds'],
285
+ acceptedContracts: [{ schema: EXTERNAL_FINDINGS_SCHEMA, versions: [...SUPPORTED_EXTERNAL_FINDINGS_VERSIONS], mode: 'direct' }], emittedContract: null,
286
+ shapeIds: ['external-interchange.findings.v4', 'external-interchange.findings.v5'], boundProfileIds: ['external-interchange.document.bounds'],
219
287
  hookIds: ['external-interchange.findings.merge'], serialisationProfileId: null,
220
288
  privacyProfileId: 'external-interchange.privacy.case-merge', retentionEffect: 'browser_indexeddb', ...externalConsumerCommon,
221
289
  },
@@ -1,4 +1,5 @@
1
1
  import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
2
+ import { LEGACY_REGISTRY_INSIGHTS_VERSION, REGISTRY_INSIGHTS_VERSION } from "./registry-insights.mjs";
2
3
  export const LOOKUP_EVIDENCE_SCHEMA = 'whoisleuth.lookup-evidence';
3
4
  export const V1_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION = 26;
4
5
  export const PUBLISHED_V2_LOOKUP_EVIDENCE_SCHEMA_VERSION = 27;
@@ -6,13 +7,23 @@ export const REGISTRAR_STANDING_LOOKUP_EVIDENCE_SCHEMA_VERSION = 28;
6
7
  export const LATEST_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION = REGISTRAR_STANDING_LOOKUP_EVIDENCE_SCHEMA_VERSION;
7
8
  export const HOSTNAME_SCOPED_LOOKUP_EVIDENCE_SCHEMA_VERSION = 29;
8
9
  export const PRIVACY_MINIMIZED_LOOKUP_EVIDENCE_SCHEMA_VERSION = PUBLISHED_V2_LOOKUP_EVIDENCE_SCHEMA_VERSION;
9
- export const LOOKUP_EVIDENCE_SCHEMA_VERSION = HOSTNAME_SCOPED_LOOKUP_EVIDENCE_SCHEMA_VERSION;
10
+ export const FORM_ATTRIBUTED_LOOKUP_EVIDENCE_SCHEMA_VERSION = 30;
11
+ export const AUTHORITY_AWARE_LOOKUP_EVIDENCE_SCHEMA_VERSION = 31;
12
+ export const LOOKUP_EVIDENCE_SCHEMA_VERSION = AUTHORITY_AWARE_LOOKUP_EVIDENCE_SCHEMA_VERSION;
10
13
  export const SUPPORTED_LOOKUP_EVIDENCE_SCHEMA_VERSIONS = Object.freeze([
11
14
  V1_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION,
12
15
  PUBLISHED_V2_LOOKUP_EVIDENCE_SCHEMA_VERSION,
13
16
  LATEST_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION,
17
+ HOSTNAME_SCOPED_LOOKUP_EVIDENCE_SCHEMA_VERSION,
18
+ FORM_ATTRIBUTED_LOOKUP_EVIDENCE_SCHEMA_VERSION,
14
19
  LOOKUP_EVIDENCE_SCHEMA_VERSION,
15
20
  ]);
21
+ export function lookupEvidenceRegistryInsightsVersion(schemaVersion) {
22
+ if (!SUPPORTED_LOOKUP_EVIDENCE_SCHEMA_VERSIONS.includes(schemaVersion))
23
+ throw new TypeError('Unsupported Lookup evidence version.');
24
+ return schemaVersion >= AUTHORITY_AWARE_LOOKUP_EVIDENCE_SCHEMA_VERSION
25
+ ? REGISTRY_INSIGHTS_VERSION : LEGACY_REGISTRY_INSIGHTS_VERSION;
26
+ }
16
27
  export const LOOKUP_EVIDENCE_PORTABLE_MAX_BYTES = 5 * 1024 * 1024;
17
28
  export const LOOKUP_EVIDENCE_PORTABLE_MAX_ENTRIES = 20_000;
18
29
  export const LOOKUP_EVIDENCE_PORTABLE_MAX_DEPTH = 24;
@@ -31,5 +42,5 @@ export const LOOKUP_EVIDENCE_COMPATIBILITY = defineSchemaCompatibility({
31
42
  writeSemantics: 'read_only',
32
43
  byteBudget: LOOKUP_EVIDENCE_PORTABLE_MAX_BYTES,
33
44
  owner: 'packages/contracts/lookup-evidence.mts',
34
- note: 'Exact published versions 26, 27 and 28 remain replayable with their original collection scope. Version 29 identifies the hostname used for supporting DNS, TLS and web observations; registration authority remains separate.',
45
+ note: 'Versions 26–30 retain their registry insight derivation. Version 30 adds attributed form destinations. Version 31 retains WHOIS registration classification; collection completion is not positive registration evidence.',
35
46
  });
@@ -1,5 +1,6 @@
1
1
  export const MESSAGE_INTAKE_SCHEMA = 'whoisleuth.message-intake';
2
- export const MESSAGE_INTAKE_VERSION = 1;
2
+ export const MESSAGE_INTAKE_VERSION = 2;
3
+ export const MESSAGE_INTAKE_LEGACY_VERSION = 1;
3
4
  export const MESSAGE_INTAKE_KINDS = ['text', 'email', 'calendar', 'qr', 'pdf', 'docx', 'har', 'identity'];
4
5
  export const MESSAGE_INTAKE_INPUTS = {
5
6
  text: { label: 'Pasted text or links', accept: '.txt,text/plain', binary: false },
@@ -28,4 +29,9 @@ export const MAX_INTAKE_LINKS = 512;
28
29
  export const MAX_INTAKE_URL_LENGTH = 8_192;
29
30
  export const MAX_EMBEDDED_LINK_DEPTH = 8;
30
31
  export const MAX_AUTH_SCOPES = 64;
32
+ export const MAX_INTAKE_INDICATORS = 512;
33
+ export const MAX_INTAKE_INDICATOR_CANDIDATES = 4_096;
34
+ export const INTAKE_DISTRIBUTION_CHANNELS = ['email', 'sms', 'messaging', 'social', 'advertisement', 'website', 'document', 'other', 'unknown'];
35
+ export const INTAKE_TEXT_BASES = ['supplied_text', 'manual_transcription', 'ocr_text', 'unknown'];
36
+ export const INTAKE_PHONE_ROLES = ['advertised_support_contact', 'sender_or_caller_id_claim', 'unknown'];
31
37
  export const MESSAGE_ACTION_HINTS = ['clipboard_instruction', 'shell_instruction', 'verification_prompt', 'device_code_instruction', 'consent_instruction'];
@@ -0,0 +1,6 @@
1
+ /** Version 1 is retained for exact historical evidence verification. */
2
+ export const LEGACY_REGISTRY_INSIGHTS_VERSION = 1;
3
+ export const REGISTRY_INSIGHTS_VERSION = 2;
4
+ export function isSupportedRegistryInsightsVersion(value) {
5
+ return value === LEGACY_REGISTRY_INSIGHTS_VERSION || value === REGISTRY_INSIGHTS_VERSION;
6
+ }