Skip to content

integrations/aeoess-aps: add APS PolicyDecision to TRACE Level 0 mapping - #86

Merged
imran-siddique merged 2 commits into
agentrust-io:mainfrom
aeoess:aps-trace-exporter
Aug 4, 2026
Merged

integrations/aeoess-aps: add APS PolicyDecision to TRACE Level 0 mapping#86
imran-siddique merged 2 commits into
agentrust-io:mainfrom
aeoess:aps-trace-exporter

Conversation

@aeoess

@aeoess aeoess commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Relates to #46.

What this adds

A Community-tier integration mapping one signed APS policy decision onto a TRACE v0.2 Trust Record.

The scope is deliberately narrow:

  • one APS policy decision in, one TRACE Trust Record out
  • TRACE conformance Level 0 only
  • no ActionReceipt mapping
  • no revocation mapping
  • no identity or delegation binding
  • no outcome or attribution mapping

The adapter verifies the APS decision signature and expiry before mapping, and refuses a decision that fails either. The example writes an unsigned record for the Level 0 runner and a signed sidecar verified with agentrust_trace.verify_record.

Versions

Built from upstream 4bcb30bbaec8f88fc232bc9e710e5edae453b25f. Tested with agent-passport-system==2.10.0, agentrust-trace==0.5.1, agentrust-trace-tests==0.4.1, profile tag:agentrust-io.com,2026:trace-v0.2.

Conformance result

TRACE Conformance Report -- Level 0
Format : trace
Record : trust-record.jwt

  TR-ENV  PASS        eat_profile sentinel matches
  TR-ENV  PASS        iat is valid and fresh (1785785207)
  TR-ENV  PASS        subject is a valid workload identity URI ('spiffe://agent-passport.org/evaluator/eval_example/decision/pdec_c0ae78e7a19c')
  TR-ENV  PASS        cnf.jwk.kty present ('OKP')
  TR-SIG  PASS        cnf.jwk key type is supported (kty='OKP', crv='Ed25519')
  TR-SIG  UNVERIFIED  TR-SIG-005: no signature present; this record is NOT cryptographically verified
  TR-POL  PASS        policy.bundle_hash has valid digest format
  TR-POL  PASS        policy.enforcement_mode is valid ('enforce')

Result: PASS  (8 checks, 0 skipped, 1 UNVERIFIED -- record is NOT cryptographically verified)

Integration tests: 26 passed.

Reproduction

python -m pip install --upgrade pip
pip install agentrust-trace agentrust-trace-tests agent-passport-system
pip install -e "integrations/aeoess-aps[test]"

pytest integrations/aeoess-aps/tests -q

python integrations/aeoess-aps/examples/emit_record.py --out trust-record.jwt

trace-tests verify --record trust-record.jwt --level 0

The example also writes trust-record.jwt.signed.json, which verifies through agentrust_trace.verify_record.

Build findings

Two released-package details differed from what was recorded in #46.

agentrust-trace==0.5.1 does not expose a revocation= argument on verify_record. The signature is (record, public_key_or_jwk=None, *, allow_embedded_key=False, max_age_seconds=86400, expected_nonce=None). Revocation is out of scope here regardless, but the follow-up will need a different hook.

agentrust-trace-tests==0.4.1 accepts a plain record carrying a top-level signature and verifies it. loader.py allows a bare signature key explicitly, and running the signed sidecar through Level 0 returns a verified TR-SIG result rather than UNVERIFIED. This PR still grades the unsigned record and keeps the signed form beside it, matching the current merged integration pattern in ramen-ai-cmcp and spendguard. The signed run was a diagnostic only and changed nothing in the submission. Happy to switch which artifact is graded if you would rather the catalogue showed the stronger result.

Deliberately absent fields

model, data_class and build_provenance are required by the v0.2 schema and are absent. An APS policy decision carries no model identity, no data classification and no build provenance, and inventing values would be worse than the gap. tests/test_mapping.py pins the exact absent set so it cannot widen silently.

What this does not claim

  • no conformance above Level 0
  • not a schema-complete mapping of every TRACE field
  • runtime.platform is software-only, which is not hardware or runtime attestation
  • the ephemeral TRACE signing key does not chain to a trusted issuer
  • no transparency-log inclusion
  • no revocation enforcement
  • no identity or delegation interoperability

Those need separate mappings or upstream work.

Maps exactly one signed APS policy decision, the dict returned by
agent_passport.policy.evaluate_intent, onto exactly one TRACE Trust Record at
EAT profile tag:agentrust-io.com,2026:trace-v0.2.

The mapper verifies the APS evaluator signature and expiry through
verify_policy_decision before it maps anything, and raises rather than emit a
record for a decision that fails either check. The APS evaluator signature and
the TRACE record signature are separate keys and the module never conflates
them.

Conformance level 0. The graded artifact is the unsigned record, so TR-SIG-005
reports UNVERIFIED; the signed form is written alongside it and verifies with
agentrust_trace.verify_record. model, data_class and build_provenance are
absent because an APS policy decision carries none of them, and a test pins
that set so it cannot widen silently.
@aeoess
aeoess requested a review from imran-siddique as a code owner August 3, 2026 19:38
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🔴 Contributor Check: HIGH

