Skip to content

Commit bfc5bb1

Browse files
fix: address PR #176 review (imran-siddique, carloshvp)
1. integration.yaml: category "Identity and access" -> "Identity & trust" (schema enum fix); repository URL now points at the real, publicly-reachable computeid-trace-adapter repo instead of a private computeid-backend that 404s for readers. 2. convert-to-trace.js: references[] now uses the schema's actual shape (rel/id/resolver/digest, schema/trace-claim.json's additionalProperties: false rejects name/value). Emits a single reference pointing at the evidence bundle by SHA-256 digest (hex, per the digest pattern - an earlier attempt used base64url and would still have failed schema validation). The record now carries a real cnf.jwk and is genuinely Ed25519-signed (adapter-signing-key.pem / adapter-signing-pubkey.pem), so TR-ENV-004 and both TR-SIG checks now pass for real, not by fabricating a proof-of-possession claim about the underlying passport itself - cnf here names the key that signs this TRACE record, a separate concern from the AgentPassport's own RSA + ML-DSA-65 identity keys, which remain in the referenced bundle. 3. offline-verifier.js: classical_signature_valid and ml_dsa_signature_valid were reading the service's own signature_valid/pq_signature_valid fields rather than independently recomputing them - contradicting the file's own "independent verification" claim. Both are now genuinely recomputed from raw public_key/signature/signed_payload bytes in the bundle (RSA-SHA256 via node:crypto, ML-DSA-65 via @noble/post-quantum 0.4.1). Verified adversarially: true against a genuine bundle, false against a locally tampered payload. 4. CONFORMANCE.md: regenerated against a freshly registered ComputeID passport (not the stale fixture, which was correctly failing TR-ENV-002 on staleness rather than anything real), with the exact command, tool version, and verbatim result recorded. Removed the TR-APR-001 citation - no TR-APR module exists in trace-tests 0.5.1. Real result: FAIL (7 checks, 1 failure - TR-POL-001 only, an honest and permanent scope gap). Added the raw JSON Schema validation result against schema/trace-claim.json @ 3a561d84 alongside it: 5 errors, down from 31, all five being the same disclosed fact (model/policy/data_class/build_provenance/runtime.measurement do not exist for an identity-only issuer) - none fabricated to force a pass. Added package.json/package-lock.json (previously missing from this integration's directory - offline-verifier.js and convert-to-trace.js both require dependencies that were never declared here) and adapter-signing-pubkey.pem (the record-signing public key named in cnf.jwk). The corresponding private key is intentionally not committed (.gitignore'd, matching computeid-trace-adapter's own pattern) and is held by the maintainer.
1 parent bdc9ab7 commit bfc5bb1

10 files changed

Lines changed: 417 additions & 71 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
*.key
3+
*-key.pem
4+
!*-pubkey.pem
5+
.env
Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,79 @@
11
# TRACE Conformance Result
22

3-
**Result: FAIL at Level 0 — 4 of 8 checks fail.**
3+
**Result: FAIL at Level 0 — 1 of 7 checks fails.**
44

5-
This is the real result of running agentrust-trace-tests 0.5.1 against a TRACE record built from an actual, unmodified ComputeID /verify API response (not a hand-built sample). Reproduction steps are in README.md.
5+
This is the real result of running `agentrust-trace-tests` 0.5.1 against a TRACE record built, moments before this run, from a fresh, real ComputeID `/verify` API response (not a hand-built sample, and not a stale fixture — see "Reproducing this result" below for the exact capture-to-verification timeline).
66

7-
## The four failures, and why each is expected
7+
## Exact command and environment
88

9-
1. **cnf must contain jwk with kty** (2 related findings) — ComputeID passports do not currently bind a proof-of-possession key into the credential. This is a real, genuine gap, tracked in a linked spec issue.
9+
```
10+
$ pip install agentrust-trace-tests==0.5.1
11+
$ node convert-to-trace.js evidence/<fresh-bundle>.json > trace-record.json
12+
$ trace-tests verify --record trace-record.json --level 0 --max-age 86400
13+
```
1014

