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,232 @@
|
|
|
1
|
+
"""EOA Sponsor Signing Surface — Sprint 138.4.
|
|
2
|
+
|
|
3
|
+
Provides the human-facing cryptographic boundary where EOA (Externally Owned
|
|
4
|
+
Agent) volition enters the CLM OS. A PCard cannot fire until the human EOA
|
|
5
|
+
signs the VCard pre-gate with their Ed25519 ``did:key:*`` keypair.
|
|
6
|
+
|
|
7
|
+
CLI usage:
|
|
8
|
+
clm-sponsor --generate-keypair
|
|
9
|
+
clm-sponsor --pcard <handle> --intent <text>
|
|
10
|
+
|
|
11
|
+
The keypair is stored at ``~/.clm/eoa_keypair.ed25519`` (or
|
|
12
|
+
``$CLM_EOA_KEYPAIR_PATH``). Only the private key PEM is stored on disk;
|
|
13
|
+
the public DID is derived on demand.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import time
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from cryptography.hazmat.primitives import serialization
|
|
24
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
|
25
|
+
|
|
26
|
+
from clm.cross_cutting.clm_logger import get_logger
|
|
27
|
+
from clm.layer0.utils import clm_hash_string
|
|
28
|
+
|
|
29
|
+
_logger = get_logger("clm.layer4.eoa_sponsor")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _keypair_path() -> Path:
|
|
33
|
+
"""Return the path to the EOA keypair file."""
|
|
34
|
+
env = os.environ.get("CLM_EOA_KEYPAIR_PATH")
|
|
35
|
+
if env:
|
|
36
|
+
return Path(env)
|
|
37
|
+
return Path.home() / ".clm" / "eoa_keypair.ed25519"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def generate_keypair() -> dict:
|
|
41
|
+
"""Generate an Ed25519 keypair and store it on disk.
|
|
42
|
+
|
|
43
|
+
Returns a dict with ``private_pem``, ``public_pem``, and ``did``.
|
|
44
|
+
"""
|
|
45
|
+
from clm.layer1.verifier import clm_derive_session_key
|
|
46
|
+
|
|
47
|
+
keypair = clm_derive_session_key()
|
|
48
|
+
path = _keypair_path()
|
|
49
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
path.write_text(keypair["private_key"])
|
|
51
|
+
path.chmod(0o600)
|
|
52
|
+
return keypair
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_private_key() -> Ed25519PrivateKey:
|
|
56
|
+
"""Load the EOA Ed25519 private key from disk."""
|
|
57
|
+
path = _keypair_path()
|
|
58
|
+
if not path.exists():
|
|
59
|
+
raise FileNotFoundError(
|
|
60
|
+
f"EOA keypair not found at {path}. Run 'clm-sponsor --generate-keypair' first."
|
|
61
|
+
)
|
|
62
|
+
pem = path.read_bytes()
|
|
63
|
+
return serialization.load_pem_private_key(pem, password=None)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def derive_did() -> str:
|
|
67
|
+
"""Derive the ``did:key:*`` DID from the stored EOA keypair."""
|
|
68
|
+
from clm.layer1.verifier import pem_to_did
|
|
69
|
+
|
|
70
|
+
private_key = load_private_key()
|
|
71
|
+
public_pem = private_key.public_key().public_bytes(
|
|
72
|
+
encoding=serialization.Encoding.PEM,
|
|
73
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
74
|
+
)
|
|
75
|
+
return pem_to_did(public_pem)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def derive_did_from_pubkey(public_key) -> str:
|
|
79
|
+
"""Derive did:key:z* from an Ed25519PublicKey object."""
|
|
80
|
+
from clm.layer1.verifier import pem_to_did
|
|
81
|
+
public_pem = public_key.public_bytes(
|
|
82
|
+
encoding=serialization.Encoding.PEM,
|
|
83
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
84
|
+
)
|
|
85
|
+
return pem_to_did(public_pem)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def generate_in_memory_keypair():
|
|
89
|
+
"""Generate in-memory Ed25519PrivateKey and its did:key:z* string."""
|
|
90
|
+
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
91
|
+
private_key = ed25519.Ed25519PrivateKey.generate()
|
|
92
|
+
did = derive_did_from_pubkey(private_key.public_key())
|
|
93
|
+
return private_key, did
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def sign_intent_with_key(
|
|
97
|
+
private_key,
|
|
98
|
+
pcard_handle: str,
|
|
99
|
+
intent: str,
|
|
100
|
+
timestamp: str | None = None,
|
|
101
|
+
use_blake3: bool = True,
|
|
102
|
+
) -> dict:
|
|
103
|
+
"""Sign intent using an in-memory Ed25519 private key."""
|
|
104
|
+
if use_blake3:
|
|
105
|
+
from clm.layer0.hash import compute_blake3
|
|
106
|
+
intent_hash = compute_blake3(intent)
|
|
107
|
+
else:
|
|
108
|
+
intent_hash = clm_hash_string(intent)
|
|
109
|
+
|
|
110
|
+
if timestamp is None:
|
|
111
|
+
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
|
112
|
+
|
|
113
|
+
sponsor_did = derive_did_from_pubkey(private_key.public_key())
|
|
114
|
+
payload = f"{pcard_handle}:{intent_hash}:{timestamp}".encode()
|
|
115
|
+
signature = private_key.sign(payload)
|
|
116
|
+
sig_hex = signature.hex()
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"sponsor_did": sponsor_did,
|
|
120
|
+
"pcard_handle": pcard_handle,
|
|
121
|
+
"intent": intent,
|
|
122
|
+
"intent_hash": intent_hash,
|
|
123
|
+
"signature": sig_hex,
|
|
124
|
+
"timestamp": timestamp,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def sign_intent(pcard_handle: str, intent: str, timestamp: str | None = None, use_blake3: bool = True) -> dict:
|
|
129
|
+
"""Sign the VCard pre-gate hash for a PCard intent using the on-disk key."""
|
|
130
|
+
private_key = load_private_key()
|
|
131
|
+
return sign_intent_with_key(private_key, pcard_handle, intent, timestamp, use_blake3=use_blake3)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def verify_signature(sponsor_did: str, pcard_handle: str, intent_hash: str, timestamp: str, signature_hex: str) -> bool:
|
|
135
|
+
"""Verify a VCard pre-gate signature against a sponsor DID."""
|
|
136
|
+
from clm.layer1.verifier import did_to_pubkey
|
|
137
|
+
|
|
138
|
+
pubkey = did_to_pubkey(sponsor_did)
|
|
139
|
+
payload = f"{pcard_handle}:{intent_hash}:{timestamp}".encode()
|
|
140
|
+
signature = bytes.fromhex(signature_hex)
|
|
141
|
+
try:
|
|
142
|
+
pubkey.verify(signature, payload)
|
|
143
|
+
return True
|
|
144
|
+
except Exception:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def commit_sponsor_did(run_id: str, sponsor_did: str, signature_hex: str) -> str:
|
|
149
|
+
"""Commit the sponsor_did to the run:<run_id>:sponsor handle.
|
|
150
|
+
|
|
151
|
+
This follows the Sprint 44 pattern of committing sponsor identity
|
|
152
|
+
to a handle in the execution_log pillar.
|
|
153
|
+
"""
|
|
154
|
+
import sqlite3
|
|
155
|
+
|
|
156
|
+
db_path = os.environ.get("CLM_DB_PATH", os.path.join(os.getcwd(), "db", "execution_log.db"))
|
|
157
|
+
handle_name = f"run:{run_id}:sponsor"
|
|
158
|
+
content = json.dumps({"sponsor_did": sponsor_did, "signature": signature_hex})
|
|
159
|
+
content_hash = clm_hash_string(content)
|
|
160
|
+
ts = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
|
161
|
+
|
|
162
|
+
conn = sqlite3.connect(db_path)
|
|
163
|
+
cursor = conn.cursor()
|
|
164
|
+
cursor.execute(
|
|
165
|
+
"INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)",
|
|
166
|
+
(content_hash, content, ts),
|
|
167
|
+
)
|
|
168
|
+
cursor.execute(
|
|
169
|
+
"INSERT OR REPLACE INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
|
|
170
|
+
(handle_name, content_hash, ts, ts),
|
|
171
|
+
)
|
|
172
|
+
conn.commit()
|
|
173
|
+
conn.close()
|
|
174
|
+
return content_hash
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def main():
|
|
178
|
+
parser = argparse.ArgumentParser(
|
|
179
|
+
prog="clm-sponsor",
|
|
180
|
+
description="EOA Sponsor Signing Surface — sign VCard pre-gates with Ed25519 did:key",
|
|
181
|
+
)
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
"--generate-keypair",
|
|
184
|
+
action="store_true",
|
|
185
|
+
help="Generate a new Ed25519 keypair at ~/.clm/eoa_keypair.ed25519",
|
|
186
|
+
)
|
|
187
|
+
parser.add_argument("--pcard", metavar="HANDLE", help="PCard handle to sponsor")
|
|
188
|
+
parser.add_argument("--intent", metavar="TEXT", help="Intent text to sign")
|
|
189
|
+
parser.add_argument("--run-id", metavar="ID", default="default", help="Run ID for sponsor handle")
|
|
190
|
+
parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
|
|
191
|
+
args = parser.parse_args()
|
|
192
|
+
|
|
193
|
+
if args.generate_keypair:
|
|
194
|
+
kp = generate_keypair()
|
|
195
|
+
print(f"[sponsor] Keypair generated at {_keypair_path()}")
|
|
196
|
+
print(f"[sponsor] Sponsor DID: {kp['did']}")
|
|
197
|
+
sys.exit(0)
|
|
198
|
+
|
|
199
|
+
if not args.pcard or not args.intent:
|
|
200
|
+
parser.error("--pcard and --intent are required (unless --generate-keypair)")
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
sponsor_did = derive_did()
|
|
204
|
+
except FileNotFoundError as e:
|
|
205
|
+
_logger.error(f"[sponsor] Error: {e}")
|
|
206
|
+
sys.exit(1)
|
|
207
|
+
|
|
208
|
+
print(f"[sponsor] Pending PCard: {args.pcard}")
|
|
209
|
+
print(f"[sponsor] Intent: \"{args.intent}\"")
|
|
210
|
+
print(f"[sponsor] Sponsor DID: {sponsor_did}")
|
|
211
|
+
|
|
212
|
+
if not args.yes:
|
|
213
|
+
response = input("[sponsor] Sign VCard pre-gate? [y/N]: ")
|
|
214
|
+
if response.lower() != "y":
|
|
215
|
+
print("[sponsor] Aborted.")
|
|
216
|
+
sys.exit(1)
|
|
217
|
+
|
|
218
|
+
result = sign_intent(args.pcard, args.intent)
|
|
219
|
+
print("[sponsor] VCard pre-gate signed.")
|
|
220
|
+
print(f"[sponsor] Intent hash: {result['intent_hash']}")
|
|
221
|
+
print(f"[sponsor] Signature: {result['signature'][:32]}...")
|
|
222
|
+
|
|
223
|
+
content_hash = commit_sponsor_did(args.run_id, result["sponsor_did"], result["signature"])
|
|
224
|
+
print(f"[sponsor] sponsor_did committed to run:{args.run_id}:sponsor (hash: {content_hash[:16]}...)")
|
|
225
|
+
print("[sponsor] Dispatching to Flux...")
|
|
226
|
+
|
|
227
|
+
# Output JSON for programmatic consumption
|
|
228
|
+
print(f"SPONSOR_RESULT:{json.dumps(result)}")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
main()
|
clm/layer4/events.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Structured Event Log & Execution Receipts — Sprint 364.
|
|
2
|
+
|
|
3
|
+
Canonical Path: kernel/scripts/clm/layer4/events.py
|
|
4
|
+
Parity: Rust `src/layer4/events.rs`, TypeScript `src/layer4/events.ts`
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from dataclasses import asdict, dataclass, field
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from clm.layer0.hash import compute_blake3
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ExecutionEvent:
|
|
19
|
+
transition_id: str
|
|
20
|
+
transition_name: str
|
|
21
|
+
source_places: list[str]
|
|
22
|
+
target_places: list[str]
|
|
23
|
+
token_deltas: dict[str, Any]
|
|
24
|
+
timestamp: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class EventLog:
|
|
29
|
+
run_id: str
|
|
30
|
+
sponsor_did: str
|
|
31
|
+
events: list[ExecutionEvent] = field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
def append_event(self, event: ExecutionEvent) -> None:
|
|
34
|
+
self.events.append(event)
|
|
35
|
+
|
|
36
|
+
def to_canonical_json(self) -> str:
|
|
37
|
+
data = {
|
|
38
|
+
"run_id": self.run_id,
|
|
39
|
+
"sponsor_did": self.sponsor_did,
|
|
40
|
+
"events": [asdict(e) for e in self.events],
|
|
41
|
+
}
|
|
42
|
+
return json.dumps(data, separators=(",", ":"))
|
|
43
|
+
|
|
44
|
+
def compute_receipt_hash(self) -> str:
|
|
45
|
+
canonical = self.to_canonical_json()
|
|
46
|
+
return compute_blake3(canonical)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class EventType(str, Enum):
|
|
50
|
+
MARKING_CHANGED = "marking_changed"
|
|
51
|
+
TRANSITION_FIRED = "transition_fired"
|
|
52
|
+
ERROR = "error"
|
|
53
|
+
GATEWAY_CONNECT = "gateway_connect"
|
|
54
|
+
GATEWAY_DISCONNECT = "gateway_disconnect"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class GatewayEvent:
|
|
59
|
+
event_type: EventType | str
|
|
60
|
+
payload: dict[str, Any]
|
|
61
|
+
timestamp: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
__all__ = [
|
|
65
|
+
"ExecutionEvent",
|
|
66
|
+
"EventLog",
|
|
67
|
+
"EventType",
|
|
68
|
+
"GatewayEvent",
|
|
69
|
+
]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Layer 4 Flux-as-Petri-Net Gateway & Grafana Faro / Loki Telemetry (DEL-391-02, INV-391-02).
|
|
2
|
+
|
|
3
|
+
Bridges frontend user action intents into typed CPN ingress tokens:
|
|
4
|
+
- Homomorphic mapping from MembraneAction to CPN transition firing.
|
|
5
|
+
- Injects W3C `traceparent` headers into distributed action payloads (faroFluxMiddleware).
|
|
6
|
+
- Projects updated Petri net markings back into MembraneEvent streams and ActionResult responses.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import time
|
|
12
|
+
import uuid
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from clm.layer0.hash import compute_blake3
|
|
16
|
+
from clm.layer4.membrane import ActionResult, MembraneAction, MembraneSession
|
|
17
|
+
|
|
18
|
+
# W3C Trace Context regex: version-trace_id-parent_id-trace_flags
|
|
19
|
+
W3C_TRACEPARENT_REGEX = re.compile(r"^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def generate_w3c_traceparent(
|
|
23
|
+
trace_id: Optional[str] = None,
|
|
24
|
+
parent_id: Optional[str] = None,
|
|
25
|
+
flags: str = "01",
|
|
26
|
+
) -> str:
|
|
27
|
+
"""Generates a valid W3C traceparent header string (version 00).
|
|
28
|
+
Format: 00-{trace_id_32hex}-{parent_id_16hex}-{flags_2hex}
|
|
29
|
+
"""
|
|
30
|
+
t_id = trace_id or uuid.uuid4().hex
|
|
31
|
+
p_id = parent_id or uuid.uuid4().hex[:16]
|
|
32
|
+
return f"00-{t_id}-{p_id}-{flags}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def parse_w3c_traceparent(traceparent: str) -> Dict[str, str]:
|
|
36
|
+
"""Parses a W3C traceparent header string into its components."""
|
|
37
|
+
match = W3C_TRACEPARENT_REGEX.match(traceparent)
|
|
38
|
+
if not match:
|
|
39
|
+
raise ValueError(f"Invalid W3C traceparent format: '{traceparent}'")
|
|
40
|
+
return {
|
|
41
|
+
"version": match.group(1),
|
|
42
|
+
"trace_id": match.group(2),
|
|
43
|
+
"parent_id": match.group(3),
|
|
44
|
+
"flags": match.group(4),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def faro_flux_middleware(action: MembraneAction) -> MembraneAction:
|
|
49
|
+
"""Faro telemetry middleware ensuring W3C traceparent injection into MembraneAction payload."""
|
|
50
|
+
payload = dict(action.payload)
|
|
51
|
+
if "traceparent" not in payload or not payload["traceparent"]:
|
|
52
|
+
payload["traceparent"] = generate_w3c_traceparent()
|
|
53
|
+
return MembraneAction(action_type=action.action_type, payload=payload)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class FluxPetriGateway:
|
|
57
|
+
"""Pluggable Flux-as-Petri-Net Gateway connecting MembraneActions to CPN transitions."""
|
|
58
|
+
|
|
59
|
+
def __init__(self, scheduler: Any = None) -> None:
|
|
60
|
+
self.scheduler = scheduler
|
|
61
|
+
|
|
62
|
+
def dispatch_action(
|
|
63
|
+
self,
|
|
64
|
+
action: MembraneAction,
|
|
65
|
+
session: Optional[MembraneSession] = None,
|
|
66
|
+
timestamp: Optional[str] = None,
|
|
67
|
+
) -> ActionResult:
|
|
68
|
+
"""Dispatches a user action intent into the CPN VM substrate with W3C telemetry."""
|
|
69
|
+
ts = timestamp or time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
|
70
|
+
sess = session or MembraneSession(session_id="flux-default", user_did="did:clm:operator")
|
|
71
|
+
|
|
72
|
+
# Step 1: Inject W3C telemetry via faroFluxMiddleware
|
|
73
|
+
instrumented_action = faro_flux_middleware(action)
|
|
74
|
+
traceparent = instrumented_action.payload["traceparent"]
|
|
75
|
+
|
|
76
|
+
# Step 2: Compute intent hash
|
|
77
|
+
raw_repr = f"{instrumented_action.action_type}:{instrumented_action.payload}"
|
|
78
|
+
intent_hash = compute_blake3(raw_repr)
|
|
79
|
+
|
|
80
|
+
# Step 3: Emit ingress token event into CPN marking
|
|
81
|
+
sess.push_event(
|
|
82
|
+
event_type="flux_ingress_token",
|
|
83
|
+
payload={
|
|
84
|
+
"action_type": instrumented_action.action_type,
|
|
85
|
+
"intent_hash": intent_hash,
|
|
86
|
+
"traceparent": traceparent,
|
|
87
|
+
},
|
|
88
|
+
timestamp=ts,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Step 4: Execute transition through scheduler if available
|
|
92
|
+
result_message = f"Flux action '{instrumented_action.action_type}' dispatched"
|
|
93
|
+
if self.scheduler is not None and hasattr(self.scheduler, "execute_step"):
|
|
94
|
+
try:
|
|
95
|
+
step_res = self.scheduler.execute_step(instrumented_action.payload)
|
|
96
|
+
result_message = f"CPN VM executed: {step_res}"
|
|
97
|
+
except Exception as e:
|
|
98
|
+
sess.push_event(
|
|
99
|
+
event_type="flux_execution_error",
|
|
100
|
+
payload={"error": str(e), "traceparent": traceparent},
|
|
101
|
+
timestamp=ts,
|
|
102
|
+
)
|
|
103
|
+
return ActionResult(
|
|
104
|
+
success=False,
|
|
105
|
+
message=f"CPN VM transition failed: {e}",
|
|
106
|
+
emitted_cards=[],
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Step 5: Project updated Petri net marking
|
|
110
|
+
sess.push_event(
|
|
111
|
+
event_type="flux_marking_projected",
|
|
112
|
+
payload={
|
|
113
|
+
"intent_hash": intent_hash,
|
|
114
|
+
"emitted_card": intent_hash,
|
|
115
|
+
"traceparent": traceparent,
|
|
116
|
+
},
|
|
117
|
+
timestamp=ts,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return ActionResult(
|
|
121
|
+
success=True,
|
|
122
|
+
message=result_message,
|
|
123
|
+
emitted_cards=[intent_hash],
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
"W3C_TRACEPARENT_REGEX",
|
|
129
|
+
"generate_w3c_traceparent",
|
|
130
|
+
"parse_w3c_traceparent",
|
|
131
|
+
"faro_flux_middleware",
|
|
132
|
+
"FluxPetriGateway",
|
|
133
|
+
]
|
clm/layer4/gateway.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Headless Gateway Router — Sprint 364.
|
|
2
|
+
|
|
3
|
+
Canonical Path: kernel/scripts/clm/layer4/gateway.py
|
|
4
|
+
Parity: Rust `src/layer4/gateway.rs`, TypeScript `src/layer4/gateway/`
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from clm.layer0.hash import compute_blake3
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GatewayRouter:
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
self.card_registry: dict[str, Any] = {}
|
|
17
|
+
|
|
18
|
+
def register_card(self, card_hash: str, content: Any) -> None:
|
|
19
|
+
self.card_registry[card_hash] = content
|
|
20
|
+
|
|
21
|
+
def dispatch(self, request: dict[str, Any]) -> dict[str, Any]:
|
|
22
|
+
req_id = request.get("id")
|
|
23
|
+
if request.get("jsonrpc") != "2.0":
|
|
24
|
+
return {
|
|
25
|
+
"jsonrpc": "2.0",
|
|
26
|
+
"id": req_id,
|
|
27
|
+
"error": {
|
|
28
|
+
"code": -32600,
|
|
29
|
+
"message": "Invalid Request: jsonrpc must be '2.0'",
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
method = request.get("method")
|
|
34
|
+
params = request.get("params") or {}
|
|
35
|
+
|
|
36
|
+
if method == "clm.status":
|
|
37
|
+
return {
|
|
38
|
+
"jsonrpc": "2.0",
|
|
39
|
+
"id": req_id,
|
|
40
|
+
"result": {
|
|
41
|
+
"status": "ready",
|
|
42
|
+
"layers": [0, 1, 2, 3, 4],
|
|
43
|
+
"registered_cards": len(self.card_registry),
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
elif method == "clm.eval":
|
|
47
|
+
expr = params.get("expression", "")
|
|
48
|
+
content_hash = compute_blake3(expr)
|
|
49
|
+
return {
|
|
50
|
+
"jsonrpc": "2.0",
|
|
51
|
+
"id": req_id,
|
|
52
|
+
"result": {
|
|
53
|
+
"evaluated": expr,
|
|
54
|
+
"content_hash": content_hash,
|
|
55
|
+
"success": True,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
elif method == "clm.card_get":
|
|
59
|
+
card_hash = params.get("hash", "")
|
|
60
|
+
if card_hash in self.card_registry:
|
|
61
|
+
return {
|
|
62
|
+
"jsonrpc": "2.0",
|
|
63
|
+
"id": req_id,
|
|
64
|
+
"result": self.card_registry[card_hash],
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
"jsonrpc": "2.0",
|
|
68
|
+
"id": req_id,
|
|
69
|
+
"error": {
|
|
70
|
+
"code": -32004,
|
|
71
|
+
"message": f"Card not found: {card_hash}",
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
else:
|
|
75
|
+
return {
|
|
76
|
+
"jsonrpc": "2.0",
|
|
77
|
+
"id": req_id,
|
|
78
|
+
"error": {
|
|
79
|
+
"code": -32601,
|
|
80
|
+
"message": f"Method not found: {method}",
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Alias for coordinating frameworks and canary verification
|
|
86
|
+
CLMGateway = GatewayRouter
|
|
87
|
+
|
|
88
|
+
__all__ = [
|
|
89
|
+
"GatewayRouter",
|
|
90
|
+
"CLMGateway",
|
|
91
|
+
]
|