clm-kernel 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,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
+ ]