11"""Pure, no-broker P5 shadow-ledger receipt construction for frozen TQQQ input.
22
33There is intentionally no Alpaca client in this module. The caller supplies a
4- validated forward decision and writes the resulting receipt to its own
5- create-only storage. The module neither reads credentials nor makes network
6- requests nor produces broker order payloads.
4+ validated forward decision and a bounded receipt from the independently
5+ protected policy gate, then writes the resulting receipt to its own create-only
6+ storage. The module neither reads credentials nor makes network requests nor
7+ produces broker order payloads.
78"""
89
910from __future__ import annotations
1920from pathlib import Path
2021from typing import Any
2122
22- INPUT_SCHEMA = "qsl.tqqq_shadow_cycle_input.v1"
23- RECEIPT_SCHEMA = "qsl.tqqq_shadow_ledger_receipt.v1"
23+ INPUT_SCHEMA = "qsl.tqqq_shadow_cycle_input.v2"
24+ RECEIPT_SCHEMA = "qsl.tqqq_shadow_ledger_receipt.v2"
25+ POLICY_GATE_RECEIPT_SCHEMA = "qsl.gcp_kms_policy_gate_receipt.v1"
2426CANDIDATE_ID = "tqqq_core_only_p2_v5"
2527_DIGEST = re .compile (r"^[0-9a-f]{64}$" )
2628_REVISION = re .compile (r"^[0-9a-f]{40}$" )
3840 "schema" ,
3941 "cycle_id" ,
4042 "produced_at" ,
43+ "deployment_bundle_sha256" ,
4144 "candidate" ,
4245 "source_evidence" ,
4346 "forward_decision" ,
4447 "risk_control" ,
48+ "policy_gate_receipt" ,
4549 "input_sha256" ,
4650}
4751_CANDIDATE_FIELDS = {"candidate_id" , "config_sha256" , "strategy_repository" , "strategy_revision" }
4852_EVIDENCE_FIELDS = {"p1_manifest_sha256" , "p2_config_sha256" , "p3_evidence_sha256" , "producer_revision" }
4953_DECISION_FIELDS = {"decision_id" , "effective_session" , "producer_revision" , "allocation_bps" , "decision_sha256" }
5054_RISK_CONTROL_FIELDS = {"stage" , "execution_lane" , "risk_policy_id" , "risk_policy_version" , "risk_policy_sha256" }
55+ _POLICY_GATE_RECEIPT_FIELDS = {
56+ "schema" ,
57+ "verified_at" ,
58+ "deployment_bundle" ,
59+ "policy" ,
60+ "activation" ,
61+ "target" ,
62+ "risk_control" ,
63+ "trusted_policy_root" ,
64+ "signature_sha256" ,
65+ "receipt_sha256" ,
66+ }
67+ _POLICY_GATE_BUNDLE_FIELDS = {"schema" , "bundle_id" , "bundle_sha256" }
68+ _POLICY_GATE_POLICY_FIELDS = {
69+ "policy_id" ,
70+ "policy_version" ,
71+ "policy_sha256" ,
72+ "stage" ,
73+ "effective_at" ,
74+ "expires_at" ,
75+ }
76+ _POLICY_GATE_ACTIVATION_FIELDS = {"activation_id" , "activation_sha256" , "effective_at" , "expires_at" }
77+ _POLICY_GATE_TARGET_FIELDS = {"platform" , "repository" , "revision" , "environment" , "target_sha256" }
78+ _POLICY_GATE_RISK_FIELDS = {"risk_policy_id" , "risk_policy_version" , "risk_policy_sha256" }
79+ _POLICY_GATE_ROOT_FIELDS = {"root_id" , "trusted_policy_root_sha256" , "expires_at" }
5180_RECEIPT_FIELDS = {
52- "schema" , "cycle_id" , "produced_at" , "candidate" , "source_evidence" , "forward_decision" , "risk_control" ,
81+ "schema" , "cycle_id" , "produced_at" , "deployment_bundle_sha256" , "candidate" , "source_evidence" , "forward_decision" , "risk_control" ,
82+ "policy_gate_receipt" ,
5383 "ledger_parent_sha256" , "shadow_adjustments_bps" , "receipt_sha256" ,
5484}
5585
@@ -153,6 +183,13 @@ def calculate_receipt_sha256(value: Mapping[str, Any]) -> str:
153183 return hashlib .sha256 (_canonical_json (value , "receipt_sha256" , "shadow receipt" ).encode ("utf-8" )).hexdigest ()
154184
155185
186+ def calculate_policy_gate_receipt_sha256 (value : Mapping [str , Any ]) -> str :
187+ """Return the digest of the non-secret receipt emitted by the P0 KMS gate."""
188+ return hashlib .sha256 (
189+ _canonical_json (value , "receipt_sha256" , "policy-gate receipt" ).encode ("utf-8" )
190+ ).hexdigest ()
191+
192+
156193def _allocation (value : Any , path : str ) -> dict [str , int ]:
157194 allocation = _object (value , path )
158195 _exact_keys (allocation , set (_ALLOCATION_SYMBOLS ), path )
@@ -242,25 +279,174 @@ def _risk_control(value: Any) -> dict[str, str]:
242279 }
243280
244281
282+ def _repository (value : Any , path : str ) -> str :
283+ if not isinstance (value , str ) or not re .fullmatch (r"[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*" , value ):
284+ _fail (f"{ path } must be an owner/repository identity" )
285+ return value
286+
287+
288+ def _policy_gate_receipt (
289+ value : Any ,
290+ * ,
291+ observed_at : datetime ,
292+ expected_bundle_sha256 : str ,
293+ risk_control : Mapping [str , str ],
294+ ) -> dict [str , Any ]:
295+ """Validate a bounded upstream policy-gate receipt for the P5 shadow lane.
296+
297+ The receipt must arrive from the independently protected KMS gate. This
298+ consumer checks its closed structure, digest, control window, and exact
299+ risk-policy binding; it neither verifies a KMS signature nor issues a
300+ policy itself.
301+ """
302+ _reject_unsafe_material (value , "policy_gate_receipt" )
303+ receipt = _object (value , "policy_gate_receipt" )
304+ _exact_keys (receipt , _POLICY_GATE_RECEIPT_FIELDS , "policy_gate_receipt" )
305+ if receipt ["schema" ] != POLICY_GATE_RECEIPT_SCHEMA :
306+ _fail (f"policy_gate_receipt.schema must be { POLICY_GATE_RECEIPT_SCHEMA } " )
307+ verified_at_text = receipt ["verified_at" ]
308+ verified_at = _timestamp (verified_at_text , "policy_gate_receipt.verified_at" )
309+
310+ bundle = _object (receipt ["deployment_bundle" ], "policy_gate_receipt.deployment_bundle" )
311+ _exact_keys (bundle , _POLICY_GATE_BUNDLE_FIELDS , "policy_gate_receipt.deployment_bundle" )
312+ if bundle ["schema" ] != "qsl.deployment_bundle.v1" :
313+ _fail ("policy_gate_receipt.deployment_bundle.schema must be qsl.deployment_bundle.v1" )
314+ normalized_bundle = {
315+ "schema" : "qsl.deployment_bundle.v1" ,
316+ "bundle_id" : _identity (bundle ["bundle_id" ], "policy_gate_receipt.deployment_bundle.bundle_id" ),
317+ "bundle_sha256" : _digest (bundle ["bundle_sha256" ], "policy_gate_receipt.deployment_bundle.bundle_sha256" ),
318+ }
319+ if normalized_bundle ["bundle_sha256" ] != expected_bundle_sha256 :
320+ _fail ("policy_gate_receipt deployment bundle does not match shadow input" )
321+
322+ policy = _object (receipt ["policy" ], "policy_gate_receipt.policy" )
323+ _exact_keys (policy , _POLICY_GATE_POLICY_FIELDS , "policy_gate_receipt.policy" )
324+ if policy ["stage" ] != "SHADOW" :
325+ _fail ("policy_gate_receipt.policy.stage must be SHADOW" )
326+ policy_effective_text = policy ["effective_at" ]
327+ policy_expires_text = policy ["expires_at" ]
328+ policy_effective = _timestamp (policy_effective_text , "policy_gate_receipt.policy.effective_at" )
329+ policy_expires = _timestamp (policy_expires_text , "policy_gate_receipt.policy.expires_at" )
330+ if policy_expires <= policy_effective :
331+ _fail ("policy_gate_receipt policy window is invalid" )
332+ normalized_policy = {
333+ "policy_id" : _identity (policy ["policy_id" ], "policy_gate_receipt.policy.policy_id" ),
334+ "policy_version" : _identity (policy ["policy_version" ], "policy_gate_receipt.policy.policy_version" ),
335+ "policy_sha256" : _digest (policy ["policy_sha256" ], "policy_gate_receipt.policy.policy_sha256" ),
336+ "stage" : "SHADOW" ,
337+ "effective_at" : policy_effective_text ,
338+ "expires_at" : policy_expires_text ,
339+ }
340+
341+ activation = _object (receipt ["activation" ], "policy_gate_receipt.activation" )
342+ _exact_keys (activation , _POLICY_GATE_ACTIVATION_FIELDS , "policy_gate_receipt.activation" )
343+ activation_effective_text = activation ["effective_at" ]
344+ activation_expires_text = activation ["expires_at" ]
345+ activation_effective = _timestamp (activation_effective_text , "policy_gate_receipt.activation.effective_at" )
346+ activation_expires = _timestamp (activation_expires_text , "policy_gate_receipt.activation.expires_at" )
347+ if activation_expires <= activation_effective :
348+ _fail ("policy_gate_receipt activation window is invalid" )
349+ if activation_effective < policy_effective or activation_expires > policy_expires :
350+ _fail ("policy_gate_receipt activation window is not contained in policy window" )
351+ normalized_activation = {
352+ "activation_id" : _identity (activation ["activation_id" ], "policy_gate_receipt.activation.activation_id" ),
353+ "activation_sha256" : _digest (activation ["activation_sha256" ], "policy_gate_receipt.activation.activation_sha256" ),
354+ "effective_at" : activation_effective_text ,
355+ "expires_at" : activation_expires_text ,
356+ }
357+
358+ target = _object (receipt ["target" ], "policy_gate_receipt.target" )
359+ _exact_keys (target , _POLICY_GATE_TARGET_FIELDS , "policy_gate_receipt.target" )
360+ normalized_target = {
361+ "platform" : _identity (target ["platform" ], "policy_gate_receipt.target.platform" ),
362+ "repository" : _repository (target ["repository" ], "policy_gate_receipt.target.repository" ),
363+ "revision" : _revision (target ["revision" ], "policy_gate_receipt.target.revision" ),
364+ "environment" : _identity (target ["environment" ], "policy_gate_receipt.target.environment" ),
365+ "target_sha256" : _digest (target ["target_sha256" ], "policy_gate_receipt.target.target_sha256" ),
366+ }
367+ if normalized_target ["platform" ] != "alpaca" or normalized_target ["repository" ] != "QuantStrategyLab/AlpacaPlatform" :
368+ _fail ("policy_gate_receipt target must bind this Alpaca P5 gateway" )
369+ if normalized_target ["environment" ] != "alpaca-shadow" :
370+ _fail ("policy_gate_receipt target.environment must be alpaca-shadow" )
371+
372+ receipt_risk = _object (receipt ["risk_control" ], "policy_gate_receipt.risk_control" )
373+ _exact_keys (receipt_risk , _POLICY_GATE_RISK_FIELDS , "policy_gate_receipt.risk_control" )
374+ normalized_risk = {
375+ "risk_policy_id" : _identity (receipt_risk ["risk_policy_id" ], "policy_gate_receipt.risk_control.risk_policy_id" ),
376+ "risk_policy_version" : _identity (
377+ receipt_risk ["risk_policy_version" ], "policy_gate_receipt.risk_control.risk_policy_version"
378+ ),
379+ "risk_policy_sha256" : _digest (
380+ receipt_risk ["risk_policy_sha256" ], "policy_gate_receipt.risk_control.risk_policy_sha256"
381+ ),
382+ }
383+ if normalized_risk != {
384+ "risk_policy_id" : risk_control ["risk_policy_id" ],
385+ "risk_policy_version" : risk_control ["risk_policy_version" ],
386+ "risk_policy_sha256" : risk_control ["risk_policy_sha256" ],
387+ }:
388+ _fail ("policy_gate_receipt risk control does not match shadow input" )
389+
390+ trusted_root = _object (receipt ["trusted_policy_root" ], "policy_gate_receipt.trusted_policy_root" )
391+ _exact_keys (trusted_root , _POLICY_GATE_ROOT_FIELDS , "policy_gate_receipt.trusted_policy_root" )
392+ root_expires_text = trusted_root ["expires_at" ]
393+ root_expires = _timestamp (root_expires_text , "policy_gate_receipt.trusted_policy_root.expires_at" )
394+ normalized_root = {
395+ "root_id" : _identity (trusted_root ["root_id" ], "policy_gate_receipt.trusted_policy_root.root_id" ),
396+ "trusted_policy_root_sha256" : _digest (
397+ trusted_root ["trusted_policy_root_sha256" ], "policy_gate_receipt.trusted_policy_root.trusted_policy_root_sha256"
398+ ),
399+ "expires_at" : root_expires_text ,
400+ }
401+ normalized : dict [str , Any ] = {
402+ "schema" : POLICY_GATE_RECEIPT_SCHEMA ,
403+ "verified_at" : verified_at_text ,
404+ "deployment_bundle" : normalized_bundle ,
405+ "policy" : normalized_policy ,
406+ "activation" : normalized_activation ,
407+ "target" : normalized_target ,
408+ "risk_control" : normalized_risk ,
409+ "trusted_policy_root" : normalized_root ,
410+ "signature_sha256" : _digest (receipt ["signature_sha256" ], "policy_gate_receipt.signature_sha256" ),
411+ "receipt_sha256" : _digest (receipt ["receipt_sha256" ], "policy_gate_receipt.receipt_sha256" ),
412+ }
413+ if normalized ["receipt_sha256" ] != calculate_policy_gate_receipt_sha256 (normalized ):
414+ _fail ("policy_gate_receipt.receipt_sha256 mismatch" )
415+ if observed_at < verified_at or observed_at < activation_effective :
416+ _fail ("policy_gate_receipt is not yet effective for this shadow cycle" )
417+ if observed_at >= min (policy_expires , activation_expires , root_expires ):
418+ _fail ("policy_gate_receipt is expired for this shadow cycle" )
419+ return normalized
420+
421+
245422def validate_shadow_cycle_input (value : Any ) -> dict [str , Any ]:
246- """Validate one bounded P5 input; this does not verify or issue its policy."""
423+ """Validate one bounded P5 input; this does not verify or issue a KMS policy."""
247424 _reject_unsafe_material (value , "shadow_input" )
248425 root = _object (value , "shadow_input" )
249426 _exact_keys (root , _INPUT_FIELDS , "shadow_input" )
250427 if root ["schema" ] != INPUT_SCHEMA :
251428 _fail (f"shadow_input.schema must be { INPUT_SCHEMA } " )
252429 candidate = _candidate (root ["candidate" ])
430+ produced_at = _timestamp (root ["produced_at" ], "shadow_input.produced_at" )
431+ deployment_bundle_sha256 = _digest (root ["deployment_bundle_sha256" ], "shadow_input.deployment_bundle_sha256" )
432+ risk_control = _risk_control (root ["risk_control" ])
253433 normalized = {
254434 "schema" : INPUT_SCHEMA ,
255435 "cycle_id" : _identity (root ["cycle_id" ], "shadow_input.cycle_id" ),
256436 "produced_at" : root ["produced_at" ],
437+ "deployment_bundle_sha256" : deployment_bundle_sha256 ,
257438 "candidate" : candidate ,
258439 "source_evidence" : _source_evidence (root ["source_evidence" ], candidate ),
259440 "forward_decision" : _forward_decision (root ["forward_decision" ], candidate ),
260- "risk_control" : _risk_control (root ["risk_control" ]),
441+ "risk_control" : risk_control ,
442+ "policy_gate_receipt" : _policy_gate_receipt (
443+ root ["policy_gate_receipt" ],
444+ observed_at = produced_at ,
445+ expected_bundle_sha256 = deployment_bundle_sha256 ,
446+ risk_control = risk_control ,
447+ ),
261448 "input_sha256" : _digest (root ["input_sha256" ], "shadow_input.input_sha256" ),
262449 }
263- _timestamp (normalized ["produced_at" ], "shadow_input.produced_at" )
264450 if normalized ["input_sha256" ] != calculate_input_sha256 (normalized ):
265451 _fail ("shadow_input.input_sha256 mismatch" )
266452 return normalized
@@ -274,21 +460,30 @@ def validate_shadow_ledger_receipt(value: Any) -> dict[str, Any]:
274460 if root ["schema" ] != RECEIPT_SCHEMA :
275461 _fail (f"shadow_receipt.schema must be { RECEIPT_SCHEMA } " )
276462 candidate = _candidate (root ["candidate" ])
463+ produced_at = _timestamp (root ["produced_at" ], "shadow_receipt.produced_at" )
464+ deployment_bundle_sha256 = _digest (root ["deployment_bundle_sha256" ], "shadow_receipt.deployment_bundle_sha256" )
465+ risk_control = _risk_control (root ["risk_control" ])
277466 normalized = {
278467 "schema" : RECEIPT_SCHEMA ,
279468 "cycle_id" : _identity (root ["cycle_id" ], "shadow_receipt.cycle_id" ),
280469 "produced_at" : root ["produced_at" ],
470+ "deployment_bundle_sha256" : deployment_bundle_sha256 ,
281471 "candidate" : candidate ,
282472 "source_evidence" : _source_evidence (root ["source_evidence" ], candidate ),
283473 "forward_decision" : _forward_decision (root ["forward_decision" ], candidate ),
284- "risk_control" : _risk_control (root ["risk_control" ]),
474+ "risk_control" : risk_control ,
475+ "policy_gate_receipt" : _policy_gate_receipt (
476+ root ["policy_gate_receipt" ],
477+ observed_at = produced_at ,
478+ expected_bundle_sha256 = deployment_bundle_sha256 ,
479+ risk_control = risk_control ,
480+ ),
285481 "ledger_parent_sha256" : _digest (root ["ledger_parent_sha256" ], "shadow_receipt.ledger_parent_sha256" ),
286482 "shadow_adjustments_bps" : _adjustment_bps (
287483 root ["shadow_adjustments_bps" ], "shadow_receipt.shadow_adjustments_bps"
288484 ),
289485 "receipt_sha256" : _digest (root ["receipt_sha256" ], "shadow_receipt.receipt_sha256" ),
290486 }
291- _timestamp (normalized ["produced_at" ], "shadow_receipt.produced_at" )
292487 if normalized ["receipt_sha256" ] != calculate_receipt_sha256 (normalized ):
293488 _fail ("shadow_receipt.receipt_sha256 mismatch" )
294489 return normalized
@@ -320,10 +515,12 @@ def build_shadow_ledger_receipt(cycle_input: Any, *, prior_receipt: Any | None =
320515 "schema" : RECEIPT_SCHEMA ,
321516 "cycle_id" : cycle ["cycle_id" ],
322517 "produced_at" : cycle ["produced_at" ],
518+ "deployment_bundle_sha256" : cycle ["deployment_bundle_sha256" ],
323519 "candidate" : cycle ["candidate" ],
324520 "source_evidence" : cycle ["source_evidence" ],
325521 "forward_decision" : cycle ["forward_decision" ],
326522 "risk_control" : cycle ["risk_control" ],
523+ "policy_gate_receipt" : cycle ["policy_gate_receipt" ],
327524 "ledger_parent_sha256" : parent_sha256 ,
328525 "shadow_adjustments_bps" : _adjustments (cycle ["forward_decision" ]["allocation_bps" ], previous_allocation ),
329526 "receipt_sha256" : "" ,
0 commit comments