Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/comply54-conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# agentrust-io conformance workflow for the comply54 integration.
# Lives in the repository root because GitHub Actions only discovers workflows
# in the root .github/workflows directory; scoped to this integration via paths.
name: comply54 conformance
on:
push:
paths:
- "integrations/comply54/**"
- ".github/workflows/comply54-conformance.yml"
pull_request:
paths:
- "integrations/comply54/**"
- ".github/workflows/comply54-conformance.yml"
schedule:
- cron: "0 6 * * 1" # weekly: catch drift against the latest released packages
workflow_dispatch:

permissions:
contents: read

jobs:
conformance:
strategy:
fail-fast: false
matrix:
python: ["3.11", "3.12", "3.13"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python }}
- name: Install released agentrust-io packages
run: |
python -m pip install --upgrade pip
pip install agentrust-trace-tests
- name: Install this integration
run: pip install -e "integrations/comply54[test]"
- name: Integration tests
run: pytest integrations/comply54/tests -q
- name: Emit a sample TRACE record
run: python integrations/comply54/examples/emit_record.py --out trust-record.jwt
- name: TRACE conformance
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 }}
path: |
trust-record.jwt
trust-record.jwt.signed.jwt
74 changes: 74 additions & 0 deletions integrations/comply54/examples/emit_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""Emit a TRACE Trust Record from a comply54 ComplianceResult.

Maps the committed example fixture (a denied high-value transfer, exceeding
the CBN NIP transaction cap) onto a TRACE v0.2 Trust Record using the same
`comply54_to_trace_payload` mapping the adapter's own test suite already
verifies against agentrust-trace-tests Level 0.

Two files are written:

<out> unsigned record for `trace-tests verify` — this is the
plain payload dict (includes `cnf.jwk`, no `signature`
field), the same shape the adapter's own
TestLevel0Conformance suite grades via the internal
trace_tests API.
<out>.signed.jwt the same payload, compact-serialized and signed as an
Ed25519 JWT via PyJWT (comply54_to_trace.py's own
signing path), with an immediate decode-and-verify
round trip against the public key — proves the
sign/verify path is real, not just that the payload
shape is correct.

The ephemeral key is generated per run and never persisted.
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

import jwt as pyjwt

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from comply54_to_trace import (
comply54_to_trace_payload,
load_or_generate_key,
)

DEFAULT_RESULT = Path(__file__).resolve().parent / "fixtures" / "deny-cbn-nip-cap.json"


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--out", required=True, help="Path for the trace-tests-gradable record")
parser.add_argument("--result", default=str(DEFAULT_RESULT), help="comply54 ComplianceResult JSON path")
parser.add_argument("--agent-id", default="payments-agent", help="Agent SPIFFE identity suffix")
parser.add_argument("--model", default="anthropic/claude-sonnet-4-6", help="Model in provider/model-id format")
args = parser.parse_args()

result = json.loads(Path(args.result).read_text(encoding="utf-8"))

key = load_or_generate_key()
payload = comply54_to_trace_payload(result, args.agent_id, args.model, key=key)

token = pyjwt.encode(payload, key, algorithm="EdDSA", headers={"alg": "EdDSA", "typ": "JWT"})
decoded = pyjwt.decode(token, key.public_key(), algorithms=["EdDSA"])
assert decoded["eat_profile"] == payload["eat_profile"], "sign/verify round trip mismatch"

out = Path(args.out)
out.write_text(json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
signed_out = out.with_name(out.name + ".signed.jwt")
signed_out.write_text(token + "\n", encoding="utf-8")

print(f"subject: {payload['subject']}")
print(f"appraisal: {payload['appraisal']['status']}")
print(f"unsigned (for trace-tests): {out}")
print(f"signed (decode/verify OK): {signed_out}")
return 0


if __name__ == "__main__":
raise SystemExit(main())
27 changes: 27 additions & 0 deletions integrations/comply54/examples/fixtures/deny-cbn-nip-cap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"overall": "deny",
"audit_id": "example-audit-001",
"decisions": [
{
"pack": "nigeria/cbn",
"regulation": "CBN Transaction Controls",
"jurisdiction": "NG",
"action": "deny",
"messages": ["CBN NIP cap exceeded: ₦15,000,000 > ₦10,000,000 limit"]
},
{
"pack": "nigeria/nfiu-aml",
"regulation": "NFIU AML Guidelines",
"jurisdiction": "NG",
"action": "deny",
"messages": ["Currency Transaction Report required for amounts above ₦5,000,000"]
},
{
"pack": "universal/human-approval",
"regulation": "OWASP LLM09",
"jurisdiction": "UNIVERSAL",
"action": "deny",
"messages": ["High-value transfer requires human approval before execution"]
}
]
}
25 changes: 25 additions & 0 deletions integrations/comply54/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project]
name = "comply54-agentrust-integration"
version = "0.1.0"
description = "Converts comply54 ComplianceResult decisions into signed TRACE Trust Records"
requires-python = ">=3.11"
license = "Apache-2.0"
dependencies = [
"PyJWT>=2.8.0",
"cryptography>=42.0.0",
]

[project.optional-dependencies]
test = [
"pytest>=7.0.0",
"jsonschema>=4.0.0",
"agentrust-trace-tests>=0.4,<0.5",
]

[tool.setuptools]
package-dir = {"" = "src"}
py-modules = ["comply54_to_trace"]
Loading