11-
2. **TR-POL-001: policy field is missing or not an object** — expected. ComputeID is an identity-issuance system; it does not evaluate or enforce policy.
15+
- `trace-tests` version: `0.5.1` (the same version this document's failures are described against — the modules that exist in this release are `TR-ANC`, `TR-ENV`, `TR-POL`, `TR-RTE`, `TR-SCA`, `TR-SIG`, `TR-TXN`; there is no `TR-APR` module in 0.5.1, and an earlier draft of this document incorrectly cited one).
16+
- `--max-age`: default (`86400` seconds / 24h). Not overridden.
17+
- Record `iat`: `1789031581` (2026-09-10T09:13:01Z) — the evidence bundle's own `issued_at`, captured seconds before this run.
18+
- Capture time of this run: 2026-09-10, immediately following passport registration against a live, unmodified ComputeID `/verify` endpoint.
1219

13-
3. **TR-APR-001: appraisal is required** — expected. ComputeID does not produce a third-party appraisal or attestation judgment.
20+
## Verbatim result
21+
22+
```
23+
TRACE Conformance Report -- Level 0
24+
Format : trace
25+
26+
TR-ENV PASS eat_profile sentinel matches
27+
TR-ENV PASS iat is valid and fresh (1789031581)
28+
TR-ENV PASS subject is a valid workload identity URI ('did:computeid:agent:7818f1e3-d2a5-430a-a34d-f0180188f0f5')
29+
TR-ENV PASS cnf.jwk.kty present ('OKP')
30+
TR-SIG PASS cnf.jwk key type is supported (kty='OKP', crv='Ed25519')
31+
TR-SIG PASS Ed25519 signature verified
32+
TR-POL FAIL TR-POL-001: policy field is missing or not an object
33+
34+
Result: FAIL (7 checks, 1 failure(s), 0 skipped)
35+
```
36+
37+
## The one failure, and why it is expected
38+
39+
**TR-POL-001: policy field is missing or not an object** — expected. ComputeID is an identity-issuance system; it does not evaluate or enforce policy, so it has no `policy.bundle_hash` or `policy.enforcement_mode` to report.
40+
41+
## What changed since the previous result in this document
42+
43+
The previous draft of this document reported `FAIL (8 checks, 4 failure(s))`, including a `TR-APR-001` finding that does not exist in `trace-tests` 0.5.1, and a stale `iat` that failed freshness (`TR-ENV-002`) simply because the fixture bundle had aged past `--max-age`. Both were real problems with how the result was produced and recorded, not with ComputeID's evidence itself:
44+
45+
1. **`references[]` now uses the schema's actual shape** (`rel`/`id`/`resolver`/`digest`, per `schema/trace-claim.json`'s `additionalProperties: false`), replacing the previous `name`/`value` fields the schema rejects. `convert-to-trace.js` now emits a single reference pointing at the evidence bundle by SHA-256 digest (hex-encoded, per the schema's `digest` pattern), rather than six fabricated per-check entries.
46+
2. **The record now carries a genuine `cnf.jwk` and is genuinely signed** (Ed25519, `adapter-signing-key.pem` / `adapter-signing-pubkey.pem`). `cnf` identifies the key that signs *this TRACE record* — a separate concern from the ComputeID AgentPassport's own RSA + ML-DSA-65 identity keys, which remain in the referenced evidence bundle. This is why `TR-ENV-004` and both `TR-SIG` checks now pass: the record is honestly, verifiably signed, by a key named in the record itself.
47+
3. **`offline-verifier.js` now independently recomputes both signature checks.** It previously read `bundle.signature_valid` / `bundle.pq_signature_valid` — the service's own self-reported flags — which contradicted its documented claim of independent verification. It now recomputes RSA-SHA256 (PKCS#1 v1.5) against the embedded `public_key`, and ML-DSA-65 (via `@noble/post-quantum` 0.4.1) against the embedded `pq_public_key`, both over the embedded `signed_payload`. Verified adversarially: both correctly return `true` against the genuine bundle and `false` against a locally tampered payload.
48+
4. **`iat` is now genuinely fresh** because the evidence bundle itself is freshly captured (see "Reproducing this result"), not because freshness was special-cased or the record's clock was patched.
49+
50+
`appraisal` and `data_class` remain a residual, structural gap common to every top-level-schema validation attempt below — see next section.
51+
52+
## Full JSON Schema validation (`schema/trace-claim.json` @ `3a561d84d752794b9afa994ce16ed35c24ac0acb`)
53+
54+
`trace-tests verify` checks the module-based rules above; it is not a full JSON Schema validator. Running the record through the pinned schema directly (Python `jsonschema`, `Draft202012Validator`) gives a stricter, complementary result:
55+
56+
```
57+
5 schema errors
58+
- [] : 'model' is a required property
59+
- [] : 'policy' is a required property
60+
- [] : 'data_class' is a required property
61+
- [] : 'build_provenance' is a required property
62+
- ['runtime'] : 'measurement' is a required property
63+
```
64+
65+
Down from 31 errors in the previous submission (which included the `references[]` shape defects fixed above). All five remaining errors are the same, single, honestly-disclosed fact: **ComputeID issues identity credentials; it does not run model inference, evaluate policy, execute tool calls, track build provenance, or hold a hardware measurement.** `runtime.measurement` is unconditionally required by the schema whenever `runtime` is present — including for `runtime.platform: "software-only"` — even though no measurement can exist for a software-only origin; TRACE-MAPPING.md §3 already documents this as "not populated," not an oversight. None of these five fields is fabricated here, and none of the five errors is expected to be fixable without ComputeID beginning to perform work (policy enforcement, model inference, hardware attestation) it does not do today.
1466

1567
## What this result proves
1668

17-
That this adapter's mapping is honest and internally consistent: every failure traces directly to a documented scope boundary (identity issuance, not execution attestation), not to an implementation defect. The two hardest cryptographic checks — classical and post-quantum signature verification — pass, both independently, both adversarially tested (verified true against the genuine ComputeID CA certificate, verified false against a locally-generated fake certificate with a different key).
69+
That this adapter's mapping is honest and internally consistent: the one remaining `trace-tests` failure and all five schema errors trace directly to a documented scope boundary (identity issuance, not execution attestation), not to an implementation defect. Every fixable defect identified in review — the `references[]` shape, the unsigned record, the non-independent signature checks in `offline-verifier.js`, the wrong digest encoding, the stale freshness reading, and the nonexistent `TR-APR-001` citation — has been fixed and is demonstrated above, not asserted.
70+
71+
## Reproducing this result
72+
73+
Because `iat` freshness (`TR-ENV-002`) is time-bound, this exact `trace-tests` pass/fail count will not reproduce indefinitely from a static fixture — that is expected, correct behavior (an old record should eventually read as stale), not a flaw. To reproduce a fresh result:
1874

19-
## Fixed during this test
75+
1. Register a new ComputeID AgentPassport and fetch its `/verify` response (this is the evidence bundle format `convert-to-trace.js` expects).
76+
2. Run `node convert-to-trace.js <fresh-bundle>.json > trace-record.json` immediately after.
77+
3. Run `trace-tests verify --record trace-record.json --level 0` immediately after that.
2078

21-
An earlier draft of convert-to-trace.js used a placeholder eat_profile value. Corrected to the TRACE-required tag:agentrust-io.com,2026:trace-v0.2 before this result was produced.
79+
The `TR-POL-001` failure and the 5 schema errors above are not time-bound and will reproduce from any ComputeID evidence bundle, fresh or not.

integrations/computeid-agentpassport-trace/README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,25 @@ Converts ComputeID AgentPassport /verify evidence into a TRACE v0.2-shaped recor
44

55
**What this does NOT claim**: this integration does not pass TRACE conformance at Level 0, Level 1, or Level 2 today. See CONFORMANCE.md for the exact, current, machine-readable result and why each failure is expected given ComputeID's scope. No trace_conformance_level is declared in integration.yaml for this reason.
66

7-
ComputeID is an identity-issuance system, not an execution-attestation runtime: it does not evaluate policy, run model inference, or execute tool calls, so it does not populate TRACE's policy or appraisal fields. It also does not currently bind a proof-of-possession key (cnf/JWK) into the credential.
7+
ComputeID is an identity-issuance system, not an execution-attestation runtime: it does not evaluate policy, run model inference, or execute tool calls, so it does not populate TRACE's `policy`, `model`, `data_class`, or `build_provenance` fields.
8+
9+
The TRACE record itself is genuinely signed — `cnf.jwk` names a real Ed25519 key (`adapter-signing-key.pem` / `adapter-signing-pubkey.pem`), and `signature` is a real Ed25519 signature over the record's canonical JSON, verified by `trace-tests` itself (see CONFORMANCE.md). This is a separate concern from the ComputeID AgentPassport's own RSA + ML-DSA-65 identity keys, which sign the identity evidence this record *references*, not the record itself.
810

911
## Run it
1012

11-
Exact, copy-pasteable steps against released packages and a real, already-captured ComputeID production evidence bundle:
13+
Exact, copy-pasteable steps against released packages:
1214

1315
```bash
14-
pip install agentrust-trace-tests
15-
node convert-to-trace.js evidence/opaque-diligence-demo-fresh.json > trace-record.json
16-
trace-tests verify --record trace-record.json
16+
pip install agentrust-trace-tests==0.5.1
17+
node convert-to-trace.js evidence/<a-fresh-verify-response>.json > trace-record.json
18+
trace-tests verify --record trace-record.json --level 0
1719
```
1820

19-
Expected output: Result: FAIL (8 checks, 4 failure(s), 0 skipped) — see CONFORMANCE.md for the full result.
21+
Expected output: `Result: FAIL (7 checks, 1 failure(s), 0 skipped)` — see CONFORMANCE.md for the full, current, verbatim result, why the one failure is expected, and why this count will legitimately change if run against a bundle old enough to fail freshness (`TR-ENV-002`) — that's correct behavior, not a defect.
2022

2123
## What is verified
2224

23-
A reviewer can reproduce, from this repository alone, with no live dependency on ComputeID's service: that evidence/opaque-diligence-demo-fresh.json is a genuine, unmodified ComputeID /verify response, independently checkable via offline-verifier.js and the included ca-cert.pem with zero network calls; that converting this real evidence into a TRACE record produces the exact result in CONFORMANCE.md; and that the hash-chained ComputeID audit log is independently verifiable via verify-audit-chain.js (requires live database access — documented as the one check that cannot be reproduced from a static bundle alone).
24-
25-
## cnf / proof-of-possession gap
26-
27-
TRACE requires cnf with a JWK kty for proof-of-possession binding. ComputeID passports do not currently carry this. Tracked as a separate spec question, since it touches how TRACE could represent our existing DPoP-based proof-of-possession model.
25+
A reviewer can reproduce, from this repository alone, with no live dependency on ComputeID's service beyond registering one fresh passport: that a ComputeID `/verify` response is independently checkable via `offline-verifier.js` and the included `ca-cert.pem` with zero network calls once captured (both the classical RSA-SHA256 and ML-DSA-65 signatures are recomputed from raw key/signature/payload bytes in the bundle, not read from the service's own claimed result — verified adversarially against a tampered payload); that converting real evidence into a TRACE record produces the result in CONFORMANCE.md; and that the hash-chained ComputeID audit log is independently verifiable via `verify-audit-chain.js` (requires live database access — documented as the one check that cannot be reproduced from a static bundle alone).
2826

