@slicedearth/whoisleuth-cli 1.45.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/DISCLOSURE +7 -3
  2. package/NOTICE +5 -4
  3. package/README.md +74 -124
  4. package/SECURITY.md +6 -3
  5. package/bin/whoisleuth.mjs +7 -4
  6. package/cli/archive-inspect.mjs +28 -10
  7. package/cli/arguments.mjs +450 -173
  8. package/cli/artifact-structure.mjs +2507 -0
  9. package/cli/artifact-verify.mjs +286 -64
  10. package/cli/bounded-json.mjs +1 -0
  11. package/cli/bulk-checkpoint.mjs +35 -23
  12. package/cli/bulk-command-runner.mjs +2 -5
  13. package/cli/bulk.mjs +9 -2
  14. package/cli/case-pack.mjs +435 -46
  15. package/cli/ci-report.mjs +6 -1
  16. package/cli/collection-preflight.mjs +2 -2
  17. package/cli/command-catalogue.mjs +12 -3
  18. package/cli/command-reference.mjs +948 -106
  19. package/cli/completion.mjs +504 -218
  20. package/cli/config-profile.mjs +13 -3
  21. package/cli/ct-event-intake.mjs +32 -2
  22. package/cli/discover.mjs +7 -1
  23. package/cli/discovery-observation-snapshot.mjs +24 -28
  24. package/cli/discovery-scan-command-runner.mjs +13 -14
  25. package/cli/discovery-scan.mjs +17 -3
  26. package/cli/discovery-snapshot.mjs +15 -7
  27. package/cli/discovery-workflow.mjs +2 -3
  28. package/cli/doctor.mjs +2 -2
  29. package/cli/domain-control-monitor.mjs +215 -36
  30. package/cli/domain-control-observations.mjs +34 -17
  31. package/cli/errors.mjs +8 -3
  32. package/cli/evidence-command-runner.mjs +17 -11
  33. package/cli/evidence-signing.mjs +68 -50
  34. package/cli/export-evidence.mjs +11 -4
  35. package/cli/external-observation-mapping.mjs +3 -3
  36. package/cli/formatters/html.mjs +3 -3
  37. package/cli/formatters/json.mjs +20 -32
  38. package/cli/formatters/terminal.mjs +586 -22
  39. package/cli/input.mjs +62 -0
  40. package/cli/interactive-launcher.mjs +226 -0
  41. package/cli/interchange-report.mjs +226 -0
  42. package/cli/investigation-manifest.mjs +16 -17
  43. package/cli/investigation-plan.mjs +167 -16
  44. package/cli/investigation-run.mjs +80 -24
  45. package/cli/local-mmdb-review.mjs +45 -14
  46. package/cli/lookalike-calibration.mjs +3 -1
  47. package/cli/lookup-brief.mjs +13 -8
  48. package/cli/lookup-browser.mjs +844 -0
  49. package/cli/lookup-command-runner.mjs +96 -33
  50. package/cli/lookup-diff.mjs +11 -2
  51. package/cli/lookup-plan.mjs +1 -0
  52. package/cli/mail-review.mjs +206 -94
  53. package/cli/manual.mjs +5 -5
  54. package/cli/network-command-runner.mjs +78 -0
  55. package/cli/offline-evidence-review.mjs +45 -14
  56. package/cli/open-asset-model-bridge.mjs +1 -1
  57. package/cli/output-file.mjs +70 -14
  58. package/cli/page-compare.mjs +18 -10
  59. package/cli/progress-events.mjs +2 -1
  60. package/cli/progress.mjs +3 -3
  61. package/cli/registry-cohort.mjs +343 -61
  62. package/cli/registry-doctor.mjs +67 -28
  63. package/cli/registry-support.mjs +2 -1
  64. package/cli/retained-artifact-diff.mjs +375 -0
  65. package/cli/risk-calibration.mjs +89 -40
  66. package/cli/runner.mjs +322 -246
  67. package/cli/saved-lookup.mjs +439 -49
  68. package/cli/sharing-review.mjs +18 -7
  69. package/cli/source-reliability.mjs +75 -36
  70. package/cli/terminal-presentation.mjs +43 -18
  71. package/docs/cli-reference.md +125 -1334
  72. package/docs/cli.md +87 -236
  73. package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -0
  74. package/frontend/src/lib/analysis/domain-control-records.js +3 -0
  75. package/lib/application-version.mjs +3 -0
  76. package/lib/availability.mjs +72 -52
  77. package/lib/bounded-contract-normalizers.mjs +2 -4
  78. package/lib/bounded-file.mjs +91 -4
  79. package/lib/bounded-json.mjs +287 -0
  80. package/lib/browser-library-profile.mjs +1 -1
  81. package/lib/certificate-extension-profile.mjs +2 -1
  82. package/lib/classify.mjs +56 -5
  83. package/lib/client-behavior-profile.mjs +1 -1
  84. package/lib/credential-surface-profile.mjs +1 -1
  85. package/lib/cryptographic-assurance.mjs +173 -0
  86. package/lib/ct-query.mjs +3 -3
  87. package/lib/ct-search.mjs +120 -64
  88. package/lib/distributed-operation-budget.mjs +4 -2
  89. package/lib/dns-delegation-health.mjs +9 -6
  90. package/lib/dns-intelligence.mjs +4 -3
  91. package/lib/dns-resolver-selection.mjs +6 -4
  92. package/lib/dnssec-chain-validation.mjs +1052 -0
  93. package/lib/dnssec-evidence-validation.mjs +23 -10
  94. package/lib/domain-assurance.mjs +12 -12
  95. package/lib/domain-change-packet.mjs +7 -5
  96. package/lib/domain-control-flight-recorder.mjs +442 -78
  97. package/lib/domain-control-manifest.mjs +314 -171
  98. package/lib/domain-posture-analysis.mjs +5 -2
  99. package/lib/domain-posture.mjs +7 -4
  100. package/lib/encrypted-dns-contract.mjs +2 -3
  101. package/lib/evidence-export.mjs +1243 -88
  102. package/lib/evidence-report-markdown.mjs +3 -0
  103. package/lib/evidence-report.mjs +5 -0
  104. package/lib/external-intelligence-risk.mjs +26 -10
  105. package/lib/favicon.mjs +8 -5
  106. package/lib/feature-policy.mjs +1 -1
  107. package/lib/homepage-metadata-contract.mjs +290 -0
  108. package/lib/hostname.mjs +1 -1
  109. package/lib/html-signals.mjs +89 -5
  110. package/lib/http-evidence-bounds.mjs +3 -0
  111. package/lib/http-intelligence.mjs +332 -6
  112. package/lib/interchange-fidelity-registry.mjs +124 -0
  113. package/lib/local-geoip-evidence.mjs +2 -1
  114. package/lib/lookup-cache.mjs +6 -4
  115. package/lib/lookup-network-evidence-bounds.mjs +9 -0
  116. package/lib/lookup.mjs +27 -4
  117. package/lib/observed-network-context.mjs +1 -1
  118. package/lib/outbound-identity.mjs +2 -3
  119. package/lib/page-fingerprints.mjs +2 -2
  120. package/lib/page-role-profile.mjs +1 -1
  121. package/lib/perceptual-hash.mjs +12 -4
  122. package/lib/portable-generator.mjs +29 -0
  123. package/lib/provider-policy-admission.mjs +11 -4
  124. package/lib/rdap-bootstrap.mjs +76 -16
  125. package/lib/rdap-client.mjs +20 -12
  126. package/lib/rdap-normalization.mjs +324 -80
  127. package/lib/rdap-registrar.mjs +5 -1
  128. package/lib/rdap-transport.mjs +2 -2
  129. package/lib/rdap-validation.mjs +65 -14
  130. package/lib/rdap.mjs +2 -2
  131. package/lib/registrable-domain.mjs +36 -0
  132. package/lib/registry-capabilities.mjs +3 -0
  133. package/lib/registry-capability-catalogue.mjs +12 -2
  134. package/lib/registry-comparison.mjs +28 -11
  135. package/lib/registry-insights.mjs +7 -0
  136. package/lib/response-policy.mjs +27 -4
  137. package/lib/risk-calibration-summary.mjs +340 -0
  138. package/lib/risk-scoring.mjs +4 -3
  139. package/lib/rpki-evidence.mjs +9 -8
  140. package/lib/safe-fetch.mjs +121 -50
  141. package/lib/security-txt.mjs +27 -4
  142. package/lib/service-binding-dns.mjs +6 -1
  143. package/lib/smtp-transport-review.mjs +787 -0
  144. package/lib/sslbl-certificates.generated.mjs +136 -134
  145. package/lib/sslbl-intelligence.mjs +6 -8
  146. package/lib/static-html-analysis.mjs +301 -4
  147. package/lib/structured-data-identity.mjs +51 -19
  148. package/lib/threat-intelligence-fixtures.mjs +2 -4
  149. package/lib/threat-intelligence-manifests.mjs +2 -7
  150. package/lib/threat-intelligence-runtime.mjs +5 -15
  151. package/lib/threat-intelligence-types.mjs +21 -1
  152. package/lib/threatfox-intelligence.mjs +7 -3
  153. package/lib/tls-intelligence.mjs +15 -9
  154. package/lib/tlsa-evidence.mjs +37 -7
  155. package/lib/urlhaus-intelligence.mjs +4 -2
  156. package/lib/urlscan-intelligence.mjs +6 -5
  157. package/lib/website-security-posture.mjs +1 -1
  158. package/lib/website-technology.mjs +1 -1
  159. package/lib/whois-authority.mjs +43 -8
  160. package/lib/whois-chain.mjs +25 -3
  161. package/lib/whois-normalization.mjs +3 -1
  162. package/lib/whois-transport.mjs +12 -3
  163. package/lib/zone-intent-review.mjs +1 -1
  164. package/package.json +12 -4
  165. package/packages/cases/case-brand-profile-references.mjs +85 -0
  166. package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +43 -14
  167. package/packages/cases/case-investigation-branch-model.mjs +132 -0
  168. package/packages/cases/case-migration-model.mjs +476 -0
  169. package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
  170. package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -19
  171. package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
  172. package/packages/cases/case-record-model.mjs +9 -0
  173. package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +106 -33
  174. package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +143 -16
  175. package/packages/cases/case-response-model.mjs +1604 -0
  176. package/packages/cases/case-response-packet.mjs +1312 -0
  177. package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
  178. package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
  179. package/packages/cases/opaque-reference-id.mjs +9 -0
  180. package/packages/comparison/comparison-ledger-bulk.mjs +366 -0
  181. package/packages/comparison/comparison-ledger-contract.mjs +241 -0
  182. package/packages/comparison/comparison-ledger-json.mjs +80 -0
  183. package/packages/comparison/comparison-ledger-serialization.mjs +52 -0
  184. package/packages/comparison/favicon-similarity.mjs +78 -0
  185. package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
  186. package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
  187. package/packages/contracts/analyst-review-state-contract.mjs +61 -0
  188. package/packages/contracts/analyst-review-state.mjs +50 -0
  189. package/packages/contracts/case-portability.mjs +1310 -0
  190. package/packages/contracts/cli-command-catalogue.mjs +2 -0
  191. package/packages/contracts/cli-lookup.mjs +394 -0
  192. package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
  193. package/packages/contracts/domain-control-manifest.mjs +1027 -0
  194. package/packages/contracts/domain-control-monitor.mjs +310 -0
  195. package/packages/contracts/domain-control-review.mjs +828 -0
  196. package/packages/contracts/domain-name.mjs +4 -0
  197. package/packages/contracts/external-observation-interchange.mjs +224 -0
  198. package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
  199. package/packages/contracts/investigation-portability.mjs +331 -0
  200. package/packages/contracts/offline-comparison.mjs +160 -0
  201. package/packages/contracts/risk-calibration.mjs +1288 -0
  202. package/packages/contracts/schema-compatibility.mjs +71 -0
  203. package/packages/contracts/schema-lifecycle.mjs +1702 -0
  204. package/packages/contracts/sslbl-snapshot.mjs +17 -0
  205. package/packages/contracts/workspace-portability.mjs +1347 -0
  206. package/packages/evidence/artifact-integrity.mjs +65 -0
  207. package/packages/evidence/decision-fact.mjs +735 -0
  208. package/packages/evidence/domain-control-runtime.mjs +547 -0
  209. package/packages/evidence/domain-name.mjs +41 -0
  210. package/packages/evidence/observation.mjs +384 -0
  211. package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +86 -20
  212. package/packages/interchange/retained-text.mjs +8 -0
  213. package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +11 -8
  214. package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +28 -9
  215. package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +67 -18
  216. package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +10 -9
  217. package/packages/investigation/bulk-review-policy.mjs +1 -0
  218. package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +50 -18
  219. package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
  220. package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
  221. package/packages/investigation/lookup-claim-passport.mjs +232 -0
  222. package/packages/investigation/lookup-claim-readiness.mjs +287 -0
  223. package/packages/investigation/lookup-investigation-brief.mjs +172 -0
  224. package/packages/monitoring/analyst-review-state.mjs +680 -0
  225. package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +97 -50
  226. package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +15 -15
  227. package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +179 -39
  228. package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +12 -5
  229. package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
  230. package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
  231. package/packages/workspace/hostile-input.mjs +172 -0
  232. package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +51 -46
  233. package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +15 -15
  234. package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
  235. package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
  236. package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
  237. package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
  238. package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
  239. package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +18 -12
  240. package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
  241. package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +195 -84
  242. package/third-party-notices.txt +112 -5834
  243. package/frontend/src/lib/analysis/artifact-integrity.js +0 -21
  244. package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
  245. package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
  246. package/frontend/src/lib/analysis/case-migration-model.js +0 -253
  247. package/frontend/src/lib/analysis/case-record-model.js +0 -7
  248. package/frontend/src/lib/analysis/case-response-model.js +0 -669
  249. package/frontend/src/lib/analysis/case-response-packet.js +0 -573
  250. package/frontend/src/lib/analysis/lookup-decision-support.js +0 -487
  251. package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
  252. package/frontend/src/lib/analysis/lookup-presentation.js +0 -58
  253. package/frontend/src/lib/analysis/lookup-response.js +0 -2
  254. package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
  255. package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
  256. package/frontend/src/lib/analysis/scoring.js +0 -55
  257. package/frontend/src/lib/analysis/utils.js +0 -232
  258. package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
  259. package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
  260. package/frontend/src/lib/analysis/visualization-matrix.js +0 -88
  261. package/frontend/src/lib/analysis/visualization-models.js +0 -369
  262. package/frontend/src/lib/analysis/visualization-time-series.js +0 -144
  263. package/lib/bounded-json-response.mjs +0 -157
  264. package/lib/lookup-response-contract.mjs +0 -346
  265. package/lib/observation.mjs +0 -114
  266. /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
  267. /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
  268. /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
