clm-kernel 0.0.0__py3-none-any.whl

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 (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,163 @@
1
+ """Content Handle system for MCard.
2
+
3
+ A ContentHandle provides a mutable pointer to immutable MCard content,
4
+ enabling human-friendly naming and versioning while preserving data immutability.
5
+
6
+ ## UTF-8 Support
7
+ Handles support Unicode characters for internationalization:
8
+ - Must start with a Unicode letter (any language)
9
+ - Can contain letters, digits, underscores, and hyphens
10
+ - Maximum 63 characters
11
+ - Normalized to NFC (Canonical Decomposition, then Canonical Composition)
12
+
13
+ Examples: "my_doc", "文檔", "مستند", "ドキュメント", "документ"
14
+ """
15
+
16
+ import logging
17
+ import unicodedata
18
+ from datetime import datetime, timezone
19
+ from typing import TYPE_CHECKING
20
+
21
+ if TYPE_CHECKING:
22
+ pass
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # Maximum handle length
27
+ MAX_HANDLE_LENGTH = 255
28
+
29
+
30
+ def _is_valid_start_char(char: str) -> bool:
31
+ """Check if character is valid as the first character of a handle.
32
+
33
+ Must be a Unicode letter (category 'L').
34
+ """
35
+ return unicodedata.category(char).startswith("L")
36
+
37
+
38
+ def _is_valid_body_char(char: str) -> bool:
39
+ """Check if character is valid in the body of a handle.
40
+
41
+ Can be: letter, number, underscore, hyphen.
42
+ """
43
+ cat = unicodedata.category(char)
44
+ return (
45
+ cat.startswith("L") # Letters (any language)
46
+ or cat.startswith("N") # Numbers (any language)
47
+ or char == "_" # Underscore
48
+ or char == "-" # Hyphen
49
+ or char == "." # Dot
50
+ or char == "/" # Forward slash
51
+ or char == " " # Space
52
+ or char == ":" # Colon (for URIs)
53
+ )
54
+
55
+
56
+ class HandleValidationError(ValueError):
57
+ """Raised when a handle string is invalid."""
58
+
59
+ pass
60
+
61
+
62
+ def validate_handle(handle: str) -> str:
63
+ """Validate and normalize a handle string.
64
+
65
+ Args:
66
+ handle: The handle string to validate.
67
+
68
+ Returns:
69
+ The validated and normalized handle (NFC normalized, case-preserving).
70
+
71
+ Raises:
72
+ HandleValidationError: If the handle is invalid.
73
+ """
74
+ if not handle:
75
+ raise HandleValidationError("Handle cannot be empty.")
76
+
77
+ # Normalize: strip whitespace, apply Unicode NFC normalization (case-preserving)
78
+ normalized = unicodedata.normalize("NFC", handle.strip())
79
+
80
+ if len(normalized) > MAX_HANDLE_LENGTH:
81
+ raise HandleValidationError(
82
+ f"Handle '{handle}' is too long ({len(normalized)} chars). "
83
+ f"Maximum length is {MAX_HANDLE_LENGTH} characters."
84
+ )
85
+
86
+ if len(normalized) == 0:
87
+ raise HandleValidationError("Handle cannot be empty after normalization.")
88
+
89
+ # Validate first character
90
+ if not _is_valid_start_char(normalized[0]):
91
+ raise HandleValidationError(
92
+ f"Invalid handle '{handle}'. Must start with a letter (any language)."
93
+ )
94
+
95
+ # Validate remaining characters
96
+ for i, char in enumerate(normalized[1:], start=1):
97
+ if not _is_valid_body_char(char):
98
+ raise HandleValidationError(
99
+ f"Invalid character '{char}' at position {i} in handle '{handle}'. "
100
+ "Allowed: letters, digits, underscores, hyphens."
101
+ )
102
+
103
+ return normalized
104
+
105
+
106
+ class ContentHandle:
107
+ """A mutable pointer to an immutable MCard hash.
108
+
109
+ Attributes:
110
+ handle: The validated handle string.
111
+ current_hash: The hash of the MCard this handle points to.
112
+ created_at: When this handle was first created.
113
+ updated_at: When this handle was last updated to point to a new hash.
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ handle: str,
119
+ current_hash: str,
120
+ created_at: datetime | None = None,
121
+ updated_at: datetime | None = None,
122
+ ):
123
+ """Initialize a ContentHandle.
124
+
125
+ Args:
126
+ handle: The user-defined handle string (will be validated).
127
+ current_hash: The MCard hash this handle points to.
128
+ created_at: Creation timestamp (defaults to now).
129
+ updated_at: Last update timestamp (defaults to created_at).
130
+ """
131
+ self.handle = validate_handle(handle)
132
+ self.current_hash = current_hash
133
+ self.created_at = created_at or datetime.now(timezone.utc)
134
+ self.updated_at = updated_at or self.created_at
135
+
136
+ def update(self, new_hash: str) -> str:
137
+ """Update the handle to point to a new hash.
138
+
139
+ Args:
140
+ new_hash: The new MCard hash to point to.
141
+
142
+ Returns:
143
+ The previous hash (for history tracking).
144
+ """
145
+ previous_hash = self.current_hash
146
+ self.current_hash = new_hash
147
+ self.updated_at = datetime.now(timezone.utc)
148
+ logger.info(
149
+ f"Handle '{self.handle}' updated: {previous_hash[:8]}... -> {new_hash[:8]}..."
150
+ )
151
+ return previous_hash
152
+
153
+ def __repr__(self) -> str:
154
+ return f"ContentHandle(handle='{self.handle}', current_hash='{self.current_hash[:8]}...')"
155
+
156
+ def to_dict(self) -> dict:
157
+ """Convert to dictionary representation."""
158
+ return {
159
+ "handle": self.handle,
160
+ "current_hash": self.current_hash,
161
+ "created_at": self.created_at.isoformat(),
162
+ "updated_at": self.updated_at.isoformat(),
163
+ }
@@ -0,0 +1,6 @@
1
+ """Hash module for MCard."""
2
+
3
+ from .constants import HASH_ALGORITHM_HIERARCHY
4
+ from .enums import HashAlgorithm
5
+
6
+ __all__ = ["HashAlgorithm", "HASH_ALGORITHM_HIERARCHY"]
@@ -0,0 +1,3 @@
1
+ from ..validator import HashValidator
2
+
3
+ __all__ = ["HashValidator"]
@@ -0,0 +1,7 @@
1
+ # Custom hash algorithms can be implemented here.
2
+
3
+
4
+ class MyCustomHash:
5
+ def compute(self, content: bytes) -> str:
6
+ # Implement your custom hashing logic here
7
+ return "custom_hash_value"
@@ -0,0 +1,178 @@
1
+ import struct
2
+
3
+ # SHA-256 constants
4
+ K = [
5
+ 0x428A2F98,
6
+ 0x71374491,
7
+ 0xB5C0FBCF,
8
+ 0xE9B5DBA5,
9
+ 0x3956C25B,
10
+ 0x59F111F1,
11
+ 0x923F82A4,
12
+ 0xAB1C5ED5,
13
+ 0xD807AA98,
14
+ 0x12835B01,
15
+ 0x243185BE,
16
+ 0x550C7DC3,
17
+ 0x72BE5D74,
18
+ 0x80DEB1FE,
19
+ 0x9BDC06A7,
20
+ 0xC19BF174,
21
+ 0xE49B69C1,
22
+ 0xEFBE4786,
23
+ 0x0FC19DC6,
24
+ 0x240CA1CC,
25
+ 0x2DE92C6F,
26
+ 0x4A7484AA,
27
+ 0x5CB0A9DC,
28
+ 0x76F988DA,
29
+ 0x983E5152,
30
+ 0xA831C66D,
31
+ 0xB00327C8,
32
+ 0xBF597FC7,
33
+ 0xC6E00BF3,
34
+ 0xD5A79147,
35
+ 0x06CA6351,
36
+ 0x14292967,
37
+ 0x27B70A85,
38
+ 0x2E1B2138,
39
+ 0x4D2C6DFC,
40
+ 0x53380D13,
41
+ 0x650A7354,
42
+ 0x766A0ABB,
43
+ 0x81C2C92E,
44
+ 0x92722C85,
45
+ 0xA2BFE8A1,
46
+ 0xA81A664B,
47
+ 0xC24B8B70,
48
+ 0xC76C51A3,
49
+ 0xD192E819,
50
+ 0xD6990624,
51
+ 0xF40E3585,
52
+ 0x106AA070,
53
+ 0x19A4C116,
54
+ 0x1E376C08,
55
+ 0x2748774C,
56
+ 0x34B0BCB5,
57
+ 0x391C0CB3,
58
+ 0x4ED8AA4A,
59
+ 0x5B9CCA4F,
60
+ 0x682E6FF3,
61
+ 0x748F82EE,
62
+ 0x78A5636F,
63
+ 0x84C87814,
64
+ 0x8CC70208,
65
+ 0x90BEFFFA,
66
+ 0xA4506CEB,
67
+ 0xBEF9A3F7,
68
+ 0xC67178F2,
69
+ ]
70
+
71
+ # Initial hash values
72
+ H = [
73
+ 0x6A09E667,
74
+ 0xBB67AE85,
75
+ 0x3C6EF372,
76
+ 0xA54FF53A,
77
+ 0x510E527F,
78
+ 0x9B05688C,
79
+ 0x1F83D9AB,
80
+ 0x5BE0CD19,
81
+ ]
82
+
83
+
84
+ def right_rotate(value, shift):
85
+ """Perform a right circular rotation on a 32-bit integer."""
86
+ return ((value >> shift) | (value << (32 - shift))) & 0xFFFFFFFF
87
+
88
+
89
+ def localized_sha256(message):
90
+ """Compute the SHA-256 hash of a message."""
91
+ global H
92
+ H = [
93
+ 0x6A09E667,
94
+ 0xBB67AE85,
95
+ 0x3C6EF372,
96
+ 0xA54FF53A,
97
+ 0x510E527F,
98
+ 0x9B05688C,
99
+ 0x1F83D9AB,
100
+ 0x5BE0CD19,
101
+ ]
102
+
103
+ # Ensure message is in bytes
104
+ if isinstance(message, str):
105
+ message = message.encode("utf-8")
106
+ elif not isinstance(message, bytes):
107
+ raise TypeError("Input must be a string or bytes")
108
+
109
+ original_length_bits = len(message) * 8
110
+ message += b"\x80"
111
+
112
+ while (len(message) * 8) % 512 != 448:
113
+ message += b"\x00"
114
+
115
+ message += struct.pack(">Q", original_length_bits)
116
+
117
+ chunks = [message[i : i + 64] for i in range(0, len(message), 64)]
118
+
119
+ for chunk in chunks:
120
+ w = [0] * 64
121
+ for i in range(16):
122
+ w[i] = struct.unpack(">I", chunk[i * 4 : (i + 1) * 4])[0]
123
+
124
+ for i in range(16, 64):
125
+ s0 = (
126
+ right_rotate(w[i - 15], 7)
127
+ ^ right_rotate(w[i - 15], 18)
128
+ ^ (w[i - 15] >> 3)
129
+ )
130
+ s1 = (
131
+ right_rotate(w[i - 2], 17)
132
+ ^ right_rotate(w[i - 2], 19)
133
+ ^ (w[i - 2] >> 10)
134
+ )
135
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xFFFFFFFF
136
+
137
+ a, b, c, d, e, f, g, h = H[:]
138
+
139
+ for i in range(64):
140
+ S1 = right_rotate(e, 6) ^ right_rotate(e, 11) ^ right_rotate(e, 25)
141
+ ch = (e & f) ^ (~e & g)
142
+ temp1 = (h + S1 + ch + K[i] + w[i]) & 0xFFFFFFFF
143
+ S0 = right_rotate(a, 2) ^ right_rotate(a, 13) ^ right_rotate(a, 22)
144
+ maj = (a & b) ^ (a & c) ^ (b & c)
145
+ temp2 = (S0 + maj) & 0xFFFFFFFF
146
+
147
+ h = g
148
+ g = f
149
+ f = e
150
+ e = (d + temp1) & 0xFFFFFFFF
151
+ d = c
152
+ c = b
153
+ b = a
154
+ a = (temp1 + temp2) & 0xFFFFFFFF
155
+
156
+ H[0] = (H[0] + a) & 0xFFFFFFFF
157
+ H[1] = (H[1] + b) & 0xFFFFFFFF
158
+ H[2] = (H[2] + c) & 0xFFFFFFFF
159
+ H[3] = (H[3] + d) & 0xFFFFFFFF
160
+ H[4] = (H[4] + e) & 0xFFFFFFFF
161
+ H[5] = (H[5] + f) & 0xFFFFFFFF
162
+ H[6] = (H[6] + g) & 0xFFFFFFFF
163
+ H[7] = (H[7] + h) & 0xFFFFFFFF
164
+
165
+ return "".join(f"{h:08x}" for h in H)
166
+
167
+
168
+ # Add debugging for the specific test case
169
+ if __name__ == "__main__":
170
+ data = "The quick brown fox jumps over the lazy dog"
171
+ hash_value = localized_sha256(data)
172
+ print(f"Input: {data}")
173
+ print(f"SHA-256 Hash: {hash_value}")
174
+
175
+ # Compare with expected hash for debugging
176
+ expected_hash = "d7a8fbb307d7809469ca9abcb0082e4f2d0c0b5a0c0b5a0c0b5a0c0b5a0c0b5"
177
+ print(f"Expected SHA-256 Hash: {expected_hash}")
178
+ print(f"Match: {hash_value == expected_hash}")
@@ -0,0 +1,53 @@
1
+ """Constants related to hash algorithms and operations."""
2
+
3
+ from clm.layer0.model.hash.enums import HashAlgorithm
4
+
5
+ # Hash Algorithm Hierarchy - Maps each algorithm to the set of stronger algorithms
6
+ HASH_ALGORITHM_HIERARCHY = {
7
+ HashAlgorithm.MD5: {
8
+ HashAlgorithm.SHA1,
9
+ HashAlgorithm.SHA224,
10
+ HashAlgorithm.SHA256,
11
+ HashAlgorithm.SHA384,
12
+ HashAlgorithm.SHA512,
13
+ HashAlgorithm.BLAKE3,
14
+ },
15
+ HashAlgorithm.SHA1: {
16
+ HashAlgorithm.SHA224,
17
+ HashAlgorithm.SHA256,
18
+ HashAlgorithm.SHA384,
19
+ HashAlgorithm.SHA512,
20
+ HashAlgorithm.BLAKE3,
21
+ },
22
+ HashAlgorithm.SHA224: {
23
+ HashAlgorithm.SHA256,
24
+ HashAlgorithm.SHA384,
25
+ HashAlgorithm.SHA512,
26
+ HashAlgorithm.BLAKE3,
27
+ },
28
+ HashAlgorithm.SHA256: {
29
+ HashAlgorithm.SHA384,
30
+ HashAlgorithm.SHA512,
31
+ HashAlgorithm.BLAKE3,
32
+ },
33
+ HashAlgorithm.SHA384: {
34
+ HashAlgorithm.SHA512,
35
+ HashAlgorithm.BLAKE3,
36
+ },
37
+ HashAlgorithm.SHA512: {
38
+ HashAlgorithm.BLAKE3,
39
+ },
40
+ HashAlgorithm.BLAKE3: set(),
41
+ }
42
+
43
+ # Known MD5 collision pairs for testing
44
+ KNOWN_MD5_COLLISION_PAIRS = [
45
+ (
46
+ bytes.fromhex(
47
+ "4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea2"
48
+ ),
49
+ bytes.fromhex(
50
+ "4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2"
51
+ ),
52
+ )
53
+ ]
@@ -0,0 +1,60 @@
1
+ """Hash algorithm enums."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class HashAlgorithm(Enum):
7
+ """Supported hash algorithms."""
8
+
9
+ MD5 = "md5"
10
+ SHA1 = "sha1"
11
+ SHA224 = "sha224"
12
+ SHA256 = "sha256"
13
+ SHA384 = "sha384"
14
+ SHA512 = "sha512"
15
+ BLAKE3 = "blake3"
16
+ CUSTOM = "custom"
17
+
18
+ # Default hash algorithm
19
+ DEFAULT = BLAKE3
20
+
21
+ @classmethod
22
+ def from_string(cls, value: str) -> "HashAlgorithm":
23
+ """Convert a string to a HashAlgorithm enum.
24
+
25
+ Args:
26
+ value: String value to convert
27
+
28
+ Returns:
29
+ HashAlgorithm: The corresponding enum value
30
+
31
+ Raises:
32
+ ValueError: If the string is not a valid hash algorithm
33
+ """
34
+ try:
35
+ return cls(value.lower())
36
+ except ValueError as e:
37
+ raise ValueError(f"'{value}' is not a valid hash algorithm") from e
38
+
39
+ @classmethod
40
+ def get_default(cls) -> "HashAlgorithm":
41
+ """Get the default hash algorithm from environment or fallback to SHA1."""
42
+ import os
43
+
44
+ env_algo = os.environ.get("MCARD_HASH_ALGORITHM")
45
+ if env_algo:
46
+ try:
47
+ return cls.from_string(env_algo)
48
+ except ValueError:
49
+ pass
50
+
51
+ try:
52
+ from clm.cross_cutting.config.settings import settings
53
+
54
+ env_algo = settings.hashing.algorithm
55
+ if env_algo:
56
+ return cls.from_string(env_algo)
57
+ except Exception:
58
+ pass
59
+
60
+ return cls.DEFAULT