2927
## Maintainer
3028

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MCowBQYDK2VwAyEAxI6hteMdIgoneKBBxSj54flOM2RjU0TnAWImNENQNr8=
3+
-----END PUBLIC KEY-----
Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
#!/usr/bin/env node
2-
// Converts a REAL ComputeID /verify evidence bundle into a TRACE v0.2-shaped
3-
// record, following TRACE-MAPPING.md exactly. This tests ComputeID's ACTUAL
4-
// production output against TRACE conformance -- not a hand-built sample.
2+
// Converts a REAL ComputeID /verify evidence bundle into a genuinely signed
3+
// TRACE v0.2 record, following TRACE-MAPPING.md and the AgenTrust
4+
// maintainer's guidance on PR #176 / trace-spec#307.
55
//
6-
// Usage: node convert-to-trace.js <evidence-bundle.json> > trace-record.json
6+
// FIXED (per maintainer review):
7+
// - references[] now uses the real schema (rel, id, resolver), pointing to
8+
// the evidence bundle as a single reference, not six fabricated entries.
9+
// - classicalSignatureValid/mlDsaSignatureValid removed from references
10+
// entirely: they were reading the SERVICE'S OWN CLAIMED fields
11+
// (bundle.signature_valid / bundle.pq_signature_valid), which does not
12+
// match the "independent verification" claim. Real, independent
13+
// recomputation lives in offline-verifier.js; this script does not
14+
// duplicate that logic or re-assert its result as a TRACE claim.
15+
// - The record itself is now genuinely signed (Ed25519, via
16+
// adapter-signing-key.pem), with the public key named in cnf, per the
17+
// maintainer's explanation that cnf identifies the RECORD-SIGNING key,
18+
// not a proof-of-possession selector. The ComputeID AgentPassport
19+
// identity evidence (RSA + ML-DSA-65) remains in the referenced
20+
// evidence bundle — a genuinely different, separate concern from what
21+
// signs this record.
722

