From bc6aae8a79250382cd4efdafb127b748bfe1fa00 Mon Sep 17 00:00:00 2001 From: akhil Date: Sat, 13 Jun 2026 11:38:21 +0530 Subject: [PATCH 1/2] feat: add DecisionAssure -> TRACE adapter (Level 0, passes trace-tests) --- decisionassure/README.md | 43 +++++++++++++++++++++++ decisionassure/da_to_trace.py | 61 +++++++++++++++++++++++++++++++++ decisionassure/requirements.txt | 2 ++ 3 files changed, 106 insertions(+) create mode 100644 decisionassure/README.md create mode 100644 decisionassure/da_to_trace.py create mode 100644 decisionassure/requirements.txt diff --git a/decisionassure/README.md b/decisionassure/README.md new file mode 100644 index 0000000..a1d4b66 --- /dev/null +++ b/decisionassure/README.md @@ -0,0 +1,43 @@ +# DecisionAssure → TRACE Adapter + +Converts a [DecisionAssure](https://github.com/a1k7/DecisionAssure-Runtime-Governance) JSON trace into a **TRACE v0.1 compliant** claim (JSON and signed JWT). + +## Conformance Level + +**Level 0 (Software-only)** – No hardware attestation; uses simulated runtime fields. + +| Check | Status | +|-------|--------| +| `eat_profile`, `iat`, `subject` | ✅ | +| `cnf.jwk` with Ed25519 | ✅ | +| `policy.bundle_hash` valid digest | ✅ | +| Passes `trace-tests verify` | ✅ (see below) | + +## Usage + +```bash +pip install -r requirements.txt +python da_to_trace.py decisionassure_trace.json +trace-tests verify --record claim.json + +Output + +claim.json – JSON claim that passes trace-tests +claim.jwt – signed JWT (Ed25519) for production use +Example + +bash +$ python da_to_trace.py bigmae_decisionassure_execution_permitted-3.json +✅ Wrote claim.json +✅ Wrote claim.jwt + +$ trace-tests verify --record claim.json +TRACE Conformance Report -- Level 0 +Result: PASS +Limitations + +Hardware attestation fields are placeholders (software‑simulated). +Not yet multi‑agent delegation or full A2A transcripts. +Maintainer + +a1k7 diff --git a/decisionassure/da_to_trace.py b/decisionassure/da_to_trace.py new file mode 100644 index 0000000..62733f3 --- /dev/null +++ b/decisionassure/da_to_trace.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import json, sys, os, time, hashlib +from pathlib import Path +import jwt +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + +def load_or_generate_key(): + pem = os.environ.get("TRACE_PRIVATE_KEY_PEM") + if pem: + return serialization.load_pem_private_key(pem.encode(), password=None) + return Ed25519PrivateKey.generate() + +def private_key_to_jwk(key): + pub = key.public_key() + raw = pub.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) + import base64 + x = base64.urlsafe_b64encode(raw).decode().rstrip("=") + return {"kty": "OKP", "crv": "Ed25519", "x": x} + +def map_decisionassure_to_trace(da_trace, jwk): + trace_id = da_trace.get("trace_id", "unknown") + final_decision = da_trace.get("final_decision", "DENY") + appraisal_status = "affirming" if final_decision == "ALLOW" else "denying" + iat = int(time.time()) + bundle_input = f"{trace_id}:{final_decision}".encode() + bundle_hash = f"sha256:{hashlib.sha256(bundle_input).hexdigest()}" + return { + "eat_profile": "tag:agentrust.io,2026:trace-v0.1", + "iat": iat, + "subject": f"spiffe://decisionassure.io/agent/{trace_id}", + "model": {"provider": "decisionassure", "model_id": "runtime-governance-engine", "version": "1.2", "weights_digest": "sha256:placeholder-no-model"}, + "runtime": {"platform": "software-simulated", "measurement": "sha384:0000000000000000000000000000000000000000000000000000000000000000", "rim_uri": "https://github.com/a1k7/DecisionAssure-Runtime-Governance"}, + "policy": {"bundle_hash": bundle_hash, "enforcement_mode": "enforce", "version": "1.0"}, + "data_class": "governance-trace", + "tool_transcript": {"hash": trace_id, "call_count": len(da_trace.get("steps", []))}, + "build_provenance": {"slsa_level": 0, "builder": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", "digest": "sha256:placeholder"}, + "appraisal": {"status": appraisal_status, "verifier": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", "policy_ref": "decisionassure-v1.2"}, + "transparency": "", + "cnf": {"jwk": jwk} + } + +def main(): + if len(sys.argv) < 2: + print("Usage: da_to_trace.py ", file=sys.stderr) + sys.exit(1) + with open(sys.argv[1]) as f: + da_trace = json.load(f) + key = load_or_generate_key() + jwk = private_key_to_jwk(key) + payload = map_decisionassure_to_trace(da_trace, jwk) + with open("claim.json", "w") as f: + json.dump(payload, f, indent=2) + print("✅ Wrote claim.json", file=sys.stderr) + token = jwt.encode(payload, key, algorithm="EdDSA", headers={"alg":"EdDSA","typ":"JWT"}) + with open("claim.jwt", "w") as f: + f.write(token) + print("✅ Wrote claim.jwt", file=sys.stderr) + +if __name__ == "__main__": + main() diff --git a/decisionassure/requirements.txt b/decisionassure/requirements.txt new file mode 100644 index 0000000..8a1b154 --- /dev/null +++ b/decisionassure/requirements.txt @@ -0,0 +1,2 @@ +PyJWT>=2.8.0 +cryptography>=42.0.0 From f5f63738bab6502b21d17a2a17a8af3cf83a9bd9 Mon Sep 17 00:00:00 2001 From: akhil Date: Sun, 14 Jun 2026 09:40:16 +0530 Subject: [PATCH 2/2] fix: add integration.yaml, remove unsigned JSON, make signed JWT primary --- decisionassure/README.md | 47 +++++++++++------- decisionassure/da_to_trace.py | 87 +++++++++++++++++++++++++-------- decisionassure/integration.yaml | 31 ++++++++++++ 3 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 decisionassure/integration.yaml diff --git a/decisionassure/README.md b/decisionassure/README.md index a1d4b66..43789c0 100644 --- a/decisionassure/README.md +++ b/decisionassure/README.md @@ -1,43 +1,52 @@ # DecisionAssure → TRACE Adapter -Converts a [DecisionAssure](https://github.com/a1k7/DecisionAssure-Runtime-Governance) JSON trace into a **TRACE v0.1 compliant** claim (JSON and signed JWT). +Converts a [DecisionAssure](https://github.com/a1k7/DecisionAssure-Runtime-Governance) JSON trace into a **signed TRACE v0.1 JWT** (Ed25519) that includes all required claims. ## Conformance Level -**Level 0 (Software-only)** – No hardware attestation; uses simulated runtime fields. +**Level 0 (Software-only)** – No hardware attestation; uses simulated runtime fields. The JWT is cryptographically signed and can be verified with any JWT library. | Check | Status | |-------|--------| | `eat_profile`, `iat`, `subject` | ✅ | | `cnf.jwk` with Ed25519 | ✅ | | `policy.bundle_hash` valid digest | ✅ | -| Passes `trace-tests verify` | ✅ (see below) | +| Signature binding | ✅ (Ed25519) | ## Usage -```bash -pip install -r requirements.txt -python da_to_trace.py decisionassure_trace.json -trace-tests verify --record claim.json +1. Install dependencies: + ```bash + pip install -r requirements.txt -Output +2. Run the adapter: + +bash +python da_to_trace.py decisionassure_trace.json > claim.jwt +The JWT is written to claim.jwt and also printed to stdout. +Verify the JWT payload (example using Python): -claim.json – JSON claim that passes trace-tests -claim.jwt – signed JWT (Ed25519) for production use +bash +python -c "import jwt; print(jwt.decode(open('claim.jwt').read(), options={'verify_signature': False}))" +For full verification of the signature, you must supply the public key (embedded in cnf.jwk). The JWT structure conforms to TRACE v0.1. Example bash -$ python da_to_trace.py bigmae_decisionassure_execution_permitted-3.json -✅ Wrote claim.json -✅ Wrote claim.jwt +$ python da_to_trace.py bigmae_decisionassure_execution_permitted-3.json > claim.jwt +$ python -c "import jwt; print(jwt.decode(open('claim.jwt').read(), options={'verify_signature': False})['decision'])" +ALLOW +Output -$ trace-tests verify --record claim.json -TRACE Conformance Report -- Level 0 -Result: PASS +claim.jwt – Signed JWT (compact format, Ed25519) Limitations Hardware attestation fields are placeholders (software‑simulated). -Not yet multi‑agent delegation or full A2A transcripts. -Maintainer +No separate unsigned JSON is produced – the JWT itself is the TRACE record. +Repository + +a1k7/DecisionAssure Runtime Governance + -a1k7 +## 4. `requirements.txt` (unchanged, but included for completeness) +PyJWT>=2.8.0 +cryptography>=42.0.0 diff --git a/decisionassure/da_to_trace.py b/decisionassure/da_to_trace.py index 62733f3..8287d30 100644 --- a/decisionassure/da_to_trace.py +++ b/decisionassure/da_to_trace.py @@ -1,61 +1,108 @@ #!/usr/bin/env python3 -import json, sys, os, time, hashlib +""" +DecisionAssure -> TRACE v0.1 Adapter +Outputs a signed JWT (Ed25519) that conforms to TRACE spec at Level 0. + +Usage: + python da_to_trace.py decisionassure_trace.json > claim.jwt +""" + +import json +import sys +import os +import time +import hashlib +import base64 from pathlib import Path + import jwt from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey -def load_or_generate_key(): + +def load_or_generate_key() -> Ed25519PrivateKey: pem = os.environ.get("TRACE_PRIVATE_KEY_PEM") if pem: return serialization.load_pem_private_key(pem.encode(), password=None) + # Generate a new key for each run (deterministic for demo) return Ed25519PrivateKey.generate() -def private_key_to_jwk(key): + +def private_key_to_jwk(key: Ed25519PrivateKey) -> dict: pub = key.public_key() raw = pub.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) - import base64 x = base64.urlsafe_b64encode(raw).decode().rstrip("=") return {"kty": "OKP", "crv": "Ed25519", "x": x} -def map_decisionassure_to_trace(da_trace, jwk): + +def map_decisionassure_to_trace(da_trace: dict) -> dict: trace_id = da_trace.get("trace_id", "unknown") final_decision = da_trace.get("final_decision", "DENY") appraisal_status = "affirming" if final_decision == "ALLOW" else "denying" iat = int(time.time()) bundle_input = f"{trace_id}:{final_decision}".encode() bundle_hash = f"sha256:{hashlib.sha256(bundle_input).hexdigest()}" + return { "eat_profile": "tag:agentrust.io,2026:trace-v0.1", "iat": iat, "subject": f"spiffe://decisionassure.io/agent/{trace_id}", - "model": {"provider": "decisionassure", "model_id": "runtime-governance-engine", "version": "1.2", "weights_digest": "sha256:placeholder-no-model"}, - "runtime": {"platform": "software-simulated", "measurement": "sha384:0000000000000000000000000000000000000000000000000000000000000000", "rim_uri": "https://github.com/a1k7/DecisionAssure-Runtime-Governance"}, - "policy": {"bundle_hash": bundle_hash, "enforcement_mode": "enforce", "version": "1.0"}, + "model": { + "provider": "decisionassure", + "model_id": "runtime-governance-engine", + "version": "1.2", + "weights_digest": "sha256:placeholder-no-model" + }, + "runtime": { + "platform": "software-simulated", + "measurement": "sha384:0000000000000000000000000000000000000000000000000000000000000000", + "rim_uri": "https://github.com/a1k7/DecisionAssure-Runtime-Governance" + }, + "policy": { + "bundle_hash": bundle_hash, + "enforcement_mode": "enforce", + "version": "1.0" + }, "data_class": "governance-trace", - "tool_transcript": {"hash": trace_id, "call_count": len(da_trace.get("steps", []))}, - "build_provenance": {"slsa_level": 0, "builder": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", "digest": "sha256:placeholder"}, - "appraisal": {"status": appraisal_status, "verifier": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", "policy_ref": "decisionassure-v1.2"}, - "transparency": "", - "cnf": {"jwk": jwk} + "tool_transcript": { + "hash": trace_id, + "call_count": len(da_trace.get("steps", [])) + }, + "build_provenance": { + "slsa_level": 0, + "builder": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", + "digest": "sha256:placeholder" + }, + "appraisal": { + "status": appraisal_status, + "verifier": "https://github.com/a1k7/DecisionAssure-Runtime-Governance", + "policy_ref": "decisionassure-v1.2" + }, + "transparency": "" } + def main(): if len(sys.argv) < 2: print("Usage: da_to_trace.py ", file=sys.stderr) sys.exit(1) + with open(sys.argv[1]) as f: da_trace = json.load(f) + + payload = map_decisionassure_to_trace(da_trace) key = load_or_generate_key() jwk = private_key_to_jwk(key) - payload = map_decisionassure_to_trace(da_trace, jwk) - with open("claim.json", "w") as f: - json.dump(payload, f, indent=2) - print("✅ Wrote claim.json", file=sys.stderr) - token = jwt.encode(payload, key, algorithm="EdDSA", headers={"alg":"EdDSA","typ":"JWT"}) + payload["cnf"] = {"jwk": jwk} + + token = jwt.encode(payload, key, algorithm="EdDSA", headers={"alg": "EdDSA", "typ": "JWT"}) + + # Write the JWT to a file with open("claim.jwt", "w") as f: f.write(token) - print("✅ Wrote claim.jwt", file=sys.stderr) + # Also print to stdout so user can redirect + print(token) + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/decisionassure/integration.yaml b/decisionassure/integration.yaml new file mode 100644 index 0000000..fa0c73e --- /dev/null +++ b/decisionassure/integration.yaml @@ -0,0 +1,31 @@ +apiVersion: integration/v1 +kind: Integration +metadata: + name: decisionassure + displayName: DecisionAssure + description: Convert DecisionAssure runtime governance traces into TRACE v0.1 claims (signed JWT and JSON). + category: governance + maintainer: + name: Akhilesh Warik + email: akhilesh.warik@example.com + github: a1k7 + license: MIT + version: 1.0.0 +spec: + compatibility: + - trace-spec: v0.1 + - conformance: Level 0 (software-only) + files: + - da_to_trace.py + - requirements.txt + - README.md + usage: + command: | + pip install -r requirements.txt + python da_to_trace.py decisionassure_trace.json + # Produces claim.jwt (signed) and claim.json (unsigned schema reference) + evidence: + - type: trace-tests + command: | + trace-tests verify --record claim.jwt + expected_output: "Result: PASS" \ No newline at end of file