@@ -0,0 +1,1310 @@
1
+ import { MAX_DOMAIN_NAME_LENGTH } from "./domain-name.mjs";
2
+ import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
3
+ import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
4
+ export const CASE_CONTRACT_OWNER = 'packages/contracts/case-portability.mts';
5
+ export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
6
+ 'CASE_BROWSER_STORE_LIFECYCLE_SCHEMA',
7
+ 'CASE_EXPORT_LIFECYCLE_SCHEMA',
8
+ 'CASE_SCHEMA_VERSION',
9
+ 'CASE_BROWSER_SUPPORTED_VERSIONS',
10
+ 'CASE_IMPORT_VERSIONS',
11
+ 'CASE_REPORT_SCHEMA',
12
+ 'CASE_REPORT_SCHEMA_VERSION',
13
+ 'CASE_REPORT_OUTPUT_VERSIONS',
14
+ 'CASE_RESPONSE_PACKET_SCHEMA',
15
+ 'CASE_RESPONSE_PACKET_VERSION',
16
+ 'CASE_RESPONSE_PACKET_OUTPUT_VERSIONS',
17
+ 'SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS',
18
+ 'CASE_RESPONSE_REVIEW_INPUTS_SCHEMA',
19
+ 'CASE_RESPONSE_REVIEW_INPUTS_VERSION',
20
+ 'CLI_CASE_PACK_SCHEMA',
21
+ 'CLI_CASE_PACK_VERSION',
22
+ 'SUPPORTED_CLI_CASE_PACK_VERSIONS',
23
+ 'CLI_CASE_PACK_CASE_REPORT_EPOCHS',
24
+ 'WORKSPACE_ARCHIVE_SCHEMA',
25
+ 'PUBLIC_WORKSPACE_ARCHIVE_VERSION',
26
+ 'WORKSPACE_ARCHIVE_VERSION',
27
+ 'SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS',
28
+ 'PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS',
29
+ 'WORKSPACE_ARCHIVE_SECTION_IDS',
30
+ 'WORKSPACE_ARCHIVE_CASE_SECTION',
31
+ 'WORKSPACE_SETTINGS_SCHEMA',
32
+ 'WORKSPACE_SETTINGS_VERSION',
33
+ 'ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA',
34
+ 'ENCRYPTED_WORKSPACE_ARCHIVE_VERSION',
35
+ ]);
36
+ export const CASE_PORTABILITY_BOUND_CONSTANTS = Object.freeze([
37
+ 'MAX_CASES',
38
+ 'MAX_NOTES_PER_CASE',
39
+ 'MAX_NOTE_LENGTH',
40
+ 'MAX_TAGS_PER_CASE',
41
+ 'MAX_CASE_IMPORT_BYTES',
42
+ 'MAX_CASE_STORE_BYTES',
43
+ 'MAX_EVIDENCE_SNAPSHOTS_PER_CASE',
44
+ 'MAX_EVIDENCE_FACTORS',
45
+ 'MAX_EVIDENCE_NAMESERVERS',
46
+ 'MAX_EVIDENCE_MUTATIONS',
47
+ 'MAX_EVIDENCE_STRING_LENGTH',
48
+ 'MAX_EVIDENCE_TITLE_LENGTH',
49
+ 'MAX_EVIDENCE_DETAIL_LENGTH',
50
+ 'MAX_EVIDENCE_CHANGES',
51
+ 'MAX_CASE_BRAND_PROFILE_IDS',
52
+ 'MAX_CASE_BRAND_PROFILE_ID_CANDIDATES',
53
+ 'MAX_CASE_EVIDENCE_PINS',
54
+ 'MAX_CASE_CHECKPOINT_FACTS',
55
+ 'MAX_CASE_DECISIONS',
56
+ 'MAX_CASE_ACTIONS',
57
+ 'MAX_CASE_ACTION_EVENTS_PER_ACTION',
58
+ 'MAX_CASE_ACTION_EVENTS_PER_CASE',
59
+ 'MAX_CASE_ACTION_BYTES',
60
+ 'MAX_CASE_ACTION_HISTORY_BYTES_PER_CASE',
61
+ 'MAX_CASE_OBSERVED_EFFECT_REVIEWS',
62
+ 'MAX_CASE_CLOSURES',
63
+ 'MAX_CASE_ASSERTIONS',
64
+ 'MAX_CASE_MANUAL_TRAIL_EVENTS',
65
+ 'MAX_CASE_SIGHTINGS',
66
+ 'MAX_RESPONSE_LABEL_LENGTH',
67
+ 'MAX_RESPONSE_VALUE_LENGTH',
68
+ 'MAX_RESPONSE_RATIONALE_LENGTH',
69
+ 'MAX_RESPONSE_RECIPIENT_LENGTH',
70
+ 'MAX_RESPONSE_REFERENCE_LENGTH',
71
+ 'MAX_RESPONSE_LIMITATIONS',
72
+ 'MAX_RESPONSE_LIMITATION_LENGTH',
73
+ 'MAX_DECISION_PIN_REFERENCES',
74
+ 'MAX_TRAIL_TARGET_LENGTH',
75
+ 'MAX_ASSERTION_PROVENANCE_LABELS',
76
+ 'MAX_ASSERTION_PROVENANCE_MARKINGS',
77
+ 'MAX_ABUSIVE_URLS',
78
+ 'MAX_RESPONSE_CONTACTS',
79
+ 'MAX_RESPONSE_ACTION_HISTORY',
80
+ 'MAX_RESPONSE_HARM_LENGTH',
81
+ 'MAX_AFFECTED_PARTY_LENGTH',
82
+ 'MAX_ABUSE_CATEGORY_LENGTH',
83
+ 'MAX_EXACT_URL_LENGTH',
84
+ 'MAX_RESPONSE_ARTEFACT_REFERENCES',
85
+ 'MAX_RESPONSE_SELECTED_EVIDENCE',
86
+ 'MAX_RESPONSE_CONTRADICTIONS',
87
+ 'RESPONSE_ROUTE_STALE_AFTER_DAYS',
88
+ 'MAX_RESPONSE_AUTHORISATION_CLOCK_SKEW_MS',
89
+ 'MAX_CASE_PACK_INPUT_BYTES',
90
+ 'MAX_CASE_PACK_CASES',
91
+ 'MAX_WORKSPACE_ARCHIVE_BYTES',
92
+ 'MAX_WORKSPACE_ARCHIVE_SECTION_BYTES',
93
+ 'MAX_WORKSPACE_ARCHIVE_SECTIONS',
94
+ 'WORKSPACE_ARCHIVE_PBKDF2_ITERATIONS',
95
+ 'MIN_WORKSPACE_ARCHIVE_PASSPHRASE_CHARACTERS',
96
+ 'MAX_WORKSPACE_ARCHIVE_PASSPHRASE_BYTES',
97
+ 'MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES',
98
+ ]);
99
+ export const CASE_DOMAIN_COMPATIBILITY_FACADES = Object.freeze([
100
+ ['frontend/src/lib/analysis/case-brand-profile-references.ts', 'packages/cases/case-brand-profile-references.mts'],
101
+ ['frontend/src/lib/analysis/case-evidence-model.ts', 'packages/cases/case-evidence-model.mts'],
102
+ ['frontend/src/lib/analysis/case-investigation-branch-model.ts', 'packages/cases/case-investigation-branch-model.mts'],
103
+ ['frontend/src/lib/analysis/case-migration-model.ts', 'packages/cases/case-migration-model.mts'],
104
+ ['frontend/src/lib/analysis/case-model.ts', 'packages/cases/case-model.mts'],
105
+ ['frontend/src/lib/analysis/case-record-contracts.ts', 'packages/cases/case-record-contracts.mts'],
106
+ ['frontend/src/lib/analysis/case-record-core.ts', 'packages/cases/case-record-core.mts'],
107
+ ['frontend/src/lib/analysis/case-record-model.ts', 'packages/cases/case-record-model.mts'],
108
+ ['frontend/src/lib/analysis/case-record-operations.ts', 'packages/cases/case-record-operations.mts'],
109
+ ['frontend/src/lib/analysis/case-report.ts', 'packages/cases/case-report.mts'],
110
+ ['frontend/src/lib/analysis/case-response-model.ts', 'packages/cases/case-response-model.mts'],
111
+ ['frontend/src/lib/analysis/case-response-packet.ts', 'packages/cases/case-response-packet.mts'],
112
+ ['frontend/src/lib/analysis/case-storage-model.ts', 'packages/cases/case-storage-model.mts'],
113
+ ['frontend/src/lib/analysis/http-summary.ts', 'packages/cases/http-summary.mts'],
114
+ ['frontend/src/lib/analysis/opaque-reference-id.ts', 'packages/cases/opaque-reference-id.mts'],
115
+ ['frontend/src/lib/analysis/workspace-archive.ts', 'packages/workspace/workspace-archive.mts'],
116
+ ['frontend/src/lib/analysis/workspace-archive-crypto.ts', 'packages/workspace/workspace-archive-crypto.mts'],
117
+ ]);
118
+ export const CASE_DOMAIN_RUNTIME_ADAPTERS = Object.freeze([
119
+ 'frontend/src/lib/cases.ts',
120
+ 'frontend/src/lib/workspace-archive.ts',
121
+ ]);
122
+ export const CASE_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.case-store';
123
+ export const CASE_EXPORT_LIFECYCLE_SCHEMA = 'whoisleuth.case-export';
124
+ export const PUBLIC_CASE_SCHEMA_VERSION = 12;
125
+ export const CASE_SCHEMA_VERSION = 13;
126
+ export const CASE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([PUBLIC_CASE_SCHEMA_VERSION, CASE_SCHEMA_VERSION]);
127
+ export const CASE_IMPORT_VERSIONS = CASE_BROWSER_SUPPORTED_VERSIONS;
128
+ export const CASE_REPORT_SCHEMA = 'whoisleuth.case-report';
129
+ export const PUBLIC_CASE_REPORT_SCHEMA_VERSION = 8;
130
+ export const CASE_REPORT_SCHEMA_VERSION = 9;
131
+ export const CASE_REPORT_OUTPUT_VERSIONS = Object.freeze([CASE_REPORT_SCHEMA_VERSION]);
132
+ export const CASE_RESPONSE_PACKET_SCHEMA = 'whoisleuth.case-response-packet';
133
+ export const PUBLIC_CASE_RESPONSE_PACKET_VERSION = 6;
134
+ export const CASE_RESPONSE_PACKET_VERSION = 7;
135
+ export const CASE_RESPONSE_PACKET_OUTPUT_VERSIONS = Object.freeze([PUBLIC_CASE_RESPONSE_PACKET_VERSION, CASE_RESPONSE_PACKET_VERSION]);
136
+ export const SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS = CASE_RESPONSE_PACKET_OUTPUT_VERSIONS;
137
+ export const CASE_RESPONSE_REVIEW_INPUTS_SCHEMA = 'whoisleuth.case-response-review-inputs';
138
+ export const CASE_RESPONSE_REVIEW_INPUTS_VERSION = 1;
139
+ export const CLI_CASE_PACK_SCHEMA = 'whoisleuth.cli.case-pack';
140
+ export const CLI_CASE_PACK_VERSION = 2;
141
+ export const SUPPORTED_CLI_CASE_PACK_VERSIONS = Object.freeze([CLI_CASE_PACK_VERSION]);
142
+ export const CLI_CASE_PACK_ROOT_KEYS = Object.freeze(['version', 'exportedAt', 'cases', 'packet', 'integrity']);
143
+ export const CLI_CASE_PACK_PACKET_KEYS = Object.freeze(['schema', 'version', 'audience', 'reviewed', 'reports', 'redactionManifest', 'limitations']);
144
+ export const CLI_CASE_PACK_PUBLIC_REPORT_KEYS = Object.freeze(['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'limitations']);
145
+ export const CLI_CASE_PACK_REPORT_KEYS = Object.freeze(['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations']);
146
+ export const CLI_CASE_PACK_INTEGRITY_KEYS = Object.freeze(['algorithm', 'canonicalization', 'digestSha256']);
147
+ export const CLI_CASE_PACK_CURRENT_REDACTION_KEYS = Object.freeze(['excluded', 'sourceCaseCount', 'brandProfileReferencesOmitted']);
148
+ export const CLI_CASE_PACK_LIMITATIONS = Object.freeze([
149
+ 'This local package is browser-importable through its top-level case collection and does not upload or submit evidence.',
150
+ 'The reviewed flag records a deliberate CLI choice; it does not prove recipient authorisation, factual correctness, or legal sufficiency.',
151
+ 'Importing the package does not restore fields excluded by its audience profile.',
152
+ ]);
153
+ export const CLI_CASE_PACK_CASE_REPORT_EPOCHS = Object.freeze([
154
+ Object.freeze({ caseVersions: Object.freeze([PUBLIC_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLIC_CASE_REPORT_SCHEMA_VERSION]) }),
155
+ Object.freeze({ caseVersions: Object.freeze([CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([CASE_REPORT_SCHEMA_VERSION]) }),
156
+ ]);
157
+ export function caseReportVersionMatchesCase(caseVersion, reportVersion) {
158
+ return typeof reportVersion === 'number'
159
+ && Number.isSafeInteger(reportVersion)
160
+ && CLI_CASE_PACK_CASE_REPORT_EPOCHS.some((epoch) => (epoch.caseVersions.some((version) => version === caseVersion)
161
+ && epoch.reportVersions.some((version) => version === reportVersion)));
162
+ }
163
+ export const WORKSPACE_ARCHIVE_SCHEMA = 'whoisleuth.workspace-archive';
164
+ export const PUBLIC_WORKSPACE_ARCHIVE_VERSION = 5;
165
+ export const WORKSPACE_ARCHIVE_VERSION = 6;
166
+ export const SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS = Object.freeze([PUBLIC_WORKSPACE_ARCHIVE_VERSION, WORKSPACE_ARCHIVE_VERSION]);
167
+ export function isSupportedWorkspaceArchiveVersion(value) {
168
+ return typeof value === 'number'
169
+ && Number.isSafeInteger(value)
170
+ && SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.some((version) => version === value);
171
+ }
172
+ export const WORKSPACE_ARCHIVE_SECTION_IDS = Object.freeze([
173
+ 'cases', 'campaigns', 'brandProfiles', 'watchlists', 'shortlist', 'detectionRules',
174
+ 'relationshipObservations', 'bulkSessions', 'websiteSnapshots', 'investigationTemplates',
175
+ 'bulkReview', 'analystReviewState', 'settings',
176
+ ]);
177
+ export const PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS = Object.freeze(WORKSPACE_ARCHIVE_SECTION_IDS.filter((id) => id !== 'analystReviewState'));
178
+ export const WORKSPACE_ARCHIVE_CASE_SECTION = Object.freeze({
179
+ id: 'cases',
180
+ schema: null,
181
+ currentVersion: CASE_SCHEMA_VERSION,
182
+ supportedVersions: CASE_IMPORT_VERSIONS,
183
+ });
184
+ export const WORKSPACE_SETTINGS_SCHEMA = 'whoisleuth.workspace-settings';
185
+ export const WORKSPACE_SETTINGS_VERSION = 1;
186
+ export const ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA = 'whoisleuth.encrypted-workspace-archive';
187
+ export const ENCRYPTED_WORKSPACE_ARCHIVE_VERSION = 1;
188
+ export const CASE_PORTABILITY_VERIFIER_DISPATCH = Object.freeze([
189
+ Object.freeze({
190
+ id: 'case-response-packet',
191
+ artifactKind: 'case_response_packet',
192
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
193
+ discriminator: 'root_schema',
194
+ versionField: 'schemaVersion',
195
+ supportedVersions: SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS,
196
+ structureHookId: 'case.packet.structure',
197
+ integrityHookId: 'case.packet.verify',
198
+ }),
199
+ Object.freeze({
200
+ id: 'cli-case-pack',
201
+ artifactKind: 'cli_case_pack',
202
+ schema: CLI_CASE_PACK_SCHEMA,
203
+ discriminator: 'packet_schema',
204
+ versionField: 'packet.version',
205
+ supportedVersions: SUPPORTED_CLI_CASE_PACK_VERSIONS,
206
+ structureHookId: 'case.cli-pack.verify',
207
+ integrityHookId: 'case.cli-pack.verify',
208
+ }),
209
+ ]);
210
+ export const MAX_CASES = 500;
211
+ export const MAX_NOTES_PER_CASE = 50;
212
+ export const MAX_NOTE_LENGTH = 2000;
213
+ export const MAX_TAGS_PER_CASE = 20;
214
+ export const MAX_TAG_LENGTH = 40;
215
+ export const MAX_DOMAIN_LENGTH = MAX_DOMAIN_NAME_LENGTH;
216
+ export const MAX_CASE_IMPORT_BYTES = 2 * 1024 * 1024;
217
+ export const MAX_CASE_STORE_BYTES = 4 * 1024 * 1024;
218
+ export const MAX_EVIDENCE_SNAPSHOTS_PER_CASE = 25;
219
+ export const MAX_EVIDENCE_FACTORS = 20;
220
+ export const MAX_EVIDENCE_NAMESERVERS = 12;
221
+ export const MAX_EVIDENCE_MUTATIONS = 20;
222
+ export const MAX_EVIDENCE_STRING_LENGTH = 200;
223
+ export const MAX_EVIDENCE_TITLE_LENGTH = 200;
224
+ export const MAX_EVIDENCE_DETAIL_LENGTH = 200;
225
+ export const MAX_EVIDENCE_CHANGES = 40;
226
+ export const MAX_CASE_BRAND_PROFILE_IDS = 8;
227
+ export const MAX_CASE_BRAND_PROFILE_ID_CANDIDATES = 32;
228
+ export const MAX_CASE_EVIDENCE_PINS = 40;
229
+ export const MAX_CASE_CHECKPOINT_FACTS = 20;
230
+ export const MAX_CASE_DECISIONS = 30;
231
+ export const MAX_CASE_ACTIONS = 50;
232
+ export const MAX_CASE_ACTION_EVENTS_PER_ACTION = 40;
233
+ export const MAX_CASE_ACTION_EVENTS_PER_CASE = 400;
234
+ export const MAX_CASE_ACTION_BYTES = 32 * 1024;
235
+ export const MAX_CASE_ACTION_HISTORY_BYTES_PER_CASE = 512 * 1024;
236
+ export const MAX_CASE_OBSERVED_EFFECT_REVIEWS = 40;
237
+ export const MAX_CASE_CLOSURES = 12;
238
+ export const MAX_CASE_ASSERTIONS = 50;
239
+ export const MAX_CASE_MANUAL_TRAIL_EVENTS = 80;
240
+ export const MAX_CASE_SIGHTINGS = 80;
241
+ export const MAX_RESPONSE_LABEL_LENGTH = 80;
242
+ export const MAX_RESPONSE_VALUE_LENGTH = 1000;
243
+ export const MAX_RESPONSE_RATIONALE_LENGTH = 2000;
244
+ export const MAX_RESPONSE_RECIPIENT_LENGTH = 320;
245
+ export const MAX_RESPONSE_REFERENCE_LENGTH = 500;
246
+ export const MAX_RESPONSE_LIMITATIONS = 8;
247
+ export const MAX_RESPONSE_LIMITATION_LENGTH = 240;
248
+ export const MAX_DECISION_PIN_REFERENCES = 20;
249
+ export const MAX_TRAIL_TARGET_LENGTH = 500;
250
+ export const MAX_ASSERTION_PROVENANCE_LABELS = 20;
251
+ export const MAX_ASSERTION_PROVENANCE_MARKINGS = 12;
252
+ export const MAX_ABUSIVE_URLS = 20;
253
+ export const MAX_RESPONSE_CONTACTS = 12;
254
+ export const MAX_RESPONSE_ACTION_HISTORY = 20;
255
+ export const MAX_RESPONSE_HARM_LENGTH = 2000;
256
+ export const MAX_AFFECTED_PARTY_LENGTH = 200;
257
+ export const MAX_ABUSE_CATEGORY_LENGTH = 80;
258
+ export const MAX_EXACT_URL_LENGTH = 2048;
259
+ export const MAX_RESPONSE_ARTEFACT_REFERENCES = 12;
260
+ export const MAX_RESPONSE_SELECTED_EVIDENCE = 20;
261
+ export const MAX_RESPONSE_CONTRADICTIONS = 20;
262
+ export const RESPONSE_ROUTE_STALE_AFTER_DAYS = 30;
263
+ export const MAX_RESPONSE_AUTHORISATION_CLOCK_SKEW_MS = 300_000;
264
+ export const MAX_CASE_PACK_INPUT_BYTES = 4 * 1024 * 1024;
265
+ export const MAX_CASE_PACK_CASES = 25;
266
+ export const MAX_WORKSPACE_ARCHIVE_BYTES = 10 * 1024 * 1024;
267
+ export const MAX_WORKSPACE_ARCHIVE_SECTION_BYTES = 5 * 1024 * 1024;
268
+ export const MAX_WORKSPACE_ARCHIVE_SECTIONS = 13;
269
+ export const WORKSPACE_ARCHIVE_PBKDF2_ITERATIONS = 600_000;
270
+ export const MIN_WORKSPACE_ARCHIVE_PASSPHRASE_CHARACTERS = 12;
271
+ export const MAX_WORKSPACE_ARCHIVE_PASSPHRASE_BYTES = 1024;
272
+ export const MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES = Math.ceil(MAX_WORKSPACE_ARCHIVE_BYTES * 4 / 3) + 4096;
273
+ export const CASE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
274
+ id: 'browser.cases', kind: 'browser_store', schema: null, currentVersion: CASE_SCHEMA_VERSION,
275
+ supportedVersions: CASE_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
276
+ futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
277
+ writeSemantics: 'normalized_rewrite', byteBudget: MAX_CASE_STORE_BYTES, owner: CASE_CONTRACT_OWNER,
278
+ note: 'The exact public version 12 writer migrates directly to version 13; other retired and all future stores remain untouched.',
279
+ });
280
+ export const CASE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
281
+ id: 'export.cases', kind: 'export', schema: null, currentVersion: CASE_SCHEMA_VERSION,
282
+ supportedVersions: CASE_IMPORT_VERSIONS, acceptsUnversionedLegacy: false,
283
+ futureVersionBehavior: 'reject', migration: 'normalize_to_current',
284
+ writeSemantics: 'non_destructive_merge', byteBudget: MAX_CASE_IMPORT_BYTES, owner: CASE_CONTRACT_OWNER,
285
+ note: 'Exact public Case version 12 and current version 13 merge non-destructively through one bounded reader; other versions fail without mutation.',
286
+ });
287
+ export const CASE_REPORT_COMPATIBILITY = defineSchemaCompatibility({
288
+ id: 'export.case-report', kind: 'export', schema: CASE_REPORT_SCHEMA, currentVersion: CASE_REPORT_SCHEMA_VERSION,
289
+ supportedVersions: CASE_REPORT_OUTPUT_VERSIONS, acceptsUnversionedLegacy: false,
290
+ futureVersionBehavior: 'not_applicable', migration: 'read_only', writeSemantics: 'read_only',
291
+ byteBudget: null, owner: CASE_CONTRACT_OWNER,
292
+ note: 'Case report version 9 is the durable output contract; retired and future report versions are unsupported.',
293
+ });
294
+ export const CASE_RESPONSE_PACKET_COMPATIBILITY = defineSchemaCompatibility({
295
+ id: 'export.case-response-packet', kind: 'export', schema: CASE_RESPONSE_PACKET_SCHEMA,
296
+ currentVersion: CASE_RESPONSE_PACKET_VERSION, supportedVersions: CASE_RESPONSE_PACKET_OUTPUT_VERSIONS,
297
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only',
298
+ writeSemantics: 'read_only', byteBudget: null, owner: CASE_CONTRACT_OWNER,
299
+ note: 'Exact public packet version 6 and current version 7 are verified independently; other versions fail closed.',
300
+ });
301
+ export const CASE_RESPONSE_REVIEW_INPUTS_COMPATIBILITY = defineSchemaCompatibility({
302
+ id: 'derived.case-response-review-inputs', kind: 'derived', schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
303
+ currentVersion: CASE_RESPONSE_REVIEW_INPUTS_VERSION, supportedVersions: [CASE_RESPONSE_REVIEW_INPUTS_VERSION],
304
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
305
+ writeSemantics: 'read_only', byteBudget: null, owner: CASE_CONTRACT_OWNER,
306
+ note: 'Exact bounded review inputs are canonicalised with sorted-json-v2 for one response authorisation digest and reject extensions or future versions.',
307
+ });
308
+ export const CLI_CASE_PACK_COMPATIBILITY = defineSchemaCompatibility({
309
+ id: 'export.cli-case-pack', kind: 'export', schema: CLI_CASE_PACK_SCHEMA, currentVersion: CLI_CASE_PACK_VERSION,
310
+ supportedVersions: SUPPORTED_CLI_CASE_PACK_VERSIONS, acceptsUnversionedLegacy: false,
311
+ futureVersionBehavior: 'reject', migration: 'read_only', writeSemantics: 'read_only',
312
+ byteBudget: MAX_CASE_IMPORT_BYTES, owner: CASE_CONTRACT_OWNER,
313
+ note: 'Case-pack version 2 verifies the exact public Case 12/report 8 epoch and the current Case 13/report 9 epoch with deterministic sorted-json-v2 integrity.',
314
+ });
315
+ export const WORKSPACE_ARCHIVE_COMPATIBILITY = defineSchemaCompatibility({
316
+ id: 'export.workspace-archive', kind: 'export', schema: WORKSPACE_ARCHIVE_SCHEMA,
317
+ currentVersion: WORKSPACE_ARCHIVE_VERSION, supportedVersions: SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS,
318
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
319
+ writeSemantics: 'non_destructive_merge', byteBudget: MAX_WORKSPACE_ARCHIVE_BYTES, owner: CASE_CONTRACT_OWNER,
320
+ note: 'Workspace envelope version 6 adds the bounded analyst Review Item lifecycle section. Exact version 5 remains readable and migrates to an empty review-state section without inventing decisions; future envelopes fail without mutation.',
321
+ });
322
+ export const WORKSPACE_SETTINGS_COMPATIBILITY = defineSchemaCompatibility({
323
+ id: 'export.workspace-settings-section', kind: 'export', schema: WORKSPACE_SETTINGS_SCHEMA,
324
+ currentVersion: WORKSPACE_SETTINGS_VERSION, supportedVersions: [WORKSPACE_SETTINGS_VERSION],
325
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
326
+ writeSemantics: 'non_destructive_merge', byteBudget: null, owner: CASE_CONTRACT_OWNER,
327
+ note: 'Nested archive section limited to the active Brand Profile identifier and dark, light, or system theme preference.',
328
+ });
329
+ export const ENCRYPTED_WORKSPACE_ARCHIVE_COMPATIBILITY = defineSchemaCompatibility({
330
+ id: 'export.encrypted-workspace-archive', kind: 'export', schema: ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA,
331
+ currentVersion: ENCRYPTED_WORKSPACE_ARCHIVE_VERSION, supportedVersions: [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION],
332
+ acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'exact_current_only',
333
+ writeSemantics: 'non_destructive_merge', byteBudget: MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES,
334
+ owner: CASE_CONTRACT_OWNER,
335
+ note: 'Version 1 is the exact PBKDF2 and AES-GCM wrapper for supported ordinary workspace envelopes; passphrases remain transient.',
336
+ });
337
+ export const CASE_PORTABILITY_COMPATIBILITY = Object.freeze([
338
+ CASE_BROWSER_COMPATIBILITY, CASE_EXPORT_COMPATIBILITY, CASE_REPORT_COMPATIBILITY,
339
+ CASE_RESPONSE_PACKET_COMPATIBILITY, CASE_RESPONSE_REVIEW_INPUTS_COMPATIBILITY,
340
+ CLI_CASE_PACK_COMPATIBILITY, WORKSPACE_ARCHIVE_COMPATIBILITY,
341
+ WORKSPACE_SETTINGS_COMPATIBILITY,
342
+ ENCRYPTED_WORKSPACE_ARCHIVE_COMPATIBILITY,
343
+ ]);
344
+ export function serialiseCasePortableJson(value) {
345
+ return `${JSON.stringify(value, null, 2)}\n`;
346
+ }
347
+ /** Canonical compact bytes used for workspace-section checksums and byte counts. */
348
+ export function serialiseWorkspaceArchiveSection(value) {
349
+ const serialised = JSON.stringify(value);
350
+ if (typeof serialised !== 'string') {
351
+ throw new TypeError('Workspace archive sections must be JSON-serialisable documents.');
352
+ }
353
+ return serialised;
354
+ }
355
+ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
356
+ Object.freeze({
357
+ id: 'browser-case-v12-public',
358
+ path: 'test/fixtures/case-lifecycle/browser-case-v12.json',
359
+ bytes: 1286,
360
+ sha256: '86d4f3be343034ff05f1214229294d9c6424ccc13fc63e884358fb691b2e0d4e',
361
+ contentDigestSha256: null,
362
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
363
+ version: PUBLIC_CASE_SCHEMA_VERSION,
364
+ role: 'historical',
365
+ expectation: 'normalises_to_current_output',
366
+ expectedOutputFixtureId: 'browser-case-v13',
367
+ shapeId: 'case.browser-store.public',
368
+ scope: 'repository',
369
+ }),
370
+ Object.freeze({
371
+ id: 'browser-case-v13',
372
+ path: 'test/fixtures/case-lifecycle/browser-case-v13.json',
373
+ bytes: 3096,
374
+ sha256: 'd679030e52340ecc7795ac88de15316d88c8c07b94b34da60fd52589a0375712',
375
+ contentDigestSha256: null,
376
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
377
+ version: CASE_SCHEMA_VERSION,
378
+ role: 'current',
379
+ expectation: 'accepted_exact',
380
+ expectedOutputFixtureId: null,
381
+ shapeId: 'case.browser-store.current',
382
+ scope: 'repository',
383
+ }),
384
+ Object.freeze({
385
+ id: 'case-export-v12-public',
386
+ path: 'test/fixtures/case-v12-response-lifecycle.json',
387
+ bytes: 1290,
388
+ sha256: 'c5182bc1d413a1e1940e06e158d8dde47971d3fb034090934fe600753ffd7f0b',
389
+ contentDigestSha256: null,
390
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
391
+ version: PUBLIC_CASE_SCHEMA_VERSION,
392
+ role: 'historical',
393
+ expectation: 'normalises_to_current_output',
394
+ expectedOutputFixtureId: 'case-export-v13',
395
+ shapeId: 'case.export.public',
396
+ scope: 'repository',
397
+ }),
398
+ Object.freeze({
399
+ id: 'case-export-v13',
400
+ path: 'test/fixtures/case-lifecycle/case-export-v13.json',
401
+ bytes: 3140,
402
+ sha256: 'fd2aa7623c2aaea385b952727ad74cf5af0f1ee21dc478c0e5c820a332376a0d',
403
+ contentDigestSha256: null,
404
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
405
+ version: CASE_SCHEMA_VERSION,
406
+ role: 'current',
407
+ expectation: 'accepted_exact',
408
+ expectedOutputFixtureId: null,
409
+ shapeId: 'case.export.current',
410
+ scope: 'repository',
411
+ }),
412
+ Object.freeze({
413
+ id: 'case-response-packet-v6-public',
414
+ path: 'test/fixtures/case-lifecycle/case-response-packet-v6.json',
415
+ bytes: 5100,
416
+ sha256: '32030d3941119dce378dc03f6b3f5d710ce3883e7253cd43fa322c989ff529cb',
417
+ contentDigestSha256: 'sha256:29a5185a3d944af38c9656706fe89d96e60f949e8684646a55c8bcd10de54746',
418
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
419
+ version: PUBLIC_CASE_RESPONSE_PACKET_VERSION,
420
+ role: 'historical',
421
+ expectation: 'accepted_exact',
422
+ expectedOutputFixtureId: null,
423
+ shapeId: 'case.response-packet.v6',
424
+ scope: 'repository',
425
+ }),
426
+ Object.freeze({
427
+ id: 'case-report-v9',
428
+ path: 'test/fixtures/case-lifecycle/case-report-v9.json',
429
+ bytes: 2761,
430
+ sha256: 'd9903318123c63ae7a385f153286a5596585a401771c8def8149792b9b7b9326',
431
+ contentDigestSha256: null,
432
+ schema: CASE_REPORT_SCHEMA,
433
+ version: CASE_REPORT_SCHEMA_VERSION,
434
+ role: 'current',
435
+ expectation: 'accepted_exact',
436
+ expectedOutputFixtureId: null,
437
+ shapeId: 'case.report.v9',
438
+ scope: 'repository',
439
+ }),
440
+ Object.freeze({
441
+ id: 'case-response-packet-v7',
442
+ path: 'test/fixtures/case-lifecycle/case-response-packet-v7.json',
443
+ bytes: 10000,
444
+ sha256: '0ea150d920710b331839be0a0f995d4597a133ecb930ef850de1eecfca4d94e3',
445
+ contentDigestSha256: 'sha256:fa5b7f079882dd72b15365d844937ae99e47f81465e79b3911c25e70c157e562',
446
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
447
+ version: CASE_RESPONSE_PACKET_VERSION,
448
+ role: 'current',
449
+ expectation: 'accepted_exact',
450
+ expectedOutputFixtureId: null,
451
+ shapeId: 'case.response-packet.v7',
452
+ scope: 'repository',
453
+ }),
454
+ Object.freeze({
455
+ id: 'case-response-review-inputs-v1',
456
+ path: 'test/fixtures/case-lifecycle/case-response-review-inputs-v1.json',
457
+ bytes: 5555,
458
+ sha256: '67d3d36f316cf17898e04c39f2ba0ba7599abdc4b4e387baef5f5866a73c7b9c',
459
+ contentDigestSha256: null,
460
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
461
+ version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
462
+ role: 'current',
463
+ expectation: 'accepted_exact',
464
+ expectedOutputFixtureId: null,
465
+ shapeId: 'case.response-review-inputs.v1',
466
+ scope: 'repository',
467
+ }),
468
+ Object.freeze({
469
+ id: 'cli-case-pack-v2-case-v12-public',
470
+ path: 'test/fixtures/case-lifecycle/cli-case-pack-v2-case-v12-public.json',
471
+ bytes: 4947,
472
+ sha256: '4c8117dfb73c1eed5bd3f4cf78bf350459cdcc8f406120f089b34e6a6fcbfc7c',
473
+ contentDigestSha256: 'sha256:93cc725c03052aa0232fb6965154c58810d8d1aef3ea0203e451a94bf9d091cf',
474
+ schema: CLI_CASE_PACK_SCHEMA,
475
+ version: CLI_CASE_PACK_VERSION,
476
+ role: 'current',
477
+ expectation: 'accepted_exact',
478
+ expectedOutputFixtureId: null,
479
+ shapeId: 'case.cli-pack.current',
480
+ scope: 'repository',
481
+ }),
482
+ Object.freeze({
483
+ id: 'cli-case-pack-v2-case-v13',
484
+ path: 'test/fixtures/case-lifecycle/cli-case-pack-v2-case-v13.json',
485
+ bytes: 9288,
486
+ sha256: '77b7bce093a967d04a2cf0ead3b715625c00903c4c194227c5b74886f2b847c6',
487
+ contentDigestSha256: 'sha256:2cda7f529ec9b5ec5234180c80286274dc0545b9ba360015b073ae3eb3e97f3d',
488
+ schema: CLI_CASE_PACK_SCHEMA,
489
+ version: CLI_CASE_PACK_VERSION,
490
+ role: 'current',
491
+ expectation: 'accepted_exact',
492
+ expectedOutputFixtureId: null,
493
+ shapeId: 'case.cli-pack.current',
494
+ scope: 'repository',
495
+ }),
496
+ Object.freeze({
497
+ id: 'workspace-archive-v5-public',
498
+ path: 'test/fixtures/case-lifecycle/workspace-archive-v5-public.json',
499
+ bytes: 5271,
500
+ sha256: '27fd5f590b493be1ce57708be8b5c11d95eb600ae54e1ed3a530e78db433ec0d',
501
+ contentDigestSha256: null,
502
+ schema: WORKSPACE_ARCHIVE_SCHEMA,
503
+ version: PUBLIC_WORKSPACE_ARCHIVE_VERSION,
504
+ role: 'historical',
505
+ expectation: 'normalises_to_current_output',
506
+ expectedOutputFixtureId: 'workspace-archive-v6-empty-current',
507
+ shapeId: 'case.workspace-archive.v5',
508
+ scope: 'repository',
509
+ }),
510
+ Object.freeze({
511
+ id: 'workspace-archive-v6-empty-current',
512
+ path: 'test/fixtures/case-lifecycle/workspace-archive-v6-empty-current.json',
513
+ bytes: 5557,
514
+ sha256: '7a0701c7b0401de76df132fe7b2332fd1e2f1fade007a8af0103c8cec5a1b2f9',
515
+ contentDigestSha256: null,
516
+ schema: WORKSPACE_ARCHIVE_SCHEMA,
517
+ version: WORKSPACE_ARCHIVE_VERSION,
518
+ role: 'current',
519
+ expectation: 'accepted_exact',
520
+ expectedOutputFixtureId: null,
521
+ shapeId: 'case.workspace-archive.v6',
522
+ scope: 'repository',
523
+ }),
524
+ Object.freeze({
525
+ id: 'workspace-settings-v1',
526
+ path: 'test/fixtures/case-lifecycle/workspace-settings-v1.json',
527
+ bytes: 110,
528
+ sha256: '75675baf68765da5d36e9911d106dd8b72a8fd3c859136b651411d8041a4e184',
529
+ contentDigestSha256: null,
530
+ schema: WORKSPACE_SETTINGS_SCHEMA,
531
+ version: WORKSPACE_SETTINGS_VERSION,
532
+ role: 'current',
533
+ expectation: 'accepted_exact',
534
+ expectedOutputFixtureId: null,
535
+ shapeId: 'case.workspace-settings.v1',
536
+ scope: 'repository',
537
+ }),
538
+ Object.freeze({
539
+ id: 'encrypted-workspace-archive-v1',
540
+ path: 'test/fixtures/case-lifecycle/encrypted-workspace-archive-v1.json',
541
+ bytes: 7502,
542
+ sha256: 'd7279ba0afb548d3ce41bdfc56acebffd05dc9b0b5b1451cb57a1164be881733',
543
+ contentDigestSha256: null,
544
+ schema: ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA,
545
+ version: ENCRYPTED_WORKSPACE_ARCHIVE_VERSION,
546
+ role: 'current',
547
+ expectation: 'accepted_exact',
548
+ expectedOutputFixtureId: null,
549
+ shapeId: 'case.encrypted-workspace-archive.v1',
550
+ scope: 'repository',
551
+ }),
552
+ ]);
553
+ function fixtureIdsFor(schema, version) {
554
+ return Object.freeze(CASE_LIFECYCLE_FIXTURES
555
+ .filter((fixture) => fixture.schema === schema && fixture.version === version)
556
+ .map((fixture) => fixture.id));
557
+ }
558
+ function lifecycleContract(input) {
559
+ return { ...input, role: input.role ?? 'document', fixtureIds: fixtureIdsFor(input.schema, input.version) };
560
+ }
561
+ const browserContracts = CASE_BROWSER_SUPPORTED_VERSIONS.map((version) => lifecycleContract({
562
+ compatibilityId: CASE_BROWSER_COMPATIBILITY.id, schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, version,
563
+ lifecycle: version === CASE_SCHEMA_VERSION ? 'current' : 'legacy', readable: true,
564
+ emitted: version === CASE_SCHEMA_VERSION, exactKeys: false,
565
+ extensionPolicy: 'discard_bounded', futureVersionBehaviour: 'preserve_without_write',
566
+ migrationTarget: version === CASE_SCHEMA_VERSION ? null : { schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION },
567
+ canonicalisation: null, byteBudget: MAX_CASE_STORE_BYTES,
568
+ }));
569
+ const exportContracts = CASE_IMPORT_VERSIONS.map((version) => lifecycleContract({
570
+ compatibilityId: CASE_EXPORT_COMPATIBILITY.id, schema: CASE_EXPORT_LIFECYCLE_SCHEMA, version,
571
+ lifecycle: version === CASE_SCHEMA_VERSION ? 'current' : 'legacy', readable: true,
572
+ emitted: version === CASE_SCHEMA_VERSION, exactKeys: true, extensionPolicy: 'reject',
573
+ futureVersionBehaviour: 'reject', migrationTarget: version === CASE_SCHEMA_VERSION
574
+ ? null
575
+ : { schema: CASE_EXPORT_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION },
576
+ canonicalisation: null, byteBudget: MAX_CASE_IMPORT_BYTES,
577
+ }));
578
+ const reportContracts = CASE_REPORT_OUTPUT_VERSIONS.map((version) => lifecycleContract({
579
+ compatibilityId: CASE_REPORT_COMPATIBILITY.id, schema: CASE_REPORT_SCHEMA, version,
580
+ lifecycle: 'current', readable: false, emitted: true, exactKeys: true, extensionPolicy: 'reject',
581
+ futureVersionBehaviour: 'not_applicable', migrationTarget: null, canonicalisation: null, byteBudget: null,
582
+ }));
583
+ const packetContracts = CASE_RESPONSE_PACKET_OUTPUT_VERSIONS.map((version) => lifecycleContract({
584
+ compatibilityId: CASE_RESPONSE_PACKET_COMPATIBILITY.id, schema: CASE_RESPONSE_PACKET_SCHEMA, version,
585
+ lifecycle: version === CASE_RESPONSE_PACKET_VERSION ? 'current' : 'legacy', readable: true,
586
+ emitted: version === CASE_RESPONSE_PACKET_VERSION, exactKeys: true,
587
+ extensionPolicy: 'reject', futureVersionBehaviour: 'reject', migrationTarget: null,
588
+ canonicalisation: 'sorted-json-v2',
589
+ byteBudget: null,
590
+ }));
591
+ const workspaceContracts = SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.map((version) => lifecycleContract({
592
+ compatibilityId: WORKSPACE_ARCHIVE_COMPATIBILITY.id, schema: WORKSPACE_ARCHIVE_SCHEMA, version,
593
+ lifecycle: version === WORKSPACE_ARCHIVE_VERSION ? 'current' : 'legacy', readable: true,
594
+ emitted: version === WORKSPACE_ARCHIVE_VERSION, exactKeys: true, extensionPolicy: 'reject',
595
+ futureVersionBehaviour: 'reject', migrationTarget: version === WORKSPACE_ARCHIVE_VERSION
596
+ ? null
597
+ : { schema: WORKSPACE_ARCHIVE_SCHEMA, version: WORKSPACE_ARCHIVE_VERSION },
598
+ canonicalisation: 'workspace-section-sorted-json-v1', byteBudget: MAX_WORKSPACE_ARCHIVE_BYTES,
599
+ }));
600
+ function shape(id, schema, versions, requiredKeys, normalisation, target = null) {
601
+ return {
602
+ id,
603
+ schema,
604
+ versions,
605
+ objects: [{
606
+ path: '$',
607
+ requiredKeys,
608
+ alternativeRequiredKeys: [],
609
+ optionalKeys: schema === CASE_EXPORT_LIFECYCLE_SCHEMA ? ['exportedAt'] : [],
610
+ unknownKeys: schema === CASE_BROWSER_STORE_LIFECYCLE_SCHEMA ? 'discard_bounded' : 'reject',
611
+ }],
612
+ fixedArrays: [],
613
+ normalisation,
614
+ target,
615
+ discriminator: null,
616
+ };
617
+ }
618
+ const CASE_LIFECYCLE_SHAPES = Object.freeze([
619
+ shape('case.browser-store.public', CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'project_known_fields'),
620
+ shape('case.browser-store.current', CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], ['version', 'cases'], 'project_known_fields'),
621
+ shape('case.export.public', CASE_EXPORT_LIFECYCLE_SCHEMA, [PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'preserve_document'),
622
+ shape('case.response-packet.v6', CASE_RESPONSE_PACKET_SCHEMA, [PUBLIC_CASE_RESPONSE_PACKET_VERSION], [
623
+ 'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
624
+ 'profile', 'case', 'incident', 'contacts', 'preflight', 'escalationHistory',
625
+ 'provenance', 'integrity',
626
+ ], 'preserve_signed_document'),
627
+ shape('case.export.current', CASE_EXPORT_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], ['version', 'cases'], 'preserve_document'),
628
+ shape('case.report.v9', CASE_REPORT_SCHEMA, [CASE_REPORT_SCHEMA_VERSION], ['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations'], 'preserve_document'),
629
+ shape('case.response-packet.v7', CASE_RESPONSE_PACKET_SCHEMA, [CASE_RESPONSE_PACKET_VERSION], [
630
+ 'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
631
+ 'profile', 'case', 'incident', 'contacts', 'provenance', 'selectedEvidence',
632
+ 'contradictions', 'readiness', 'artefactReferences', 'preflight', 'escalationHistory',
633
+ 'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
634
+ 'authorisation', 'integrity',
635
+ ], 'preserve_signed_document'),
636
+ shape('case.response-review-inputs.v1', CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, [CASE_RESPONSE_REVIEW_INPUTS_VERSION], [
637
+ 'contract', 'version', 'profile', 'case', 'incident', 'contacts', 'selectedEvidence',
638
+ 'contradictions', 'readiness', 'artefactReferences', 'escalationHistory',
639
+ 'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
640
+ ], 'preserve_signed_document'),
641
+ shape('case.cli-pack.current', CLI_CASE_PACK_SCHEMA, [CLI_CASE_PACK_VERSION], CLI_CASE_PACK_ROOT_KEYS, 'preserve_signed_document'),
642
+ shape('case.workspace-archive.v5', WORKSPACE_ARCHIVE_SCHEMA, [5], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
643
+ shape('case.workspace-archive.v6', WORKSPACE_ARCHIVE_SCHEMA, [WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
644
+ shape('case.workspace-settings.v1', WORKSPACE_SETTINGS_SCHEMA, [WORKSPACE_SETTINGS_VERSION], ['schema', 'version', 'activeProfileId', 'theme'], 'preserve_document'),
645
+ shape('case.encrypted-workspace-archive.v1', ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA, [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'createdAt', 'content', 'kdf', 'cipher', 'ciphertext'], 'preserve_signed_document'),
646
+ ]);
647
+ const CASE_LIFECYCLE_HOOKS = Object.freeze([
648
+ {
649
+ id: 'case.browser.normalise',
650
+ role: 'normaliser',
651
+ runtime: 'shared',
652
+ module: 'packages/cases/case-model.mts',
653
+ exportName: 'normalizeCaseStore',
654
+ },
655
+ {
656
+ id: 'case.browser.serialise',
657
+ role: 'serialiser',
658
+ runtime: 'shared',
659
+ module: 'packages/cases/case-model.mts',
660
+ exportName: 'serializeCaseStore',
661
+ },
662
+ {
663
+ id: 'case.export.merge',
664
+ role: 'merger',
665
+ runtime: 'shared',
666
+ module: 'packages/cases/case-model.mts',
667
+ exportName: 'mergeCases',
668
+ },
669
+ {
670
+ id: 'case.export.build',
671
+ role: 'builder',
672
+ runtime: 'shared',
673
+ module: 'packages/cases/case-model.mts',
674
+ exportName: 'buildCaseExport',
675
+ },
676
+ {
677
+ id: 'case.portable.serialise',
678
+ role: 'serialiser',
679
+ runtime: 'shared',
680
+ module: CASE_CONTRACT_OWNER,
681
+ exportName: 'serialiseCasePortableJson',
682
+ },
683
+ {
684
+ id: 'case.report.build',
685
+ role: 'builder',
686
+ runtime: 'shared',
687
+ module: 'packages/cases/case-report.mts',
688
+ exportName: 'buildCaseReport',
689
+ },
690
+ {
691
+ id: 'case.packet.build',
692
+ role: 'builder',
693
+ runtime: 'shared',
694
+ module: 'packages/cases/case-response-packet.mts',
695
+ exportName: 'buildCaseResponsePacket',
696
+ },
697
+ {
698
+ id: 'case.packet.verify',
699
+ role: 'integrity_verifier',
700
+ runtime: 'shared',
701
+ module: 'packages/cases/case-response-packet.mts',
702
+ exportName: 'verifyCaseResponsePacketIntegrity',
703
+ },
704
+ {
705
+ id: 'case.packet.structure',
706
+ role: 'structure_validator',
707
+ runtime: 'cli',
708
+ module: 'cli/artifact-structure.mts',
709
+ exportName: 'validateOfflineArtifactStructure',
710
+ },
711
+ {
712
+ id: 'case.review-inputs.build',
713
+ role: 'builder',
714
+ runtime: 'shared',
715
+ module: 'packages/cases/case-response-packet.mts',
716
+ exportName: 'buildCaseResponseReviewInputs',
717
+ },
718
+ {
719
+ id: 'case.review-inputs.validate',
720
+ role: 'structure_validator',
721
+ runtime: 'shared',
722
+ module: 'packages/cases/case-response-packet.mts',
723
+ exportName: 'validateCaseResponseReviewInputs',
724
+ },
725
+ {
726
+ id: 'case.cli-pack.build',
727
+ role: 'builder',
728
+ runtime: 'cli',
729
+ module: 'cli/case-pack.mts',
730
+ exportName: 'buildCliCasePack',
731
+ },
732
+ {
733
+ id: 'case.cli-pack.verify',
734
+ role: 'integrity_verifier',
735
+ runtime: 'cli',
736
+ module: 'cli/case-pack.mts',
737
+ exportName: 'verifyCliCasePack',
738
+ },
739
+ {
740
+ id: 'case.cli-pack.serialise',
741
+ role: 'serialiser',
742
+ runtime: 'cli',
743
+ module: 'cli/case-pack.mts',
744
+ exportName: 'formatCliCasePack',
745
+ },
746
+ {
747
+ id: 'case.workspace.build',
748
+ role: 'builder',
749
+ runtime: 'shared',
750
+ module: 'packages/workspace/workspace-archive.mts',
751
+ exportName: 'buildWorkspaceArchive',
752
+ },
753
+ {
754
+ id: 'case.workspace.verify',
755
+ role: 'integrity_verifier',
756
+ runtime: 'shared',
757
+ module: 'packages/workspace/workspace-archive.mts',
758
+ exportName: 'readWorkspaceArchive',
759
+ },
760
+ {
761
+ id: 'case.encrypted-workspace.build',
762
+ role: 'builder',
763
+ runtime: 'shared',
764
+ module: 'packages/workspace/workspace-archive-crypto.mts',
765
+ exportName: 'encryptWorkspaceArchive',
766
+ },
767
+ {
768
+ id: 'case.encrypted-workspace.verify',
769
+ role: 'integrity_verifier',
770
+ runtime: 'shared',
771
+ module: 'packages/workspace/workspace-archive-crypto.mts',
772
+ exportName: 'decryptWorkspaceArchive',
773
+ },
774
+ ]);
775
+ const CASE_LIFECYCLE_BOUNDS = Object.freeze([
776
+ {
777
+ id: 'case.browser-store.bounds',
778
+ bounds: [
779
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_CASE_STORE_BYTES, handling: 'reject' },
780
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_CASE_STORE_BYTES, handling: 'reject' },
781
+ ],
782
+ },
783
+ {
784
+ id: 'case.portable.bounds',
785
+ bounds: [
786
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_CASE_IMPORT_BYTES, handling: 'reject' },
787
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_CASE_IMPORT_BYTES, handling: 'reject' },
788
+ ],
789
+ },
790
+ {
791
+ id: 'case.domain.bounds',
792
+ bounds: [
793
+ { id: 'cases', path: 'cases', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_CASES, handling: 'truncate' },
794
+ { id: 'notes', path: 'cases[].notes', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_NOTES_PER_CASE, handling: 'truncate' },
795
+ { id: 'evidence-history', path: 'cases[].evidenceHistory', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_EVIDENCE_SNAPSHOTS_PER_CASE, handling: 'truncate' },
796
+ { id: 'actions', path: 'cases[].actions', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_CASE_ACTIONS, handling: 'truncate' },
797
+ { id: 'action-events', path: 'cases[].actions[].history', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_CASE_ACTION_EVENTS_PER_ACTION, handling: 'truncate' },
798
+ ],
799
+ },
800
+ {
801
+ id: 'case.packet.bounds',
802
+ bounds: [
803
+ { id: 'contacts', path: 'contacts', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_RESPONSE_CONTACTS, handling: 'truncate' },
804
+ { id: 'urls', path: 'incident.abusiveUrls', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_ABUSIVE_URLS, handling: 'truncate' },
805
+ { id: 'selected-evidence', path: 'selectedEvidence', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_RESPONSE_SELECTED_EVIDENCE, handling: 'truncate' },
806
+ { id: 'contradictions', path: 'contradictions', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_RESPONSE_CONTRADICTIONS, handling: 'truncate' },
807
+ { id: 'artefact-references', path: 'artefactReferences', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_RESPONSE_ARTEFACT_REFERENCES, handling: 'truncate' },
808
+ { id: 'escalation-history', path: 'escalationHistory', phase: 'normalised', unit: 'items', minimum: 0, maximum: MAX_RESPONSE_ACTION_HISTORY, handling: 'truncate' },
809
+ ],
810
+ },
811
+ {
812
+ id: 'case.cli-pack.bounds',
813
+ bounds: [
814
+ { id: 'cases', path: 'cases', phase: 'normalised', unit: 'items', minimum: 1, maximum: MAX_CASE_PACK_CASES, handling: 'reject' },
815
+ { id: 'reports', path: 'packet.reports', phase: 'normalised', unit: 'items', minimum: 1, maximum: MAX_CASE_PACK_CASES, handling: 'reject' },
816
+ ],
817
+ },
818
+ {
819
+ id: 'case.workspace.bounds',
820
+ bounds: [
821
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_WORKSPACE_ARCHIVE_BYTES, handling: 'reject' },
822
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_WORKSPACE_ARCHIVE_BYTES, handling: 'reject' },
823
+ { id: 'sections', path: 'manifest.sections', phase: 'pre_accumulation', unit: 'items', minimum: 1, maximum: MAX_WORKSPACE_ARCHIVE_SECTIONS, handling: 'reject' },
824
+ { id: 'section-bytes', path: 'manifest.sections[].bytes', phase: 'pre_accumulation', unit: 'bytes', minimum: 0, maximum: MAX_WORKSPACE_ARCHIVE_SECTION_BYTES, handling: 'reject' },
825
+ ],
826
+ },
827
+ {
828
+ id: 'case.encrypted-workspace.bounds',
829
+ bounds: [
830
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES, handling: 'reject' },
831
+ { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES, handling: 'reject' },
832
+ { id: 'passphrase-characters', path: '$.passphrase', phase: 'action', unit: 'characters', minimum: MIN_WORKSPACE_ARCHIVE_PASSPHRASE_CHARACTERS, maximum: MAX_WORKSPACE_ARCHIVE_PASSPHRASE_BYTES, handling: 'reject' },
833
+ { id: 'passphrase-bytes', path: '$.passphrase', phase: 'action', unit: 'bytes', minimum: MIN_WORKSPACE_ARCHIVE_PASSPHRASE_CHARACTERS, maximum: MAX_WORKSPACE_ARCHIVE_PASSPHRASE_BYTES, handling: 'reject' },
834
+ ],
835
+ },
836
+ ]);
837
+ const CASE_LIFECYCLE_PRIVACY = Object.freeze([
838
+ {
839
+ id: 'case.privacy.browser-state',
840
+ classification: 'analyst_authored_sensitive',
841
+ projection: 'browser_import',
842
+ includedCategories: ['case-identifiers', 'analyst-notes', 'bounded-evidence-summaries', 'exact-submitted-hostnames', 'response-history'],
843
+ excludedCategories: ['raw-upstream-payloads', 'credentials', 'cookies', 'session-data'],
844
+ notePolicy: 'allowed_bounded',
845
+ retention: 'browser_indexeddb',
846
+ network: 'none',
847
+ sharingReview: 'not_applicable',
848
+ },
849
+ {
850
+ id: 'case.privacy.portable-input',
851
+ classification: 'analyst_authored_sensitive',
852
+ projection: 'browser_import',
853
+ includedCategories: ['case-identifiers', 'bounded-case-records', 'exact-submitted-hostnames', 'declared-integrity'],
854
+ excludedCategories: ['raw-upstream-payloads', 'credentials', 'cookies', 'session-data'],
855
+ notePolicy: 'allowed_bounded',
856
+ retention: 'deliberate_local_file',
857
+ network: 'none',
858
+ sharingReview: 'required',
859
+ },
860
+ {
861
+ id: 'case.privacy.portable-output',
862
+ classification: 'analyst_authored_sensitive',
863
+ projection: 'browser_export',
864
+ includedCategories: ['case-identifiers', 'bounded-case-records', 'exact-submitted-hostnames', 'declared-integrity'],
865
+ excludedCategories: ['raw-upstream-payloads', 'credentials', 'cookies', 'session-data'],
866
+ notePolicy: 'allowed_bounded',
867
+ retention: 'operator_controlled_output',
868
+ network: 'none',
869
+ sharingReview: 'required',
870
+ },
871
+ ]);
872
+ const EXPIRY_ID = 'case.expiry.not-applicable';
873
+ const CASE_LIFECYCLE_EXPIRY = Object.freeze([{
874
+ id: EXPIRY_ID,
875
+ field: null,
876
+ anchor: null,
877
+ handling: 'not_applicable',
878
+ phase: 'not_applicable',
879
+ maximumLifetimeDays: null,
880
+ }]);
881
+ const CASE_LIFECYCLE_SERIALISATION = Object.freeze([
882
+ {
883
+ id: 'case.browser-store.json.v13',
884
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
885
+ versions: [CASE_SCHEMA_VERSION],
886
+ mediaType: 'application/json',
887
+ encoding: 'utf-8',
888
+ bom: false,
889
+ indentSpaces: 2,
890
+ terminalLf: true,
891
+ propertyOrder: 'normalised_fixed',
892
+ canonicalisation: null,
893
+ integrity: 'none',
894
+ serializerHookId: 'case.browser.serialise',
895
+ verifierHookIds: [],
896
+ },
897
+ ...[
898
+ ['case.export.json.v13', CASE_EXPORT_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], 'none', []],
899
+ ['case.report.json.v9', CASE_REPORT_SCHEMA, [CASE_REPORT_SCHEMA_VERSION], 'none', []],
900
+ ['case.packet.json.v7', CASE_RESPONSE_PACKET_SCHEMA, [...SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS], 'structural_only_requires_separate_verification', ['case.packet.verify']],
901
+ ['case.workspace.json.v6', WORKSPACE_ARCHIVE_SCHEMA, [WORKSPACE_ARCHIVE_VERSION], 'structural_only_requires_separate_verification', ['case.workspace.verify']],
902
+ ['case.encrypted-workspace.json.v1', ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA, [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION], 'structural_only_requires_separate_verification', ['case.encrypted-workspace.verify']],
903
+ ].map(([id, schema, versions, integrity, verifierHookIds]) => ({
904
+ id,
905
+ schema,
906
+ versions,
907
+ mediaType: 'application/json',
908
+ encoding: 'utf-8',
909
+ bom: false,
910
+ indentSpaces: 2,
911
+ terminalLf: true,
912
+ propertyOrder: 'normalised_fixed',
913
+ canonicalisation: null,
914
+ integrity,
915
+ serializerHookId: 'case.portable.serialise',
916
+ verifierHookIds,
917
+ })),
918
+ {
919
+ id: 'case.cli-pack.json.v2',
920
+ schema: CLI_CASE_PACK_SCHEMA,
921
+ versions: [...SUPPORTED_CLI_CASE_PACK_VERSIONS],
922
+ mediaType: 'application/json',
923
+ encoding: 'utf-8',
924
+ bom: false,
925
+ indentSpaces: 2,
926
+ terminalLf: true,
927
+ propertyOrder: 'normalised_fixed',
928
+ canonicalisation: null,
929
+ integrity: 'structural_only_requires_separate_verification',
930
+ serializerHookId: 'case.cli-pack.serialise',
931
+ verifierHookIds: ['case.cli-pack.verify'],
932
+ },
933
+ ]);
934
+ const sharedEdge = {
935
+ expiryPolicyId: EXPIRY_ID,
936
+ requestMode: 'none',
937
+ bindingState: 'declared_unenforced',
938
+ policyState: 'current',
939
+ };
940
+ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
941
+ {
942
+ id: 'case.browser.read-write',
943
+ plane: 'browser',
944
+ operation: 'normalise-and-store',
945
+ acceptedContracts: [{
946
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
947
+ versions: [...CASE_BROWSER_SUPPORTED_VERSIONS],
948
+ mode: 'direct',
949
+ discriminator: null,
950
+ }],
951
+ emittedContract: { schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION, discriminator: null },
952
+ shapeIds: ['case.browser-store.public', 'case.browser-store.current'],
953
+ boundProfileIds: ['case.browser-store.bounds', 'case.domain.bounds'],
954
+ hookIds: ['case.browser.normalise', 'case.browser.serialise'],
955
+ serialisationProfileId: 'case.browser-store.json.v13',
956
+ privacyProfileId: 'case.privacy.browser-state',
957
+ retentionEffect: 'browser_indexeddb',
958
+ ...sharedEdge,
959
+ },
960
+ {
961
+ id: 'case.export.import',
962
+ plane: 'shared',
963
+ operation: 'non-destructive-merge',
964
+ acceptedContracts: [{
965
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
966
+ versions: [...CASE_IMPORT_VERSIONS],
967
+ mode: 'direct',
968
+ discriminator: null,
969
+ }],
970
+ emittedContract: null,
971
+ shapeIds: ['case.export.public', 'case.export.current'],
972
+ boundProfileIds: ['case.portable.bounds', 'case.domain.bounds'],
973
+ hookIds: ['case.export.merge'],
974
+ serialisationProfileId: null,
975
+ privacyProfileId: 'case.privacy.portable-input',
976
+ retentionEffect: 'deliberate_local_file',
977
+ ...sharedEdge,
978
+ },
979
+ {
980
+ id: 'case.consumer.export-build',
981
+ plane: 'shared',
982
+ operation: 'build-portable-export',
983
+ acceptedContracts: [],
984
+ emittedContract: { schema: CASE_EXPORT_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION, discriminator: null },
985
+ shapeIds: ['case.export.current'],
986
+ boundProfileIds: ['case.portable.bounds', 'case.domain.bounds'],
987
+ hookIds: ['case.export.build', 'case.portable.serialise'],
988
+ serialisationProfileId: 'case.export.json.v13',
989
+ privacyProfileId: 'case.privacy.portable-output',
990
+ retentionEffect: 'operator_controlled_output',
991
+ ...sharedEdge,
992
+ },
993
+ {
994
+ id: 'case.consumer.report-build',
995
+ plane: 'shared',
996
+ operation: 'build-report',
997
+ acceptedContracts: [],
998
+ emittedContract: { schema: CASE_REPORT_SCHEMA, version: CASE_REPORT_SCHEMA_VERSION, discriminator: null },
999
+ shapeIds: ['case.report.v9'],
1000
+ boundProfileIds: ['case.domain.bounds'],
1001
+ hookIds: ['case.report.build', 'case.portable.serialise'],
1002
+ serialisationProfileId: 'case.report.json.v9',
1003
+ privacyProfileId: 'case.privacy.portable-output',
1004
+ retentionEffect: 'operator_controlled_output',
1005
+ ...sharedEdge,
1006
+ },
1007
+ {
1008
+ id: 'case.consumer.packet-verify',
1009
+ plane: 'shared',
1010
+ operation: 'verify-integrity',
1011
+ acceptedContracts: [{
1012
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
1013
+ versions: [...SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS],
1014
+ mode: 'direct',
1015
+ discriminator: null,
1016
+ }],
1017
+ emittedContract: null,
1018
+ shapeIds: ['case.response-packet.v6', 'case.response-packet.v7'],
1019
+ boundProfileIds: ['case.packet.bounds'],
1020
+ hookIds: ['case.packet.verify'],
1021
+ serialisationProfileId: null,
1022
+ privacyProfileId: 'case.privacy.portable-input',
1023
+ retentionEffect: 'deliberate_local_file',
1024
+ ...sharedEdge,
1025
+ },
1026
+ {
1027
+ id: 'case.consumer.packet-offline-verify',
1028
+ plane: 'cli',
1029
+ operation: 'validate-structure-and-integrity',
1030
+ acceptedContracts: [{
1031
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
1032
+ versions: [...SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS],
1033
+ mode: 'direct',
1034
+ discriminator: null,
1035
+ }],
1036
+ emittedContract: null,
1037
+ shapeIds: ['case.response-packet.v6', 'case.response-packet.v7'],
1038
+ boundProfileIds: ['case.packet.bounds'],
1039
+ hookIds: ['case.packet.structure', 'case.packet.verify'],
1040
+ serialisationProfileId: null,
1041
+ privacyProfileId: 'case.privacy.portable-input',
1042
+ retentionEffect: 'deliberate_local_file',
1043
+ ...sharedEdge,
1044
+ },
1045
+ {
1046
+ id: 'case.consumer.packet-build',
1047
+ plane: 'shared',
1048
+ operation: 'build-packet',
1049
+ acceptedContracts: [],
1050
+ emittedContract: { schema: CASE_RESPONSE_PACKET_SCHEMA, version: CASE_RESPONSE_PACKET_VERSION, discriminator: null },
1051
+ shapeIds: ['case.response-packet.v7'],
1052
+ boundProfileIds: ['case.packet.bounds'],
1053
+ hookIds: ['case.packet.build', 'case.portable.serialise'],
1054
+ serialisationProfileId: 'case.packet.json.v7',
1055
+ privacyProfileId: 'case.privacy.portable-output',
1056
+ retentionEffect: 'operator_controlled_output',
1057
+ ...sharedEdge,
1058
+ },
1059
+ {
1060
+ id: 'case.consumer.review-inputs-validate',
1061
+ plane: 'shared',
1062
+ operation: 'validate-review-inputs',
1063
+ acceptedContracts: [{
1064
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
1065
+ versions: [CASE_RESPONSE_REVIEW_INPUTS_VERSION],
1066
+ mode: 'direct',
1067
+ discriminator: null,
1068
+ }],
1069
+ emittedContract: null,
1070
+ shapeIds: ['case.response-review-inputs.v1'],
1071
+ boundProfileIds: ['case.packet.bounds'],
1072
+ hookIds: ['case.review-inputs.validate'],
1073
+ serialisationProfileId: null,
1074
+ privacyProfileId: 'case.privacy.portable-input',
1075
+ retentionEffect: 'deliberate_local_file',
1076
+ ...sharedEdge,
1077
+ },
1078
+ {
1079
+ id: 'case.consumer.review-inputs-build',
1080
+ plane: 'shared',
1081
+ operation: 'build-review-inputs',
1082
+ acceptedContracts: [],
1083
+ emittedContract: {
1084
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
1085
+ version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
1086
+ discriminator: null,
1087
+ },
1088
+ shapeIds: ['case.response-review-inputs.v1'],
1089
+ boundProfileIds: ['case.packet.bounds'],
1090
+ hookIds: ['case.review-inputs.build'],
1091
+ serialisationProfileId: null,
1092
+ privacyProfileId: 'case.privacy.portable-output',
1093
+ retentionEffect: 'operator_controlled_output',
1094
+ ...sharedEdge,
1095
+ },
1096
+ {
1097
+ id: 'case.consumer.cli-pack-verify',
1098
+ plane: 'cli',
1099
+ operation: 'verify-case-pack',
1100
+ acceptedContracts: [{
1101
+ schema: CLI_CASE_PACK_SCHEMA,
1102
+ versions: [...SUPPORTED_CLI_CASE_PACK_VERSIONS],
1103
+ mode: 'direct',
1104
+ discriminator: null,
1105
+ }],
1106
+ emittedContract: null,
1107
+ shapeIds: ['case.cli-pack.current'],
1108
+ boundProfileIds: ['case.portable.bounds', 'case.cli-pack.bounds'],
1109
+ hookIds: ['case.cli-pack.verify'],
1110
+ serialisationProfileId: null,
1111
+ privacyProfileId: 'case.privacy.portable-input',
1112
+ retentionEffect: 'deliberate_local_file',
1113
+ ...sharedEdge,
1114
+ },
1115
+ {
1116
+ id: 'case.consumer.cli-pack-build',
1117
+ plane: 'cli',
1118
+ operation: 'build-case-pack',
1119
+ acceptedContracts: [{
1120
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
1121
+ versions: [CASE_SCHEMA_VERSION],
1122
+ mode: 'direct',
1123
+ discriminator: null,
1124
+ }],
1125
+ emittedContract: { schema: CLI_CASE_PACK_SCHEMA, version: CLI_CASE_PACK_VERSION, discriminator: null },
1126
+ shapeIds: ['case.export.current', 'case.cli-pack.current'],
1127
+ boundProfileIds: ['case.portable.bounds', 'case.domain.bounds', 'case.cli-pack.bounds'],
1128
+ hookIds: ['case.cli-pack.build', 'case.cli-pack.serialise'],
1129
+ serialisationProfileId: 'case.cli-pack.json.v2',
1130
+ privacyProfileId: 'case.privacy.portable-output',
1131
+ retentionEffect: 'operator_controlled_output',
1132
+ ...sharedEdge,
1133
+ },
1134
+ {
1135
+ id: 'case.consumer.workspace-verify',
1136
+ plane: 'browser',
1137
+ operation: 'verify-and-preview-workspace',
1138
+ acceptedContracts: [{
1139
+ schema: WORKSPACE_ARCHIVE_SCHEMA,
1140
+ versions: [...SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS],
1141
+ mode: 'direct',
1142
+ discriminator: null,
1143
+ }, {
1144
+ schema: WORKSPACE_SETTINGS_SCHEMA,
1145
+ versions: [WORKSPACE_SETTINGS_VERSION],
1146
+ mode: 'embedded',
1147
+ discriminator: null,
1148
+ }],
1149
+ emittedContract: null,
1150
+ shapeIds: ['case.workspace-archive.v5', 'case.workspace-archive.v6', 'case.workspace-settings.v1'],
1151
+ boundProfileIds: ['case.workspace.bounds'],
1152
+ hookIds: ['case.workspace.verify'],
1153
+ serialisationProfileId: null,
1154
+ privacyProfileId: 'case.privacy.portable-input',
1155
+ retentionEffect: 'deliberate_local_file',
1156
+ ...sharedEdge,
1157
+ },
1158
+ {
1159
+ id: 'case.consumer.workspace-build',
1160
+ plane: 'browser',
1161
+ operation: 'build-workspace',
1162
+ acceptedContracts: [],
1163
+ emittedContract: { schema: WORKSPACE_ARCHIVE_SCHEMA, version: WORKSPACE_ARCHIVE_VERSION, discriminator: null },
1164
+ shapeIds: ['case.workspace-archive.v6'],
1165
+ boundProfileIds: ['case.workspace.bounds'],
1166
+ hookIds: ['case.workspace.build', 'case.portable.serialise'],
1167
+ serialisationProfileId: 'case.workspace.json.v6',
1168
+ privacyProfileId: 'case.privacy.portable-output',
1169
+ retentionEffect: 'operator_controlled_output',
1170
+ ...sharedEdge,
1171
+ },
1172
+ {
1173
+ id: 'case.consumer.workspace-settings-build',
1174
+ plane: 'browser',
1175
+ operation: 'build-workspace-settings-section',
1176
+ acceptedContracts: [],
1177
+ emittedContract: { schema: WORKSPACE_SETTINGS_SCHEMA, version: WORKSPACE_SETTINGS_VERSION, discriminator: null },
1178
+ shapeIds: ['case.workspace-settings.v1'],
1179
+ boundProfileIds: ['case.workspace.bounds'],
1180
+ hookIds: ['case.workspace.build'],
1181
+ serialisationProfileId: null,
1182
+ privacyProfileId: 'case.privacy.portable-output',
1183
+ retentionEffect: 'operator_controlled_output',
1184
+ ...sharedEdge,
1185
+ },
1186
+ {
1187
+ id: 'case.consumer.encrypted-workspace-verify',
1188
+ plane: 'browser',
1189
+ operation: 'decrypt-and-verify-workspace',
1190
+ acceptedContracts: [{
1191
+ schema: ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA,
1192
+ versions: [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION],
1193
+ mode: 'direct',
1194
+ discriminator: null,
1195
+ }],
1196
+ emittedContract: null,
1197
+ shapeIds: ['case.encrypted-workspace-archive.v1'],
1198
+ boundProfileIds: ['case.encrypted-workspace.bounds'],
1199
+ hookIds: ['case.encrypted-workspace.verify'],
1200
+ serialisationProfileId: null,
1201
+ privacyProfileId: 'case.privacy.portable-input',
1202
+ retentionEffect: 'deliberate_local_file',
1203
+ ...sharedEdge,
1204
+ },
1205
+ {
1206
+ id: 'case.consumer.encrypted-workspace-build',
1207
+ plane: 'browser',
1208
+ operation: 'encrypt-workspace',
1209
+ acceptedContracts: [{
1210
+ schema: WORKSPACE_ARCHIVE_SCHEMA,
1211
+ versions: [...SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS],
1212
+ mode: 'direct',
1213
+ discriminator: null,
1214
+ }],
1215
+ emittedContract: {
1216
+ schema: ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA,
1217
+ version: ENCRYPTED_WORKSPACE_ARCHIVE_VERSION,
1218
+ discriminator: null,
1219
+ },
1220
+ shapeIds: ['case.workspace-archive.v5', 'case.workspace-archive.v6', 'case.encrypted-workspace-archive.v1'],
1221
+ boundProfileIds: ['case.workspace.bounds', 'case.encrypted-workspace.bounds'],
1222
+ hookIds: ['case.encrypted-workspace.build', 'case.portable.serialise'],
1223
+ serialisationProfileId: 'case.encrypted-workspace.json.v1',
1224
+ privacyProfileId: 'case.privacy.portable-output',
1225
+ retentionEffect: 'operator_controlled_output',
1226
+ ...sharedEdge,
1227
+ },
1228
+ ]);
1229
+ export const CASE_PORTABILITY_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily({
1230
+ id: 'case-portability',
1231
+ owner: CASE_CONTRACT_OWNER,
1232
+ privacy: 'analyst_authored_sensitive',
1233
+ compatibility: CASE_PORTABILITY_COMPATIBILITY,
1234
+ contracts: [
1235
+ ...browserContracts,
1236
+ ...exportContracts,
1237
+ ...reportContracts,
1238
+ ...packetContracts,
1239
+ lifecycleContract({
1240
+ compatibilityId: CASE_RESPONSE_REVIEW_INPUTS_COMPATIBILITY.id,
1241
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
1242
+ version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
1243
+ lifecycle: 'current',
1244
+ readable: true,
1245
+ emitted: true,
1246
+ exactKeys: true,
1247
+ extensionPolicy: 'reject',
1248
+ futureVersionBehaviour: 'reject',
1249
+ migrationTarget: null,
1250
+ canonicalisation: 'sorted-json-v2',
1251
+ byteBudget: null,
1252
+ }),
1253
+ lifecycleContract({
1254
+ compatibilityId: CLI_CASE_PACK_COMPATIBILITY.id,
1255
+ schema: CLI_CASE_PACK_SCHEMA,
1256
+ version: CLI_CASE_PACK_VERSION,
1257
+ lifecycle: 'current',
1258
+ readable: true,
1259
+ emitted: true,
1260
+ exactKeys: true,
1261
+ extensionPolicy: 'reject',
1262
+ futureVersionBehaviour: 'reject',
1263
+ migrationTarget: null,
1264
+ canonicalisation: 'sorted-json-v2',
1265
+ byteBudget: MAX_CASE_IMPORT_BYTES,
1266
+ }),
1267
+ ...workspaceContracts,
1268
+ lifecycleContract({
1269
+ compatibilityId: WORKSPACE_SETTINGS_COMPATIBILITY.id,
1270
+ schema: WORKSPACE_SETTINGS_SCHEMA,
1271
+ version: WORKSPACE_SETTINGS_VERSION,
1272
+ lifecycle: 'current',
1273
+ readable: true,
1274
+ emitted: true,
1275
+ exactKeys: true,
1276
+ extensionPolicy: 'reject',
1277
+ futureVersionBehaviour: 'reject',
1278
+ migrationTarget: null,
1279
+ canonicalisation: null,
1280
+ byteBudget: null,
1281
+ }),
1282
+ lifecycleContract({
1283
+ compatibilityId: ENCRYPTED_WORKSPACE_ARCHIVE_COMPATIBILITY.id,
1284
+ schema: ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA,
1285
+ version: ENCRYPTED_WORKSPACE_ARCHIVE_VERSION,
1286
+ lifecycle: 'current',
1287
+ readable: true,
1288
+ emitted: true,
1289
+ exactKeys: true,
1290
+ extensionPolicy: 'reject',
1291
+ futureVersionBehaviour: 'reject',
1292
+ migrationTarget: null,
1293
+ canonicalisation: 'aes-gcm-v1',
1294
+ byteBudget: MAX_ENCRYPTED_WORKSPACE_ARCHIVE_BYTES,
1295
+ }),
1296
+ ],
1297
+ fixtures: CASE_LIFECYCLE_FIXTURES,
1298
+ metadata: {
1299
+ metadataVersion: 4,
1300
+ enforcement: 'declarative_only',
1301
+ shapes: CASE_LIFECYCLE_SHAPES,
1302
+ boundProfiles: CASE_LIFECYCLE_BOUNDS,
1303
+ hooks: CASE_LIFECYCLE_HOOKS,
1304
+ serialisationProfiles: CASE_LIFECYCLE_SERIALISATION,
1305
+ privacyProfiles: CASE_LIFECYCLE_PRIVACY,
1306
+ expiryProfiles: CASE_LIFECYCLE_EXPIRY,
1307
+ consumerEdges: CASE_LIFECYCLE_CONSUMERS,
1308
+ consumerRelationships: [],
1309
+ },
1310
+ });