diff --git a/.github/workflows/aeoess-aps-conformance.yml b/.github/workflows/aeoess-aps-conformance.yml index 5f50543..f88ca5f 100644 --- a/.github/workflows/aeoess-aps-conformance.yml +++ b/.github/workflows/aeoess-aps-conformance.yml @@ -1,4 +1,12 @@ -# Agent Passport System TRACE exporter conformance workflow. +# Agent Passport System TRACE exporter workflow. +# +# Two jobs answering two questions (#169, #170): +# floating installs the latest released agentrust-io packages, unpinned on +# purpose, as drift detection ("harness gets pinned, subject does not"). +# fixed installs exactly the versions named in integration.yaml +# tested_against, so that claim cannot move underneath itself. +# Neither job asserts a TRACE conformance level. This integration is an +# external-evidence-source; trace-tests --level 0 runs as a coverage report. # Lives at repo root because GitHub Actions only discovers workflows there. # Scoped to this integration via the paths filter. name: aeoess-aps conformance @@ -19,7 +27,8 @@ permissions: contents: read jobs: - conformance: + floating: + name: floating (latest releases, drift detection) strategy: fail-fast: false matrix: @@ -40,11 +49,56 @@ jobs: run: pytest integrations/aeoess-aps/tests -q - name: Emit a sample TRACE record run: python integrations/aeoess-aps/examples/emit_record.py --out trust-record.jwt - - name: TRACE conformance level 0 + - name: trace-tests level 0 (coverage report, not a conformance claim) run: trace-tests verify --record trust-record.jwt --level 0 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: conformance-${{ matrix.os }}-py${{ matrix.python }} + name: floating-${{ matrix.os }}-py${{ matrix.python }} + path: | + trust-record.jwt + trust-record.jwt.signed.json + + fixed: + name: fixed (tested_against versions) + strategy: + fail-fast: false + matrix: + python: ["3.11", "3.12", "3.13", "3.14"] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python }} + - name: Install the package set that reproduces the tested_against result + # integration.yaml records agentrust-trace and agentrust-trace-tests, the + # keys its schema supports; agent-passport-system is pinned here as part + # of the same reproducible snapshot. + run: | + pip install "agentrust-trace==0.10.0" "agentrust-trace-tests==0.5.1" "agent-passport-system==3.0.1" + - name: Pinned versions match integration.yaml + run: | + python - <<'PY' + import re, sys + from importlib.metadata import version + y = open("integrations/aeoess-aps/integration.yaml").read() + want = dict(re.findall(r'^ (agentrust-trace|agentrust-trace-tests): "([^"]+)"', y, re.M)) + have = {k: version(k) for k in want} + print(want, have) + sys.exit(0 if want == have else 1) + PY + - name: Install this integration + run: pip install -e "integrations/aeoess-aps[test]" + - name: Integration tests + run: pytest integrations/aeoess-aps/tests -q + - name: Emit a sample record + run: python integrations/aeoess-aps/examples/emit_record.py --out trust-record.jwt + - name: trace-tests level 0 (coverage report, not a conformance claim) + run: trace-tests verify --record trust-record.jwt --level 0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: fixed-${{ matrix.os }}-py${{ matrix.python }} path: | trust-record.jwt trust-record.jwt.signed.json diff --git a/integrations/aeoess-aps/README.md b/integrations/aeoess-aps/README.md index 0e2dad9..8b23c6f 100644 --- a/integrations/aeoess-aps/README.md +++ b/integrations/aeoess-aps/README.md @@ -64,9 +64,11 @@ One observed divergence, kept as tested against 0.9.0: an empty `RevocationStore accepts and an omitted store skips the check, while APS treats "no artifacts observed" as no evidence rather than as not revoked. TRACE's own section 3.2.3 takes the APS position at the bundle level (a verifier with no bundle reports that it performed no -revocation check). The behaviour observed at `tested_against: 0.9.0` is retained here as a tested +revocation check). The behaviour observed at agentrust-trace 0.9.0 is retained here as a tested implementation result; agentrust-io/trace-spec#246 records it as inconsistent with -section 3.2.3. +section 3.2.3. It cannot be re-observed on this exporter's record at 0.10.0, where +`verify_record` refuses the record on schema grounds before any revocation check +runs, so these rows are 0.9.0 results and are not restated for 0.10.0. The APS artifacts examined come from aeoess/agent-passport-system#123 (revocation verification corpus). Mapping questions and answers: agentrust-io/integrations#140. @@ -138,17 +140,22 @@ convention here. The verdict is still carried, as `policy.enforcement_mode` and See rules 2 and 4 in [CONTRIBUTING.md](../../CONTRIBUTING.md). -- **The record is a partial TRACE record.** `model`, `data_class` and - `build_provenance` are required by the v0.2 JSON Schema and are absent. An APS - policy decision carries no model identity, no data classification and no build - provenance, so any value there would be invented. The record passes - `trace-tests verify --level 0`, which does not grade those fields, and it does - not satisfy the full v0.2 schema. `tests/test_mapping.py` pins the exact set of - absent required fields so the gap cannot widen silently. -- **Level 0 carries an explicit `TR-SIG-005 UNVERIFIED` finding.** The graded - artifact is the unsigned record, so trace-tests reports that it is not - cryptographically verified. The signed form is written next to it as - `.signed.json` and verifies with `agentrust_trace.verify_record`. +- **This integration is an `external-evidence-source`, not a `record-producer`, + and claims no conformance level.** An APS decision is signed external + evidence. The mapper places it into a TRACE record but cannot populate + `model`, `data_class` or `build_provenance`, which the v0.2 JSON Schema + requires, without inventing them. So the emitted record is not a TRACE Trust + Record, and from `agentrust-trace` 0.10.0 `verify_record` refuses it on + schema grounds. `tests/test_mapping.py` pins both the exact set of absent + required fields and that refusal, so neither can change silently. The same + role, for the same reason, is used by `nobulex`. +- **`trace-tests verify --level 0` is a coverage report here, not a claim.** It + reports 8 checks with an explicit `TR-SIG-005 UNVERIFIED` finding because the + graded artifact is the unsigned record. A clean Level 0 run means nothing the + suite could check went wrong. It does not mean the record verifies. +- **The signed form `.signed.json` demonstrates the sign path only.** Its + signature is valid over the emitted claims, and `verify_record` still refuses + it, because signature validity is not schema validity. - **`runtime.platform` is `software-only` and there is no hardware attestation.** `runtime.measurement` is a digest of the signed APS decision, not a TEE measurement. Per the v0.2 schema, `software-only` records must never be treated @@ -157,11 +164,19 @@ See rules 2 and 4 in [CONTRIBUTING.md](../../CONTRIBUTING.md). does not chain to a trusted issuer. - **`transparency` is `urn:aps:transparency:none`.** This integration publishes nothing to a transparency log, so there is no SCITT receipt to resolve. -- **No conformance level above 0 is claimed or configured.** +- **No conformance level is claimed or configured.** -## Conformance CI +## CI `.github/workflows/aeoess-aps-conformance.yml` at the repository root, scoped to -`integrations/aeoess-aps/**`. It installs the released packages, runs the tests, -emits a record and runs `trace-tests verify --level 0` across Python 3.11 to -3.14. +`integrations/aeoess-aps/**`, runs two jobs across Python 3.11 to 3.14: + +- **floating** installs the latest released packages, unpinned on purpose, as + drift detection. This is the "harness gets pinned, subject does not" rule + from #169. +- **fixed** installs exactly the versions named in `integration.yaml` + `tested_against`, checks that the installed versions match that file, and + runs the same steps. This is what backs the `tested_against` claim. + +Both run the tests, emit a record, and run `trace-tests verify --level 0` as a +coverage report. diff --git a/integrations/aeoess-aps/aps_trace.py b/integrations/aeoess-aps/aps_trace.py index 84eda8e..01c7a50 100644 --- a/integrations/aeoess-aps/aps_trace.py +++ b/integrations/aeoess-aps/aps_trace.py @@ -1,10 +1,12 @@ -"""aps_trace: export one signed APS policy decision as a TRACE Trust Record. +"""aps_trace: map one signed APS policy decision, as external evidence, into TRACE record shape. The Agent Passport System (APS) evaluates an ActionIntent against a Values Floor and returns a PolicyDecision: a dict signed by the evaluator, carrying a verdict of ``permit``, ``narrow`` or ``deny``. This module maps exactly one -such decision onto one TRACE Trust Record dict (EAT profile -``tag:agentrust-io.com,2026:trace-v0.2``). +such decision onto one TRACE-shaped mapping output (EAT profile +``tag:agentrust-io.com,2026:trace-v0.2``). The output is not a TRACE Trust +Record: this integration is an ``external-evidence-source`` and claims no +conformance level (agentrust-io/integrations#170). Two different signatures are involved and they are never the same key: @@ -49,8 +51,9 @@ ``model``, ``data_class`` and ``build_provenance`` are required by the TRACE v0.2 JSON Schema and are absent here. An APS policy decision carries no model identity, no data classification and no build provenance, so any value would be -invented. The record is therefore a partial TRACE record: it passes -``trace-tests verify --level 0``, and it does not satisfy the full v0.2 schema. +invented. The mapping output therefore does not satisfy the v0.2 Trust Record +schema and ``agentrust_trace.verify_record`` refuses it. ``trace-tests verify +--level 0`` runs on it as a coverage probe, not as a conformance claim. ``tests/test_mapping.py`` pins the exact set of absent required fields so the gap stays deliberate. See the README section "What it does NOT claim". @@ -111,7 +114,7 @@ def build_trace_record(decision: dict[str, Any], *, trace_jwk: dict[str, str]) -> dict[str, Any]: - """Map one signed APS policy decision onto an unsigned TRACE Trust Record. + """Map one signed APS policy decision onto an unsigned TRACE-shaped mapping output. Args: decision: The dict returned by ``agent_passport.policy.evaluate_intent``, @@ -121,7 +124,8 @@ def build_trace_record(decision: dict[str, Any], *, trace_jwk: dict[str, str]) - the APS evaluator key. Returns: - An unsigned TRACE Trust Record dict. + An unsigned dict in TRACE v0.2 record shape. It omits ``model``, + ``data_class`` and ``build_provenance`` and is not a TRACE Trust Record. Raises: ValueError: if the decision is malformed, its evaluator signature does @@ -175,7 +179,7 @@ def build_trace_record(decision: dict[str, Any], *, trace_jwk: dict[str, str]) - def _validate_decision(decision: dict[str, Any]) -> None: - """Refuse anything that must not become a TRACE Trust Record. + """Refuse anything that must not be mapped into TRACE record shape. Checks shape, then the evaluator signature and expiry through ``verify_policy_decision``, then the verdict. Every rejection raises diff --git a/integrations/aeoess-aps/examples/emit_record.py b/integrations/aeoess-aps/examples/emit_record.py index aa2ef51..c11207e 100644 --- a/integrations/aeoess-aps/examples/emit_record.py +++ b/integrations/aeoess-aps/examples/emit_record.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -"""Emit a TRACE Trust Record from a freshly evaluated APS policy decision. +"""Emit a TRACE-shaped mapping output from a freshly evaluated APS policy decision. Runs the real APS path with ephemeral keys: an agent declares an ActionIntent, an evaluator evaluates it against the Values Floor with FloorValidatorV1, and -the resulting signed PolicyDecision is mapped onto a TRACE Trust Record by +the resulting signed PolicyDecision is mapped into TRACE record shape by :func:`aps_trace.build_trace_record`. No network access and no credentials. The decision is minted at run time rather than loaded from a committed fixture @@ -19,8 +19,8 @@ that function enforces the full v0.2 schema, and this record deliberately omits ``model``, ``data_class`` and ``build_provenance`` (see "Deliberately absent" in - aps_trace.py). ``trace-tests verify --level 0`` is the - gate this integration targets, and it passes. + aps_trace.py). ``trace-tests verify --level 0`` runs on + it as a coverage probe. No conformance level is claimed. Usage: python examples/emit_record.py --out trust-record.jwt @@ -96,10 +96,10 @@ def main() -> int: signed = agentrust_trace.sign_record(dict(record), key) # Deliberately not calling verify_record here. Since agentrust-trace 0.10.0 - # it enforces the full v0.2 schema, and this record is a documented partial: - # asking for a verdict it cannot honestly give would either fail the example - # or push someone to invent a model identity. Level 0 is the claim, and the - # conformance job runs it on the unsigned artifact immediately after this. + # it enforces the full v0.2 schema, and this mapping output is documented as + # schema-incomplete: asking for a verdict it cannot honestly give would either + # fail the example or push someone to invent a model identity. Level 0 is a + # coverage probe, and CI runs it on the unsigned artifact right after this. out = Path(args.out) out.write_text( @@ -116,7 +116,7 @@ def main() -> int: print(f"appraisal.status: {record['appraisal']['status']}") print(f"subject: {record['subject']}") print(f"unsigned (for trace-tests): {out}") - print(f"signed (signature only, partial record): {signed_out}") + print(f"signed (signature only, not a Trust Record): {signed_out}") return 0 diff --git a/integrations/aeoess-aps/integration.yaml b/integrations/aeoess-aps/integration.yaml index 5c650dd..f3b3f22 100644 --- a/integrations/aeoess-aps/integration.yaml +++ b/integrations/aeoess-aps/integration.yaml @@ -15,16 +15,30 @@ marketplace: category: Identity & trust mark: AP keywords: [passport, identity, policy] -# Level 0 passes with an explicit TR-SIG-005 UNVERIFIED finding: the graded -# artifact is the unsigned record, so trace-tests reports it as not -# cryptographically verified. The signed form is written alongside it and -# verifies via agentrust_trace.verify_record (see README). -trace_conformance_level: 0 +# Role. APS decisions are Ed25519-signed external evidence. This integration +# maps one onto a TRACE record but cannot populate `model`, `data_class` or +# `build_provenance` without inventing them, so the output does not validate +# against the v0.2 schema and is not a TRACE Trust Record. Same case as +# nobulex: external-evidence-source, no conformance level (#170). trace_roles: - - record-producer + - external-evidence-source tested_against: - agentrust-trace: "0.9.0" - # trace-spec#236 (merged 2026-08-29, unreleased) changes the RevocationStore path: - # a callable returning a non-bool now fails closed instead of reading as not - # revoked. The revocation observations in README were made against 0.9.0 and - # are re-run on the release that carries #236. + agentrust-trace: "0.10.0" + agentrust-trace-tests: "0.5.1" + # This file records only the package keys its schema supports. The fixed CI + # job pins the complete package set needed to reproduce this result, which + # also includes agent-passport-system 3.0.1. + # Position at these versions: the emitted record carries every field the + # mapper can source from an APS decision and validates field by field against + # the v0.2 JSON Schema, but `model`, `data_class` and `build_provenance` are + # absent, so `agentrust_trace.verify_record` refuses it (pinned by + # tests/test_mapping.py::test_verify_record_refuses_schema_incomplete_mapping). + # `trace-tests verify --level 0` reports 8 checks with TR-SIG-005 UNVERIFIED; + # that is a coverage result, not a conformance claim. + # These versions are the fixed run in .github/workflows/aeoess-aps-conformance.yml. + # The floating run installs unpinned releases as drift detection (#169). + # The revocation observations in README were made against 0.9.0 and stand as + # 0.9.0 results. They cannot be re-run on this exporter's record at 0.10.0: + # verify_record refuses it on schema grounds before any revocation check runs + # (observed with an empty store, a callable returning a non-bool, and a + # callable returning True; all three raise the schema error first). diff --git a/integrations/aeoess-aps/tests/test_mapping.py b/integrations/aeoess-aps/tests/test_mapping.py index 4fbad9a..6282973 100644 --- a/integrations/aeoess-aps/tests/test_mapping.py +++ b/integrations/aeoess-aps/tests/test_mapping.py @@ -1,8 +1,8 @@ """APS to TRACE mapping tests. Covers the mapping itself, the refusals that keep an unverified APS decision -from becoming a TRACE record, and a level 0 conformance run against the emitted -record. Decisions are minted in-process with ephemeral keys, so no network +from being mapped into TRACE record shape, and a level 0 coverage run against +the mapping output. Decisions are minted in-process with ephemeral keys, so no network access, no credentials and no committed fixtures are involved. A committed decision fixture is impossible here on purpose: APS decisions @@ -248,10 +248,10 @@ def test_non_dict_is_refused(): # --- TRACE sign and verify round-trip --------------------------------------- -def test_signed_record_passes_trace_tests_level_0(record): - """Signing works and the signature checks out, on a deliberately partial record. +def test_signed_mapping_has_no_level0_findings(record): + """Signing works and the signature checks out, on a schema-incomplete mapping output. - Level 0 is this integration's gate, as the README says. The unsigned record + Level 0 is a coverage probe here, not a conformance gate. The unsigned output leaves TR-SIG-005 UNVERIFIED; once signed, nothing should be unverified. """ runner = pytest.importorskip("trace_tests.runner") @@ -268,7 +268,7 @@ def test_signed_record_passes_trace_tests_level_0(record): assert [f.code for f in findings if f.status is Status.UNVERIFIED] == [] -def test_full_verify_record_refuses_a_partial_record(record): +def test_verify_record_refuses_schema_incomplete_mapping(record): """The boundary this integration lives on, asserted rather than assumed. agentrust-trace 0.10.0 made ``verify_record`` enforce the full v0.2 schema, @@ -330,8 +330,8 @@ def test_present_fields_all_validate_against_the_v02_schema(record): assert missing == DOCUMENTED_ABSENT_REQUIRED -def test_record_passes_trace_tests_level_0(record): - """Level 0 must pass, with TR-SIG-005 UNVERIFIED on the unsigned record.""" +def test_mapping_level0_coverage_result(record): + """Level 0 coverage result: 8 checks, TR-SIG-005 UNVERIFIED on the unsigned output.""" runner = pytest.importorskip("trace_tests.runner") from trace_tests.result import Status @@ -341,3 +341,16 @@ def test_record_passes_trace_tests_level_0(record): assert [f.code for f in findings if f.status is Status.FAIL] == [] unverified = [f for f in findings if f.status is Status.UNVERIFIED] assert [f.code for f in unverified] == ["TR-SIG-005"] + + +def test_integration_metadata_claims_no_level(): + """#170: external-evidence-source, and no conformance level anywhere in the manifest.""" + import pathlib + import re + text = pathlib.Path(__file__).resolve().parents[1].joinpath("integration.yaml").read_text() + # Parsed with re rather than a YAML library so the test extras stay as they are. + body = "\n".join(line for line in text.splitlines() if not line.lstrip().startswith("#")) + roles = re.search(r"^trace_roles:\n((?: - .*\n?)+)", body, re.M) + assert roles is not None + assert [r.strip()[2:] for r in roles.group(1).strip().splitlines()] == ["external-evidence-source"] + assert re.search(r"^trace_conformance_level:", body, re.M) is None diff --git a/marketplace/catalog.json b/marketplace/catalog.json index 482b5cc..fcfc57e 100644 --- a/marketplace/catalog.json +++ b/marketplace/catalog.json @@ -1,6 +1,6 @@ { "catalog_version": 1, - "count": 35, + "count": 36, "integrations": [ { "name": "Claude Code", @@ -271,6 +271,24 @@ "compliance" ] }, + { + "name": "ComputeID AgentPassport TRACE Adapter", + "package_name": "ComputeID AgentPassport TRACE Adapter", + "vendor": "ComputeID", + "description": "Converts ComputeID AgentPassport /verify evidence into a TRACE-shaped record via the third-party-control-plane origin path; does not currently pass TRACE conformance at any level.", + "path": "integrations/computeid-agentpassport-trace", + "url": "https://github.com/agentrust-io/integrations/tree/main/integrations/computeid-agentpassport-trace", + "homepage": null, + "repository": "https://github.com/trustedaicompute-ops/computeid-trace-adapter", + "tier": "community", + "stack": [ + "TRACE" + ], + "category": "Identity & trust", + "mark": "CID", + "featured": null, + "keywords": [] + }, { "name": "Confidential Containers Trustee", "package_name": "WCM Confidential Containers Trustee",