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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- 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,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
|