@slicedearth/whoisleuth-cli 1.47.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +74 -177
  2. package/SECURITY.md +6 -3
  3. package/bin/whoisleuth.mjs +2 -1
  4. package/cli/archive-inspect.mjs +28 -10
  5. package/cli/arguments.mjs +204 -174
  6. package/cli/artifact-structure.mjs +837 -163
  7. package/cli/artifact-verify.mjs +90 -61
  8. package/cli/bounded-json.mjs +1 -1
  9. package/cli/bulk-checkpoint.mjs +33 -20
  10. package/cli/bulk.mjs +9 -2
  11. package/cli/case-pack.mjs +180 -576
  12. package/cli/ci-report.mjs +6 -1
  13. package/cli/collection-preflight.mjs +2 -2
  14. package/cli/command-catalogue.mjs +12 -3
  15. package/cli/command-reference.mjs +910 -92
  16. package/cli/completion.mjs +504 -226
  17. package/cli/config-profile.mjs +13 -3
  18. package/cli/discover.mjs +7 -1
  19. package/cli/discovery-observation-snapshot.mjs +24 -28
  20. package/cli/discovery-scan-command-runner.mjs +9 -9
  21. package/cli/discovery-scan.mjs +17 -3
  22. package/cli/discovery-snapshot.mjs +15 -7
  23. package/cli/domain-control-monitor.mjs +203 -30
  24. package/cli/domain-control-observations.mjs +15 -21
  25. package/cli/errors.mjs +8 -3
  26. package/cli/evidence-signing.mjs +10 -20
  27. package/cli/external-observation-mapping.mjs +3 -3
  28. package/cli/formatters/json.mjs +20 -32
  29. package/cli/formatters/terminal.mjs +583 -22
  30. package/cli/input.mjs +7 -2
  31. package/cli/interactive-launcher.mjs +226 -0
  32. package/cli/interchange-report.mjs +19 -19
  33. package/cli/investigation-manifest.mjs +1 -1
  34. package/cli/investigation-plan.mjs +167 -16
  35. package/cli/investigation-run.mjs +9 -1
  36. package/cli/lookalike-calibration.mjs +3 -1
  37. package/cli/lookup-brief.mjs +13 -8
  38. package/cli/lookup-browser.mjs +844 -0
  39. package/cli/lookup-command-runner.mjs +94 -31
  40. package/cli/lookup-diff.mjs +2 -2
  41. package/cli/lookup-plan.mjs +1 -0
  42. package/cli/mail-review.mjs +206 -94
  43. package/cli/manual.mjs +5 -5
  44. package/cli/offline-evidence-review.mjs +18 -7
  45. package/cli/open-asset-model-bridge.mjs +1 -1
  46. package/cli/output-file.mjs +70 -14
  47. package/cli/page-compare.mjs +11 -6
  48. package/cli/progress-events.mjs +2 -1
  49. package/cli/progress.mjs +3 -3
  50. package/cli/registry-cohort.mjs +9 -51
  51. package/cli/registry-doctor.mjs +67 -28
  52. package/cli/retained-artifact-diff.mjs +7 -8
  53. package/cli/risk-calibration.mjs +85 -38
  54. package/cli/runner.mjs +189 -75
  55. package/cli/saved-lookup.mjs +424 -57
  56. package/cli/sharing-review.mjs +6 -1
  57. package/cli/source-reliability.mjs +75 -36
  58. package/cli/terminal-presentation.mjs +43 -18
  59. package/docs/cli-reference.md +125 -1708
  60. package/docs/cli.md +86 -254
  61. package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
  62. package/frontend/src/lib/analysis/domain-control-records.js +3 -88
  63. package/lib/availability.mjs +58 -40
  64. package/lib/bounded-contract-normalizers.mjs +2 -4
  65. package/lib/bounded-file.mjs +78 -4
  66. package/lib/bounded-json.mjs +51 -13
  67. package/lib/browser-library-profile.mjs +1 -1
  68. package/lib/classify.mjs +56 -5
  69. package/lib/client-behavior-profile.mjs +1 -1
  70. package/lib/credential-surface-profile.mjs +1 -1
  71. package/lib/cryptographic-assurance.mjs +35 -17
  72. package/lib/ct-query.mjs +3 -3
  73. package/lib/ct-search.mjs +37 -21
  74. package/lib/distributed-operation-budget.mjs +4 -2
  75. package/lib/dns-delegation-health.mjs +9 -6
  76. package/lib/dns-intelligence.mjs +1 -1
  77. package/lib/dnssec-chain-validation.mjs +2 -3
  78. package/lib/dnssec-evidence-validation.mjs +13 -7
  79. package/lib/domain-assurance.mjs +12 -12
  80. package/lib/domain-change-packet.mjs +1 -1
  81. package/lib/domain-control-flight-recorder.mjs +442 -78
  82. package/lib/domain-control-manifest.mjs +314 -59
  83. package/lib/domain-posture-analysis.mjs +5 -2
  84. package/lib/encrypted-dns-contract.mjs +2 -3
  85. package/lib/evidence-export.mjs +518 -36
  86. package/lib/external-intelligence-risk.mjs +26 -10
  87. package/lib/feature-policy.mjs +1 -1
  88. package/lib/homepage-metadata-contract.mjs +290 -0
  89. package/lib/hostname.mjs +1 -1
  90. package/lib/html-signals.mjs +89 -5
  91. package/lib/http-intelligence.mjs +331 -2
  92. package/lib/interchange-fidelity-registry.mjs +28 -26
  93. package/lib/lookup-cache.mjs +6 -4
  94. package/lib/lookup.mjs +25 -2
  95. package/lib/observed-network-context.mjs +1 -1
  96. package/lib/page-fingerprints.mjs +2 -2
  97. package/lib/page-role-profile.mjs +1 -1
  98. package/lib/portable-generator.mjs +8 -0
  99. package/lib/provider-policy-admission.mjs +3 -1
  100. package/lib/rdap-bootstrap.mjs +76 -16
  101. package/lib/rdap-client.mjs +20 -12
  102. package/lib/rdap-normalization.mjs +306 -75
  103. package/lib/rdap-transport.mjs +2 -2
  104. package/lib/rdap-validation.mjs +65 -14
  105. package/lib/rdap.mjs +2 -2
  106. package/lib/registrable-domain.mjs +36 -0
  107. package/lib/registry-insights.mjs +7 -0
  108. package/lib/response-policy.mjs +27 -4
  109. package/lib/risk-calibration-summary.mjs +35 -33
  110. package/lib/risk-scoring.mjs +4 -3
  111. package/lib/safe-fetch.mjs +62 -40
  112. package/lib/security-txt.mjs +1 -1
  113. package/lib/smtp-transport-review.mjs +8 -5
  114. package/lib/sslbl-intelligence.mjs +6 -8
  115. package/lib/static-html-analysis.mjs +301 -4
  116. package/lib/structured-data-identity.mjs +51 -19
  117. package/lib/threat-intelligence-fixtures.mjs +2 -4
  118. package/lib/threat-intelligence-manifests.mjs +2 -7
  119. package/lib/threat-intelligence-runtime.mjs +3 -5
  120. package/lib/threat-intelligence-types.mjs +3 -0
  121. package/lib/threatfox-intelligence.mjs +7 -3
  122. package/lib/tls-intelligence.mjs +4 -4
  123. package/lib/urlhaus-intelligence.mjs +4 -2
  124. package/lib/urlscan-intelligence.mjs +6 -5
  125. package/lib/website-security-posture.mjs +1 -1
  126. package/lib/website-technology.mjs +1 -1
  127. package/lib/whois-authority.mjs +43 -8
  128. package/lib/whois-chain.mjs +25 -3
  129. package/lib/whois-normalization.mjs +3 -1
  130. package/lib/whois-transport.mjs +12 -3
  131. package/lib/zone-intent-review.mjs +1 -1
  132. package/package.json +12 -4
  133. package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
  134. package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
  135. package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
  136. package/packages/cases/case-migration-model.mjs +476 -0
  137. package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
  138. package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
  139. package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
  140. package/packages/cases/case-record-model.mjs +9 -0
  141. package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
  142. package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
  143. package/packages/cases/case-response-model.mjs +1604 -0
  144. package/packages/cases/case-response-packet.mjs +1312 -0
  145. package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
  146. package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
  147. package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
  148. package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
  149. package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
  150. package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
  151. package/packages/comparison/favicon-similarity.mjs +78 -0
  152. package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
  153. package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
  154. package/packages/contracts/analyst-review-state-contract.mjs +61 -0
  155. package/packages/contracts/analyst-review-state.mjs +50 -0
  156. package/packages/contracts/case-portability.mjs +1310 -0
  157. package/packages/contracts/cli-command-catalogue.mjs +2 -0
  158. package/packages/contracts/cli-lookup.mjs +394 -0
  159. package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
  160. package/packages/contracts/domain-control-manifest.mjs +1027 -0
  161. package/packages/contracts/domain-control-monitor.mjs +310 -0
  162. package/packages/contracts/domain-control-review.mjs +828 -0
  163. package/packages/contracts/domain-name.mjs +4 -0
  164. package/packages/contracts/external-observation-interchange.mjs +224 -0
  165. package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
  166. package/packages/contracts/investigation-portability.mjs +331 -0
  167. package/packages/contracts/offline-comparison.mjs +160 -0
  168. package/packages/contracts/risk-calibration.mjs +1288 -0
  169. package/packages/contracts/schema-compatibility.mjs +71 -0
  170. package/packages/contracts/schema-lifecycle.mjs +1702 -0
  171. package/packages/contracts/sslbl-snapshot.mjs +17 -0
  172. package/packages/contracts/workspace-portability.mjs +1347 -0
  173. package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
  174. package/packages/evidence/decision-fact.mjs +735 -0
  175. package/packages/evidence/domain-control-runtime.mjs +547 -0
  176. package/packages/evidence/domain-name.mjs +41 -0
  177. package/packages/evidence/observation.mjs +384 -0
  178. package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
  179. package/packages/interchange/retained-text.mjs +8 -0
  180. package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
  181. package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
  182. package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
  183. package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
  184. package/packages/investigation/bulk-review-policy.mjs +1 -0
  185. package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
  186. package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
  187. package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
  188. package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
  189. package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
  190. package/packages/investigation/lookup-investigation-brief.mjs +172 -0
  191. package/packages/monitoring/analyst-review-state.mjs +680 -0
  192. package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
  193. package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
  194. package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
  195. package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
  196. package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
  197. package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
  198. package/packages/workspace/hostile-input.mjs +172 -0
  199. package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
  200. package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
  201. package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
  202. package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
  203. package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
  204. package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
  205. package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
  206. package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
  207. package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
  208. package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
  209. package/third-party-notices.txt +14 -14
  210. package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
  211. package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
  212. package/frontend/src/lib/analysis/case-migration-model.js +0 -305
  213. package/frontend/src/lib/analysis/case-record-model.js +0 -9
  214. package/frontend/src/lib/analysis/case-response-model.js +0 -728
  215. package/frontend/src/lib/analysis/case-response-packet.js +0 -583
  216. package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
  217. package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
  218. package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
  219. package/frontend/src/lib/analysis/lookup-response.js +0 -2
  220. package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
  221. package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
  222. package/frontend/src/lib/analysis/scoring.js +0 -55
  223. package/frontend/src/lib/analysis/utils.js +0 -255
  224. package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
  225. package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
  226. package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
  227. package/frontend/src/lib/analysis/visualization-models.js +0 -338
  228. package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
  229. package/lib/bounded-json-response.mjs +0 -170
  230. package/lib/lookup-response-contract.mjs +0 -1034
  231. package/lib/observation.mjs +0 -115
  232. /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
  233. /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
  234. /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
  235. /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
@@ -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
+ });