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
clm/layer0/context.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Runtime context container for the CLM environment (Sprint 176).
|
|
2
|
+
|
|
3
|
+
Provides thread-safe, fiber-isolated execution scoping via contextvars.ContextVar,
|
|
4
|
+
eliminating process-global os.environ mutations while preserving 100% backward compatibility.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import contextlib
|
|
10
|
+
import contextvars
|
|
11
|
+
import os
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any, Generator
|
|
14
|
+
|
|
15
|
+
_CURRENT_CONTEXT: contextvars.ContextVar[CLMContext | None] = contextvars.ContextVar(
|
|
16
|
+
"clm_current_context", default=None
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class CLMContext:
|
|
22
|
+
"""Unified runtime context container holding storage, configuration, and execution flags.
|
|
23
|
+
|
|
24
|
+
Implements Cordis-style monadic scoping (current, scope, isolate) to guarantee
|
|
25
|
+
zero residual global environment mutations.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
root: str
|
|
29
|
+
db: Any | None = None
|
|
30
|
+
zero_fs: bool = False
|
|
31
|
+
repo: Any | None = field(default=None)
|
|
32
|
+
petri_kenotic: bool = False
|
|
33
|
+
runner: str = "rust"
|
|
34
|
+
active_agent_did: str | None = None
|
|
35
|
+
hash_algorithm: str = "blake3"
|
|
36
|
+
custom_flags: dict[str, str] = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
def __post_init__(self) -> None:
|
|
39
|
+
if self.repo is None and self.db is not None:
|
|
40
|
+
try:
|
|
41
|
+
import sys
|
|
42
|
+
target = "clm.layer2.storage"
|
|
43
|
+
mod = sys.modules.get(target)
|
|
44
|
+
if mod is None:
|
|
45
|
+
__import__(target)
|
|
46
|
+
mod = sys.modules.get(target)
|
|
47
|
+
if mod is not None:
|
|
48
|
+
repo_cls = getattr(mod, "CLMRepository", None)
|
|
49
|
+
if repo_cls:
|
|
50
|
+
self.repo = repo_cls(self.db, self.root)
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def clm_root(self) -> str:
|
|
56
|
+
"""Alias for root directory."""
|
|
57
|
+
return self.root
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def current(cls) -> CLMContext:
|
|
61
|
+
"""Retrieve the context bound to the current thread/fiber, or bootstrap defaults from environment."""
|
|
62
|
+
ctx = _CURRENT_CONTEXT.get()
|
|
63
|
+
if ctx is None:
|
|
64
|
+
return cls.create_default()
|
|
65
|
+
return ctx
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def create_default(cls, root: str | None = None) -> CLMContext:
|
|
69
|
+
"""Create a default CLMContext without binding to thread local."""
|
|
70
|
+
root_dir = root or os.environ.get("CLM_ROOT", os.getcwd())
|
|
71
|
+
return cls(
|
|
72
|
+
root=root_dir,
|
|
73
|
+
zero_fs=os.environ.get("CLM_ZERO_FS", "").lower() in ("true", "1"),
|
|
74
|
+
petri_kenotic=os.environ.get("CLM_PETRI_KENOTIC", "").lower() in ("true", "1"),
|
|
75
|
+
runner=os.environ.get("CLM_RUNNER", "rust"),
|
|
76
|
+
hash_algorithm=os.environ.get("CLM_HASH_ALGO", "blake3"),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@contextlib.contextmanager
|
|
80
|
+
def scope(self) -> Generator[CLMContext, None, None]:
|
|
81
|
+
"""Bind this context to the current thread/task scope, restoring previous on exit."""
|
|
82
|
+
token = _CURRENT_CONTEXT.set(self)
|
|
83
|
+
try:
|
|
84
|
+
yield self
|
|
85
|
+
finally:
|
|
86
|
+
_CURRENT_CONTEXT.reset(token)
|
|
87
|
+
|
|
88
|
+
def isolate(self, **overrides: Any) -> CLMContext:
|
|
89
|
+
"""Create a child isolated context with specific overrides without mutating parent state."""
|
|
90
|
+
params: dict[str, Any] = {
|
|
91
|
+
"root": self.root,
|
|
92
|
+
"db": self.db,
|
|
93
|
+
"zero_fs": self.zero_fs,
|
|
94
|
+
"repo": self.repo,
|
|
95
|
+
"petri_kenotic": self.petri_kenotic,
|
|
96
|
+
"runner": self.runner,
|
|
97
|
+
"active_agent_did": self.active_agent_did,
|
|
98
|
+
"hash_algorithm": self.hash_algorithm,
|
|
99
|
+
"custom_flags": self.custom_flags.copy(),
|
|
100
|
+
}
|
|
101
|
+
params.update(overrides)
|
|
102
|
+
return CLMContext(**params)
|
|
103
|
+
|
|
104
|
+
def to_env_dict(self) -> dict[str, str]:
|
|
105
|
+
"""Export context flags as an environment dictionary for safe subprocess passing."""
|
|
106
|
+
env = os.environ.copy()
|
|
107
|
+
env["CLM_ROOT"] = self.root
|
|
108
|
+
env["CLM_ZERO_FS"] = "true" if self.zero_fs else "false"
|
|
109
|
+
env["CLM_PETRI_KENOTIC"] = "true" if self.petri_kenotic else "false"
|
|
110
|
+
env["CLM_RUNNER"] = self.runner
|
|
111
|
+
env["CLM_HASH_ALGO"] = self.hash_algorithm
|
|
112
|
+
if self.active_agent_did:
|
|
113
|
+
env["CLM_AGENT_DID"] = self.active_agent_did
|
|
114
|
+
for k, v in self.custom_flags.items():
|
|
115
|
+
env[f"CLM_FLAG_{k.upper()}"] = str(v)
|
|
116
|
+
return env
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Backward and forward compatibility alias
|
|
120
|
+
EvaluationContext = CLMContext
|
|
121
|
+
|
|
122
|
+
__all__ = ["CLMContext", "EvaluationContext"]
|
clm/layer0/db.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Deprecated backward-compatibility facade for Tri-Database Engine (Sprint 387, INV-01, INV-09, INV-10).
|
|
2
|
+
|
|
3
|
+
Layer 0 Kenotic Purity: Physical database storage, drivers, pools, and repositories
|
|
4
|
+
have been relocated to `clm.layer2.storage`.
|
|
5
|
+
Layer 0 defines only abstract protocols (TriDatabaseEngineProtocol, CardRepositoryProtocol)
|
|
6
|
+
and static schemas (`clm.layer0.schema`).
|
|
7
|
+
|
|
8
|
+
All active production callers must import from `clm.layer2.storage` directly.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import warnings
|
|
15
|
+
from typing import Any, Protocol, runtime_checkable
|
|
16
|
+
|
|
17
|
+
from clm.layer0.exceptions import CLMError, DatabaseError, KenoticPurityViolationError
|
|
18
|
+
|
|
19
|
+
# =============================================================================
|
|
20
|
+
# PEP 544 Structural Protocols (Pure Layer 0 Contracts)
|
|
21
|
+
# =============================================================================
|
|
22
|
+
|
|
23
|
+
@runtime_checkable
|
|
24
|
+
class TriDatabaseEngineProtocol(Protocol):
|
|
25
|
+
"""Protocol for low-level Tri-Database driver operations."""
|
|
26
|
+
|
|
27
|
+
def execute(self, pillar: str, sql: str, params: list[Any] | None = None) -> str:
|
|
28
|
+
"""Execute SQL query against the designated pillar."""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@runtime_checkable
|
|
33
|
+
class CardRepositoryProtocol(Protocol):
|
|
34
|
+
"""Protocol for MCard and semantic handle persistence."""
|
|
35
|
+
|
|
36
|
+
def has_card(self, hash_val: str, pillar: str) -> bool:
|
|
37
|
+
"""Check if an MCard exists in the CAS by content hash."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def get_card_content(self, hash_val: str, pillar: str) -> str | None:
|
|
41
|
+
"""Retrieve MCard content from the CAS by content hash."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def save_card(self, hash_val: str, content: str, pillar: str, timestamp: str | None = None, replace: bool = False) -> None:
|
|
45
|
+
"""Persist an MCard to the CAS."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def save_card_and_handle(self, handle_name: str, hash_val: str, content: str, pillar: str, timestamp: str | None = None, replace: bool = True) -> None:
|
|
49
|
+
"""Persist an MCard and update the semantic handle pointer."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def resolve_handle_hash(self, handle_name: str, pillar: str = "content_memory") -> str:
|
|
53
|
+
"""Resolve a semantic handle to its current content hash."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def resolve_handle_content(self, handle_name: str, pillar: str = "content_memory") -> str:
|
|
57
|
+
"""Resolve a semantic handle to its current content string."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# =============================================================================
|
|
62
|
+
# Backward-Compatibility Delegation Facade
|
|
63
|
+
# =============================================================================
|
|
64
|
+
|
|
65
|
+
_warned_symbols: set[str] = set()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _warn_deprecated(symbol: str) -> None:
|
|
69
|
+
if symbol not in _warned_symbols and os.environ.get("CLM_ZERO_FS", "").lower() != "true":
|
|
70
|
+
warnings.warn(
|
|
71
|
+
f"clm.layer0.db (symbol: {symbol}) is deprecated as part of Layer 0 Kenotic Purification. "
|
|
72
|
+
"Import from clm.layer2.storage instead.",
|
|
73
|
+
DeprecationWarning,
|
|
74
|
+
stacklevel=3,
|
|
75
|
+
)
|
|
76
|
+
_warned_symbols.add(symbol)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
"TriDatabaseEngineProtocol",
|
|
81
|
+
"CardRepositoryProtocol",
|
|
82
|
+
"CLMDB",
|
|
83
|
+
"CLMRepository",
|
|
84
|
+
"CardCollection",
|
|
85
|
+
"ClmDbPool",
|
|
86
|
+
"RepositoryBase",
|
|
87
|
+
"BitemporalMixin",
|
|
88
|
+
"HyperlinkMixin",
|
|
89
|
+
"savepoint",
|
|
90
|
+
"DisposableList",
|
|
91
|
+
"reset_shared_socket",
|
|
92
|
+
"init_tridb",
|
|
93
|
+
"memorize_mcard",
|
|
94
|
+
"resolve_handle_hash",
|
|
95
|
+
"resolve_handle_content",
|
|
96
|
+
"register_handle",
|
|
97
|
+
"resolve_handle_hash_at",
|
|
98
|
+
"resolve_handle_content_at",
|
|
99
|
+
"parse_yaml_mcard",
|
|
100
|
+
"_ENGINES",
|
|
101
|
+
"_SHARED_SOCKET",
|
|
102
|
+
"DatabaseError",
|
|
103
|
+
"CLMError",
|
|
104
|
+
"KenoticPurityViolationError",
|
|
105
|
+
"resolve_clm_path",
|
|
106
|
+
"get_bash_command",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def __getattr__(name: str) -> Any:
|
|
111
|
+
if name in (
|
|
112
|
+
"CLMDB",
|
|
113
|
+
"CLMRepository",
|
|
114
|
+
"CardCollection",
|
|
115
|
+
"ClmDbPool",
|
|
116
|
+
"RepositoryBase",
|
|
117
|
+
"BitemporalMixin",
|
|
118
|
+
"HyperlinkMixin",
|
|
119
|
+
"savepoint",
|
|
120
|
+
"DisposableList",
|
|
121
|
+
"reset_shared_socket",
|
|
122
|
+
"init_tridb",
|
|
123
|
+
"memorize_mcard",
|
|
124
|
+
"resolve_handle_hash",
|
|
125
|
+
"resolve_handle_content",
|
|
126
|
+
"register_handle",
|
|
127
|
+
"resolve_handle_hash_at",
|
|
128
|
+
"resolve_handle_content_at",
|
|
129
|
+
"parse_yaml_mcard",
|
|
130
|
+
"_ENGINES",
|
|
131
|
+
"_SHARED_SOCKET",
|
|
132
|
+
):
|
|
133
|
+
_warn_deprecated(name)
|
|
134
|
+
target = "clm.layer2.storage"
|
|
135
|
+
mod = sys.modules.get(target)
|
|
136
|
+
if mod is None:
|
|
137
|
+
__import__(target)
|
|
138
|
+
mod = sys.modules.get(target)
|
|
139
|
+
if mod is not None:
|
|
140
|
+
return getattr(mod, name)
|
|
141
|
+
|
|
142
|
+
if name in ("DatabaseError", "CLMError", "KenoticPurityViolationError"):
|
|
143
|
+
target_exc = "clm.layer0.exceptions"
|
|
144
|
+
mod = sys.modules.get(target_exc)
|
|
145
|
+
if mod is None:
|
|
146
|
+
__import__(target_exc)
|
|
147
|
+
mod = sys.modules.get(target_exc)
|
|
148
|
+
if mod is not None:
|
|
149
|
+
return getattr(mod, name)
|
|
150
|
+
|
|
151
|
+
if name in ("resolve_clm_path", "get_bash_command"):
|
|
152
|
+
target_utils = "clm.layer0.utils"
|
|
153
|
+
mod = sys.modules.get(target_utils)
|
|
154
|
+
if mod is None:
|
|
155
|
+
__import__(target_utils)
|
|
156
|
+
mod = sys.modules.get(target_utils)
|
|
157
|
+
if mod is not None:
|
|
158
|
+
return getattr(mod, name)
|
|
159
|
+
|
|
160
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
clm/layer0/exceptions.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from clm.cross_cutting.errors import (
|
|
2
|
+
AlgebraicClosedOperatorError,
|
|
3
|
+
CelEvaluationError,
|
|
4
|
+
ClmDomainError,
|
|
5
|
+
ContentHashMismatchError,
|
|
6
|
+
CpnVMError,
|
|
7
|
+
DatabaseLockTimeoutError,
|
|
8
|
+
DeadlockDetectedError,
|
|
9
|
+
ExecutionEngineError,
|
|
10
|
+
HandleNotFoundError,
|
|
11
|
+
IdentityAuthError,
|
|
12
|
+
InvalidDidSignatureError,
|
|
13
|
+
ManifestParseError,
|
|
14
|
+
OperationNotFoundError,
|
|
15
|
+
PeerUnreachableError,
|
|
16
|
+
PostconditionViolationError,
|
|
17
|
+
PreconditionViolationError,
|
|
18
|
+
StorageEngineError,
|
|
19
|
+
StratificationViolationError,
|
|
20
|
+
TokenConservationViolationError,
|
|
21
|
+
TransitionAclDeniedError,
|
|
22
|
+
TypeLatticeError,
|
|
23
|
+
VCardEvaluationFaultError,
|
|
24
|
+
VCardGateError,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Backward-compatibility aliases
|
|
28
|
+
CLMError = ClmDomainError
|
|
29
|
+
TypeValidationError = TypeLatticeError
|
|
30
|
+
DatabaseError = StorageEngineError
|
|
31
|
+
ExecutionError = ExecutionEngineError
|
|
32
|
+
AlgebraError = AlgebraicClosedOperatorError
|
|
33
|
+
GatewayError = IdentityAuthError
|
|
34
|
+
|
|
35
|
+
class KenoticPurityViolationError(ClmDomainError):
|
|
36
|
+
"""Raised when an operation in Layer 0 violates Kenotic Purity (e.g. physical disk I/O under CLM_ZERO_FS)."""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"AlgebraError",
|
|
42
|
+
"AlgebraicClosedOperatorError",
|
|
43
|
+
"CLMError",
|
|
44
|
+
"CelEvaluationError",
|
|
45
|
+
"ClmDomainError",
|
|
46
|
+
"ContentHashMismatchError",
|
|
47
|
+
"CpnVMError",
|
|
48
|
+
"DatabaseError",
|
|
49
|
+
"DatabaseLockTimeoutError",
|
|
50
|
+
"DeadlockDetectedError",
|
|
51
|
+
"ExecutionEngineError",
|
|
52
|
+
"ExecutionError",
|
|
53
|
+
"GatewayError",
|
|
54
|
+
"HandleNotFoundError",
|
|
55
|
+
"IdentityAuthError",
|
|
56
|
+
"InvalidDidSignatureError",
|
|
57
|
+
"KenoticPurityViolationError",
|
|
58
|
+
"ManifestParseError",
|
|
59
|
+
"OperationNotFoundError",
|
|
60
|
+
"PeerUnreachableError",
|
|
61
|
+
"PostconditionViolationError",
|
|
62
|
+
"PreconditionViolationError",
|
|
63
|
+
"StorageEngineError",
|
|
64
|
+
"StratificationViolationError",
|
|
65
|
+
"TokenConservationViolationError",
|
|
66
|
+
"TransitionAclDeniedError",
|
|
67
|
+
"TypeLatticeError",
|
|
68
|
+
"TypeValidationError",
|
|
69
|
+
"VCardEvaluationFaultError",
|
|
70
|
+
"VCardGateError",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
clm/layer0/fibration.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
r"""Grothendieck Fibration Category: Process -> MCard.
|
|
2
|
+
Canonical Path: kernel/scripts/clm/layer0/fibration.py
|
|
3
|
+
Deliverable: DEL-254-01 / DEL-254-03
|
|
4
|
+
|
|
5
|
+
Formalizes the total category `Process = \int P` over the base category `MCard`.
|
|
6
|
+
Each object in Process is a pair `(M, F)` where:
|
|
7
|
+
- `M \in MCard`: Immutable spatial carrier resting state in content-addressed storage (CAS).
|
|
8
|
+
- `F \in Fiber(M)`: Active cordis execution fiber equipped with an unwinding stack of
|
|
9
|
+
reversible side-effects (`DisposableList`).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from typing import Any, Callable
|
|
17
|
+
|
|
18
|
+
from clm.cross_cutting.domain_models import MCard
|
|
19
|
+
|
|
20
|
+
_target = "clm.layer2.fiber"
|
|
21
|
+
_mod = sys.modules.get(_target)
|
|
22
|
+
if _mod is None:
|
|
23
|
+
__import__(_target)
|
|
24
|
+
_mod = sys.modules.get(_target)
|
|
25
|
+
DisposableList = _mod.DisposableList
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FibrationError(Exception):
|
|
29
|
+
"""Base error for Fibration axiom violations or unwinding failures."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ProcessObject:
|
|
34
|
+
r"""An object in the Total Category Process (\int P).
|
|
35
|
+
|
|
36
|
+
Represents the coupled pair (M, F) with execution fiber state.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
carrier: MCard,
|
|
42
|
+
carrier_handle: str | None = None,
|
|
43
|
+
pcard: Any | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
self.carrier = carrier
|
|
46
|
+
self.carrier_handle = carrier_handle
|
|
47
|
+
self.disposables = DisposableList()
|
|
48
|
+
self.pcard = pcard
|
|
49
|
+
self.created_at = int(time.time() * 1000)
|
|
50
|
+
self.is_active = True
|
|
51
|
+
|
|
52
|
+
def project(self) -> MCard:
|
|
53
|
+
r"""Projection functor \pi(M, F) = M."""
|
|
54
|
+
return self.carrier
|
|
55
|
+
|
|
56
|
+
def register_disposable(self, disposer: Callable[[], None]) -> None:
|
|
57
|
+
"""Registers a reversible cleanup callback onto the fiber's LIFO stack."""
|
|
58
|
+
self.disposables.push(disposer)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def pending_disposers_count(self) -> int:
|
|
62
|
+
"""Returns the number of registered disposers awaiting execution."""
|
|
63
|
+
return len(self.disposables)
|
|
64
|
+
|
|
65
|
+
def unwind(self) -> list[Exception]:
|
|
66
|
+
"""Unwinds all registered disposers in strict LIFO order."""
|
|
67
|
+
return self.disposables.dispose()
|
|
68
|
+
|
|
69
|
+
def lift_mutation(self, next_carrier: MCard) -> None:
|
|
70
|
+
"""Executes Cartesian lift of mutation f: M -> M'.
|
|
71
|
+
|
|
72
|
+
Unwinds existing fiber disposables in strict LIFO order, then updates the carrier.
|
|
73
|
+
"""
|
|
74
|
+
errors = self.unwind()
|
|
75
|
+
if errors:
|
|
76
|
+
raise FibrationError(f"Disposable unwind failed during lift: {errors}")
|
|
77
|
+
|
|
78
|
+
self.carrier = next_carrier
|
|
79
|
+
self.disposables = DisposableList()
|
|
80
|
+
self.is_active = True
|
|
81
|
+
|
|
82
|
+
def lift_handle_rebind(self, new_handle: str) -> None:
|
|
83
|
+
"""Executes Cartesian lift of handle re-binding."""
|
|
84
|
+
self.carrier_handle = new_handle
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def verify_fibration_axioms(
|
|
88
|
+
initial: ProcessObject,
|
|
89
|
+
lifted: ProcessObject,
|
|
90
|
+
target_carrier: MCard,
|
|
91
|
+
) -> None:
|
|
92
|
+
r"""Validates Grothendieck fibration axioms:
|
|
93
|
+
1. Base Projection Commutativity: \pi(lift(f)) == cod(f).
|
|
94
|
+
2. Zero Residual State: Fiber disposables after lift == 0.
|
|
95
|
+
"""
|
|
96
|
+
# Axiom 1: Base projection commutativity
|
|
97
|
+
if lifted.project().hash != target_carrier.hash:
|
|
98
|
+
raise FibrationError(
|
|
99
|
+
f"Projection commutativity failed: {lifted.project().hash} != {target_carrier.hash}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Axiom 2: Zero residual state
|
|
103
|
+
if lifted.pending_disposers_count != 0:
|
|
104
|
+
raise FibrationError(
|
|
105
|
+
f"Zero residual state failed: {lifted.pending_disposers_count} disposers remaining"
|
|
106
|
+
)
|
clm/layer0/gates.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Layer 0: Stateless Kenotic Boolean Gate Predicates (FND X -> 2).
|
|
2
|
+
|
|
3
|
+
Canonical Path: kernel/scripts/clm/layer0/gates.py
|
|
4
|
+
Parity: Rust `src/layer0/gates.rs`, TypeScript `src/layer0/gates.ts`
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class GatePredicate:
|
|
15
|
+
gate_type: str # 'AlwaysTrue', 'AlwaysFalse', 'Equals', 'NotEquals', 'And', 'Or', 'Not', 'Threshold'
|
|
16
|
+
config: Any = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def evaluate_gate(predicate: GatePredicate, input_val: Any) -> bool:
|
|
20
|
+
"""Evaluates a gate predicate against an input value in a purely stateless manner."""
|
|
21
|
+
t = predicate.gate_type
|
|
22
|
+
if t == "AlwaysTrue":
|
|
23
|
+
return True
|
|
24
|
+
elif t == "AlwaysFalse":
|
|
25
|
+
return False
|
|
26
|
+
elif t == "Equals":
|
|
27
|
+
return input_val == predicate.config
|
|
28
|
+
elif t == "NotEquals":
|
|
29
|
+
return input_val != predicate.config
|
|
30
|
+
elif t == "And":
|
|
31
|
+
preds: list[GatePredicate] = predicate.config
|
|
32
|
+
return all(evaluate_gate(p, input_val) for p in preds)
|
|
33
|
+
elif t == "Or":
|
|
34
|
+
preds = predicate.config
|
|
35
|
+
return any(evaluate_gate(p, input_val) for p in preds)
|
|
36
|
+
elif t == "Not":
|
|
37
|
+
p: GatePredicate = predicate.config
|
|
38
|
+
return not evaluate_gate(p, input_val)
|
|
39
|
+
elif t == "Threshold":
|
|
40
|
+
required: int = predicate.config["required"]
|
|
41
|
+
preds = predicate.config["predicates"]
|
|
42
|
+
satisfied = sum(1 for p in preds if evaluate_gate(p, input_val))
|
|
43
|
+
return satisfied >= required
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError(f"Unknown gate type: {t}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def evaluate_gate_set(gates: list[tuple[str, GatePredicate]], state: Any) -> list[tuple[str, bool]]:
|
|
49
|
+
"""Evaluates a list of named gates against an input state."""
|
|
50
|
+
return [(name, evaluate_gate(pred, state)) for name, pred in gates]
|
clm/layer0/hash.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Layer 0: Stateless Kenotic Cryptographic Hashing.
|
|
2
|
+
|
|
3
|
+
Normative Reference:
|
|
4
|
+
docs/spec/20-architecture/system-architecture.md § 1.1 (Universal MCard Ontology)
|
|
5
|
+
docs/spec/20-architecture/card-triad-architecture.md § 0 (Parameterized Content Hashing, INV-02)
|
|
6
|
+
|
|
7
|
+
Invariants:
|
|
8
|
+
INV-02: Cryptographic Content Hashing (BLAKE3 default, SHA-256 fallback).
|
|
9
|
+
INV-01: Stateless Kenotic Purity (pure memory, zero disk or network I/O).
|
|
10
|
+
|
|
11
|
+
Parity: Rust `src/layer0/hash.rs`, TypeScript `src/layer0/hash.ts`
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
import hmac
|
|
18
|
+
|
|
19
|
+
from clm.layer0.utils import pure_python_blake3
|
|
20
|
+
|
|
21
|
+
BLAKE3_PREFIX = "blake3:"
|
|
22
|
+
SHA256_PREFIX = "sha256:"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _to_bytes(data: bytes | str) -> bytes:
|
|
26
|
+
if isinstance(data, str):
|
|
27
|
+
return data.encode("utf-8")
|
|
28
|
+
return data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def compute_blake3(data: bytes | str) -> str:
|
|
32
|
+
"""Compute raw 64-character lowercase hex BLAKE3 digest."""
|
|
33
|
+
b = _to_bytes(data)
|
|
34
|
+
try:
|
|
35
|
+
import blake3 # type: ignore
|
|
36
|
+
return blake3.blake3(b).hexdigest()
|
|
37
|
+
except ImportError:
|
|
38
|
+
return pure_python_blake3(b)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def compute_sha256(data: bytes | str) -> str:
|
|
42
|
+
"""Compute raw 64-character lowercase hex SHA-256 digest."""
|
|
43
|
+
b = _to_bytes(data)
|
|
44
|
+
return hashlib.sha256(b).hexdigest()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def prefixed_hash(data: bytes | str) -> str:
|
|
48
|
+
"""Compute prefixed hash using default algorithm (BLAKE3): 'blake3:<hex>'."""
|
|
49
|
+
return f"{BLAKE3_PREFIX}{compute_blake3(data)}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def verify_hash(data: bytes | str, expected_hash: str) -> bool:
|
|
53
|
+
"""Verify that data matches expected hash (prefixed or 64-char raw hex)."""
|
|
54
|
+
b = _to_bytes(data)
|
|
55
|
+
expected = expected_hash.strip()
|
|
56
|
+
if expected.startswith(BLAKE3_PREFIX):
|
|
57
|
+
target = expected[len(BLAKE3_PREFIX):]
|
|
58
|
+
actual = compute_blake3(b)
|
|
59
|
+
return hmac.compare_digest(actual.lower(), target.lower())
|
|
60
|
+
if expected.startswith(SHA256_PREFIX):
|
|
61
|
+
target = expected[len(SHA256_PREFIX):]
|
|
62
|
+
actual = compute_sha256(b)
|
|
63
|
+
return hmac.compare_digest(actual.lower(), target.lower())
|
|
64
|
+
if len(expected) == 64:
|
|
65
|
+
actual_b3 = compute_blake3(b)
|
|
66
|
+
actual_sha = compute_sha256(b)
|
|
67
|
+
return hmac.compare_digest(actual_b3.lower(), expected.lower()) or hmac.compare_digest(
|
|
68
|
+
actual_sha.lower(), expected.lower()
|
|
69
|
+
)
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
"BLAKE3_PREFIX",
|
|
75
|
+
"SHA256_PREFIX",
|
|
76
|
+
"compute_blake3",
|
|
77
|
+
"compute_sha256",
|
|
78
|
+
"prefixed_hash",
|
|
79
|
+
"verify_hash",
|
|
80
|
+
]
|