Check Result
Profile HIGH
Credential LOW
Overall HIGH

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:HIGH Contributor check flagged HIGH risk label Aug 3, 2026
Comment thread integrations/aeoess-aps/tests/test_mapping.py Fixed
…rings

CodeQL flagged the startswith check as incomplete URL substring
sanitization. The assertion now builds the expected spiffe URI from
TRUST_DOMAIN and the quoted evaluator and decision ids and compares for
equality, which also pins the path structure and the percent-encoding that
the previous three assertions did not.

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the adapter, the tests, and the conformance workflow. This is the shape a Community-tier integration should have.

What makes it acceptable:

  • The APS evaluator signature and expiry are verified before any mapping happens, and a decision that fails either raises instead of producing a record. That is the important property: an unverified decision cannot become a record that looks appraised.
  • The two signatures are kept clearly separate, and the module never applies the TRACE signature itself.
  • The gap is declared rather than papered over. model, data_class and build_provenance are required by the v0.2 schema and absent here because an APS decision carries none of them, and tests/test_mapping.py pins the exact absent set so the gap cannot drift silently. integration.yaml states Level 0 and the TR-SIG-005 UNVERIFIED finding openly.
  • narrow mapping to EAR warning is a defensible reading, since TRACE has no narrow and warning is the status for an appraisal that affirms with reservations.
  • Versions are pinned and the upstream commit is named.

One housekeeping item: the CodeQL alert on this PR (py/incomplete-url-substring-sanitization, alert 85, tests/test_mapping.py:106) is a false positive. Line 106 builds an expected SPIFFE URI for an exact-equality assertion, so there is no substring sanitization to be incomplete. No code change needed. It should be dismissed as a false positive so the repo alert list stays meaningful.

Approving. Leaving the merge to Imran.

@aeoess

aeoess commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review.

Small note on alert 85: it was raised against c3382cd, where the test used a prefix assertion plus two substring checks. I addressed it in f5295149 by constructing the full expected SPIFFE URI and asserting exact equality, which is why the current line looks safe and CodeQL is green.

So the alert was resolved by the follow-up commit rather than needing a false-positive dismissal. Appreciate the approval.

@imran-siddique
imran-siddique merged commit bfd6ab8 into agentrust-io:main Aug 4, 2026
8 of 9 checks passed
aeoess added a commit to agent-passport-system/aps-web that referenced this pull request Aug 6, 2026
… pieces of work that were never entered

Acting on the roadmap restructure report. Part 1, the lane consolidation, already landed at
500e880. This is Parts 2 and 3.

IDENTIFIERS ADDED TO EXISTING CARDS

Five cards described real work without naming the thing a reader could go and check. They now
carry it: the goose rerun as aaif-goose/goose#10433 with its timestamp and the 76-commit,
3-sign-off history; the exporter as agentrust-io/integrations#86 with the boundary merge 83c3cb3
on pinned head d647cfa2; the bernstein merge as PR #2994 at 3f8d75a6 with the key-order finding
filed as #3105 and the exact code-point ranges where the divergence occurs; the suite tag as
v0.1.0 at 4b9dbb0; and the spend-unit guard with its independent verification, 3817 passing.

The prose already there was kept and the identifiers appended, rather than swapping in shorter
rewrites, because the existing descriptions were not wrong, only unverifiable.

SIX CARDS FOR WORK THAT HAPPENED AND WAS NEVER RECORDED

Day 144: the sprint reaching main as PR #65 at 1edad8c with the post-merge suite state; the Go
implementation reproducing the TypeScript action_ref vectors 4 of 4 byte-identical at loader
commit 501e5da; and the decision not to add a ConstraintFacet because 15 is a number already on
the public record. Day 154: the from-scratch COSE and Merkle verifier run against the SCITT
vectors at tag vectors-ietf126, including the vector it declines to reconstruct and records as
unsupported instead. Day 18: the four-run role-separation experiment, carrying both limits, that
the scoring is internal and that n is one per condition. Day 57: the contribution map, which
names no participant, no employer and no city.

Every number is quoted from the file it came from. Nothing was written from memory.

FOUR RECOMMENDATIONS FROM THE REPORT DELIBERATELY NOT TAKEN

The report proposed merging the two Day 123 cards as one build. The archive says Day 123 shipped
BUILD 1 as spend-unit narrowing and BUILD 2 as verification_mode, so the temporal narrowing card
is different work and stays separate.

The report proposed a Day 157 rewrite saying a contract would follow. Day 170 resolved that no
agreement applies to a lab, so that rewrite would have introduced a false claim. The live card
already carries the correct framing.

The report proposed a Day 170 rewrite naming two assertions on a thread as false against the
record. That is a public contradiction of a person on a page we control, so it stays out.

The report proposed a card for a pull request reverification. The handoff shows that work as a
push to our own fork rather than the read-only review the card described, so it is dropped rather
than published with a provenance that does not hold.

446 items, all ids unique, every item carrying its six required keys, YAML parses, zero em or en
dashes, and the roadmap renders.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:HIGH Contributor check flagged HIGH risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants