diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb3a010..e391a76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,111 +1,108 @@ -name: CI - -on: - push: - branches: [main] - pull_request: - branches: [main] - workflow_dispatch: - inputs: - ref: - description: "Branch or SHA to run CI on" - required: false - default: "" - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: ["3.11", "3.12", "3.13"] - os: [ubuntu-latest, windows-latest] - - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" - - - name: Security scan - run: pip install bandit pip-audit && bandit -r src/ -c pyproject.toml && pip-audit - - - name: Lint - run: ruff check src/ tests/ - - - name: Type check - run: mypy src/ca2a_runtime/ src/ca2a_verify/ - - - name: Test - run: pytest tests/unit/ tests/conformance/ -v --tb=short --cov=src --cov-report=xml - - - name: Upload coverage report - uses: codecov/codecov-action@v7 - with: - fail_ci_if_error: false - - governance: - runs-on: ubuntu-latest - needs: test - - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install dependencies - run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" "agent-governance-toolkit>=4.1" - - - name: Generate evidence file - run: python scripts/gen_agt_evidence.py - - # Advisory until Tier 2 (runtime peer-delegation enforcement) lands. cA2A - # does not yet run the ASI coverage modules, so strict verify reports - # INCOMPLETE. This flips to blocking (drop continue-on-error) once the - # runtime enforces attenuated delegation on inbound peer calls. See ROADMAP. - - name: AGT governance verify (advisory) - continue-on-error: true - run: agt verify --evidence agt-evidence.json - - - name: Save attestation JSON - continue-on-error: true - run: agt --json verify --evidence agt-evidence.json > agt-attestation.json - - - name: Upload governance artifacts - uses: actions/upload-artifact@v7 - with: - name: agt-governance-${{ github.sha }} - path: | - agt-evidence.json - agt-attestation.json - if-no-files-found: warn - - benchmark: - runs-on: ubuntu-latest - needs: test - if: github.ref == 'refs/heads/main' - - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install dependencies - run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" - - - name: Run benchmark (software-only, CI gate p99 < 5ms) - run: python -m ca2a_runtime.benchmarks --provider software-only --hops 10000 --out benchmarks/ - - - name: Upload benchmark results - uses: actions/upload-artifact@v7 - with: - name: benchmark-results - path: benchmarks/ - if-no-files-found: warn +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + inputs: + ref: + description: "Branch or SHA to run CI on" + required: false + default: "" + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.11", "3.12", "3.13"] + os: [ubuntu-latest, windows-latest] + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" + + - name: Security scan + run: pip install bandit pip-audit && bandit -r src/ -c pyproject.toml && pip-audit + + - name: Lint + run: ruff check src/ tests/ + + - name: Type check + run: mypy src/ca2a_runtime/ src/ca2a_verify/ + + - name: Test + run: pytest tests/unit/ tests/conformance/ -v --tb=short --cov=src --cov-report=xml + + - name: Upload coverage report + uses: codecov/codecov-action@v7 + with: + fail_ci_if_error: false + + governance: + runs-on: ubuntu-latest + needs: test + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install dependencies + run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" "agent-governance-toolkit[full]>=4.1" + + - name: Generate evidence file + run: python scripts/gen_agt_evidence.py + + # Blocking gate: the [full] toolkit provides the ASI coverage modules + # (10/10) and the evidence declares cA2A's governed capabilities, so strict + # verify is COMPLETE. A regression that drops coverage or governance fails CI. + - name: AGT governance verify (strict) + run: agt verify --evidence agt-evidence.json + + - name: Save attestation JSON + run: agt --json verify --evidence agt-evidence.json > agt-attestation.json + + - name: Upload governance artifacts + uses: actions/upload-artifact@v7 + with: + name: agt-governance-${{ github.sha }} + path: | + agt-evidence.json + agt-attestation.json + if-no-files-found: warn + + benchmark: + runs-on: ubuntu-latest + needs: test + if: github.ref == 'refs/heads/main' + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install dependencies + run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" + + - name: Run benchmark (software-only, CI gate p99 < 5ms) + run: python -m ca2a_runtime.benchmarks --provider software-only --hops 10000 --out benchmarks/ + + - name: Upload benchmark results + uses: actions/upload-artifact@v7 + with: + name: benchmark-results + path: benchmarks/ + if-no-files-found: warn diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c76e067..c118e4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,7 +57,7 @@ jobs: python-version: "3.12" - name: Install package and AGT - run: python -m pip install --upgrade pip && pip install -e "." agent-governance-toolkit-core + run: python -m pip install --upgrade pip && pip install -e "." "agent-governance-toolkit[full]>=4.1" - name: Generate evidence file run: python scripts/gen_agt_evidence.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d0206a4..ed70e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - cA2A-compatible conformance suite: `tests/conformance/` with a normative README (stable MUST/SHOULD test IDs across delegation, scope-policy, attestation, sealed channel, provenance, and the inbound pipeline) and runnable checks that exercise every MUST-level requirement. Wired into CI and documented at `docs/spec/conformance.md`; ties to the CHARTER trademark language. - TPM 2.0 attestation backend: `ca2a_runtime.tee.tpm` (TPMS_ATTEST parsing, `TpmProvider`) and `ca2a_verify.tpm.verify_tpm_quote` (AK chain to a caller-supplied vendor root, AK signature over the attest blob (ECDSA or RSA), magic/type checks, and qualifying-data/PCR-digest binding), all fail-closed. Synthetic-vector validated; TPM AK roots are per-vendor so the caller supplies its trusted roots. Quote generation requires a real TPM. - Intel TDX attestation backend: `ca2a_runtime.tee.tdx` (DCAP Quote v4 parsing, `TdxProvider`) and `ca2a_verify.tdx.verify_tdx_quote` (PCK chain to a trusted Intel root, QE report signature, attestation-key binding, quote signature, and MRTD/report-data binding), all fail-closed. Chain path validated against the genuine Intel SGX Root CA; multi-level signature path validated with a synthetic self-consistent quote. Quote generation requires a real TDX guest. +- AGT governance gate is now **blocking** (was advisory): CI installs `agent-governance-toolkit[full]`, so the OWASP ASI 2026 coverage modules load and `agt verify` reports 10/10 coverage with 6/6 runtime checks (COMPLETE). The enforcement descriptor now declares cA2A's `governed_capabilities`, reported as the registered-tools inventory. A governance or coverage regression now fails CI. - Real Cedar policy engine binding: `ca2a_runtime.cedar.CedarPolicy` (backed by `cedarpy`, the engine cMCP runs) evaluates each capability as a Cedar authorization request. A new `ca2a_runtime.policy.Policy` protocol makes `LocalPolicy` (allow set) and `CedarPolicy` interchangeable in the peer path. Adds the `cedarpy` dependency. - Transport-agnostic inbound peer request handler: `ca2a_runtime.peer.handle_peer_request` with `PeerRequest` / `PeerResult`. Composes the full pipeline (verify chain, intersect scope and enforce, open a sealed payload with the enclave key, emit a linked provenance record) fail-closed. A transport parses its wire format into a `PeerRequest`; cA2A does not define the transport (profile, not protocol). - RFC 8785 (JSON Canonicalization Scheme) canonicalization: `ca2a_runtime.canonical.canonicalize`. Credential and provenance bodies are now signed over the JCS encoding (UTF-16 key ordering, JCS string escaping, literal non-ASCII, shortest-decimal integers), so cA2A signatures are cross-verifiable with agent-manifest. ASCII credentials are byte-identical to the previous encoding, so existing signatures still verify. diff --git a/governance/ca2a-enforcement.yaml b/governance/ca2a-enforcement.yaml index 2b52c46..0cf6b05 100644 --- a/governance/ca2a-enforcement.yaml +++ b/governance/ca2a-enforcement.yaml @@ -20,6 +20,16 @@ hardware_roots: - intel-tdx - tpm2 +# The capability actions this deployment governs. A delegated scope is honored +# only when its capabilities appear here AND the local Cedar policy permits them +# (effective = delegated scope INTERSECT local policy). This is the callee's +# declared, version-controlled inventory of governable actions. +governed_capabilities: + - delegate.invoke + - delegate.read + - delegate.write + - delegate.admin + owasp_coverage: ASI-03: scope attenuation, a child grant cannot exceed its parent's authority ASI-06: linked TRACE records per hop, tamper-evident delegation DAG diff --git a/scripts/gen_agt_evidence.py b/scripts/gen_agt_evidence.py index b66c1e4..ea3f5db 100644 --- a/scripts/gen_agt_evidence.py +++ b/scripts/gen_agt_evidence.py @@ -17,9 +17,26 @@ from datetime import datetime, timezone from pathlib import Path +import yaml + REPO_ROOT = Path(__file__).parent.parent +def _governed_capabilities() -> list[str]: + """The governed-capability inventory the enforcement descriptor declares. + + These are cA2A's registered actions: a delegated scope is honored only when + its capabilities appear here and the local Cedar policy permits them. + """ + path = REPO_ROOT / "governance" / "ca2a-enforcement.yaml" + try: + doc = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except (FileNotFoundError, yaml.YAMLError): + return [] + caps = doc.get("governed_capabilities", []) + return [str(c) for c in caps] if isinstance(caps, list) else [] + + def _pkg_version(package: str) -> str: try: return importlib.metadata.version(package) @@ -44,7 +61,7 @@ def generate_evidence() -> dict: "policy_files_loaded": [ "governance/ca2a-enforcement.yaml", ], - "registered_tools": [], + "registered_tools": _governed_capabilities(), "audit_sink": { "enabled": True, "target": "src/ca2a_runtime/delegation/credential.py",