From c4f748af65d5ebd3a1ddc3ddcd7d30a9c862b841 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Thu, 15 Oct 2026 21:38:24 -0700 Subject: [PATCH 01/17] test(confidential-workflow): exercise pinned released cMCP and cA2A APIs --- .../RELEASED-API-PROBE.md | 9 ++++ .../harness/released_protocol_probe.py | 47 +++++++++++++++++++ .../tests/test_released_protocol_probe.py | 22 +++++++++ 3 files changed, 78 insertions(+) create mode 100644 integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/released_protocol_probe.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_released_protocol_probe.py diff --git a/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md b/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md new file mode 100644 index 0000000..25c48ab --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md @@ -0,0 +1,9 @@ +# Experimental released-protocol interoperability probe + +This isolated, deterministic probe executes published cmcp-runtime 0.7.0 MCPGateway.intercept_tool_call and ca2a-runtime 0.4.0 emit_dag/trace_record_hash APIs. Synthetic transaction ID and nonce are passed into the call and software-mode record context. The two signing keys are ephemeral test keys. + +To reproduce, install exactly cmcp-runtime==0.7.0 and ca2a-runtime==0.4.0 in an isolated Python environment, then run unittest discovery in the integration's tests directory with the integration directory on PYTHONPATH. + +Claim: component API interoperability only. The probe does not execute models or tools; actual_dispatches is zero. It does not prove independent peer receipt, live deployment, hardware TEE, independent key custody or composed confidentiality acceptance. The synthetic trace hashes do not establish external trust. + +Issue #199 remains open pending real tool and peer transport adapters, independent observation at each boundary, paired refusal and mutation tests for the composed workflow, and externally operated live evidence. Do not reuse component pass results as a composed or hardware assurance verdict. diff --git a/integrations/agentrust-confidential-workflow-harness/harness/released_protocol_probe.py b/integrations/agentrust-confidential-workflow-harness/harness/released_protocol_probe.py new file mode 100644 index 0000000..b369545 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/released_protocol_probe.py @@ -0,0 +1,47 @@ +"""Synthetic interoperability probe of released cMCP and cA2A APIs. + +No tool is dispatched and no live or hardware assurance is claimed. +""" +from dataclasses import dataclass +from importlib.metadata import version +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from cmcp_runtime.runtime_gateway import GovernancePolicy, MCPGateway +from ca2a_runtime.trace_binding import HopContext, HopSpec, emit_dag, trace_record_hash + +@dataclass(frozen=True) +class ProbeResult: + admitted: bool + reason: str + versions: dict[str, str] + hashes: tuple[str, ...] + evidence_class: str = "synthetic" + actual_dispatches: int = 0 + +def probe(*, transaction_id: str, subject: str, tool_name: str, + allowed_tools: tuple[str, ...], policy_digest: str, nonce: str) -> ProbeResult: + versions = {"cmcp-runtime": version("cmcp-runtime"), + "ca2a-runtime": version("ca2a-runtime")} + if versions != {"cmcp-runtime":"0.7.0", "ca2a-runtime":"0.4.0"}: + return ProbeResult(False, "unpinned-version", versions, ()) + if not transaction_id or not subject or not nonce or len(policy_digest) != 71 or not policy_digest.startswith("sha256:") or any(c not in "0123456789abcdef" for c in policy_digest[7:]): + return ProbeResult(False, "invalid-context", versions, ()) + gateway = MCPGateway(GovernancePolicy(allowed_tools=list(allowed_tools), max_tool_calls=1)) + try: + allowed, reason = gateway.intercept_tool_call(subject, tool_name, {"transaction_id":transaction_id, "nonce":nonce, "policy_digest":policy_digest}) + except Exception: + return ProbeResult(False, "gateway-error", versions, ()) + if not allowed: + return ProbeResult(False, reason, versions, ()) + ctx = HopContext.software(model_provider="synthetic", model_id="no-model-run", + image_label="interoperability-probe", policy_bundle_hash=policy_digest, + runtime_nonce=nonce) + hops = [HopSpec(subject=f"{subject}:{transaction_id}", signing_key=Ed25519PrivateKey.generate(), context=ctx, iat=1), + HopSpec(subject=f"peer:{transaction_id}", signing_key=Ed25519PrivateKey.generate(), context=ctx, iat=2, credential_id=f"test:{transaction_id}")] + try: + records = emit_dag(hops) + hashes = tuple(trace_record_hash(record) for record in records) + except Exception: + return ProbeResult(False, "trace-api-error", versions, ()) + if len(hashes) != 2 or not all(hashes): + return ProbeResult(False, "missing-evidence", versions, ()) + return ProbeResult(True, "published-apis-executed-only", versions, hashes) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_released_protocol_probe.py b/integrations/agentrust-confidential-workflow-harness/tests/test_released_protocol_probe.py new file mode 100644 index 0000000..fb198ff --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_released_protocol_probe.py @@ -0,0 +1,22 @@ +import unittest +from harness.released_protocol_probe import probe +GOOD = dict(transaction_id="test-txn", subject="test-subject", tool_name="inspect", + allowed_tools=("inspect",), policy_digest="sha256:"+"a"*64, nonce="test-nonce") +class ReleasedProtocolTests(unittest.TestCase): + def test_positive_uses_published_apis_and_does_not_dispatch(self): + result=probe(**GOOD) + self.assertTrue(result.admitted, result.reason) + self.assertEqual(len(result.hashes),2) + self.assertNotEqual(*result.hashes) + self.assertEqual(result.actual_dispatches,0) + self.assertEqual(result.evidence_class,"synthetic") + def test_disallowed_tool_refused(self): + result=probe(**{**GOOD,"allowed_tools":("different",)}) + self.assertFalse(result.admitted) + self.assertFalse(result.hashes) + def test_missing_or_corrupted_context_refused(self): + for overrides in ({"transaction_id":""},{"nonce":""},{"policy_digest":"invalid"},{"policy_digest":"sha256:"+"z"*64}): + with self.subTest(overrides=overrides): + result=probe(**{**GOOD,**overrides}) + self.assertFalse(result.admitted) + self.assertFalse(result.hashes) From 402f8a0232a8446ae7e43495b68c7692b273bf69 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Thu, 15 Oct 2026 21:40:11 -0700 Subject: [PATCH 02/17] ci: pin released protocol probe dependency closure with hashes --- .github/workflows/validate.yml | 5 +- requirements/confidential-workflow-probe.in | 2 + requirements/confidential-workflow-probe.txt | 743 +++++++++++++++++++ 3 files changed, 748 insertions(+), 2 deletions(-) create mode 100644 requirements/confidential-workflow-probe.in create mode 100644 requirements/confidential-workflow-probe.txt diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 958db20..2722c25 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -43,5 +43,6 @@ jobs: - name: Test AGT Marketplace snapshot generation run: python -m pytest tests/test_generate_agt_marketplace_catalog.py -q - name: Test confidential workflow harness - run: python -m unittest discover -s tests -v - working-directory: integrations/agentrust-confidential-workflow-harness + run: | + python -m pip install --require-hashes -r requirements/confidential-workflow-probe.txt + python -m unittest discover -s integrations/agentrust-confidential-workflow-harness/tests -v diff --git a/requirements/confidential-workflow-probe.in b/requirements/confidential-workflow-probe.in new file mode 100644 index 0000000..29808aa --- /dev/null +++ b/requirements/confidential-workflow-probe.in @@ -0,0 +1,2 @@ +cmcp-runtime==0.7.0 +ca2a-runtime==0.4.0 diff --git a/requirements/confidential-workflow-probe.txt b/requirements/confidential-workflow-probe.txt new file mode 100644 index 0000000..eac58fa --- /dev/null +++ b/requirements/confidential-workflow-probe.txt @@ -0,0 +1,743 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements/confidential-workflow-probe.in --generate-hashes --output-file requirements/confidential-workflow-probe.txt +agent-manifest==0.15.0 \ + --hash=sha256:62b10c22c0a3b0888a91f944c703d13f567430dcb25c7e4f71568854e4299017 \ + --hash=sha256:e6f33bc7b4a1243fded44154df535c5055e09e9f1603d6e9297482eaa6cfef77 + # via + # ca2a-runtime + # cmcp-runtime +agentrust-trace==0.11.0 \ + --hash=sha256:7de621eaf795449e365d04967a82a551a18d75c99d248b4a0923f6d593bdb09e \ + --hash=sha256:8b76de58ec74fad57417093bf7977fe826ce6ade5859b4d5be8ce3938f9fcb35 + # via + # ca2a-runtime + # cmcp-runtime +annotated-types==0.8.0 \ + --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ + --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + # via pydantic +anyio==4.15.1 \ + --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \ + --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94 + # via + # cmcp-runtime + # httpx + # starlette +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # jsonschema + # referencing +ca2a-runtime==0.4.0 \ + --hash=sha256:255928c353bdf423ec85b7c9f4e4102111982e8545b11c68f724a28ebd9c798b \ + --hash=sha256:f87a235c2fb31d2bf496e24223f5d95ee2efbca743e89c8ced22a6c87fd18bd5 + # via -r requirements/confidential-workflow-probe.in +cbor2==6.1.5 \ + --hash=sha256:015ed73f10e1f7b67306d41e36e0d7dc40e4a2100bc5c29b7a7f039ad3dc9061 \ + --hash=sha256:040cf628af473fe18cb6f56bdac556d2398102e56852aab5206fbeb3dbde6b52 \ + --hash=sha256:0529a95c1330c9c381286650dd65ff5b4ef136dcee06474ad30c028b5ae99a50 \ + --hash=sha256:09eeb76177758a0fdf1627a9428b384756872b048c6c0d7d158106b29b207d2c \ + --hash=sha256:0b1fa210f23b1f822ee0c9157c99b0e851fce93c6da1dc8441aa7fb3c4089d70 \ + --hash=sha256:0c1565bcd74a389b581e292592ccab0ed9c46286c6e986256820bc68c9ad7e8c \ + --hash=sha256:0fa113902a302c22429b32e2454251a8fd14b18204fdff647c869a54114c3ed1 \ + --hash=sha256:10d5237100190133d6a770181a63d93752cb67a2849c18484d196b5f8880784e \ + --hash=sha256:151f624186a6b607d14074dfffe7b601f403445ab430554e3d920390c3068b05 \ + --hash=sha256:1538e87b4b32764bc4940a37b6aa72e3bc6855033aac18d392d70daa89113a2b \ + --hash=sha256:1ebbc6e2d5ea8acf44cc2247d48ca4ccae724fcdb97eaa673903e2d87f0ffc5d \ + --hash=sha256:2634a4e8dbd86cfbdace0a546a1ded1fb024ebc4fbbeaea0232cc76721e6bc91 \ + --hash=sha256:30f88d1aff6c8c58ffec56591468f820d5ce6aee0bd64ae7443c0d7ef653eaf8 \ + --hash=sha256:40754de6aef3f3d37f2ab36bb431da145359d0e28fce739683f8717ad2e97280 \ + --hash=sha256:4144e2ba881534f62968cdb4a4f134e07a351e75c997d8debca65fcb2edd61c8 \ + --hash=sha256:42217c9de0ead6c5a6c1a6ca6b836204ac46b5bf4f57c758f522f308d7784bf0 \ + --hash=sha256:4c824355799799ab065686a05f65398319109955544db35cc797c60ad208b174 \ + --hash=sha256:4db32eefe9fc173939d114fb78e09f967e69627714ad2e3bca807d0ea9d386ad \ + --hash=sha256:4e298c8a88488ebbf5475e51273b8d80da08f7b47aebfa79eb904fc82da49474 \ + --hash=sha256:519f3f0d0d9467091c678f4a19a31e1b8756c10bbd6294cb3f906092f3da1597 \ + --hash=sha256:547c58e758462f06ba542b0af21afb150ee64c4c81d7ca6d1ecae0655c6a283d \ + --hash=sha256:5a5859d1f82dce094a1bdd6a5b318411b750262070bf5d37fbc9607d185f0b1b \ + --hash=sha256:65a677ff460f5c31f060a4bf8518f3e8184c321fddc0223a5ac2fac59a7f9f30 \ + --hash=sha256:68bcabc5b36a7c7c8825625b7b331a74098a4839d5d38b5cc29cb30a7acfee49 \ + --hash=sha256:694f75fdcdb8c6b9a71ab77f789f56be1deab20bbdbf948d5ff53cd7c2543dfc \ + --hash=sha256:6eb06160c42315ac0c4ded461c7d84d92fa18c69d13d17fc1dfc1fae96580c95 \ + --hash=sha256:6f340682e2481ab729c399f8b81147476c5a179cfef65d02402702aeb9429088 \ + --hash=sha256:73b97d92ce64a344015909f1888de0abec76211b9c1f33b075563a05512f3a98 \ + --hash=sha256:773ef85feea8beb5666a525e88197e3ef1c6629c6b6cf721e31b228c97cf6555 \ + --hash=sha256:789ef813f416d353aecd5c8824860ee4be94e0f1179a385eb2beccfbeb615e4f \ + --hash=sha256:7de5383eb059498291415f5b07f99e54dac4603dc99960eb0e2307c9cb2dc352 \ + --hash=sha256:7dfb68b65d6b0d0d90512626247bfa4993354f1e2b2d83b28b51785e63853422 \ + --hash=sha256:833db11fbea9808b080e5340d5f96615e28a6a6617618a4331e60082d0dc1ca4 \ + --hash=sha256:8665b7970e563fb807cca5c42815fe0741192a899b74bf9052557486a46f9188 \ + --hash=sha256:9140388e9a732f3748641abb91d257d30cc466a7ed13c2c5a3d1aaa6af37bd66 \ + --hash=sha256:9677ce1c3c0cb1fa5a4f721a127fc2cc06e8efc43ee8e5f94e292186d6b51953 \ + --hash=sha256:9907225060f8afcf31b5c97711cd057272160056a6b1b488313cc2b20c0afe74 \ + --hash=sha256:994b09c578e9dd7c5687a9f151f545bde705d12e47427b5a78c9d6cc970187f5 \ + --hash=sha256:9b3ba6f694ec196ebefc9c67ebc862b0fecdd3d6f85d5557378cf20ff8b1fb31 \ + --hash=sha256:a14edbdc9e02d9daa72c3b8805edb297a6025a35e708f7dd8ccbdf1b18adb40f \ + --hash=sha256:a4956f498cbf5eab192e0f838cc787e09bef4caab57f05ccbf00451935cacb8b \ + --hash=sha256:a9a154e010044662ce2e433f7c49e9c0f89ad7b86cb20e5d2e5afe6fd1753162 \ + --hash=sha256:af14089f5fb36f89b3f766acc7d4990cdfba7487ec0249d51bfa3a8caad25f0a \ + --hash=sha256:b586912cdb086dbad12052250acd5922fbe66a341ebee7031039eedf90fe84b1 \ + --hash=sha256:b70d7c47ea84d456034d2be02e89d92eef7044cfcedf6f05058e21d4452f0fef \ + --hash=sha256:b73d982e35a60e602a200feb2a9d272e850efdc9ff767b0f4887bdbc16d23e52 \ + --hash=sha256:bb58549a45e3f6355338345a2df449f42f45d55e4a20af24d4302d76a1578650 \ + --hash=sha256:c87272763122be24213c7bb3d47750a3af034da8755fbd3fcb0694c1efb6c3e8 \ + --hash=sha256:c916d7af4edcbf5dba157e9a8dd927bbf1fd66d3f137618226f7ad8b54bd944a \ + --hash=sha256:cf89dd755e9781bea60bb67c1569d32ca10c38412126ab58bbc0235c697d98fc \ + --hash=sha256:db607ae2b12c7eb85d463fe502a2f50111125bee69e70f85f793f0b7da7896e7 \ + --hash=sha256:dd3e4f08aaf25bca5db6274ac40e4d138b0e09890510c1fda20d5b7840e505fa \ + --hash=sha256:e1028f34af9158ee810c705a1c6c0b7c71f1e0a3c890fb343afd75725a80c191 \ + --hash=sha256:e1e8a6a72c7ab2f82579497cb1d5564987b02559ab980fe6a5f82a7d65031d19 \ + --hash=sha256:e6d54e11887e649345b2ecb491a8e2866f4abdb6d83abc2a1a52d5ee23785ff8 \ + --hash=sha256:eb30032171afc7ab95e524f13eee0c9a79af356b0414fa3a3736b3febca7d641 \ + --hash=sha256:eba54489d82683e8cdb9af80a2e55c2089e439e76b60cdb9fd4dfdc62ecfee3c \ + --hash=sha256:edc4a4dfa313b2cd78d7562cb99b51615e06c89832b78c0c02e2b5c2e27906ae \ + --hash=sha256:f02c339ab9942578b63a5d54c8956191f6e88f3d8b2c918024ff565f7faa1bde \ + --hash=sha256:f0bd6334302a5016a2b0f5530b7aea3ff588b6894523fd8491b49f7ce9e67f11 \ + --hash=sha256:f294e65db28424fe89985faf74648622e04da7977ca5401ac65c7d1b6538d08a \ + --hash=sha256:f850860e43d47312cb962bfdfe1cd879b180a04d0e7352f80e426b3852be8b79 \ + --hash=sha256:f8f85a49db66df77546d278de4d249772a4557d715df07ba8ae155cfa6a7fb31 \ + --hash=sha256:fd34b35b0a2b366f5b4bd53489ccd10d7576b0d4dd68db38ef64b4e617ea8f76 \ + --hash=sha256:fe81e4ff1b6bab72856d020dab89d86d4dcfbe18af4ff3fe2f391e1b03d0793c + # via agent-manifest +cedarpy==4.12.1 \ + --hash=sha256:14630de081249519d76a6c9aca9f677251aa5aa722a056ac4e1f365db392f1ed \ + --hash=sha256:1c765643cc3c1472d85324f42147f3732b1c8dd47f71ff066386e7c8afe109b7 \ + --hash=sha256:2609aaa1e2041599592a821de3741c56b2a778b9c20e87ac97c84a0531120dbd \ + --hash=sha256:28f99b8c3f8faf0cd78a82c2a845360f60f47d54ab8b97ba64e19b04a42e70d5 \ + --hash=sha256:3010ab38bd691387944d57d925aab6b743694a89a1cd3de4295421161bad1609 \ + --hash=sha256:333e89642a5914037bead6ddc16ad4d246d7a27f227856c5636db5524d16e27c \ + --hash=sha256:34316ad5eb18d5ed34beb5b00398b1928f9add67dca6fef7f12f6bf11eb86e8d \ + --hash=sha256:357002b436d06ad00cd24c1dc27287933af9c45af0541b0cd9d52036cccfe8c2 \ + --hash=sha256:35e7446894bf8889e5c04118391b8720afee7dcd2e9d4ebecfa646674301fb96 \ + --hash=sha256:3c216ce8e4e9f8edb03154424dd99611e99533c3fb2f552147f61b0fcde5ac22 \ + --hash=sha256:42e238d25695f92898d4249cd659b67e6d9cde0c3f5ab1403cc642af5e4f5b85 \ + --hash=sha256:46429641712af012e1b2579cc1083eb5ab1ea7df2e7117d94b965cfe515255ba \ + --hash=sha256:547fda0b8f9948634a8b4445a449d4736de4312c11a2133aa5df7deb1d2b99fb \ + --hash=sha256:6f814415d96eed4d724b2ea34f1017217a7022a958307986eb009d5878d482c9 \ + --hash=sha256:7047ff6368a9e52453dbd2ac33f8d7d062a67ff1ee3f3a887f41c0a857f3f6e4 \ + --hash=sha256:74b3d6deb0d482f9645b7709016ab81dc7405be83270b10790b646a60f475518 \ + --hash=sha256:7f71d2285a4cd048866bd66aac36c0cff791554d6755f4230a71987f0b601b71 \ + --hash=sha256:80dca63da4cea16969d023daff31a15665ba54a769f633fb26ac5594b7ffd291 \ + --hash=sha256:832bdcd9586bd9597e52b6adf248adc77c1e2b0c55f3cfc10259feb6627bcd96 \ + --hash=sha256:84aaac429b3cae8b9d496aef9ef99426c130bde390767f46738dea04b7a05714 \ + --hash=sha256:869e4318dbd827aea996fe0b858592866fb7ad1888484b71b5fdd15334eaf8a0 \ + --hash=sha256:91410cbd0027a91a1a3dadf919f1d54bc75a62705247b833a004c5e24add1803 \ + --hash=sha256:9497da64835f431a0acdc6f393a841235bc0657188978238badae2ded3653b02 \ + --hash=sha256:970ecd03259e691d8b947c972b03d3ae34c7dd1d3e9e39d03fbe62dccc03227f \ + --hash=sha256:a935e3ed9e5873f88348469eb917e816335f62387d0f9ebed7058fac76c9a547 \ + --hash=sha256:bbcfb99ef9dc0c155677473d7e5bea8baac6ff2abe27b89a530a7cabb184e3e0 \ + --hash=sha256:c5eea3a4ce98862f1dc06bef1038ae8edf52c74d36980e8e75b59a38168f53ab \ + --hash=sha256:d00d151a1b2bd3697d778fd89597cc16aa1fca11da25594d7a000c725477f151 \ + --hash=sha256:e8ed70991b958f344017663f753f044070cd0e61dfd4ffe408ba05d48c48dea4 \ + --hash=sha256:f164ca11999e1d3a1641548d17755c3c45a5385b2590a642ef3122a1196947ca + # via + # ca2a-runtime + # cmcp-runtime +certifi==2026.7.22 \ + --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ + --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 + # via + # httpcore + # httpx +cffi==2.1.1 \ + --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ + --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ + --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ + --hash=sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0 \ + --hash=sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6 \ + --hash=sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971 \ + --hash=sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c \ + --hash=sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d \ + --hash=sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9 \ + --hash=sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517 \ + --hash=sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735 \ + --hash=sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80 \ + --hash=sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f \ + --hash=sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1 \ + --hash=sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29 \ + --hash=sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8 \ + --hash=sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c \ + --hash=sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e \ + --hash=sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48 \ + --hash=sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813 \ + --hash=sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac \ + --hash=sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632 \ + --hash=sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6 \ + --hash=sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1 \ + --hash=sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659 \ + --hash=sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688 \ + --hash=sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004 \ + --hash=sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0 \ + --hash=sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062 \ + --hash=sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779 \ + --hash=sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94 \ + --hash=sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50 \ + --hash=sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab \ + --hash=sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac \ + --hash=sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6 \ + --hash=sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676 \ + --hash=sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 \ + --hash=sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9 \ + --hash=sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf \ + --hash=sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13 \ + --hash=sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e \ + --hash=sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e \ + --hash=sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973 \ + --hash=sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527 \ + --hash=sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 \ + --hash=sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890 \ + --hash=sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c \ + --hash=sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990 \ + --hash=sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd \ + --hash=sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9 \ + --hash=sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94 \ + --hash=sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3 \ + --hash=sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80 \ + --hash=sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41 \ + --hash=sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5 \ + --hash=sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c \ + --hash=sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a \ + --hash=sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4 \ + --hash=sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e \ + --hash=sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6 \ + --hash=sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98 \ + --hash=sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b \ + --hash=sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1 \ + --hash=sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03 \ + --hash=sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af \ + --hash=sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231 \ + --hash=sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2 \ + --hash=sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3 \ + --hash=sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836 \ + --hash=sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5 \ + --hash=sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399 \ + --hash=sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 \ + --hash=sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e \ + --hash=sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be \ + --hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf \ + --hash=sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc \ + --hash=sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455 \ + --hash=sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0 \ + --hash=sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12 \ + --hash=sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b \ + --hash=sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7 \ + --hash=sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692 \ + --hash=sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54 \ + --hash=sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3 \ + --hash=sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b \ + --hash=sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be \ + --hash=sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d \ + --hash=sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358 \ + --hash=sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a \ + --hash=sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7 \ + --hash=sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc \ + --hash=sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960 \ + --hash=sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125 \ + --hash=sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb \ + --hash=sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a \ + --hash=sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa \ + --hash=sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf \ + --hash=sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3 \ + --hash=sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4 \ + --hash=sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264 + # via cryptography +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 + # via + # cmcp-runtime + # uvicorn +cmcp-runtime==0.7.0 \ + --hash=sha256:3728a2ade10b7239b81815b2708900d4cfc6b9110091d58d19e62a93904dd06e \ + --hash=sha256:bdb083ab7925d44c36984de6b788b49d086d70fa8ee8b001dcef92804dcfc1b0 + # via -r requirements/confidential-workflow-probe.in +cryptography==50.0.2 \ + --hash=sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602 \ + --hash=sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2 \ + --hash=sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047 \ + --hash=sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c \ + --hash=sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42 \ + --hash=sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18 \ + --hash=sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51 \ + --hash=sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81 \ + --hash=sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856 \ + --hash=sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2 \ + --hash=sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de \ + --hash=sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7 \ + --hash=sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd \ + --hash=sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2 \ + --hash=sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be \ + --hash=sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45 \ + --hash=sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0 \ + --hash=sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e \ + --hash=sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c \ + --hash=sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5 \ + --hash=sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452 \ + --hash=sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48 \ + --hash=sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05 \ + --hash=sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1 \ + --hash=sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93 \ + --hash=sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04 \ + --hash=sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e \ + --hash=sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67 \ + --hash=sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7 \ + --hash=sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107 \ + --hash=sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079 \ + --hash=sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134 \ + --hash=sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227 \ + --hash=sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1 \ + --hash=sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539 \ + --hash=sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e \ + --hash=sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d \ + --hash=sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c \ + --hash=sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd \ + --hash=sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020 \ + --hash=sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd \ + --hash=sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94 \ + --hash=sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a \ + --hash=sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408 \ + --hash=sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37 \ + --hash=sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e \ + --hash=sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454 \ + --hash=sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c \ + --hash=sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc \ + --hash=sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37 \ + --hash=sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767 \ + --hash=sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a \ + --hash=sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5 \ + --hash=sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc \ + --hash=sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67 \ + --hash=sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8 \ + --hash=sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480 \ + --hash=sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb \ + --hash=sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b + # via + # agent-manifest + # agentrust-trace + # ca2a-runtime + # cmcp-runtime +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # uvicorn +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via cmcp-runtime +idna==3.20 \ + --hash=sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44 \ + --hash=sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c + # via + # anyio + # httpx +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via agentrust-trace +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +pycparser==3.11 \ + --hash=sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80 \ + --hash=sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc + # via cffi +pydantic==2.14.1 \ + --hash=sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454 \ + --hash=sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26 + # via + # agent-manifest + # agentrust-trace +pydantic-core==2.50.1 \ + --hash=sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c \ + --hash=sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc \ + --hash=sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b \ + --hash=sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63 \ + --hash=sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769 \ + --hash=sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019 \ + --hash=sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685 \ + --hash=sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b \ + --hash=sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482 \ + --hash=sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658 \ + --hash=sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498 \ + --hash=sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec \ + --hash=sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a \ + --hash=sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b \ + --hash=sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4 \ + --hash=sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a \ + --hash=sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8 \ + --hash=sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb \ + --hash=sha256:1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87 \ + --hash=sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e \ + --hash=sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8 \ + --hash=sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7 \ + --hash=sha256:2ab756b72bd5054e4c7ef3ded331b35786cbd3cf931531a508f79a9537517064 \ + --hash=sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae \ + --hash=sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a \ + --hash=sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e \ + --hash=sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396 \ + --hash=sha256:2eedf82ee4753cdab8e50044c6bd569577eebc3859b11fecf4eb9223761ff966 \ + --hash=sha256:30ddf019d082c117b5d309e5b86710c2a78909907ec1a9381feec3eec02eca0b \ + --hash=sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255 \ + --hash=sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899 \ + --hash=sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1 \ + --hash=sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43 \ + --hash=sha256:3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0 \ + --hash=sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb \ + --hash=sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f \ + --hash=sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d \ + --hash=sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415 \ + --hash=sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c \ + --hash=sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea \ + --hash=sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2 \ + --hash=sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e \ + --hash=sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568 \ + --hash=sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6 \ + --hash=sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b \ + --hash=sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe \ + --hash=sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9 \ + --hash=sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a \ + --hash=sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2 \ + --hash=sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9 \ + --hash=sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1 \ + --hash=sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff \ + --hash=sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef \ + --hash=sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f \ + --hash=sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5 \ + --hash=sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5 \ + --hash=sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e \ + --hash=sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb \ + --hash=sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4 \ + --hash=sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87 \ + --hash=sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0 \ + --hash=sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597 \ + --hash=sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84 \ + --hash=sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b \ + --hash=sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa \ + --hash=sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242 \ + --hash=sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f \ + --hash=sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad \ + --hash=sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf \ + --hash=sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc \ + --hash=sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f \ + --hash=sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c \ + --hash=sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133 \ + --hash=sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61 \ + --hash=sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102 \ + --hash=sha256:8b4c3df25bd323bf1d36a648d563cf1fc69d717451569927151bdad7cad07a77 \ + --hash=sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4 \ + --hash=sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e \ + --hash=sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54 \ + --hash=sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350 \ + --hash=sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070 \ + --hash=sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed \ + --hash=sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb \ + --hash=sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5 \ + --hash=sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19 \ + --hash=sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92 \ + --hash=sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5 \ + --hash=sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112 \ + --hash=sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9 \ + --hash=sha256:a44101320cfe99432db74237545a63057dc7a88dfe792cbcad0647f2af56cb81 \ + --hash=sha256:a4aaaa791bdae1c972a7e81765f4f3571c926b8e0b9b6e47346499fb80079665 \ + --hash=sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5 \ + --hash=sha256:a7c58106de36ac6a56314182958de20db8d3a29dfd5db527192cc754e4f8e7fb \ + --hash=sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72 \ + --hash=sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2 \ + --hash=sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8 \ + --hash=sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295 \ + --hash=sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e \ + --hash=sha256:b087b1c5be7ac687cf22eabfe4b6b608d40df23610651e93611e1f49118baf84 \ + --hash=sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f \ + --hash=sha256:b281a3b0f0822618fe5e3e0d8a2048b6356b14388505dc9374ccffeb69989713 \ + --hash=sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71 \ + --hash=sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253 \ + --hash=sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe \ + --hash=sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290 \ + --hash=sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea \ + --hash=sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662 \ + --hash=sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41 \ + --hash=sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44 \ + --hash=sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807 \ + --hash=sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d \ + --hash=sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78 \ + --hash=sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c \ + --hash=sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3 \ + --hash=sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831 \ + --hash=sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86 \ + --hash=sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2 \ + --hash=sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f \ + --hash=sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f \ + --hash=sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10 \ + --hash=sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5 \ + --hash=sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b \ + --hash=sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980 \ + --hash=sha256:d939de9c82e2126f7f48a7e658f8a85ed46d57662d53f44c49b8895fe94a3eb7 \ + --hash=sha256:de531ce1e2a3364e8767878b58f4ff728a434b4fde089781fe30b1e08e2396e0 \ + --hash=sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09 \ + --hash=sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a \ + --hash=sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5 \ + --hash=sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e \ + --hash=sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459 \ + --hash=sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760 \ + --hash=sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08 \ + --hash=sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906 \ + --hash=sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709 \ + --hash=sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a \ + --hash=sha256:f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3 \ + --hash=sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20 + # via pydantic +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via + # ca2a-runtime + # cmcp-runtime +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # agentrust-trace + # jsonschema + # jsonschema-specifications +rfc3986-validator==0.1.1 \ + --hash=sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9 \ + --hash=sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055 + # via agentrust-trace +rfc8785==0.1.4 \ + --hash=sha256:520d690b448ecf0703691c76e1a34a24ddcd4fc5bc41d589cb7c58ec651bcd48 \ + --hash=sha256:e545841329fe0eee4f6a3b44e7034343100c12b4ec566dc06ca9735681deb4da + # via + # agentrust-trace + # ca2a-runtime + # cmcp-runtime +rpds-py==2026.9.1 \ + --hash=sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25 \ + --hash=sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68 \ + --hash=sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495 \ + --hash=sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32 \ + --hash=sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96 \ + --hash=sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834 \ + --hash=sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0 \ + --hash=sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6 \ + --hash=sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e \ + --hash=sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa \ + --hash=sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759 \ + --hash=sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff \ + --hash=sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7 \ + --hash=sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c \ + --hash=sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903 \ + --hash=sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d \ + --hash=sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9 \ + --hash=sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e \ + --hash=sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b \ + --hash=sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a \ + --hash=sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a \ + --hash=sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2 \ + --hash=sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945 \ + --hash=sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91 \ + --hash=sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044 \ + --hash=sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69 \ + --hash=sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e \ + --hash=sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3 \ + --hash=sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4 \ + --hash=sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33 \ + --hash=sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52 \ + --hash=sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9 \ + --hash=sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb \ + --hash=sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35 \ + --hash=sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299 \ + --hash=sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c \ + --hash=sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063 \ + --hash=sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a \ + --hash=sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6 \ + --hash=sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12 \ + --hash=sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14 \ + --hash=sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0 \ + --hash=sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30 \ + --hash=sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31 \ + --hash=sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02 \ + --hash=sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af \ + --hash=sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef \ + --hash=sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0 \ + --hash=sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888 \ + --hash=sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a \ + --hash=sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c \ + --hash=sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b \ + --hash=sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86 \ + --hash=sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f \ + --hash=sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531 \ + --hash=sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6 \ + --hash=sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82 \ + --hash=sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d \ + --hash=sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7 \ + --hash=sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448 \ + --hash=sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391 \ + --hash=sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb \ + --hash=sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4 \ + --hash=sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e \ + --hash=sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c \ + --hash=sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15 \ + --hash=sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f \ + --hash=sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297 \ + --hash=sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36 \ + --hash=sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d \ + --hash=sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b \ + --hash=sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237 \ + --hash=sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb \ + --hash=sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477 \ + --hash=sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c \ + --hash=sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e \ + --hash=sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608 \ + --hash=sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07 \ + --hash=sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c \ + --hash=sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35 \ + --hash=sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0 \ + --hash=sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785 \ + --hash=sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad \ + --hash=sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc \ + --hash=sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9 \ + --hash=sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025 \ + --hash=sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f \ + --hash=sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009 \ + --hash=sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d \ + --hash=sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118 \ + --hash=sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db \ + --hash=sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9 \ + --hash=sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab \ + --hash=sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37 \ + --hash=sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d \ + --hash=sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc \ + --hash=sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9 \ + --hash=sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3 \ + --hash=sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569 \ + --hash=sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69 \ + --hash=sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b \ + --hash=sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018 \ + --hash=sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f \ + --hash=sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c \ + --hash=sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f \ + --hash=sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5 \ + --hash=sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445 \ + --hash=sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1 \ + --hash=sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e \ + --hash=sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429 \ + --hash=sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa \ + --hash=sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b \ + --hash=sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250 \ + --hash=sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e \ + --hash=sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83 \ + --hash=sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f \ + --hash=sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd \ + --hash=sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346 \ + --hash=sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858 \ + --hash=sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871 \ + --hash=sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da \ + --hash=sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0 \ + --hash=sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f \ + --hash=sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050 \ + --hash=sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31 \ + --hash=sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df \ + --hash=sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12 \ + --hash=sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567 + # via + # jsonschema + # referencing +starlette==1.8.0 \ + --hash=sha256:1565dc0b35d5737a271ed1e0e04e949f4e81198799f216d2667b0a0fb9cf9522 \ + --hash=sha256:dfdd6b29c26483288088d990eee59631dedadd66ce20d203402a7ca8e3c4656f + # via cmcp-runtime +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # anyio + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.4 \ + --hash=sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47 \ + --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + # via pydantic +uvicorn==0.54.0 \ + --hash=sha256:505bdb0f318731d45f1f712071fc781a8981f6847a31c902c9f5e652d4f67faf \ + --hash=sha256:a2e33cbfaa0306f8e6b0c13e0cb89d7d7a2da3e62b90c66e18c33d9807b28620 + # via cmcp-runtime From 976627bd87363003f9b5bcd0fe17f72d17cfa99e Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Thu, 15 Oct 2026 21:40:51 -0700 Subject: [PATCH 03/17] ci: run pinned protocol checks from harness module root --- .github/workflows/validate.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 2722c25..683a7d1 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -44,5 +44,6 @@ jobs: run: python -m pytest tests/test_generate_agt_marketplace_catalog.py -q - name: Test confidential workflow harness run: | - python -m pip install --require-hashes -r requirements/confidential-workflow-probe.txt - python -m unittest discover -s integrations/agentrust-confidential-workflow-harness/tests -v + python -m pip install --require-hashes -r ../../requirements/confidential-workflow-probe.txt + python -m unittest discover -s tests -v + working-directory: integrations/agentrust-confidential-workflow-harness From 95a51b3bb406a1d8a65d80d387dffb1178985872 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Thu, 15 Oct 2026 22:30:37 -0700 Subject: [PATCH 04/17] test: authenticate cA2A responses after cMCP admission with paired negatives --- .../RELEASED-API-PROBE.md | 34 ++++++++-- .../harness/authenticated_exchange.py | 67 +++++++++++++++++++ .../tests/test_authenticated_exchange.py | 29 ++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/authenticated_exchange.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_authenticated_exchange.py diff --git a/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md b/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md index 25c48ab..5af1a0d 100644 --- a/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md +++ b/integrations/agentrust-confidential-workflow-harness/RELEASED-API-PROBE.md @@ -1,9 +1,33 @@ -# Experimental released-protocol interoperability probe +# Experimental released-protocol interoperability and authenticated response -This isolated, deterministic probe executes published cmcp-runtime 0.7.0 MCPGateway.intercept_tool_call and ca2a-runtime 0.4.0 emit_dag/trace_record_hash APIs. Synthetic transaction ID and nonce are passed into the call and software-mode record context. The two signing keys are ephemeral test keys. +This deterministic acceptance milestone executes the published `cmcp-runtime==0.7.0` +`MCPGateway.intercept_tool_call` and `ca2a-runtime==0.4.0` +`emit_dag` / `trace_record_hash` APIs, plus +`PendingResponse` / `ResponseProducer` authenticated response verification. +Positive and refusal tests are paired. All dependencies are transitively pinned +by the SHA-256 hash-locked requirements file. -To reproduce, install exactly cmcp-runtime==0.7.0 and ca2a-runtime==0.4.0 in an isolated Python environment, then run unittest discovery in the integration's tests directory with the integration directory on PYTHONPATH. +A local test-generated X25519 peer key is marked assurance `none`, rather than +hardware-verified. Signed TRACE DAG records use temporary Ed25519 keys and a +synthetic source. The actual dispatch count is zero. This is **not** a live +model, tool or network workflow. -Claim: component API interoperability only. The probe does not execute models or tools; actual_dispatches is zero. It does not prove independent peer receipt, live deployment, hardware TEE, independent key custody or composed confidentiality acceptance. The synthetic trace hashes do not establish external trust. +Reproduce from the repository root with Python 3.12: -Issue #199 remains open pending real tool and peer transport adapters, independent observation at each boundary, paired refusal and mutation tests for the composed workflow, and externally operated live evidence. Do not reuse component pass results as a composed or hardware assurance verdict. +```sh +python -m pip install --require-hashes -r requirements/confidential-workflow-probe.txt +cd integrations/agentrust-confidential-workflow-harness +python -m unittest discover -s tests -v +``` + +The authenticated-response probe covers permitted execution, unauthorized tool, +MAC alteration, single-use replay, expiry, signed denial, request/response +context mismatch, and unobserved receipt remaining unknown. The valid twin is +executed independently for each adversarial case. It does **not** establish +independent issuer enrollment, externally operated peer receipt, a real execution +side effect, secure confinement or hardware-backed runtime measurement. + +Issue #199 must remain open until actual mediated tool and peer network transport, +independent boundary observations, composed mutation testing, and independently +operated live evidence are produced and externally reproduced. Component +interoperability may not be substituted for these requirements. diff --git a/integrations/agentrust-confidential-workflow-harness/harness/authenticated_exchange.py b/integrations/agentrust-confidential-workflow-harness/harness/authenticated_exchange.py new file mode 100644 index 0000000..f2461a4 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/authenticated_exchange.py @@ -0,0 +1,67 @@ +"""Bounded, in-process cMCP admission and cA2A authenticated-response exercise. + +This is NOT network delivery, hardware attestation or independent verification. +The caller explicitly appraises a test-generated software key at assurance 'none'. +""" +from dataclasses import dataclass +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cmcp_runtime.runtime_gateway import MCPGateway, GovernancePolicy +from ca2a_runtime.attestation import VerifiedPeer +from ca2a_runtime.response import PendingResponse, ResponseProducer, decode, encode + +@dataclass(frozen=True) +class ExchangeResult: + outcome: str + request_sha256: str | None = None + observation: str = "synthetic-process-local" + actual_dispatches: int = 0 + +def exchange(*, transaction_id: str, nonce: str, allowed: bool = True, + tamper: bool = False, replay: bool = False, + expired: bool = False, refusal: bool = False, + binding_mismatch: bool = False, missing_receipt: bool = False) -> ExchangeResult: + if not transaction_id or not nonce: + return ExchangeResult("refused-missing-context") + gateway = MCPGateway(GovernancePolicy(allowed_tools=["test-tool"] if allowed else [])) + ok, _ = gateway.intercept_tool_call("test-agent", "test-tool", + {"transaction_id": transaction_id, "nonce": nonce}) + if not ok: + return ExchangeResult("refused-authorization") + key = X25519PrivateKey.generate() + peer = VerifiedPeer(key.public_key().public_bytes_raw().hex(), "none", "synthetic") + now = [10.0] + pending = PendingResponse(peer, {"transaction_id": transaction_id, "nonce": nonce}, + timeout=5.0, clock=lambda: now[0]) + producer = ResponseProducer(key, decode(pending.request_bytes)) + if missing_receipt: + return ExchangeResult("unknown-unobserved-delivery", pending.request_sha256) + if refusal: + raw = encode(producer.finish(403, {"error": {"code": "denied"}})) + try: + verified = pending.verify(403, raw) + return ExchangeResult("refused-authenticated", verified.request_sha256) + except Exception: + return ExchangeResult("unknown-verification-error", pending.request_sha256) + body = {"accepted": True, "transaction_id": transaction_id, "nonce": nonce} + if binding_mismatch: + body["nonce"] = "wrong-" + nonce + raw = encode(producer.finish(200, body)) + if tamper: + altered = decode(raw) + altered["body"]["nonce"] = "tampered" + raw = encode(altered) + if expired: + now[0] = 16.0 + try: + verified = pending.verify(200, raw) + if verified.body.get("transaction_id") != transaction_id or verified.body.get("nonce") != nonce: + return ExchangeResult("refused-response-binding", verified.request_sha256) + if replay: + try: + pending.verify(200, raw) + except Exception: + return ExchangeResult("refused-replay", verified.request_sha256) + return ExchangeResult("unsafe-replay-accepted", verified.request_sha256) + return ExchangeResult("authenticated-process-local", verified.request_sha256) + except Exception: + return ExchangeResult("refused-response-authentication", pending.request_sha256) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_authenticated_exchange.py b/integrations/agentrust-confidential-workflow-harness/tests/test_authenticated_exchange.py new file mode 100644 index 0000000..02c9da8 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_authenticated_exchange.py @@ -0,0 +1,29 @@ +import unittest +from harness.authenticated_exchange import exchange + +GOOD = {"transaction_id": "test-transaction", "nonce": "nonce-01"} +class AuthenticatedExchangeTests(unittest.TestCase): + def test_valid_authenticated_response_has_no_live_dispatch(self): + result = exchange(**GOOD) + self.assertEqual(result.outcome, "authenticated-process-local") + self.assertEqual(result.actual_dispatches, 0) + self.assertTrue(result.request_sha256) + def test_disallowed_tool_refused(self): + self.assertEqual(exchange(**GOOD,allowed=False).outcome,"refused-authorization") + def test_tamper_refused(self): + self.assertEqual(exchange(**GOOD,tamper=True).outcome,"refused-response-authentication") + def test_authenticated_refusal(self): + self.assertEqual(exchange(**GOOD,refusal=True).outcome,"refused-authenticated") + def test_replay_refused(self): + self.assertEqual(exchange(**GOOD,replay=True).outcome,"refused-replay") + def test_timeout_refused(self): + self.assertEqual(exchange(**GOOD,expired=True).outcome,"refused-response-authentication") + def test_missing_receipt_remains_unknown(self): + self.assertEqual(exchange(**GOOD,missing_receipt=True).outcome,"unknown-unobserved-delivery") + def test_response_binding_mismatch_refused(self): + self.assertEqual(exchange(**GOOD,binding_mismatch=True).outcome,"refused-response-binding") + def test_valid_twin_independent_of_each_negative(self): + for opt in ("tamper","replay","expired","refusal","binding_mismatch","missing_receipt"): + with self.subTest(opt=opt): + self.assertNotEqual(exchange(**GOOD, **{opt:True}).outcome, "authenticated-process-local") + self.assertEqual(exchange(**GOOD).outcome,"authenticated-process-local") From a9f72d71ea34bf48fe95a504fed398a6766da0a8 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 07:59:32 -0700 Subject: [PATCH 05/17] test: connect published cMCP admission to observed remote Conduit job --- .../LIVE-CONDUIT-EDRG.md | 31 +++++++ .../harness/conduit_live.py | 92 +++++++++++++++++++ .../tests/test_conduit_live.py | 65 +++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/conduit_live.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_conduit_live.py diff --git a/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md new file mode 100644 index 0000000..1190c55 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md @@ -0,0 +1,31 @@ +# Live Frequency Conduit bridge acceptance, October 16 2026 + +The `harness.conduit_live` bounded adapter performs cMCP 0.7.0 authorization +before dispatching one fixed `inspect` intent to the VPS with the preexisting +Frequency Conduit CLI. That CLI uses restricted-key SSH transport and a +whitelisted, resource-limited, network-disabled worker intent. + +The consumer verifies the start and finish receipt hashes, immediate hash-chain +link, session identity, remote node identity, clean worktree, pinned commit, +network-disabled plan, and successful completion. It fails closed to unknown +rather than treating a lost response as refusal or confirmed execution. + +Reproduced locally as 45 unit tests passing (six new controls). Live run: + +- Transaction: `edrg-632c5f773d8d42d980f59b4dbcaf72fb` +- Result: `observed-live-conduit-inspect` +- VPS commit: `53c3399c1fa74a985cd74de3c40d48ca02e53bfb` +- Start receipt: `24656869ff9decd0bc3fff92f06f8932eb68f14f268b71eb0236cce8c2eedfe5` +- Final receipt: `80474eeaa16bdc78cd6c6bf5bc3f3c146049d74be02c2bd9c7d6c798de8eb2a8` +- Classification: live two-host, same operator; SHA-256 chained software receipts + +Run the *optional, host-specific* probe on a configured Prometheus: +`PYTHONPATH=integrations/agentrust-confidential-workflow-harness python -c 'from harness.conduit_live import run; print(run(transaction_id="unique-id", repo="/home/ubuntu/src/integrations", cli="/home/triangulatorium/.local/bin/frequency-conduit"))'` + +This bridge does **not** issue a cA2A credential, verify a real cA2A peer +attestation, create a remote cA2A authenticated response, or produce a TRACE +standard-conforming externally attested execution receipt. It also does not +provide independently operated or hardware-backed evidence. The validated +hash-chain alone is not a digital signature and is not tamper proof against +the operator who controls the receipt store. Those distinctions remain release +gates for upstream Issue #199 and should not be elided. diff --git a/integrations/agentrust-confidential-workflow-harness/harness/conduit_live.py b/integrations/agentrust-confidential-workflow-harness/harness/conduit_live.py new file mode 100644 index 0000000..f975acc --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/conduit_live.py @@ -0,0 +1,92 @@ +"""Bounded live cMCP-to-Conduit experiment, not a cA2A peer acceptance claim. + +Uses the published cMCP admission API, then an existing locked-down Conduit +CLI SSH transport to run an approved, non-networked 'inspect' intent remotely. +The remote receipt is hash chained but NOT digitally signed or independent. +""" +import hashlib +import json +import subprocess +from dataclasses import dataclass +from pathlib import Path + +from cmcp_runtime.runtime_gateway import GovernancePolicy, MCPGateway + + +@dataclass(frozen=True) +class LiveResult: + outcome: str + transaction_id: str + start_hash: str | None = None + finish_hash: str | None = None + commit_sha: str | None = None + evidence_class: str = "live-two-host-same-operator" + assurance: str = "software-hash-chain-only" + + +def _call(cli: str, action: str, params: dict, timeout: int = 45) -> dict: + process = subprocess.run( + [cli, "call", "vps", action, "--params", json.dumps(params, sort_keys=True)], + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=timeout, + check=False, + ) + if process.returncode: + raise RuntimeError("conduit-transport-failure") + response = json.loads(process.stdout) + if not isinstance(response, dict): + raise ValueError("non-object-conduit-response") + return response + + +def _receipt_hash(r: dict) -> bool: + if not isinstance(r, dict) or not isinstance(r.get("hash"), str): + return False + material = {k: v for k, v in r.items() if k != "hash"} + digest = hashlib.sha256(json.dumps(material, sort_keys=True, separators=(",", ":")).encode()).hexdigest() + return digest == r["hash"] + + +def run(*, transaction_id: str, repo: str, cli: str, + tool_allowed: bool = True, transport=_call) -> LiveResult: + if not transaction_id or not repo or not Path(cli).is_file(): + return LiveResult("invalid-request", transaction_id) + admitted, _ = MCPGateway(GovernancePolicy( + allowed_tools=["conduit.inspect"] if tool_allowed else [], + max_tool_calls=1, + )).intercept_tool_call("frequency-live-harness", "conduit.inspect", + {"transaction_id": transaction_id, "repo": repo}) + if not admitted: + return LiveResult("refused-cmcp", transaction_id) + try: + start = transport(cli, "job_start", {"intent": "inspect", "repo": repo}) + if not start.get("ok"): + return LiveResult("refused-conduit", transaction_id) + sr = start["receipt"] + job = start["result"] + session = job["session_id"] + if (not _receipt_hash(sr) or sr["status"] != "PASS" + or sr["action"] != "job_start" or sr["node"] != "vps-377a113a" + or sr.get("detail", {}).get("session_id") != session + or job.get("intent") != "inspect" + or job.get("network") is not False): + return LiveResult("untrusted-start", transaction_id) + poll = transport(cli, "job_poll", {"session_id": session}) + if not poll.get("ok"): + return LiveResult("unknown-unobserved-result", transaction_id, sr["hash"]) + fr = poll["receipt"] + finished = poll["result"] + if (not _receipt_hash(fr) or fr.get("action") != "job_poll" + or fr.get("node") != sr["node"] + or fr.get("detail", {}).get("session_id") != session + or fr.get("prev_hash") != sr["hash"] + or finished.get("session_id") != session + or finished.get("running") or finished.get("exit_code") != 0 + or finished.get("commit_sha") != job.get("commit_sha") + or finished.get("commit_sha_end") != job.get("commit_sha") + or finished.get("worktree_clean_start") is not True + or finished.get("worktree_clean_end") is not True): + return LiveResult("untrusted-or-unsuccessful-result", transaction_id, sr["hash"]) + return LiveResult("observed-live-conduit-inspect", transaction_id, + sr["hash"], fr["hash"], job["commit_sha"]) + except (ValueError, KeyError, TypeError, OSError, subprocess.TimeoutExpired, RuntimeError): + return LiveResult("unknown-transport-or-protocol", transaction_id) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_conduit_live.py b/integrations/agentrust-confidential-workflow-harness/tests/test_conduit_live.py new file mode 100644 index 0000000..b6d34e7 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_conduit_live.py @@ -0,0 +1,65 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path +from harness.conduit_live import run + + +def receipt(action, session, previous="", **detail): + record = {"schema":"frequency-conduit-receipt/1", "node":"vps-377a113a", + "status":"PASS","action":action,"request_id":"test","ts":1, + "prev_hash":previous,"detail":{"session_id":session, **detail}} + record["hash"]=hashlib.sha256(json.dumps(record,sort_keys=True,separators=(",",":")).encode()).hexdigest() + return record + +class ConduitLiveTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory() + self.addCleanup(self.tmp.cleanup) + self.cli=str(Path(self.tmp.name)/"cli") + Path(self.cli).touch() + self.session="session-1" + self.started=receipt("job_start",self.session) + self.finished=receipt("job_poll",self.session,self.started["hash"]) + def transport(self,cli,action,params): + if action=="job_start": + return {"ok":True, "receipt":self.started, + "result":{"session_id":self.session,"intent":"inspect", + "commit_sha":"abc","network":False}} + return {"ok":True,"receipt":self.finished, + "result":{"session_id":self.session,"running":False,"exit_code":0, + "commit_sha":"abc","commit_sha_end":"abc", + "worktree_clean_start":True,"worktree_clean_end":True}} + def invoke(self,**opts): + return run(transaction_id="txn-1",repo="/home/ubuntu/src/integrations", + cli=self.cli,transport=self.transport,**opts) + def test_valid_receipts(self): + self.assertEqual(self.invoke().outcome,"observed-live-conduit-inspect") + def test_cmcp_denial(self): + self.assertEqual(self.invoke(tool_allowed=False).outcome,"refused-cmcp") + def test_receipt_tampering(self): + self.finished["detail"]["session_id"]="substituted" + self.assertEqual(self.invoke().outcome,"untrusted-or-unsuccessful-result") + def test_receipt_linkage(self): + self.finished["prev_hash"]="wrong" + data={k:v for k,v in self.finished.items() if k!="hash"} + self.finished["hash"]=hashlib.sha256(json.dumps(data,sort_keys=True,separators=(",",":")).encode()).hexdigest() + self.assertEqual(self.invoke().outcome,"untrusted-or-unsuccessful-result") + def test_unobserved_finish(self): + def transport(cli,action,params): + if action=="job_poll": + return {"ok":False} + return self.transport(cli,action,params) + out=run(transaction_id="txn-1",repo="/home/ubuntu/src/integrations", + cli=self.cli,transport=transport) + self.assertEqual(out.outcome,"unknown-unobserved-result") + def test_stale_commit(self): + original=self.transport + def transport(cli,action,params): + result=original(cli,action,params) + if action=="job_poll": + result["result"]["commit_sha_end"]="changed" + return result + self.transport=transport + self.assertEqual(self.invoke().outcome,"untrusted-or-unsuccessful-result") From 4842ca103d135f7ff94398f5c1218979ec093b6b Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:07:51 -0700 Subject: [PATCH 06/17] test: gate live Conduit bridge on released cA2A holder-bound delegation --- .../LIVE-CONDUIT-EDRG.md | 16 +++++ .../harness/delegated_conduit.py | 35 +++++++++ .../harness/released_delegation.py | 58 +++++++++++++++ .../tests/test_delegated_conduit.py | 71 +++++++++++++++++++ .../tests/test_released_delegation.py | 46 ++++++++++++ 5 files changed, 226 insertions(+) create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/delegated_conduit.py create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_delegated_conduit.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py diff --git a/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md index 1190c55..63880e7 100644 --- a/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md +++ b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md @@ -29,3 +29,19 @@ provide independently operated or hardware-backed evidence. The validated hash-chain alone is not a digital signature and is not tamper proof against the operator who controls the receipt store. Those distinctions remain release gates for upstream Issue #199 and should not be elided. + +## Released cA2A holder-bound delegation preflight + +The `released_delegation` and `delegated_conduit` modules now verify a +released `ca2a-runtime==0.4.0` signed delegation against an explicitly supplied +trusted root, a challenge-bound holder-possession proof, requested capability, +and local policy **before** the cMCP/Conduit job begins. Negative tests prove +untrusted roots, holder substitution, and altered scope cannot dispatch. This +extends the local composition to 55 tests. + +The challenge and peer appraisal are process-local to the origin and the test +keys are ephemeral. The remote VPS has not checked a cA2A credential. Therefore +this remains **not cA2A peer delivery or independently enrolled verification**. +Neither cryptographic authentication of the remote Conduit job receipt nor a +TRACE-standard external attestation has been added. Do not mark full Issue #199 +acceptance complete based on these tests. diff --git a/integrations/agentrust-confidential-workflow-harness/harness/delegated_conduit.py b/integrations/agentrust-confidential-workflow-harness/harness/delegated_conduit.py new file mode 100644 index 0000000..b1eec50 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/delegated_conduit.py @@ -0,0 +1,35 @@ +"""Compose released cA2A holder authorization and cMCP remote Conduit admission. + +Both authorization checks occur on the originating host before live dispatch. +This is NOT actual cA2A network transport or a remotely issued peer receipt. +""" +from dataclasses import dataclass +from harness.conduit_live import run as conduit_run +from harness.released_delegation import appraise + + +@dataclass(frozen=True) +class CompositionResult: + outcome: str + transaction_id: str + start_hash: str | None = None + finish_hash: str | None = None + evidence_class: str = "software-two-host-same-operator" + ca2a_peer_transport: bool = False + trace_verification: bool = False + + +def run(*, transaction_id: str, repo: str, cli: str, + credential, holder_private, trusted_issuer: str, + transport=None) -> CompositionResult: + decision = appraise( + credential=credential, holder_private=holder_private, + trusted_issuer=trusted_issuer, record_id=transaction_id) + if not decision.granted: + return CompositionResult("refused-ca2a-delegation", transaction_id) + args = {"transaction_id": transaction_id, "repo": repo, "cli": cli} + if transport is not None: + args["transport"] = transport + result = conduit_run(**args) + return CompositionResult(result.outcome, transaction_id, + result.start_hash, result.finish_hash) diff --git a/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py b/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py new file mode 100644 index 0000000..fdde8fd --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py @@ -0,0 +1,58 @@ +"""Released cA2A 0.4.0 holder-bound delegation check. + +The verifier's root is explicitly supplied by the caller. It is NOT enrolled +with a remote independently controlled peer. The signed delegation and +holder proof are independently checked by the published runtime. +""" +from dataclasses import dataclass +from ca2a_runtime.delegation import build_holder_proof +from ca2a_runtime.delegation.credential import DelegationCredential +from ca2a_runtime.peer import PeerRequest, handle_peer_request +from ca2a_runtime.policy import LocalPolicy +from ca2a_runtime.challenge import generate_secret, issue_challenge +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey + + +@dataclass(frozen=True) +class DelegationDecision: + granted: bool + reason: str + record_id: str + + +def appraise(*, credential: DelegationCredential, holder_private, + trusted_issuer: str, record_id: str, + capability: str = "conduit.inspect", + local_capabilities=frozenset({"conduit.inspect"}), + challenge_secret=None, + holder_proof=None) -> DelegationDecision: + if not record_id or not trusted_issuer: + return DelegationDecision(False, "missing-context", record_id) + # The challenge and audience are bound to a single local verifier occurrence. + # Supplying proof overrides is intended for negative tests; other callers + # must generate their proof for this verifier's challenge themselves. + if challenge_secret is None: + challenge_secret = generate_secret() + challenge = issue_challenge(challenge_secret) + channel = X25519PrivateKey.generate() + audience = channel.public_key().public_bytes_raw().hex() + try: + if holder_proof is None: + holder_proof = build_holder_proof( + holder_private, credential, audience=audience, + challenge=challenge, requested_capability=capability, + record_id=record_id) + except Exception as exc: + return DelegationDecision(False, type(exc).__name__, record_id) + request = PeerRequest([credential], capability, record_id, holder_proof=holder_proof) + try: + result = handle_peer_request( + request, policy=LocalPolicy(frozenset(local_capabilities)), + trusted_root_issuers={trusted_issuer}, + require_holder_proof=True, + challenge_secret=challenge_secret, audience=audience) + return DelegationDecision( + result.granted_capability == capability, + "released-ca2a-holder-and-delegation-verified", record_id) + except Exception as exc: + return DelegationDecision(False, type(exc).__name__, record_id) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_delegated_conduit.py b/integrations/agentrust-confidential-workflow-harness/tests/test_delegated_conduit.py new file mode 100644 index 0000000..a822480 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_delegated_conduit.py @@ -0,0 +1,71 @@ +import tempfile +import unittest +from pathlib import Path + +from ca2a_runtime.delegation import new_keypair +from ca2a_runtime.delegation.credential import DelegationCredential +from harness.delegated_conduit import run +from test_conduit_live import receipt + + +class DelegatedConduitTests(unittest.TestCase): + def setUp(self): + root, self.root_pub = new_keypair() + self.holder, pub = new_keypair() + self.credential = DelegationCredential( + "test-delegation", self.root_pub, pub, + frozenset({"conduit.inspect"}), 0).sign(root) + tmp = tempfile.TemporaryDirectory() + self.addCleanup(tmp.cleanup) + self.cli = str(Path(tmp.name) / "conduit") + Path(self.cli).touch() + self.calls = [] + self.start = receipt("job_start", "session") + self.finish = receipt("job_poll", "session", self.start["hash"]) + + def transport(self, cli, action, params): + self.calls.append(action) + if action == "job_start": + return {"ok": True, "receipt": self.start, + "result": {"session_id": "session", "intent": "inspect", + "network": False, "commit_sha": "abc"}} + return {"ok": True, "receipt": self.finish, + "result": {"session_id": "session", "running": False, + "exit_code": 0, "commit_sha": "abc", + "commit_sha_end": "abc", + "worktree_clean_start": True, + "worktree_clean_end": True}} + + def invoke(self, **override): + kw = dict(transaction_id="test-id", repo="/home/ubuntu/src/integrations", + cli=self.cli, credential=self.credential, holder_private=self.holder, + trusted_issuer=self.root_pub, transport=self.transport) + kw.update(override) + return run(**kw) + + def test_positive_composed_software(self): + result = self.invoke() + self.assertEqual(result.outcome, "observed-live-conduit-inspect") + self.assertEqual(self.calls, ["job_start", "job_poll"]) + self.assertFalse(result.ca2a_peer_transport) + self.assertFalse(result.trace_verification) + + def test_untrusted_root_never_dispatches(self): + _, other_root = new_keypair() + self.assertEqual(self.invoke(trusted_issuer=other_root).outcome, + "refused-ca2a-delegation") + self.assertEqual(self.calls, []) + + def test_wrong_holder_never_dispatches(self): + attacker, _ = new_keypair() + self.assertEqual(self.invoke(holder_private=attacker).outcome, + "refused-ca2a-delegation") + self.assertEqual(self.calls, []) + + def test_modified_scope_never_dispatches(self): + bad = DelegationCredential( + "test-delegation", self.root_pub, self.credential.subject, + frozenset({"conduit.delete"}), 0, signature=self.credential.signature) + self.assertEqual(self.invoke(credential=bad).outcome, + "refused-ca2a-delegation") + self.assertEqual(self.calls, []) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py b/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py new file mode 100644 index 0000000..4bd1a20 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py @@ -0,0 +1,46 @@ +import unittest +from ca2a_runtime.delegation import new_keypair +from ca2a_runtime.delegation.credential import DelegationCredential +from harness.released_delegation import appraise + + +class ReleasedDelegationTests(unittest.TestCase): + def setUp(self): + self.root, self.root_pub = new_keypair() + self.holder, holder_pub = new_keypair() + self.credential = DelegationCredential( + "experimental", self.root_pub, holder_pub, + frozenset({"conduit.inspect"}), 0).sign(self.root) + + def check(self, **kw): + return appraise(credential=kw.pop("credential", self.credential), + holder_private=kw.pop("holder_private", self.holder), + trusted_issuer=kw.pop("trusted_issuer", self.root_pub), + record_id="live-test", **kw) + + def test_valid_delegation(self): + self.assertTrue(self.check().granted) + + def test_untrusted_root(self): + _, other_root = new_keypair() + self.assertFalse(self.check(trusted_issuer=other_root).granted) + self.assertTrue(self.check().granted) + + def test_holder_substitution(self): + other_holder, _ = new_keypair() + self.assertFalse(self.check(holder_private=other_holder).granted) + self.assertTrue(self.check().granted) + + def test_ungranted_capability(self): + self.assertFalse(self.check(capability="conduit.delete").granted) + self.assertTrue(self.check().granted) + + def test_policy_refusal(self): + self.assertFalse(self.check(local_capabilities=frozenset()).granted) + self.assertTrue(self.check().granted) + + def test_unsigned_credential(self): + c = DelegationCredential("experimental",self.root_pub, + self.credential.subject,frozenset({"conduit.inspect"}),0) + self.assertFalse(self.check(credential=c).granted) + self.assertTrue(self.check().granted) From 8c70afd4980e01144ff3e04f20b4588912eb7b5f Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:10:22 -0700 Subject: [PATCH 07/17] docs: pin composed live software execution evidence and assurance limits --- .../LIVE-CONDUIT-EDRG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md index 63880e7..14b98f2 100644 --- a/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md +++ b/integrations/agentrust-confidential-workflow-harness/LIVE-CONDUIT-EDRG.md @@ -45,3 +45,20 @@ this remains **not cA2A peer delivery or independently enrolled verification**. Neither cryptographic authentication of the remote Conduit job receipt nor a TRACE-standard external attestation has been added. Do not mark full Issue #199 acceptance complete based on these tests. + +### Composed software live control (October 16) + +The origin verified an ephemeral signed cA2A 0.4.0 delegation and +holder-possession proof, then the published cMCP 0.7.0 tool gate, then invoked +the existing VPS Conduit restricted `inspect` job over SSH. Results: + +- Origin transaction: `composed-14243eb31122473282b779da9daf0551` +- Software Conduit start receipt SHA-256: `425dd606ea4a887424d18be6981abd4a72a5aced60d96d1a207b3e8bb845d4d6` +- Software Conduit final receipt SHA-256: `a37c5933fb472416fa8fb0cd9d5b7d2cafd5ad15ba5733fbe882eec93a8df92b` +- Result: `observed-live-conduit-inspect` +- cA2A peer transport: **false**; remote TRACE verification: **false** + +The issuer and holder keys were generated and trusted on the origin during this +experiment. No separate remote peer enrolled or verified them. This is genuine +remote Conduit software execution after origin-side preflight, not a claim of +end-to-end remotely enforced cA2A delegation or TRACE acceptance. From 3ebc585f15538ffc206f756ac26b4bc700a5ce22 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:18:57 -0700 Subject: [PATCH 08/17] feat: bounded released-cA2A remote peer authority and authenticated response --- .../harness/remote_peer_client.py | 127 ++++++++++++ .../harness/remote_peer_service.py | 189 ++++++++++++++++++ .../tests/test_remote_ca2a.py | 114 +++++++++++ 3 files changed, 430 insertions(+) create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py new file mode 100644 index 0000000..3f387d2 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py @@ -0,0 +1,127 @@ +"""Live SSH-constrained released-cA2A request/response with caller-side cMCP. + +The responder independently enforces delegation and holder binding. Transport +is an existing pinned-host-key SSH connection; the response is authenticated +by ca2a-runtime 0.4.0. This remains software-only, same-operator evidence. +""" +import hashlib +import json +import os +import subprocess +from dataclasses import dataclass + +from ca2a_runtime.attestation import VerifiedPeer +from ca2a_runtime.delegation import build_holder_proof +from ca2a_runtime.delegation.credential import DelegationCredential +from ca2a_runtime.response import PendingResponse, decode, encode +from ca2a_runtime.errors import CA2AError +from cmcp_runtime.runtime_gateway import GovernancePolicy, MCPGateway + +CAPABILITY = "conduit.inspect" +SCHEMA = "frequency-ca2a-peer/1" + + +@dataclass(frozen=True) +class LivePeerResult: + outcome: str + transaction_id: str + request_sha256: str | None = None + start_receipt_hash: str | None = None + finish_receipt_hash: str | None = None + evidence_class: str = "live-software-two-host-same-operator" + peer_assurance: str = "none" + remote_ca2a_verified: bool = False + + +def ssh_sender(host: str, key_path: str, *, timeout=20): + if not host or not key_path: + raise ValueError("pinned SSH endpoint and key required") + + def send(request): + proc = subprocess.run([ + "ssh", "-F", "/dev/null", "-T", + "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", + "-o", "ConnectTimeout=6", "-o", "ClearAllForwardings=yes", + "-o", "IdentitiesOnly=yes", + "-i", key_path, "ubuntu@" + host, "frequency-ca2a-peer", + ], input=encode(request), stdout=subprocess.PIPE, + stderr=subprocess.PIPE, timeout=timeout, check=False) + if proc.returncode != 0 or len(proc.stdout) > 131072: + raise RuntimeError("ssh-peer-failed-or-too-large") + response = json.loads(proc.stdout) + if not isinstance(response, dict): + raise ValueError("invalid-peer-response") + return response + + return send + + +def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, + holder_private, peer_public_key: str, send, + permitted: bool = True) -> LivePeerResult: + if not transaction_id or not nonce or not peer_public_key: + return LivePeerResult("invalid-context", transaction_id) + gateway = MCPGateway(GovernancePolicy( + allowed_tools=[CAPABILITY] if permitted else [], max_tool_calls=1)) + allowed, _ = gateway.intercept_tool_call("frequency-peer-origin", + CAPABILITY, { + "transaction_id": transaction_id, + "nonce": nonce, "repo": repo}) + if not allowed: + return LivePeerResult("refused-cmcp", transaction_id) + pending = None + try: + hello = send({"schema": SCHEMA, "method": "challenge"}) + if (hello.get("status") != 200 or + hello.get("peer_public_key") != peer_public_key or + hello.get("assurance") != "none" or + hello.get("capability") != CAPABILITY): + return LivePeerResult("refused-peer-identity", transaction_id) + challenge = hello["challenge"] + pending = PendingResponse(VerifiedPeer(peer_public_key, "none", "software"), + {"transaction_id": transaction_id, "nonce": nonce, "repo": repo, + "capability": CAPABILITY}, timeout=20) + issuer_pub = issuer_private.public_key().public_bytes_raw().hex() + holder_pub = holder_private.public_key().public_bytes_raw().hex() + credential = DelegationCredential( + credential_id="transaction:" + transaction_id, issuer=issuer_pub, + subject=holder_pub, scope=frozenset({CAPABILITY}), depth=0).sign(issuer_private) + envelope = decode(pending.request_bytes) + record_id = hashlib.sha256(encode(envelope)).hexdigest() + proof = build_holder_proof(holder_private, credential, + audience=peer_public_key, challenge=challenge, + requested_capability=CAPABILITY, record_id=record_id) + response = send({"schema": SCHEMA, "method": "execute", + "envelope": envelope, + "credential": {**credential.body(), "signature": credential.signature}, + "holder_proof": proof.to_dict()}) + status = response["status"] + verified = pending.verify(status, encode(response["envelope"])) + if status == 403: + return LivePeerResult("refused-authenticated-peer", transaction_id, + verified.request_sha256, remote_ca2a_verified=False) + if status != 200: + return LivePeerResult("unknown-authenticated-execution", transaction_id, + verified.request_sha256, remote_ca2a_verified=False) + body = verified.body + execution = body.get("execution") + if (body.get("transaction_id") != transaction_id or + body.get("nonce") != nonce or + body.get("repo") != repo or + body.get("capability") != CAPABILITY or + body.get("assurance") != "software-only" or + not isinstance(execution, dict) or + execution.get("exit_code") != 0 or + not isinstance(execution.get("start_receipt_hash"), str) or + not isinstance(execution.get("finish_receipt_hash"), str)): + return LivePeerResult("refused-output-binding", transaction_id, + verified.request_sha256) + return LivePeerResult("authenticated-remote-ca2a-software", transaction_id, + verified.request_sha256, + execution["start_receipt_hash"], + execution["finish_receipt_hash"], + remote_ca2a_verified=True) + except (CA2AError, ValueError, TypeError, KeyError, OSError, RuntimeError, + subprocess.TimeoutExpired): + return LivePeerResult("unknown-or-refused-transport", transaction_id, + pending.request_sha256 if pending else None) diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py new file mode 100644 index 0000000..43b0c0f --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py @@ -0,0 +1,189 @@ +"""Restricted CA2A 0.4.0 peer acceptance service for one non-networked Conduit intent. + +The service is intended ONLY behind a separately restricted SSH forced command. +It is a software trust boundary, not a TEE; its issuer root and channel public +key must be provisioned and pinned out of band. Never expose as a public API. +""" +import hashlib +import json +import os +import sqlite3 +import subprocess +import sys +import time +from pathlib import Path + +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from ca2a_runtime.challenge import issue_challenge +from ca2a_runtime.delegation.credential import DelegationCredential +from ca2a_runtime.delegation import HolderProof +from ca2a_runtime.peer import PeerRequest, handle_peer_request +from ca2a_runtime.policy import LocalPolicy +from ca2a_runtime.response import ResponseProducer, encode + + +CAPABILITY = "conduit.inspect" +REPO = "/home/ubuntu/src/integrations" +MAX_REQUEST = 65536 + + +def _canon(value): + return json.dumps(value, sort_keys=True, separators=(",", ":")).encode() + + +def _digest(value): + return hashlib.sha256(_canon(value)).hexdigest() + + +def _consume(path: Path, occurrence: str) -> bool: + """Atomic persistent replay refusal; failure is denial, not cache reset.""" + path.parent.mkdir(mode=0o700, parents=True, exist_ok=True) + with sqlite3.connect(path, timeout=5) as db: + db.execute("CREATE TABLE IF NOT EXISTS spent (id TEXT PRIMARY KEY, seen INTEGER NOT NULL)") + try: + db.execute("INSERT INTO spent (id,seen) VALUES (?,?)", (occurrence, int(time.time()))) + db.commit() + return True + except sqlite3.IntegrityError: + return False + + +def _receipt_ok(receipt, action, session): + if not isinstance(receipt, dict): + return False + digest = receipt.get("hash") + body = {key: value for key, value in receipt.items() if key != "hash"} + return (isinstance(digest, str) and _digest(body) == digest + and receipt.get("action") == action + and receipt.get("status") == "PASS" + and receipt.get("detail", {}).get("session_id") == session) + + +def _agent(agent_path: str, action: str, params: dict, *, timeout=12) -> dict: + proc = subprocess.run([agent_path], input=_canon({ + "request_id": os.urandom(16).hex(), "action": action, "params": params + }), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, + timeout=timeout, check=False) + if proc.returncode != 0 or len(proc.stdout) > 262144: + raise RuntimeError("conduit-operation-failed") + data = json.loads(proc.stdout) + if not isinstance(data, dict): + raise ValueError("malformed-conduit-response") + return data + + +def execute_job(agent: str, repo: str) -> dict: + started = _agent(agent, "job_start", {"intent": "inspect", "repo": repo}) + if not started.get("ok"): + raise RuntimeError("conduit-refused-start") + start = started["receipt"] + job = started["result"] + sid = job["session_id"] + if not (_receipt_ok(start, "job_start", sid) + and job.get("intent") == "inspect" + and job.get("network") is False + and job.get("repo") == repo): + raise RuntimeError("untrusted-conduit-start") + # There is intentionally no retry of job_start on uncertain delivery. + for _ in range(16): + polled = _agent(agent, "job_poll", {"session_id": sid}) + if not polled.get("ok"): + raise RuntimeError("unobserved-conduit-completion") + finished = polled["result"] + if finished.get("running"): + time.sleep(0.3) + continue + finish = polled["receipt"] + if not (_receipt_ok(finish, "job_poll", sid) + and finish.get("prev_hash") == start["hash"] + and finished.get("session_id") == sid + and finished.get("exit_code") == 0 + and finished.get("commit_sha") == job.get("commit_sha") + and finished.get("commit_sha_end") == job.get("commit_sha") + and finished.get("worktree_clean_start") is True + and finished.get("worktree_clean_end") is True): + raise RuntimeError("invalid-conduit-outcome") + return {"session_id": sid, "start_receipt_hash": start["hash"], + "finish_receipt_hash": finish["hash"], + "commit_sha": job["commit_sha"], "exit_code": 0} + raise RuntimeError("unknown-conduit-timeout") + + +def serve(request: dict, *, channel_private, challenge_secret: bytes, + trusted_issuer: str, replay_db: Path, + agent: str = "", allowed_repo: str = REPO, dispatch=None) -> dict: + if not isinstance(request, dict) or request.get("schema") != "frequency-ca2a-peer/1": + return {"status": 400, "error": "invalid-request"} + public = channel_private.public_key().public_bytes_raw().hex() + if request.get("method") == "challenge": + return {"status": 200, "challenge": issue_challenge(challenge_secret), + "peer_public_key": public, "assurance": "none", + "capability": CAPABILITY} + if request.get("method") != "execute": + return {"status": 400, "error": "unsupported-method"} + try: + envelope = request["envelope"] + producer = ResponseProducer(channel_private, envelope) + message = producer.envelope["request"] + except (KeyError, ValueError, TypeError): + return {"status": 400, "error": "malformed-request-envelope"} + try: + if (not isinstance(message, dict) + or set(message) != {"transaction_id", "nonce", "repo", "capability"} + or not isinstance(message["transaction_id"], str) + or not 5 <= len(message["transaction_id"]) <= 128 + or not isinstance(message["nonce"], str) + or not 8 <= len(message["nonce"]) <= 128 + or message["repo"] != allowed_repo + or message["capability"] != CAPABILITY): + raise ValueError("bad-scope") + credential = DelegationCredential.from_dict(request["credential"]) + proof = HolderProof.from_dict(request["holder_proof"]) + record_id = hashlib.sha256(encode(producer.envelope)).hexdigest() + handle_peer_request( + PeerRequest([credential], CAPABILITY, record_id, holder_proof=proof), + policy=LocalPolicy(frozenset({CAPABILITY})), + trusted_root_issuers={trusted_issuer}, + challenge_secret=challenge_secret, audience=public, + require_holder_proof=True) + # Persist the one-time occurrence before any possible side effect. + unique = hashlib.sha256( + proof.challenge.encode() + ).hexdigest() + if not _consume(replay_db, unique): + raise PermissionError("already-consumed") + except Exception: + return {"status": 403, "envelope": producer.finish(403, { + "error": {"code": "delegation-or-context-refused"}})} + try: + observed = ((dispatch or execute_job)(agent, allowed_repo)) + body = {"accepted": True, + "transaction_id": message["transaction_id"], + "nonce": message["nonce"], "repo": allowed_repo, + "capability": CAPABILITY, "execution": observed, + "assurance": "software-only"} + return {"status": 200, "envelope": producer.finish(200, body)} + except Exception: + return {"status": 503, "envelope": producer.finish(503, { + "error": {"code": "execution-unknown"}})} + + +def main(): + cfg_path = Path("/home/ubuntu/.local/state/frequency-ca2a-peer/config.json") + cfg = json.loads(cfg_path.read_text()) + private = X25519PrivateKey.from_private_bytes( + bytes.fromhex(Path(cfg["private_key_path"]).read_text().strip())) + secret = bytes.fromhex(Path(cfg["challenge_secret_path"]).read_text().strip()) + raw = sys.stdin.buffer.read(MAX_REQUEST + 1) + if len(raw) > MAX_REQUEST: + raise SystemExit(1) + response = serve(json.loads(raw), channel_private=private, + challenge_secret=secret, + trusted_issuer=cfg["trusted_issuer"], + replay_db=Path(cfg["replay_db"]), agent=cfg["agent"], + allowed_repo=cfg["allowed_repo"]) + sys.stdout.buffer.write(_canon(response) + b"\n") + + +if __name__ == "__main__": + main() diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py new file mode 100644 index 0000000..73397a7 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py @@ -0,0 +1,114 @@ +"""Adversarial published-cA2A client/server interoperability and replay tests.""" +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from ca2a_runtime.delegation import new_keypair +from ca2a_runtime.response import encode + +from harness.remote_peer_client import run +from harness.remote_peer_service import serve + + +class RemoteCA2ATests(unittest.TestCase): + def setUp(self): + tmp = tempfile.TemporaryDirectory() + self.addCleanup(tmp.cleanup) + self.db = Path(tmp.name) / "replay.sqlite" + self.issuer, self.issuer_pub = new_keypair() + self.holder, self.holder_pub = new_keypair() + self.peer = X25519PrivateKey.generate() + self.peer_pub = self.peer.public_key().public_bytes_raw().hex() + self.secret = b"secret-for-test-only-" + bytes(range(32)) + self.dispatch_count = 0 + self.calls = [] + self.last_submit = None + + def dispatch(self, agent, repo): + self.dispatch_count += 1 + return {"session_id":"session", "start_receipt_hash":"a"*64, + "finish_receipt_hash":"b"*64,"commit_sha":"abc","exit_code":0} + + def send(self, request): + self.calls.append(request["method"]) + if request["method"] == "execute": + self.last_submit = json.loads(json.dumps(request)) + return serve(request, channel_private=self.peer, + challenge_secret=self.secret, trusted_issuer=self.issuer_pub, + replay_db=self.db, dispatch=self.dispatch) + + def invoke(self, **kw): + args = {"transaction_id": "transaction-1", "nonce": "0123456789abcdef", + "repo": "/home/ubuntu/src/integrations", + "issuer_private": self.issuer,"holder_private": self.holder, + "peer_public_key": self.peer_pub,"send": self.send} + args.update(kw) + return run(**args) + + def test_real_library_roundtrip(self): + r=self.invoke() + self.assertEqual(r.outcome,"authenticated-remote-ca2a-software") + self.assertTrue(r.remote_ca2a_verified) + self.assertEqual(self.dispatch_count,1) + self.assertEqual(self.calls,["challenge","execute"]) + + def test_cmcp_refusal_before_network(self): + self.assertEqual(self.invoke(permitted=False).outcome,"refused-cmcp") + self.assertEqual(self.calls,[]) + self.assertEqual(self.dispatch_count,0) + + def test_untrusted_root_no_dispatch(self): + attacker,_=new_keypair() + self.assertEqual(self.invoke(issuer_private=attacker).outcome, + "refused-authenticated-peer") + self.assertEqual(self.dispatch_count,0) + + def test_authenticated_response_mutation(self): + def tamper(request): + result=self.send(request) + if request["method"]=="execute": + result["envelope"]["body"]["nonce"]="bad-nonce" + return result + self.assertNotEqual(self.invoke(send=tamper).outcome, + "authenticated-remote-ca2a-software") + + def test_substituted_credential_scope_no_dispatch(self): + def tamper(request): + if request["method"]=="execute": + request["credential"]["scope"]=["conduit.delete"] + return self.send(request) + self.assertEqual(self.invoke(send=tamper).outcome,"refused-authenticated-peer") + self.assertEqual(self.dispatch_count,0) + + def test_substituted_envelope_no_dispatch(self): + def tamper(request): + if request["method"]=="execute": + request["envelope"]["request"]["nonce"]="substituted" + return self.send(request) + self.assertNotEqual(self.invoke(send=tamper).outcome, + "authenticated-remote-ca2a-software") + self.assertEqual(self.dispatch_count,0) + + def test_stale_or_wrong_peer_identity(self): + wrong=X25519PrivateKey.generate().public_key().public_bytes_raw().hex() + self.assertEqual(self.invoke(peer_public_key=wrong).outcome, + "refused-peer-identity") + self.assertEqual(self.dispatch_count,0) + + def test_one_time_replay_is_refused(self): + self.assertEqual(self.invoke().outcome,"authenticated-remote-ca2a-software") + self.assertEqual(self.dispatch_count,1) + replay=self.send(self.last_submit) + self.assertEqual(replay["status"],403) + self.assertEqual(self.dispatch_count,1) + + def test_unobserved_delivery_remains_unknown(self): + def lose(request): + if request["method"]=="execute": + raise RuntimeError("transport-lost") + return self.send(request) + self.assertEqual(self.invoke(send=lose).outcome,"unknown-or-refused-transport") + self.assertEqual(self.dispatch_count,0) From bf9e4e59508e5f26dd0dc4cc2af4644815a06295 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:29:52 -0700 Subject: [PATCH 09/17] feat: bind remote cA2A execution to pinned TRACE software record --- .../README.md | 15 +-- .../REMOTE-CA2A-PEER.md | 113 ++++++++++++++++++ .../harness/remote_peer_client.py | 24 +++- .../harness/remote_peer_service.py | 15 ++- .../harness/trace_live_binding.py | 91 ++++++++++++++ .../run_remote_ca2a_live.py | 38 ++++++ .../tests/test_remote_ca2a.py | 19 ++- 7 files changed, 303 insertions(+), 12 deletions(-) create mode 100644 integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py create mode 100644 integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py diff --git a/integrations/agentrust-confidential-workflow-harness/README.md b/integrations/agentrust-confidential-workflow-harness/README.md index a352a39..adc546c 100644 --- a/integrations/agentrust-confidential-workflow-harness/README.md +++ b/integrations/agentrust-confidential-workflow-harness/README.md @@ -6,7 +6,7 @@ and preserves unavailable evidence instead of inferring success from dispatch. ## Scope of this PR -This initial implementation provides: +The expanded experimental implementation provides: - a fixed harness core; - machine-readable manifest and result schemas; @@ -16,8 +16,10 @@ This initial implementation provides: - branch-preserving observations for retries; - causal provenance fields (`caused_by`) on observations. -It does **not** claim cMCP or cA2A conformance, live-peer acceptance, hardware -attestation, or completion of the full software milestone. +The published release API calls and one first-party software peer path are +exercised, but this is not a claim of independently established protocol +conformance, hardware attestation or completed Issue #199 acceptance. +See [About, licensing, IP boundaries, glossary and live-peer threat model](REMOTE-CA2A-PEER.md). ## Reproduce @@ -25,11 +27,10 @@ attestation, or completion of the full software milestone. python -m unittest discover -s tests -v ``` -The deterministic adapter records the package release targets that will be used -when protocol-specific adapters are wired: +The released component and bounded live-peer experiments use: -- `cmcp-runtime==0.5.0` -- `ca2a-runtime==0.2.0` +- `cmcp-runtime==0.7.0` +- `ca2a-runtime==0.4.0` Protocol/API changes remain in the owning repositories. Any scenario that needs an unreleased surface must carry `waiting_on_release`; a blocked required case diff --git a/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md new file mode 100644 index 0000000..0b364ab --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md @@ -0,0 +1,113 @@ +# About: bounded cA2A + cMCP + TRACE software federation experiment + +This is a **public reference integration**, built for AgenTrust integrations +Issue #199. It combines the independently published +`cmcp-runtime==0.7.0`, `ca2a-runtime==0.4.0`, and +`agentrust-trace==0.11.0` APIs with Frequency Conduit's separately operated +*software process* on another host. It does not incorporate proprietary OPAQUE, +Anthropic, or other confidential-computing software, and does not claim +institutional endorsement by those organizations. + +The repository's root `LICENSE` is Apache-2.0. External runtime packages are +separate dependencies, not re-licensed by this submission: the installed +distribution metadata identifies `cmcp-runtime` and `ca2a-runtime` as MIT, +`agentrust-trace` as Apache-2.0, and `cryptography` as +Apache-2.0 OR BSD-3-Clause. Their own upstream source, copyright notices and +licenses remain authoritative. Frequency first-party private authority, +production deployment configuration, service keys, and any unpublished +software are not transferred or relicensed by the integration. + +## What is actually implemented + +1. The originating host calls the **published cMCP gateway** to authorize + `conduit.inspect` and creates one cA2A `PendingResponse` occurrence. +2. The remote VPS forced SSH command issues an expiring cA2A challenge and + publishes a *pinned software channel public key*. The caller uses signed + cA2A `DelegationCredential` and an Ed25519 holder-possession proof bound + to the remote audience, challenge, capability and **hash of the exact + authenticated request envelope**. +3. The remote peer independently verifies the chain against a + **pre-enrolled issuer key**, local capability scope and holder possession. + A persistent SQLite replay ledger consumes the challenge before dispatch. +4. Only the fixed, network-disabled Frequency Conduit `inspect` intent is + permitted. No arbitrary shell command is exposed by the peer. +5. The remote response is authenticated using published + `ResponseProducer`/`PendingResponse.verify` primitives and binds the + source transaction, request hash, nonce, reported Conduit receipt hashes, + commit and exit status. +6. The remote process signs a **TRACE v0.2 Level 0 software record** and an + independently Ed25519-signed companion manifest binding the exact request + and Conduit observations. The origin verifies the TRACE signature and + companion manifest with an **out-of-band pinned remote signing key**. The + manifest is separate so TRACE's `policy.bundle_hash` continues to describe + the actual static access policy rather than falsely representing a receipt. + +The remote endpoint is deployed in a *separate virtual environment* using a +SHA-256 hash-locked dependency set; it is reachable only as an SSH forced +command using a dedicated restricted key, not a new public TCP listener. + +## Evidence and claims NOT established + +- Hardware TEE/TPM attestation, confidential inference, protected model + weights, protected key release or confidential data disclosure. +- Independent organizational custody: the test's source host, remote host, + issuer enrolment and verifier signing key are all under one operator. +- Live signed revocation checking, real external delegated peers, and + authoritative validation of the claimed software image identity. +- A successful read-only `inspect` job is not proof of actual mediated + sensitive-data access or a confidential AI model computation. +- The TRACE record is Level 0 software evidence and uses the documented + software-mode placeholder URIs, not a hardware RIM or transparency proof. +- Approval, merger, or standards conformance across every scenario in Issue + #199. Those require Dinakar's independent matrix and authorized maintainer + review on the exact frozen commit. + +## Reproduction and deployment controls + +The upstream CI runs unit tests against hash-pinned package releases without +any external SSH key. For a live controlled laboratory, an operator must +separately provision **on the VPS** the channel X25519 private key, challenge +HMAC secret, issuer public key, TRACE Ed25519 signing key and replay database +in `~/.local/state/frequency-ca2a-peer`. Secrets must be mode 0600 and the +directory mode 0700. The matching channel and TRACE public keys must be +pinned independently **on the originating host**. The forced SSH command +executes `python -m harness.remote_peer_service` and has no PTY, port +forwarding or unrestricted shell. Do not commit any operator keys or +deployment configuration. Do not assume that generated test keys are +independent attestation authorities. + +A live test needs the separately deployed pinned remote peer, the local +operator identity, and a valid existing Conduit `inspect` repository. Use +`run_remote_ca2a_live.py` only in the recorded first-party lab environment. +**Never** retry an uncertain `execute` occurrence automatically: a lost +response can conceal a completed side effect. + +## Evidence-driven release gate + +The complete production gate remains **BLOCKED** until both protocol-specific +remote positive and negative tests and independent third-party replay pass, +the signature/issuer and revocation custody are independently controlled, +and the acceptance scope includes mediated tool calls, uncertain deliveries, +release/disclosure and valid control/mutation twins. Mark every evidence item +as synthetic, same-operator live software, or independently anchored hardware; +never promote a lower class because its checks are green. + +## Technical glossary + +| Project term | Standard equivalent | +|---|---| +| Frequency Conduit node | Authorized remote execution worker and RPC endpoint | +| EDRG | Evidence-driven release acceptance gate | +| Governance admission | cMCP policy decision / tool-call authorization | +| Delegation | Signed capability credential / delegated authorization | +| Holder proof | Proof of possession of an Ed25519 delegation key | +| cA2A channel | Per-request X25519-derived authenticated response channel | +| Peer appraisal | Verification of issuer, subject, scope and possession | +| Receipt binding | Cryptographic commitment to request and observed result | +| TRACE Level 0 | Software-signed provenance record without hardware attestation | +| Evidence manifest | Signed, canonicalized statement of observed execution facts | +| Receipt hash chain | Tamper-evident append-only log linkage, not independent custody | +| Replay ledger | Persistent single-use nonce/challenge consumption database | +| Unknown delivery | Indeterminate execution outcome after lost confirmation | +| Mutation twin | Positive control paired with one deliberately weakened safety guard | +| Independent verifier | Separately controlled party reproducing acceptance evidence | diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py index 3f387d2..283c3b7 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py @@ -11,6 +11,8 @@ from dataclasses import dataclass from ca2a_runtime.attestation import VerifiedPeer +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey +from harness.trace_live_binding import verify_bundle from ca2a_runtime.delegation import build_holder_proof from ca2a_runtime.delegation.credential import DelegationCredential from ca2a_runtime.response import PendingResponse, decode, encode @@ -31,6 +33,8 @@ class LivePeerResult: evidence_class: str = "live-software-two-host-same-operator" peer_assurance: str = "none" remote_ca2a_verified: bool = False + trace_verified: bool = False + trace_record_hash: str | None = None def ssh_sender(host: str, key_path: str, *, timeout=20): @@ -58,7 +62,7 @@ def send(request): def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, holder_private, peer_public_key: str, send, - permitted: bool = True) -> LivePeerResult: + permitted: bool = True, trace_public_key: str | None = None) -> LivePeerResult: if not transaction_id or not nonce or not peer_public_key: return LivePeerResult("invalid-context", transaction_id) gateway = MCPGateway(GovernancePolicy( @@ -116,11 +120,27 @@ def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, not isinstance(execution.get("finish_receipt_hash"), str)): return LivePeerResult("refused-output-binding", transaction_id, verified.request_sha256) + trace_hash = None + if trace_public_key is not None: + try: + trace_hash = verify_bundle( + body.get("trace_bundle"), + public_key=Ed25519PublicKey.from_public_bytes( + bytes.fromhex(trace_public_key)), + transaction_id=transaction_id, nonce=nonce, + request_sha256=verified.request_sha256, + repo=repo, execution=execution, + trusted_issuer=issuer_pub) + except Exception: + return LivePeerResult("untrusted-trace-evidence", transaction_id, + verified.request_sha256, remote_ca2a_verified=True) return LivePeerResult("authenticated-remote-ca2a-software", transaction_id, verified.request_sha256, execution["start_receipt_hash"], execution["finish_receipt_hash"], - remote_ca2a_verified=True) + remote_ca2a_verified=True, + trace_verified=trace_hash is not None, + trace_record_hash=trace_hash) except (CA2AError, ValueError, TypeError, KeyError, OSError, RuntimeError, subprocess.TimeoutExpired): return LivePeerResult("unknown-or-refused-transport", transaction_id, diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py index 43b0c0f..09067db 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py @@ -14,6 +14,8 @@ from pathlib import Path from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from harness.trace_live_binding import sign_bundle from ca2a_runtime.challenge import issue_challenge from ca2a_runtime.delegation.credential import DelegationCredential from ca2a_runtime.delegation import HolderProof @@ -111,7 +113,8 @@ def execute_job(agent: str, repo: str) -> dict: def serve(request: dict, *, channel_private, challenge_secret: bytes, trusted_issuer: str, replay_db: Path, - agent: str = "", allowed_repo: str = REPO, dispatch=None) -> dict: + agent: str = "", allowed_repo: str = REPO, dispatch=None, + trace_private=None) -> dict: if not isinstance(request, dict) or request.get("schema") != "frequency-ca2a-peer/1": return {"status": 400, "error": "invalid-request"} public = channel_private.public_key().public_bytes_raw().hex() @@ -162,6 +165,12 @@ def serve(request: dict, *, channel_private, challenge_secret: bytes, "nonce": message["nonce"], "repo": allowed_repo, "capability": CAPABILITY, "execution": observed, "assurance": "software-only"} + if trace_private is not None: + body["trace_bundle"] = sign_bundle( + key=trace_private, transaction_id=message["transaction_id"], + nonce=message["nonce"], request_sha256=producer.request_sha256, + execution=observed, repo=allowed_repo, + trusted_issuer=trusted_issuer) return {"status": 200, "envelope": producer.finish(200, body)} except Exception: return {"status": 503, "envelope": producer.finish(503, { @@ -174,6 +183,8 @@ def main(): private = X25519PrivateKey.from_private_bytes( bytes.fromhex(Path(cfg["private_key_path"]).read_text().strip())) secret = bytes.fromhex(Path(cfg["challenge_secret_path"]).read_text().strip()) + trace_private = Ed25519PrivateKey.from_private_bytes( + bytes.fromhex(Path(cfg["trace_private_key_path"]).read_text().strip())) raw = sys.stdin.buffer.read(MAX_REQUEST + 1) if len(raw) > MAX_REQUEST: raise SystemExit(1) @@ -181,7 +192,7 @@ def main(): challenge_secret=secret, trusted_issuer=cfg["trusted_issuer"], replay_db=Path(cfg["replay_db"]), agent=cfg["agent"], - allowed_repo=cfg["allowed_repo"]) + allowed_repo=cfg["allowed_repo"], trace_private=trace_private) sys.stdout.buffer.write(_canon(response) + b"\n") diff --git a/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py b/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py new file mode 100644 index 0000000..789509e --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py @@ -0,0 +1,91 @@ +"""Software-only TRACE Level 0 record and separately signed execution manifest. + +TRACE policy.bundle_hash identifies the actual local acceptance policy, +NOT an execution receipt. A companion signed manifest binds the request and +Conduit outcome to the TRACE record's digest without abusing TRACE fields. +The signer is a VPS software key pinned by the origin, not independent custody. +""" +import hashlib +import time + +from cryptography.exceptions import InvalidSignature +from agentrust_trace import verify_record +from ca2a_runtime.response import encode +from ca2a_runtime.trace_binding import HopContext, HopSpec, emit_dag, trace_record_hash + +CAPABILITY = "conduit.inspect" +SUBJECT = "did:example:frequency-ca2a-vps-software" +PROFILE = "frequency-ca2a-software-manifest/1" + + +def policy_digest(*, repo: str, trusted_issuer: str) -> str: + policy = {"capability": CAPABILITY, "repo": repo, + "trusted_issuer": trusted_issuer, "network_allowed": False, + "peer_assurance": "none"} + return "sha256:" + hashlib.sha256(encode(policy)).hexdigest() + + +def sign_bundle(*, key, transaction_id: str, nonce: str, + request_sha256: str, execution: dict, repo: str, + trusted_issuer: str) -> dict: + policy_hash = policy_digest(repo=repo, trusted_issuer=trusted_issuer) + ctx = HopContext.software( + model_provider="frequency-software-only", + model_id="conduit-inspect-no-model", + image_label="unmeasured-software-peer-conduit-inspect", + policy_bundle_hash=policy_hash, + runtime_nonce=nonce) + record = emit_dag([HopSpec( + subject=SUBJECT, signing_key=key, + context=ctx, iat=int(time.time()))])[0] + manifest = { + "schema": PROFILE, + "trace_record_hash": trace_record_hash(record), + "policy_bundle_hash": policy_hash, + "transaction_id": transaction_id, + "nonce": nonce, + "request_sha256": request_sha256, + "repo": repo, + "capability": CAPABILITY, + "execution": execution, + "assurance": "software-only-same-operator", + } + signature = key.sign(encode(manifest)).hex() + return {"record": record, "manifest": manifest, "signature": signature} + + +def verify_bundle(bundle: dict, *, public_key, transaction_id: str, nonce: str, + request_sha256: str, repo: str, execution: dict, + trusted_issuer: str) -> str: + if not isinstance(bundle, dict) or set(bundle) != {"record","manifest","signature"}: + raise ValueError("trace-bundle-format") + record, manifest, signature = ( + bundle["record"], bundle["manifest"], bundle["signature"]) + if not isinstance(record, dict) or not isinstance(manifest, dict): + raise ValueError("trace-bundle-type") + expected_policy = policy_digest(repo=repo, trusted_issuer=trusted_issuer) + digest = trace_record_hash(record) + expected = { + "schema": PROFILE, + "trace_record_hash": digest, + "policy_bundle_hash": expected_policy, + "transaction_id": transaction_id, + "nonce": nonce, + "request_sha256": request_sha256, + "repo": repo, + "capability": CAPABILITY, + "execution": execution, + "assurance": "software-only-same-operator", + } + if manifest != expected or record.get("policy", {}).get("bundle_hash") != expected_policy: + raise ValueError("trace-manifest-binding-mismatch") + try: + public_key.verify(bytes.fromhex(signature), encode(manifest)) + except (ValueError, InvalidSignature) as exc: + raise ValueError("trace-manifest-signature-invalid") from exc + # Pin the remote signing key: embedded cnf.jwk is NOT a trust root. + verify_record(record, public_key, expected_nonce=nonce, + max_age_seconds=120) + if record.get("subject") != SUBJECT or record.get("runtime", {}).get("platform") != "software-only": + raise ValueError("unexpected-trace-claim") + return digest diff --git a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py new file mode 100644 index 0000000..d8164e3 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py @@ -0,0 +1,38 @@ +"""Run the restricted software cA2A peer proof from a private operator config. + +The config contains file paths and pinned PUBLIC keys, never private contents. +No implicit configuration, external discovery, or SSH host-key override. +""" +import argparse +import json +import uuid +from pathlib import Path +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from harness.remote_peer_client import run, ssh_sender + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--config", type=Path, required=True) + args = parser.parse_args() + cfg = json.loads(args.config.read_text()) + issuer = Ed25519PrivateKey.from_private_bytes( + bytes.fromhex(Path(cfg["issuer_key"]).read_text().strip())) + holder = Ed25519PrivateKey.from_private_bytes( + bytes.fromhex(Path(cfg["holder_key"]).read_text().strip())) + result = run( + transaction_id="live-ca2a-" + uuid.uuid4().hex, + nonce=uuid.uuid4().hex, + repo=cfg["repo"], issuer_private=issuer, holder_private=holder, + peer_public_key=cfg["peer_public_key"], + trace_public_key=cfg["trace_public_key"], + send=ssh_sender(cfg["host"], cfg["ssh_key"])) + print(json.dumps(result.__dict__, sort_keys=True)) + if (result.outcome != "authenticated-remote-ca2a-software" + or not result.remote_ca2a_verified + or not result.trace_verified): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py index 73397a7..131ac3a 100644 --- a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py @@ -6,6 +6,7 @@ from pathlib import Path from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from ca2a_runtime.delegation import new_keypair from ca2a_runtime.response import encode @@ -20,6 +21,7 @@ def setUp(self): self.db = Path(tmp.name) / "replay.sqlite" self.issuer, self.issuer_pub = new_keypair() self.holder, self.holder_pub = new_keypair() + self.trace = Ed25519PrivateKey.generate() self.peer = X25519PrivateKey.generate() self.peer_pub = self.peer.public_key().public_bytes_raw().hex() self.secret = b"secret-for-test-only-" + bytes(range(32)) @@ -38,7 +40,8 @@ def send(self, request): self.last_submit = json.loads(json.dumps(request)) return serve(request, channel_private=self.peer, challenge_secret=self.secret, trusted_issuer=self.issuer_pub, - replay_db=self.db, dispatch=self.dispatch) + replay_db=self.db, dispatch=self.dispatch, + trace_private=self.trace) def invoke(self, **kw): args = {"transaction_id": "transaction-1", "nonce": "0123456789abcdef", @@ -112,3 +115,17 @@ def lose(request): return self.send(request) self.assertEqual(self.invoke(send=lose).outcome,"unknown-or-refused-transport") self.assertEqual(self.dispatch_count,0) + + def test_real_trace_signed_record(self): + pub=self.trace.public_key().public_bytes_raw().hex() + result=self.invoke(trace_public_key=pub) + self.assertEqual(result.outcome,"authenticated-remote-ca2a-software") + self.assertTrue(result.trace_verified) + self.assertTrue(result.trace_record_hash.startswith("sha256:")) + + def test_untrusted_trace_issuer_refused(self): + wrong=Ed25519PrivateKey.generate().public_key().public_bytes_raw().hex() + result=self.invoke(trace_public_key=wrong) + self.assertEqual(result.outcome,"untrusted-trace-evidence") + self.assertTrue(result.remote_ca2a_verified) + self.assertFalse(result.trace_verified) From ac6771e4d35a68068f0a81949c17e6d46c705980 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:43:19 -0700 Subject: [PATCH 10/17] test: publish replayable signed live federation evidence and 78-case gate --- .../EDRG-FEDERATION-SOFTWARE-2026-10-16.md | 88 +++++++++++++ .../REMOTE-CA2A-PEER.md | 20 +++ .../evidence/README.md | 44 +++++++ .../evidence/live-software-20261016.json | 75 +++++++++++ .../harness/remote_peer_client.py | 19 ++- .../harness/remote_peer_service.py | 6 +- .../harness/replay_export.py | 70 ++++++++++ .../harness/trace_live_binding.py | 5 +- .../run_remote_ca2a_live.py | 12 +- .../run_remote_ca2a_matrix.py | 121 ++++++++++++++++++ .../tests/test_live_evidence_fixture.py | 40 ++++++ .../tests/test_remote_ca2a.py | 10 +- .../tests/test_replay_export.py | 85 ++++++++++++ .../verify_remote_peer_packet.py | 24 ++++ 14 files changed, 610 insertions(+), 9 deletions(-) create mode 100644 integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md create mode 100644 integrations/agentrust-confidential-workflow-harness/evidence/README.md create mode 100644 integrations/agentrust-confidential-workflow-harness/evidence/live-software-20261016.json create mode 100644 integrations/agentrust-confidential-workflow-harness/harness/replay_export.py create mode 100644 integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_live_evidence_fixture.py create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py create mode 100644 integrations/agentrust-confidential-workflow-harness/verify_remote_peer_packet.py diff --git a/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md new file mode 100644 index 0000000..7aa641f --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md @@ -0,0 +1,88 @@ +# Evidence-driven release gate: composed first-party SOFTWARE federation + +**Date:** October 16, 2026 +**Scope:** AgenTrust integrations #199, Frequency two-host software laboratory +**Decision:** **Software integration pilot PASS; full independently accepted +confidential workflow gate remains OPEN.** + +## Verified execution boundary + +The origin first uses released `cmcp-runtime==0.7.0` for `conduit.inspect` +authorization. A separately restricted SSH forced-command peer on the VPS +then challenges the caller and verifies the released +`ca2a-runtime==0.4.0` signed delegation/holder proof against an explicitly +pinned issuer. A persistent SQLite ledger consumes the challenge before any +side effect. The remote peer launches only Frequency Conduit's fixed +network-disabled `inspect` job, observes its completion, and returns the +authenticated released cA2A 0.4.0 response envelope. + +The VPS separately signs a TRACE 0.11.0 / TRACE v0.2 Level 0 +**software-only** provenance record and companion receipt-binding manifest. +The origin verifies both with the pinned remote Ed25519 key; the semantic +`policy.bundle_hash` remains the hash of the actual scope/repo/root policy, +not a disguised execution artifact. The signed manifest separately binds the +exact request hash, transaction, nonce, job result, start/end receipt hashes, +and TRACE record hash. + +**Real sample transaction:** +`live-ca2a-a40f23585ad2451f9c738971ae64c050` + +**Pinned replay fixture:** `evidence/live-software-20261016.json` + +**Historical TRACE record hash:** +`sha256:f830659f2af40ff74a42b48e04b1a6c544c41f25e9863c18aae56adc70ff77a5` + +**Fixture SHA-256:** +`402a8f0cc05bc517d2918e7a49b10051cb60122fcac8909f1a9ed308c12fdc13` + +## Adversarial evidence + +The live two-host matrix passed **10/10** scenarios on October 16, including +valid control, local cMCP denial, remote untrusted issuer, missing TRACE trust, +wrong TRACE public key, mutated credential scope, response MAC alteration, +post-execution receipt loss reported as unknown, a fresh valid replay twin, +and a consumed challenge replay refused by the peer. + +The repository unit/mutation suite passed **78/78** on Prometheus, +including signature/refusal controls, offline software TRACE replay and +historical evidence tampering. Source and dependency versions are pinned in +the PR and hash-locked requirements, and the captured packet includes only +a public, non-confidential `inspect` execution. + +## Required acceptance outcomes + +| Gate | Result | Qualification | +|---|---|---| +| cMCP permission before dispatch | PASS | Released gateway | +| cA2A signed delegation + holder proof | PASS | VPS independently checks proof within same-operator trust domain | +| Remote transport / bounded execution | PASS | Pinned SSH forced command and Conduit sandbox | +| Persistent replay refusal | PASS | SQLite consumed challenge, not hardware anti-replay | +| cA2A authenticated response | PASS | Live source, ephemeral session MAC | +| TRACE signature and companion result binding | PASS | Level 0 signed software evidence | +| Positive / refusal adversarial controls | PASS | Local suite and 10-case live matrix | +| Offline independently runnable signature check | PASS | Public packet; key provenance remains self-asserted | +| External independently operated peer | OPEN | No second organizational trust domain | +| Confidential model/tool execution and disclosure | OPEN | Only a read-only `inspect` job was exercised | +| Independently enrolled revocation/custody | OPEN | No authoritative revocation snapshot or external signer custody | +| Physical TPM / TEE confidential execution | OPEN | No hardware-backed measurement or key release | +| Independent replay by Dinakar | OPEN | Requires another reviewer to perform and record it | +| Maintainer signoff/merge | OPEN | Cannot be self-issued or bypassed | + +## Non-equivalence rules + +A successful software Level 0 TRACE record proves the pinned software +signing key endorsed its claims and the content was not altered since signing. +It does **not** prove a hardware identity, a confidential runtime, complete +monitoring, independent custody or any claim about a secret not exercised by +this test. Likewise, a positive cA2A response is not proof of independent +organizational acceptance. A missing post-dispatch response is an **unknown +execution state**, never a clean refusal or proof of no side effect. + +Historical offline replay validates signatures while deliberately not making +a *freshness* claim. The live cA2A per-occurrence state is non-exportable and +must be freshly exercised to verify the session. + +For implementation About, licensing boundaries, protocol/version pinning, +dependency attribution and glossary mapping, see +[REMOTE-CA2A-PEER.md](REMOTE-CA2A-PEER.md), the reproducible packet in +[evidence/](evidence/), and the original [README](README.md). diff --git a/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md index 0b364ab..cfe10b1 100644 --- a/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md +++ b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md @@ -111,3 +111,23 @@ never promote a lower class because its checks are green. | Unknown delivery | Indeterminate execution outcome after lost confirmation | | Mutation twin | Positive control paired with one deliberately weakened safety guard | | Independent verifier | Separately controlled party reproducing acceptance evidence | + +## Private lab live run and evidence export + +The operator supplies a local mode-0600 JSON config with SSH key **path**, +issuer and holder private key **paths**, pinned public keys, permitted repo, +and SSH hostname (never the private key contents). The following commands +create a fresh occurrence and an offline packet in an operator-controlled +private directory: + +```sh +python run_remote_ca2a_live.py --config /private/client.json --evidence-out /private/new-evidence.json +python run_remote_ca2a_matrix.py --config /private/client.json --output /private/new-matrix.json +``` + +The signed, non-sensitive example packet and verification instructions are +available in [evidence/](evidence/). The public fixture intentionally excludes +private keys, live SSH credentials, session challenge and raw authorization +payloads; the reviewer must enroll the public key through a distinct trust +channel. The exported packet is useful for *historical software signature +verification*, not live session-MAC replay or hardware assurance. diff --git a/integrations/agentrust-confidential-workflow-harness/evidence/README.md b/integrations/agentrust-confidential-workflow-harness/evidence/README.md new file mode 100644 index 0000000..2fd9882 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/evidence/README.md @@ -0,0 +1,44 @@ +# Independently replayable software-signature evidence + +The accompanying `live-software-20261016.json` is an **actual** Prometheus → +VPS software execution packet generated October 16, 2026. It contains a +TRACE v0.2 Level 0 record signed with the VPS's Ed25519 software key, a separate +signed companion manifest bound to the cA2A request hash, and hashes of the +Frequency Conduit start and completion receipts. It does **not** contain +private keys, raw cA2A holder proofs, decrypted confidential data or the VPS +network address. + +Packet file SHA-256: + +`402a8f0cc05bc517d2918e7a49b10051cb60122fcac8909f1a9ed308c12fdc13` + +## Offline verification + +Use the hash-locked released dependencies from the repository root. Then from +the harness directory: + +```sh +python verify_remote_peer_packet.py \ + --packet evidence/live-software-20261016.json \ + --trace-public 2c6ba06dde1a1a7dc8c9676a7c38de76c3f2df6268413cc27779bc6eb0c2d7f1 \ + --issuer-public 880a6aa8cd1976f69176cb1a095b5deb8449579d253b14003fc14ff9d983ed02 \ + --peer-public 69a7a6092973975edd92b2a836704a5c90c462825e69a08d20996e795a854d3d +``` + +**Trust warning:** The three public keys above are disclosed *by the same +operator that controls both hosts*. They allow anyone to independently +recompute and check the signatures, but do not establish externally enrolled +identity, independent key custody, or hardware attestation. Reviewers must +authenticate the keys through a separate trusted channel before using them +as externally trusted identities. + +Offline replay deliberately disables **current-age** enforcement to validate +a historical signed record. This cannot demonstrate *current* nonce freshness +or reproduce the cA2A session MAC: the official `PendingResponse` refuses +export or reuse of its ephemeral state by design. Live validation must instead +rerun the peer interaction, with a new challenge and nonce. Signatures prove +that the pinned software signer endorsed the recorded claims, **not** that an +external observer independently measured the job or confidential execution. +The underlying Conduit receipt log remains under the same operator. + +See [About, licensing, glossary and assurance boundaries](../REMOTE-CA2A-PEER.md). diff --git a/integrations/agentrust-confidential-workflow-harness/evidence/live-software-20261016.json b/integrations/agentrust-confidential-workflow-harness/evidence/live-software-20261016.json new file mode 100644 index 0000000..8f02659 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/evidence/live-software-20261016.json @@ -0,0 +1,75 @@ +{ + "evidence_class": "live-software-two-host-same-operator", + "execution": { + "commit_sha": "53c3399c1fa74a985cd74de3c40d48ca02e53bfb", + "exit_code": 0, + "finish_receipt_hash": "1d9569f7eeb5693faeed052511f89a0032361fb5b06a031d81a6a0ae19f3726c", + "session_id": "fe9c3918-8449-4a86-a722-0193ff1288f1", + "start_receipt_hash": "e3b1547af56264cdb790cbe86d7a313cb5d3d69dd28a9f2dd42e25dab898b63b" + }, + "nonce": "94594cfc2fdc4baca89bce376ddbbcaa", + "origin_ca2a_verification": "passed-in-process-not-exportable", + "peer_public_key": "69a7a6092973975edd92b2a836704a5c90c462825e69a08d20996e795a854d3d", + "repo": "/home/ubuntu/src/integrations", + "request_sha256": "4d20e95870afa4eda47776c9a92d43543522678e1c6f30aac98a52a6f39034f2", + "schema": "frequency-ca2a-trace-replay-packet/1", + "trace_bundle": { + "manifest": { + "assurance": "software-only-same-operator", + "capability": "conduit.inspect", + "execution": { + "commit_sha": "53c3399c1fa74a985cd74de3c40d48ca02e53bfb", + "exit_code": 0, + "finish_receipt_hash": "1d9569f7eeb5693faeed052511f89a0032361fb5b06a031d81a6a0ae19f3726c", + "session_id": "fe9c3918-8449-4a86-a722-0193ff1288f1", + "start_receipt_hash": "e3b1547af56264cdb790cbe86d7a313cb5d3d69dd28a9f2dd42e25dab898b63b" + }, + "nonce": "94594cfc2fdc4baca89bce376ddbbcaa", + "policy_bundle_hash": "sha256:edb1c936ac9e922da1ef8e6c382f14a7d65d4076472c6f8ec19d50bfb3be4956", + "repo": "/home/ubuntu/src/integrations", + "request_sha256": "4d20e95870afa4eda47776c9a92d43543522678e1c6f30aac98a52a6f39034f2", + "schema": "frequency-ca2a-software-manifest/1", + "trace_record_hash": "sha256:f830659f2af40ff74a42b48e04b1a6c544c41f25e9863c18aae56adc70ff77a5", + "transaction_id": "live-ca2a-a40f23585ad2451f9c738971ae64c050" + }, + "record": { + "appraisal": { + "status": "none", + "verifier": "https://appraisal.invalid/software-mode" + }, + "build_provenance": { + "digest": "sha256:ee038106752c12b17ed8384d8327b658173704a7ce1276a5a8c182ccb162e00f", + "slsa_level": 0 + }, + "cnf": { + "jwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "LGugbd4aGn3IyWdqfDjedsPy32JoQTzCd3m8brDC1_E" + } + }, + "data_class": "confidential", + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2", + "iat": 1792165120, + "model": { + "model_id": "conduit-inspect-no-model", + "provider": "frequency-software-only" + }, + "policy": { + "bundle_hash": "sha256:edb1c936ac9e922da1ef8e6c382f14a7d65d4076472c6f8ec19d50bfb3be4956", + "enforcement_mode": "enforce" + }, + "runtime": { + "measurement": "sha256:747428e46e6150aecb29100aec4eb8de7c343bbac0f3ea8b051457d58d6270e0", + "nonce": "94594cfc2fdc4baca89bce376ddbbcaa", + "platform": "software-only" + }, + "signature": "wVJeZkY1GP89VqSTUkNK-0wIkMvx6Djxt_MiT9ZuicnwZmSVEnc6eXE-FuHzwMWF3hWrzlZNOv2RsDTVgrlZAg", + "subject": "did:example:frequency-ca2a-vps-software", + "transparency": "https://transparency.invalid/software-mode" + }, + "signature": "d7ccc70413f9dbf17feedc59c30857329fafa5fb9000f9f3818fc7eaffe368d5d281c9ba6276effa0d6eb2e79df33a7c09e82e57f0df0b58e3561982b3b8fb01" + }, + "transaction_id": "live-ca2a-a40f23585ad2451f9c738971ae64c050", + "trusted_issuer": "880a6aa8cd1976f69176cb1a095b5deb8449579d253b14003fc14ff9d983ed02" +} diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py index 283c3b7..ead437d 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py @@ -6,13 +6,13 @@ """ import hashlib import json -import os import subprocess from dataclasses import dataclass from ca2a_runtime.attestation import VerifiedPeer from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey from harness.trace_live_binding import verify_bundle +from harness.replay_export import make_packet from ca2a_runtime.delegation import build_holder_proof from ca2a_runtime.delegation.credential import DelegationCredential from ca2a_runtime.response import PendingResponse, decode, encode @@ -62,9 +62,12 @@ def send(request): def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, holder_private, peer_public_key: str, send, - permitted: bool = True, trace_public_key: str | None = None) -> LivePeerResult: + permitted: bool = True, trace_public_key: str | None = None, + evidence_sink=None) -> LivePeerResult: if not transaction_id or not nonce or not peer_public_key: return LivePeerResult("invalid-context", transaction_id) + if not trace_public_key: + return LivePeerResult("missing-trace-trust-anchor", transaction_id) gateway = MCPGateway(GovernancePolicy( allowed_tools=[CAPABILITY] if permitted else [], max_tool_calls=1)) allowed, _ = gateway.intercept_tool_call("frequency-peer-origin", @@ -134,6 +137,18 @@ def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, except Exception: return LivePeerResult("untrusted-trace-evidence", transaction_id, verified.request_sha256, remote_ca2a_verified=True) + if evidence_sink is not None: + try: + evidence_sink(make_packet( + transaction_id=transaction_id, nonce=nonce, + request_sha256=verified.request_sha256, + repo=repo, peer_public_key=peer_public_key, + trusted_issuer=issuer_pub, + trace_bundle=body["trace_bundle"], execution=execution)) + except Exception: + return LivePeerResult("unavailable-evidence-export", transaction_id, + verified.request_sha256, remote_ca2a_verified=True, + trace_verified=True, trace_record_hash=trace_hash) return LivePeerResult("authenticated-remote-ca2a-software", transaction_id, verified.request_sha256, execution["start_receipt_hash"], diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py index 09067db..90de850 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_service.py @@ -40,14 +40,18 @@ def _digest(value): def _consume(path: Path, occurrence: str) -> bool: """Atomic persistent replay refusal; failure is denial, not cache reset.""" path.parent.mkdir(mode=0o700, parents=True, exist_ok=True) - with sqlite3.connect(path, timeout=5) as db: + db = sqlite3.connect(path, timeout=5) + try: db.execute("CREATE TABLE IF NOT EXISTS spent (id TEXT PRIMARY KEY, seen INTEGER NOT NULL)") try: db.execute("INSERT INTO spent (id,seen) VALUES (?,?)", (occurrence, int(time.time()))) db.commit() return True except sqlite3.IntegrityError: + db.rollback() return False + finally: + db.close() def _receipt_ok(receipt, action, session): diff --git a/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py b/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py new file mode 100644 index 0000000..772d9f6 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py @@ -0,0 +1,70 @@ +"""Offline verification of a remote peer's signed software evidence packet. + +The peer is identified only by keys enrolled OUTSIDE the packet. This validates +software signatures and claim binding, not that the operator is independent or +that confidential execution or hardware attestations occurred. +""" +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey +from harness.trace_live_binding import verify_bundle + +SCHEMA = "frequency-ca2a-trace-replay-packet/1" + + +def make_packet(*, transaction_id, nonce, request_sha256, repo, + peer_public_key, trusted_issuer, trace_bundle, execution): + return { + "schema": SCHEMA, + "transaction_id": transaction_id, + "nonce": nonce, + "request_sha256": request_sha256, + "repo": repo, + "peer_public_key": peer_public_key, + "trusted_issuer": trusted_issuer, + "trace_bundle": trace_bundle, + "execution": execution, + "origin_ca2a_verification": "passed-in-process-not-exportable", + "evidence_class": "live-software-two-host-same-operator", + } + + +def verify_packet(packet: dict, *, trace_public_key: str, + trusted_issuer: str, peer_public_key: str) -> dict: + if not isinstance(packet, dict) or set(packet) != { + "schema", "transaction_id", "nonce", "request_sha256", "repo", + "peer_public_key", "trusted_issuer", "trace_bundle", "execution", + "origin_ca2a_verification", "evidence_class"}: + raise ValueError("malformed-replay-packet") + if (packet["schema"] != SCHEMA or + packet["trusted_issuer"] != trusted_issuer or + packet["peer_public_key"] != peer_public_key or + packet["evidence_class"] != "live-software-two-host-same-operator" or + packet["origin_ca2a_verification"] != "passed-in-process-not-exportable"): + raise ValueError("replay-trust-or-evidence-class-mismatch") + if not isinstance(packet["execution"], dict): + raise ValueError("malformed-execution-evidence") + e=packet["execution"] + if (e.get("exit_code") != 0 or + not isinstance(e.get("session_id"), str) or + any(not isinstance(e.get(field),str) or len(e[field]) != 64 or + any(c not in "0123456789abcdef" for c in e[field]) + for field in ("start_receipt_hash","finish_receipt_hash"))): + raise ValueError("bad-execution-claim") + pk=Ed25519PublicKey.from_public_bytes(bytes.fromhex(trace_public_key)) + record_hash=verify_bundle( + packet["trace_bundle"], public_key=pk, + transaction_id=packet["transaction_id"], + nonce=packet["nonce"], + request_sha256=packet["request_sha256"], + repo=packet["repo"], execution=e, + trusted_issuer=trusted_issuer, max_age_seconds=None) + return { + "verified": True, + "record_hash": record_hash, + "manifest_binding": "valid", + "signed_by": trace_public_key, + "assurance": "software-only", + "independent_custody": False, + "hardware_attested": False, + "origin_response_authentication": "not-offline-replayable", + "freshness": "historical-signature-only-not-current", + } diff --git a/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py b/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py index 789509e..a61d4ae 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/trace_live_binding.py @@ -56,7 +56,8 @@ def sign_bundle(*, key, transaction_id: str, nonce: str, def verify_bundle(bundle: dict, *, public_key, transaction_id: str, nonce: str, request_sha256: str, repo: str, execution: dict, - trusted_issuer: str) -> str: + trusted_issuer: str, + max_age_seconds: int | None = 120) -> str: if not isinstance(bundle, dict) or set(bundle) != {"record","manifest","signature"}: raise ValueError("trace-bundle-format") record, manifest, signature = ( @@ -85,7 +86,7 @@ def verify_bundle(bundle: dict, *, public_key, transaction_id: str, nonce: str, raise ValueError("trace-manifest-signature-invalid") from exc # Pin the remote signing key: embedded cnf.jwk is NOT a trust root. verify_record(record, public_key, expected_nonce=nonce, - max_age_seconds=120) + max_age_seconds=max_age_seconds) if record.get("subject") != SUBJECT or record.get("runtime", {}).get("platform") != "software-only": raise ValueError("unexpected-trace-claim") return digest diff --git a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py index d8164e3..4bc4b66 100644 --- a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py +++ b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_live.py @@ -5,6 +5,7 @@ """ import argparse import json +import os import uuid from pathlib import Path from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey @@ -14,19 +15,28 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument("--config", type=Path, required=True) + parser.add_argument("--evidence-out", type=Path, required=True) args = parser.parse_args() cfg = json.loads(args.config.read_text()) issuer = Ed25519PrivateKey.from_private_bytes( bytes.fromhex(Path(cfg["issuer_key"]).read_text().strip())) holder = Ed25519PrivateKey.from_private_bytes( bytes.fromhex(Path(cfg["holder_key"]).read_text().strip())) + def write_packet(packet): + args.evidence_out.parent.mkdir(mode=0o700,parents=True,exist_ok=True) + fd=os.open(args.evidence_out,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600) + with os.fdopen(fd,"w") as f: + json.dump(packet,f,sort_keys=True,indent=2) + f.write("\n") + result = run( transaction_id="live-ca2a-" + uuid.uuid4().hex, nonce=uuid.uuid4().hex, repo=cfg["repo"], issuer_private=issuer, holder_private=holder, peer_public_key=cfg["peer_public_key"], trace_public_key=cfg["trace_public_key"], - send=ssh_sender(cfg["host"], cfg["ssh_key"])) + send=ssh_sender(cfg["host"], cfg["ssh_key"]), + evidence_sink=write_packet) print(json.dumps(result.__dict__, sort_keys=True)) if (result.outcome != "authenticated-remote-ca2a-software" or not result.remote_ca2a_verified diff --git a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py new file mode 100644 index 0000000..afafc4c --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py @@ -0,0 +1,121 @@ +"""Bounded LIVE two-host negative matrix. No side-effect retry on unknown results. + +Requires a separately provisioned private operator config and remote forced +SSH peer. Never treats unobserved execution as proof of refusal. +""" +import argparse +import json +import uuid +from pathlib import Path + +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from ca2a_runtime.delegation import new_keypair +from harness.remote_peer_client import run, ssh_sender + + +def private(path): + return Ed25519PrivateKey.from_private_bytes( + bytes.fromhex(Path(path).read_text().strip())) + + +def main(): + parser=argparse.ArgumentParser() + parser.add_argument("--config",type=Path,required=True) + parser.add_argument("--output",type=Path,required=True) + args=parser.parse_args() + cfg=json.loads(args.config.read_text()) + issuer=private(cfg["issuer_key"]) + holder=private(cfg["holder_key"]) + base_send=ssh_sender(cfg["host"],cfg["ssh_key"]) + results=[] + + def scenario(name, expected, *, send=None, issuer_private=None, + permitted=True, trace_public_key="default"): + key = cfg["trace_public_key"] if trace_public_key=="default" else trace_public_key + result=run(transaction_id="matrix-"+uuid.uuid4().hex, + nonce=uuid.uuid4().hex, + repo=cfg["repo"],issuer_private=issuer_private or issuer, + holder_private=holder,peer_public_key=cfg["peer_public_key"], + trace_public_key=key,permitted=permitted, + send=send or base_send) + observed=result.outcome + passed=(observed==expected) + record={"case":name,"expected":expected,"observed":observed, + "passed":passed,"transaction_id":result.transaction_id, + "evidence_class":result.evidence_class, + "remote_ca2a_verified":result.remote_ca2a_verified, + "trace_verified":result.trace_verified} + if result.start_receipt_hash: + record["start_receipt_hash"]=result.start_receipt_hash + if result.finish_receipt_hash: + record["finish_receipt_hash"]=result.finish_receipt_hash + if result.trace_record_hash: + record["trace_record_hash"]=result.trace_record_hash + results.append(record) + print(json.dumps(record,sort_keys=True),flush=True) + return result + + valid="authenticated-remote-ca2a-software" + scenario("positive-twin",valid) + scenario("local-cmcp-authorization-refusal","refused-cmcp",permitted=False) + attacker,_=new_keypair() + scenario("untrusted-issuer","refused-authenticated-peer",issuer_private=attacker) + scenario("missing-trace-trust-root","missing-trace-trust-anchor",trace_public_key=None) + other,_=new_keypair() + scenario("wrong-trace-verifier-root","untrusted-trace-evidence", + trace_public_key=other.public_key().public_bytes_raw().hex()) + + def altered_credential(request): + if request.get("method")=="execute": + request["credential"]["scope"]=["conduit.delete"] + return base_send(request) + scenario("altered-delegation-scope","refused-authenticated-peer", + send=altered_credential) + + def tampered_mac(request): + ans=base_send(request) + if request.get("method")=="execute" and "envelope" in ans: + ans["envelope"]["mac"]="00"*32 + return ans + scenario("response-authentication-mutation","unknown-or-refused-transport", + send=tampered_mac) + + def lost_response(request): + ans=base_send(request) + if request.get("method")=="execute": + raise RuntimeError("delivery-uncertain-after-execution") + return ans + scenario("unobserved-result-remains-unknown","unknown-or-refused-transport", + send=lost_response) + + last=[None] + def record_submit(request): + if request.get("method")=="execute": + last[0]=json.loads(json.dumps(request)) + return base_send(request) + scenario("replay-valid-twin",valid,send=record_submit) + repeated=base_send(last[0]) + replay_ok=repeated.get("status")==403 + rr={"case":"one-time-challenge-replay","expected":"authenticated-peer-denial", + "observed":"authenticated-peer-denial" if replay_ok else "unexpected", + "passed":replay_ok,"evidence_class":"live-software-two-host-same-operator"} + results.append(rr) + print(json.dumps(rr,sort_keys=True),flush=True) + + passed=all(x["passed"] for x in results) + report={"schema":"frequency-ca2a-live-adversarial-matrix/1", + "evidence_class":"live-software-two-host-same-operator", + "operator_independent":False,"hardware_attested":False, + "tests":len(results),"passed":sum(x["passed"] for x in results), + "all_passed":passed, "results":results} + args.output.parent.mkdir(mode=0o700,parents=True,exist_ok=True) + args.output.write_text(json.dumps(report,indent=2,sort_keys=True)+"\n") + args.output.chmod(0o600) + print("MATRIX",json.dumps({"passed":report["passed"],"tests":report["tests"], + "all_passed":passed}),flush=True) + if not passed: + raise SystemExit(1) + + +if __name__=="__main__": + main() diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_live_evidence_fixture.py b/integrations/agentrust-confidential-workflow-harness/tests/test_live_evidence_fixture.py new file mode 100644 index 0000000..25c93d3 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_live_evidence_fixture.py @@ -0,0 +1,40 @@ +"""Reproduce the historical public software signature fixture without SSH.""" +import copy +import hashlib +import json +import unittest +from pathlib import Path +from harness.replay_export import verify_packet + +FILE = Path(__file__).resolve().parents[1] / "evidence/live-software-20261016.json" +FILE_SHA256 = "402a8f0cc05bc517d2918e7a49b10051cb60122fcac8909f1a9ed308c12fdc13" +TRACE_PUB = "2c6ba06dde1a1a7dc8c9676a7c38de76c3f2df6268413cc27779bc6eb0c2d7f1" +ISSUER_PUB = "880a6aa8cd1976f69176cb1a095b5deb8449579d253b14003fc14ff9d983ed02" +PEER_PUB = "69a7a6092973975edd92b2a836704a5c90c462825e69a08d20996e795a854d3d" + + +class PublishedEvidenceFixture(unittest.TestCase): + def test_signed_packet_matches_pinned_digest(self): + blob=FILE.read_bytes() + self.assertEqual(hashlib.sha256(blob).hexdigest(), FILE_SHA256) + packet=json.loads(blob) + result=verify_packet( + packet,trace_public_key=TRACE_PUB, + trusted_issuer=ISSUER_PUB,peer_public_key=PEER_PUB) + self.assertTrue(result["verified"]) + self.assertFalse(result["hardware_attested"]) + self.assertFalse(result["independent_custody"]) + self.assertEqual(result["freshness"], + "historical-signature-only-not-current") + + def test_modified_manifest_refused(self): + packet=json.loads(FILE.read_bytes()) + mutated=copy.deepcopy(packet) + mutated["execution"]["finish_receipt_hash"]="f"*64 + with self.assertRaises(Exception): + verify_packet( + mutated,trace_public_key=TRACE_PUB, + trusted_issuer=ISSUER_PUB,peer_public_key=PEER_PUB) + self.assertTrue(verify_packet( + packet,trace_public_key=TRACE_PUB, + trusted_issuer=ISSUER_PUB,peer_public_key=PEER_PUB)["verified"]) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py index 131ac3a..421a5b0 100644 --- a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py @@ -1,5 +1,4 @@ """Adversarial published-cA2A client/server interoperability and replay tests.""" -import hashlib import json import tempfile import unittest @@ -8,7 +7,6 @@ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from ca2a_runtime.delegation import new_keypair -from ca2a_runtime.response import encode from harness.remote_peer_client import run from harness.remote_peer_service import serve @@ -47,7 +45,8 @@ def invoke(self, **kw): args = {"transaction_id": "transaction-1", "nonce": "0123456789abcdef", "repo": "/home/ubuntu/src/integrations", "issuer_private": self.issuer,"holder_private": self.holder, - "peer_public_key": self.peer_pub,"send": self.send} + "peer_public_key": self.peer_pub,"send": self.send, + "trace_public_key": self.trace.public_key().public_bytes_raw().hex()} args.update(kw) return run(**args) @@ -129,3 +128,8 @@ def test_untrusted_trace_issuer_refused(self): self.assertEqual(result.outcome,"untrusted-trace-evidence") self.assertTrue(result.remote_ca2a_verified) self.assertFalse(result.trace_verified) + + def test_missing_trusted_trace_key_never_dispatches(self): + self.assertEqual(self.invoke(trace_public_key=None).outcome, + "missing-trace-trust-anchor") + self.assertEqual(self.dispatch_count,0) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py new file mode 100644 index 0000000..a28e3dd --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py @@ -0,0 +1,85 @@ +"""Offline TRACE signature/manifest acceptance and paired mutation twins.""" +import copy +import unittest +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from harness.replay_export import make_packet, verify_packet +from harness.trace_live_binding import sign_bundle + + +class ReplayExportTests(unittest.TestCase): + def setUp(self): + self.key=Ed25519PrivateKey.generate() + self.pub=self.key.public_key().public_bytes_raw().hex() + self.issuer="ab"*32 + self.peer="cd"*32 + self.execution={ + "session_id":"lab-session","start_receipt_hash":"1"*64, + "finish_receipt_hash":"2"*64,"commit_sha":"abc","exit_code":0} + bundle=sign_bundle( + key=self.key,transaction_id="test-transaction", + nonce="fixture-nonce",request_sha256="3"*64, + execution=self.execution,repo="/home/ubuntu/src/integrations", + trusted_issuer=self.issuer) + self.packet=make_packet( + transaction_id="test-transaction",nonce="fixture-nonce", + request_sha256="3"*64,repo="/home/ubuntu/src/integrations", + peer_public_key=self.peer,trusted_issuer=self.issuer, + trace_bundle=bundle,execution=self.execution) + + def verify(self, packet=None, **kwargs): + config={"trace_public_key":self.pub,"trusted_issuer":self.issuer, + "peer_public_key":self.peer} + config.update(kwargs) + return verify_packet(packet if packet is not None else self.packet,**config) + + def assert_refuses_with_twin(self, modified): + with self.assertRaises(Exception): + self.verify(modified) + self.assertTrue(self.verify()["verified"]) + + def test_valid_signed_packet(self): + result=self.verify() + self.assertTrue(result["verified"]) + self.assertFalse(result["hardware_attested"]) + self.assertFalse(result["independent_custody"]) + self.assertEqual(result["origin_response_authentication"], + "not-offline-replayable") + + def test_modified_result_digest(self): + p=copy.deepcopy(self.packet) + p["execution"]["finish_receipt_hash"]="4"*64 + self.assert_refuses_with_twin(p) + + def test_wrong_signer(self): + other=Ed25519PrivateKey.generate().public_key().public_bytes_raw().hex() + with self.assertRaises(Exception): + self.verify(trace_public_key=other) + self.assertTrue(self.verify()["verified"]) + + def test_wrong_issuer_pin(self): + with self.assertRaises(Exception): + self.verify(trusted_issuer="ee"*32) + self.assertTrue(self.verify()["verified"]) + + def test_wrong_peer_pin(self): + with self.assertRaises(Exception): + self.verify(peer_public_key="ee"*32) + self.assertTrue(self.verify()["verified"]) + + def test_modified_request_identity(self): + p=copy.deepcopy(self.packet);p["request_sha256"]="9"*64 + self.assert_refuses_with_twin(p) + + def test_modified_trace_record(self): + p=copy.deepcopy(self.packet) + p["trace_bundle"]["record"]["model"]["model_id"]="injected" + self.assert_refuses_with_twin(p) + + def test_modified_trace_signature(self): + p=copy.deepcopy(self.packet) + p["trace_bundle"]["signature"]="00"*64 + self.assert_refuses_with_twin(p) + + def test_missing_field(self): + p=copy.deepcopy(self.packet);del p["trace_bundle"] + self.assert_refuses_with_twin(p) diff --git a/integrations/agentrust-confidential-workflow-harness/verify_remote_peer_packet.py b/integrations/agentrust-confidential-workflow-harness/verify_remote_peer_packet.py new file mode 100644 index 0000000..d5fab08 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/verify_remote_peer_packet.py @@ -0,0 +1,24 @@ +"""Verify the externally replayable TRACE software packet with independent pins.""" +import argparse +import json +from pathlib import Path +from harness.replay_export import verify_packet + + +def main(): + p=argparse.ArgumentParser() + p.add_argument("--packet",type=Path,required=True) + p.add_argument("--trace-public",required=True) + p.add_argument("--issuer-public",required=True) + p.add_argument("--peer-public",required=True) + a=p.parse_args() + packet=json.loads(a.packet.read_text()) + result=verify_packet( + packet, trace_public_key=a.trace_public, + trusted_issuer=a.issuer_public, + peer_public_key=a.peer_public) + print(json.dumps(result,sort_keys=True)) + + +if __name__=="__main__": + main() From e3e3eca5a4f8b6f5350b770fd30e13ea6ae182e9 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:44:26 -0700 Subject: [PATCH 11/17] test: clear lint violations in replay mutations --- .../tests/test_replay_export.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py index a28e3dd..9a5ef98 100644 --- a/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_export.py @@ -67,7 +67,8 @@ def test_wrong_peer_pin(self): self.assertTrue(self.verify()["verified"]) def test_modified_request_identity(self): - p=copy.deepcopy(self.packet);p["request_sha256"]="9"*64 + p=copy.deepcopy(self.packet) + p["request_sha256"]="9"*64 self.assert_refuses_with_twin(p) def test_modified_trace_record(self): @@ -81,5 +82,6 @@ def test_modified_trace_signature(self): self.assert_refuses_with_twin(p) def test_missing_field(self): - p=copy.deepcopy(self.packet);del p["trace_bundle"] + p=copy.deepcopy(self.packet) + del p["trace_bundle"] self.assert_refuses_with_twin(p) From 21ec9908795e5a4b946ef9192c9fe253d0be9b03 Mon Sep 17 00:00:00 2001 From: Val Rukhaylo Date: Fri, 16 Oct 2026 08:52:48 -0700 Subject: [PATCH 12/17] test: enforce expiring cA2A credentials and verify revocation refusals --- .../EDRG-FEDERATION-SOFTWARE-2026-10-16.md | 8 +++---- .../REMOTE-CA2A-PEER.md | 11 ++++++++++ .../harness/released_delegation.py | 7 +++++-- .../harness/remote_peer_client.py | 7 +++++-- .../run_remote_ca2a_matrix.py | 6 +++++- .../tests/test_released_delegation.py | 21 ++++++++++++++++++- .../tests/test_remote_ca2a.py | 5 +++++ 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md index 7aa641f..218d825 100644 --- a/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md +++ b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md @@ -37,13 +37,13 @@ and TRACE record hash. ## Adversarial evidence -The live two-host matrix passed **10/10** scenarios on October 16, including -valid control, local cMCP denial, remote untrusted issuer, missing TRACE trust, +The live two-host matrix passed **11/11** scenarios on October 16, including +valid control, local cMCP denial, remote untrusted issuer, expired signed delegation, missing TRACE trust, wrong TRACE public key, mutated credential scope, response MAC alteration, post-execution receipt loss reported as unknown, a fresh valid replay twin, and a consumed challenge replay refused by the peer. -The repository unit/mutation suite passed **78/78** on Prometheus, +The repository unit/mutation suite passed **81/81** on Prometheus, including signature/refusal controls, offline software TRACE replay and historical evidence tampering. Source and dependency versions are pinned in the PR and hash-locked requirements, and the captured packet includes only @@ -59,7 +59,7 @@ a public, non-confidential `inspect` execution. | Persistent replay refusal | PASS | SQLite consumed challenge, not hardware anti-replay | | cA2A authenticated response | PASS | Live source, ephemeral session MAC | | TRACE signature and companion result binding | PASS | Level 0 signed software evidence | -| Positive / refusal adversarial controls | PASS | Local suite and 10-case live matrix | +| Positive / refusal adversarial controls | PASS | Local suite and 11-case live matrix | | Offline independently runnable signature check | PASS | Public packet; key provenance remains self-asserted | | External independently operated peer | OPEN | No second organizational trust domain | | Confidential model/tool execution and disclosure | OPEN | Only a read-only `inspect` job was exercised | diff --git a/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md index cfe10b1..1c237ec 100644 --- a/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md +++ b/integrations/agentrust-confidential-workflow-harness/REMOTE-CA2A-PEER.md @@ -131,3 +131,14 @@ private keys, live SSH credentials, session challenge and raw authorization payloads; the reviewer must enroll the public key through a distinct trust channel. The exported packet is useful for *historical software signature verification*, not live session-MAC replay or hardware assurance. + +## Expiry and revocation enforcement boundary + +The origin now issues transaction-specific signed cA2A credentials with a +**60-second default expiry** and a five-second clock-skew allowance. An +expired credential was refused by the remote VPS in the 11-case live matrix. +The released SDK unit/mutation suite also proves signed revocation and stale +revocation-snapshot refusal with valid twins. **No live independently supplied +revocation snapshot has been enrolled on the VPS**, because a fresh-looking +operator-authored snapshot must not be treated as independent revocation +authority. That remains a separate release gate. diff --git a/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py b/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py index fdde8fd..c20ea70 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/released_delegation.py @@ -25,7 +25,8 @@ def appraise(*, credential: DelegationCredential, holder_private, capability: str = "conduit.inspect", local_capabilities=frozenset({"conduit.inspect"}), challenge_secret=None, - holder_proof=None) -> DelegationDecision: + holder_proof=None, revocations=None, + max_revocation_staleness=None) -> DelegationDecision: if not record_id or not trusted_issuer: return DelegationDecision(False, "missing-context", record_id) # The challenge and audience are bound to a single local verifier occurrence. @@ -50,7 +51,9 @@ def appraise(*, credential: DelegationCredential, holder_private, request, policy=LocalPolicy(frozenset(local_capabilities)), trusted_root_issuers={trusted_issuer}, require_holder_proof=True, - challenge_secret=challenge_secret, audience=audience) + challenge_secret=challenge_secret, audience=audience, + revocations=revocations, + max_revocation_staleness=max_revocation_staleness) return DelegationDecision( result.granted_capability == capability, "released-ca2a-holder-and-delegation-verified", record_id) diff --git a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py index ead437d..46e9b6a 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/remote_peer_client.py @@ -6,6 +6,7 @@ """ import hashlib import json +import time import subprocess from dataclasses import dataclass @@ -63,7 +64,7 @@ def send(request): def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, holder_private, peer_public_key: str, send, permitted: bool = True, trace_public_key: str | None = None, - evidence_sink=None) -> LivePeerResult: + evidence_sink=None, credential_not_after_offset: int = 60) -> LivePeerResult: if not transaction_id or not nonce or not peer_public_key: return LivePeerResult("invalid-context", transaction_id) if not trace_public_key: @@ -90,9 +91,11 @@ def run(*, transaction_id: str, nonce: str, repo: str, issuer_private, "capability": CAPABILITY}, timeout=20) issuer_pub = issuer_private.public_key().public_bytes_raw().hex() holder_pub = holder_private.public_key().public_bytes_raw().hex() + now=int(time.time()) credential = DelegationCredential( credential_id="transaction:" + transaction_id, issuer=issuer_pub, - subject=holder_pub, scope=frozenset({CAPABILITY}), depth=0).sign(issuer_private) + subject=holder_pub, scope=frozenset({CAPABILITY}), depth=0, + not_before=now-5, not_after=now+credential_not_after_offset).sign(issuer_private) envelope = decode(pending.request_bytes) record_id = hashlib.sha256(encode(envelope)).hexdigest() proof = build_holder_proof(holder_private, credential, diff --git a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py index afafc4c..44721b0 100644 --- a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py +++ b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py @@ -30,13 +30,15 @@ def main(): results=[] def scenario(name, expected, *, send=None, issuer_private=None, - permitted=True, trace_public_key="default"): + permitted=True, trace_public_key="default", + credential_not_after_offset=60): key = cfg["trace_public_key"] if trace_public_key=="default" else trace_public_key result=run(transaction_id="matrix-"+uuid.uuid4().hex, nonce=uuid.uuid4().hex, repo=cfg["repo"],issuer_private=issuer_private or issuer, holder_private=holder,peer_public_key=cfg["peer_public_key"], trace_public_key=key,permitted=permitted, + credential_not_after_offset=credential_not_after_offset, send=send or base_send) observed=result.outcome passed=(observed==expected) @@ -60,6 +62,8 @@ def scenario(name, expected, *, send=None, issuer_private=None, scenario("local-cmcp-authorization-refusal","refused-cmcp",permitted=False) attacker,_=new_keypair() scenario("untrusted-issuer","refused-authenticated-peer",issuer_private=attacker) + scenario("expired-signed-delegation","refused-authenticated-peer", + credential_not_after_offset=-2) scenario("missing-trace-trust-root","missing-trace-trust-anchor",trace_public_key=None) other,_=new_keypair() scenario("wrong-trace-verifier-root","untrusted-trace-evidence", diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py b/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py index 4bd1a20..cf0a558 100644 --- a/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_released_delegation.py @@ -1,5 +1,7 @@ import unittest -from ca2a_runtime.delegation import new_keypair +import time +from ca2a_runtime.delegation import (new_keypair, credential_digest, + RevocationSnapshot, RevocationStatement) from ca2a_runtime.delegation.credential import DelegationCredential from harness.released_delegation import appraise @@ -44,3 +46,20 @@ def test_unsigned_credential(self): self.credential.subject,frozenset({"conduit.inspect"}),0) self.assertFalse(self.check(credential=c).granted) self.assertTrue(self.check().granted) + + def test_signed_revocation_refuses_valid_delegation(self): + now=int(time.time()) + statement=RevocationStatement( + credential_digest(self.credential),self.root_pub,now).sign(self.root) + snapshot=RevocationSnapshot(now,(statement,)) + self.assertFalse(self.check(revocations=snapshot, + max_revocation_staleness=300).granted) + self.assertTrue(self.check(revocations=RevocationSnapshot(now), + max_revocation_staleness=300).granted) + + def test_stale_revocation_snapshot_fails_closed(self): + now=int(time.time()) + self.assertFalse(self.check(revocations=RevocationSnapshot(now-1000), + max_revocation_staleness=300).granted) + self.assertTrue(self.check(revocations=RevocationSnapshot(now), + max_revocation_staleness=300).granted) diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py index 421a5b0..7606de9 100644 --- a/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_remote_ca2a.py @@ -133,3 +133,8 @@ def test_missing_trusted_trace_key_never_dispatches(self): self.assertEqual(self.invoke(trace_public_key=None).outcome, "missing-trace-trust-anchor") self.assertEqual(self.dispatch_count,0) + + def test_expired_signed_credential_remote_refuses(self): + result=self.invoke(credential_not_after_offset=-2) + self.assertEqual(result.outcome,"refused-authenticated-peer") + self.assertEqual(self.dispatch_count,0) From d52ec867e461b790c467d7efdaa4c6b184a2d7d7 Mon Sep 17 00:00:00 2001 From: "Altru.dev" Date: Fri, 16 Oct 2026 09:06:13 -0700 Subject: [PATCH 13/17] fix(trace): correctly qualify replay authentication and unsigned peer key --- .../EDRG-FEDERATION-SOFTWARE-2026-10-16.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md index 218d825..710f826 100644 --- a/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md +++ b/integrations/agentrust-confidential-workflow-harness/EDRG-FEDERATION-SOFTWARE-2026-10-16.md @@ -86,3 +86,13 @@ For implementation About, licensing boundaries, protocol/version pinning, dependency attribution and glossary mapping, see [REMOTE-CA2A-PEER.md](REMOTE-CA2A-PEER.md), the reproducible packet in [evidence/](evidence/), and the original [README](README.md). + +## Replay-observation qualification + +The exact old-request replay row observes a returned status 403 only. Its +original client occurrence has already been consumed, so the matrix does not +verify a second response MAC for it. The row reports +`response_authentication: not-verified`; it is not an authenticated cA2A +refusal claim. A fresh client occurrence reusing the consumed challenge can +provide a separately authenticated refusal control. The historical packet's +peer channel key is also unsigned metadata, as reported by the offline verifier. From 79ad16f055217ab27079c929f66ecc1f771e3374 Mon Sep 17 00:00:00 2001 From: "Altru.dev" Date: Fri, 16 Oct 2026 09:06:17 -0700 Subject: [PATCH 14/17] fix(trace): correctly qualify replay authentication and unsigned peer key --- .../evidence/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/integrations/agentrust-confidential-workflow-harness/evidence/README.md b/integrations/agentrust-confidential-workflow-harness/evidence/README.md index 2fd9882..e2cbf03 100644 --- a/integrations/agentrust-confidential-workflow-harness/evidence/README.md +++ b/integrations/agentrust-confidential-workflow-harness/evidence/README.md @@ -42,3 +42,11 @@ external observer independently measured the job or confidential execution. The underlying Conduit receipt log remains under the same operator. See [About, licensing, glossary and assurance boundaries](../REMOTE-CA2A-PEER.md). + +The packet's `peer_public_key` is unsigned export metadata. Comparing it to +`--peer-public` checks metadata consistency; the TRACE/companion signatures +do not cryptographically bind that channel key. The verifier reports +`peer_channel_binding: metadata-only-not-signed`. Relabelling that metadata +and supplying a different expected peer pin does not invalidate the software +signatures. Authenticate the live cA2A channel separately; offline signature +verification cannot establish which channel carried the recorded exchange. From 126e336c4f6dcb3cbc3cd225f90bde1309b4ecd6 Mon Sep 17 00:00:00 2001 From: "Altru.dev" Date: Fri, 16 Oct 2026 09:06:19 -0700 Subject: [PATCH 15/17] fix(trace): correctly qualify replay authentication and unsigned peer key --- .../harness/replay_export.py | 1 + 1 file changed, 1 insertion(+) diff --git a/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py b/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py index 772d9f6..454ca63 100644 --- a/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py +++ b/integrations/agentrust-confidential-workflow-harness/harness/replay_export.py @@ -66,5 +66,6 @@ def verify_packet(packet: dict, *, trace_public_key: str, "independent_custody": False, "hardware_attested": False, "origin_response_authentication": "not-offline-replayable", + "peer_channel_binding": "metadata-only-not-signed", "freshness": "historical-signature-only-not-current", } From 1bfdc9dc56ec9c266a231a51cdd9fdc28c65f8ff Mon Sep 17 00:00:00 2001 From: "Altru.dev" Date: Fri, 16 Oct 2026 09:06:22 -0700 Subject: [PATCH 16/17] fix(trace): correctly qualify replay authentication and unsigned peer key --- .../run_remote_ca2a_matrix.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py index 44721b0..685ac54 100644 --- a/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py +++ b/integrations/agentrust-confidential-workflow-harness/run_remote_ca2a_matrix.py @@ -18,6 +18,20 @@ def private(path): bytes.fromhex(Path(path).read_text().strip())) + +def exact_replay_observation(response): + """Classify a transport status; consumed client state cannot verify its MAC.""" + refused = isinstance(response, dict) and response.get("status") == 403 + return { + "case": "one-time-challenge-replay", + "expected": "observed-status-403", + "observed": "observed-status-403" if refused else "unexpected", + "passed": refused, + "response_authentication": "not-verified", + "evidence_class": "live-software-two-host-same-operator", + } + + def main(): parser=argparse.ArgumentParser() parser.add_argument("--config",type=Path,required=True) @@ -99,10 +113,7 @@ def record_submit(request): return base_send(request) scenario("replay-valid-twin",valid,send=record_submit) repeated=base_send(last[0]) - replay_ok=repeated.get("status")==403 - rr={"case":"one-time-challenge-replay","expected":"authenticated-peer-denial", - "observed":"authenticated-peer-denial" if replay_ok else "unexpected", - "passed":replay_ok,"evidence_class":"live-software-two-host-same-operator"} + rr=exact_replay_observation(repeated) results.append(rr) print(json.dumps(rr,sort_keys=True),flush=True) From 4acb15da36747c12dfe6285e10c53c115f1e7ab9 Mon Sep 17 00:00:00 2001 From: "Altru.dev" Date: Fri, 16 Oct 2026 09:06:24 -0700 Subject: [PATCH 17/17] test(trace): preserve independent replay evidence scope --- .../tests/test_replay_reporting.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 integrations/agentrust-confidential-workflow-harness/tests/test_replay_reporting.py diff --git a/integrations/agentrust-confidential-workflow-harness/tests/test_replay_reporting.py b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_reporting.py new file mode 100644 index 0000000..21f03d4 --- /dev/null +++ b/integrations/agentrust-confidential-workflow-harness/tests/test_replay_reporting.py @@ -0,0 +1,32 @@ +"""Keep status observations and signed channel attribution claims explicit.""" +import copy +import unittest + +import test_replay_export as replay_fixture +from run_remote_ca2a_matrix import exact_replay_observation + + +class ReplayReportingTests(unittest.TestCase): + def test_forged_mac_cannot_be_labelled_authenticated(self): + observation = exact_replay_observation({ + "status": 403, "envelope": {"mac": "forged"}}) + self.assertTrue(observation["passed"]) + self.assertEqual(observation["observed"], "observed-status-403") + self.assertEqual(observation["response_authentication"], "not-verified") + + def test_absent_or_other_status_is_not_a_denial_observation(self): + for response in (None, {}, {"status": 200}, {"status": "403"}): + with self.subTest(response=response): + self.assertFalse(exact_replay_observation(response)["passed"]) + + def test_relabelled_unsigned_peer_is_reported_as_metadata(self): + fixture = replay_fixture.ReplayExportTests() + fixture.setUp() + packet = copy.deepcopy(fixture.packet) + packet["peer_public_key"] = "ee" * 32 + result = fixture.verify(packet, peer_public_key="ee" * 32) + self.assertTrue(result["verified"]) + self.assertEqual(result["peer_channel_binding"], "metadata-only-not-signed") + with self.assertRaises(ValueError): + fixture.verify(packet) # Original pin still rejects inconsistent metadata. + self.assertTrue(fixture.verify()["verified"])