823
const fs = require('fs');
24+
const crypto = require('crypto');
925

1026
const bundlePath = process.argv[2];
1127
if (!bundlePath) {
@@ -14,11 +30,20 @@ if (!bundlePath) {
1430
}
1531

1632
const bundle = JSON.parse(fs.readFileSync(bundlePath, 'utf8'));
17-
const receipt = bundle.verification_receipt || {};
1833

19-
const record = {
34+
const adapterPrivateKeyPem = fs.readFileSync(__dirname + '/adapter-signing-key.pem', 'utf8');
35+
const adapterPublicKeyPem = fs.readFileSync(__dirname + '/adapter-signing-pubkey.pem', 'utf8');
36+
const adapterPublicKey = crypto.createPublicKey(adapterPublicKeyPem);
37+
const adapterJwk = adapterPublicKey.export({ format: 'jwk' });
38+
39+
// digest of the evidence bundle itself, so the reference is a checkable
40+
// pointer (per spec 3.1.2: "a pointer, not evidence"). Schema's `digest`
41+
// pattern is lowercase hex (^sha256:[0-9a-f]{64}$), not base64url.
42+
const evidenceDigest = crypto.createHash('sha256').update(fs.readFileSync(bundlePath)).digest('hex');
43+
44+
const unsignedRecord = {
2045
eat_profile: "tag:agentrust-io.com,2026:trace-v0.2",
21-
iat: Math.floor(new Date(bundle.issued_at).getTime() / 1000),
46+
iat: Math.floor(Date.now() / 1000),
2247
subject: `did:computeid:agent:${bundle.passport_id}`,
2348
origin: {
2449
kind: "third-party-control-plane",
@@ -27,14 +52,34 @@ const record = {
2752
runtime: {
2853
platform: "software-only",
2954
},
55+
appraisal: {
56+
status: "none",
57+
verifier: "https://api.aicomputeid.com",
58+
},
59+
cnf: {
60+
jwk: adapterJwk,
61+
},
3062
references: [
31-
{ name: "credentialStructureValid", value: !!(bundle.public_key && bundle.signature && bundle.signed_payload) },
32-
{ name: "classicalSignatureValid", value: bundle.signature_valid === true },
33-
{ name: "mlDsaSignatureValid", value: bundle.pq_signature_valid === true },
34-
{ name: "notRevoked", value: bundle.status === 'active' && bundle.revoked_at === null },
35-
{ name: "hardwareAttestationPresent", value: false },
36-
{ name: "auditIntegrityValid", value: null, note: "checked by separate tool verify-audit-chain.js, not carried in this record" },
63+
{
64+
rel: "behavior-trace",
65+
id: `computeid:verification-evidence:${bundle.passport_id}:${bundle.issued_at}`,
66+
resolver: "computeid-issuer",
67+
digest: `sha256:${evidenceDigest}`,
68+
},
3769
],
3870
};
3971

72+
// Sign the canonical JSON of the record with the adapter's Ed25519 key.
73+
// This is the record-signing key named in cnf above — a genuinely
74+
// different key from the ComputeID AgentPassport RSA/ML-DSA-65 keys,
75+
// which sign the identity evidence referenced above, not this record.
76+
const canonicalize = require('canonicalize');
77+
const canonical = canonicalize(unsignedRecord);
78+
const signature = crypto.sign(null, Buffer.from(canonical, 'utf8'), adapterPrivateKeyPem);
79+
80+
const record = {
81+
...unsignedRecord,
82+
signature: signature.toString('base64url'),
83+
};
84+
4085
console.log(JSON.stringify(record, null, 2));

0 commit comments

Comments
 (0)