Skip to content

Commit 95ebc98

Browse files
a1k7akhil
andauthored
feat: add DecisionAssure TRACE adapter (Level 0, passes trace-tests) (#3)
* feat: add DecisionAssure -> TRACE adapter (Level 0, passes trace-tests) * fix: add integration.yaml, remove unsigned JSON, make signed JWT primary --------- Co-authored-by: akhil <warikakhilesh@gmail.com>
1 parent 35636ad commit 95ebc98

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

decisionassure/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# DecisionAssure → TRACE Adapter
2+
3+
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.
4+
5+
## Conformance Level
6+
7+
**Level 0 (Software-only)** – No hardware attestation; uses simulated runtime fields. The JWT is cryptographically signed and can be verified with any JWT library.
8+
9+
| Check | Status |
10+
|-------|--------|
11+
| `eat_profile`, `iat`, `subject` ||
12+
| `cnf.jwk` with Ed25519 ||
13+
| `policy.bundle_hash` valid digest ||
14+
| Signature binding | ✅ (Ed25519) |
15+
16+
## Usage
17+
18+
1. Install dependencies:
19+
```bash
20+
pip install -r requirements.txt
21+
22+
2. Run the adapter:
23+
24+
bash
25+
python da_to_trace.py decisionassure_trace.json > claim.jwt
26+
The JWT is written to claim.jwt and also printed to stdout.
27+
Verify the JWT payload (example using Python):
28+
29+
bash
30+
python -c "import jwt; print(jwt.decode(open('claim.jwt').read(), options={'verify_signature': False}))"
31+
For full verification of the signature, you must supply the public key (embedded in cnf.jwk). The JWT structure conforms to TRACE v0.1.
32+
Example
33+
34+
bash
35+
$ python da_to_trace.py bigmae_decisionassure_execution_permitted-3.json > claim.jwt
36+
$ python -c "import jwt; print(jwt.decode(open('claim.jwt').read(), options={'verify_signature': False})['decision'])"
37+
ALLOW
38+
Output
39+
40+
claim.jwt – Signed JWT (compact format, Ed25519)
41+
Limitations
42+
43+
Hardware attestation fields are placeholders (software‑simulated).
44+
No separate unsigned JSON is produced – the JWT itself is the TRACE record.
45+
Repository
46+
47+
a1k7/DecisionAssure Runtime Governance
48+
49+
50+
## 4. `requirements.txt` (unchanged, but included for completeness)
51+
PyJWT>=2.8.0
52+
cryptography>=42.0.0

decisionassure/da_to_trace.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python3
2+
"""
3+
DecisionAssure -> TRACE v0.1 Adapter
4+
Outputs a signed JWT (Ed25519) that conforms to TRACE spec at Level 0.
5+
6+
Usage:
7+
python da_to_trace.py decisionassure_trace.json > claim.jwt
8+
"""
9+
10+
import json
11+
import sys
12+
import os
13+
import time
14+
import hashlib
15+
import base64
16+
from pathlib import Path
17+
18+
import jwt
19+
from cryptography.hazmat.primitives import serialization
20+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
21+
22+
23+
def load_or_generate_key() -> Ed25519PrivateKey:
24+
pem = os.environ.get("TRACE_PRIVATE_KEY_PEM")
25+
if pem:
26+
return serialization.load_pem_private_key(pem.encode(), password=None)
27+
# Generate a new key for each run (deterministic for demo)
28+
return Ed25519PrivateKey.generate()
29+
30+
31+
def private_key_to_jwk(key: Ed25519PrivateKey) -> dict:
32+
pub = key.public_key()
33+
raw = pub.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
34+
x = base64.urlsafe_b64encode(raw).decode().rstrip("=")
35+
return {"kty": "OKP", "crv": "Ed25519", "x": x}
36+
37+
38+
def map_decisionassure_to_trace(da_trace: dict) -> dict:
39+
trace_id = da_trace.get("trace_id", "unknown")
40+
final_decision = da_trace.get("final_decision", "DENY")
41+
appraisal_status = "affirming" if final_decision == "ALLOW" else "denying"
42+
iat = int(time.time())
43+
bundle_input = f"{trace_id}:{final_decision}".encode()
44+
bundle_hash = f"sha256:{hashlib.sha256(bundle_input).hexdigest()}"
45+
46+
return {
47+
"eat_profile": "tag:agentrust.io,2026:trace-v0.1",
48+
"iat": iat,
49+
"subject": f"spiffe://decisionassure.io/agent/{trace_id}",
50+
"model": {
51+
"provider": "decisionassure",
52+
"model_id": "runtime-governance-engine",
53+
"version": "1.2",
54+
"weights_digest": "sha256:placeholder-no-model"
55+
},
56+
"runtime": {
57+
"platform": "software-simulated",
58+
"measurement": "sha384:0000000000000000000000000000000000000000000000000000000000000000",
59+
"rim_uri": "https://github.com/a1k7/DecisionAssure-Runtime-Governance"
60+
},
61+
"policy": {
62+
"bundle_hash": bundle_hash,
63+
"enforcement_mode": "enforce",
64+
"version": "1.0"
65+
},
66+
"data_class": "governance-trace",
67+
"tool_transcript": {
68+
"hash": trace_id,
69+
"call_count": len(da_trace.get("steps", []))
70+
},
71+
"build_provenance": {
72+
"slsa_level": 0,
73+
"builder": "https://github.com/a1k7/DecisionAssure-Runtime-Governance",
74+
"digest": "sha256:placeholder"
75+
},
76+
"appraisal": {
77+
"status": appraisal_status,
78+
"verifier": "https://github.com/a1k7/DecisionAssure-Runtime-Governance",
79+
"policy_ref": "decisionassure-v1.2"
80+
},
81+
"transparency": ""
82+
}
83+
84+
85+
def main():
86+
if len(sys.argv) < 2:
87+
print("Usage: da_to_trace.py <decisionassure_trace.json>", file=sys.stderr)
88+
sys.exit(1)
89+
90+
with open(sys.argv[1]) as f:
91+
da_trace = json.load(f)
92+
93+
payload = map_decisionassure_to_trace(da_trace)
94+
key = load_or_generate_key()
95+
jwk = private_key_to_jwk(key)
96+
payload["cnf"] = {"jwk": jwk}
97+
98+
token = jwt.encode(payload, key, algorithm="EdDSA", headers={"alg": "EdDSA", "typ": "JWT"})
99+
100+
# Write the JWT to a file
101+
with open("claim.jwt", "w") as f:
102+
f.write(token)
103+
# Also print to stdout so user can redirect
104+
print(token)
105+
106+
107+
if __name__ == "__main__":
108+
main()

decisionassure/integration.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: integration/v1
2+
kind: Integration
3+
metadata:
4+
name: decisionassure
5+
displayName: DecisionAssure
6+
description: Convert DecisionAssure runtime governance traces into TRACE v0.1 claims (signed JWT and JSON).
7+
category: governance
8+
maintainer:
9+
name: Akhilesh Warik
10+
email: akhilesh.warik@example.com
11+
github: a1k7
12+
license: MIT
13+
version: 1.0.0
14+
spec:
15+
compatibility:
16+
- trace-spec: v0.1
17+
- conformance: Level 0 (software-only)
18+
files:
19+
- da_to_trace.py
20+
- requirements.txt
21+
- README.md
22+
usage:
23+
command: |
24+
pip install -r requirements.txt
25+
python da_to_trace.py decisionassure_trace.json
26+
# Produces claim.jwt (signed) and claim.json (unsigned schema reference)
27+
evidence:
28+
- type: trace-tests
29+
command: |
30+
trace-tests verify --record claim.jwt
31+
expected_output: "Result: PASS"

decisionassure/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PyJWT>=2.8.0
2+
cryptography>=42.0.0

0 commit comments

Comments
 (0)