Remove non-protocol claims and make ledger optional

Move 6 metadata claims (pol_timestamp, inp_classification,
exec_time_ms, regulated_domain, model_version, witnessed_by)
from registered JWT claims to recommended ext extension keys.
Use short key names for spec-defined extensions.

Make audit ledger explicitly optional: rename pseudocode
parameter from ledger to ect_store, mark architecture diagram
ledger layer as optional, add conditional append logic, and
soften Audit Ledger Interface language.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 23:35:03 +01:00
parent d8d1524dac
commit 898b0f8747
4 changed files with 782 additions and 777 deletions

View File

@@ -1906,8 +1906,8 @@ between the identity layer and the application layer:<a href="#section-3.2-1" cl
|
v
+--------------------------------------------------+
| Ledger Layer (Immutable Record) |
| "All ECTs appended to audit ledger" |
| Optional: Audit Ledger (Immutable Record) |
| "ECTs MAY be appended to an audit ledger" |
+--------------------------------------------------+
</pre>
</div>
@@ -1978,7 +1978,8 @@ trust domain. WPT verification, if present, per
evaluated, and what precedent tasks exist.<a href="#section-3.3-7.2.1" class="pilcrow"></a></p>
</li>
<li id="section-3.3-7.3">
<p id="section-3.3-7.3.1">Ledger: Appends the verified ECT to the audit ledger.<a href="#section-3.3-7.3.1" class="pilcrow"></a></p>
<p id="section-3.3-7.3.1">Ledger (if deployed): Appends the verified ECT to the audit
ledger.<a href="#section-3.3-7.3.1" class="pilcrow"></a></p>
</li>
</ol>
</section>
@@ -2329,42 +2330,41 @@ that do not understand extension claims <span class="bcp14">MUST</span> ignore t
<dd class="break"></dd>
</dl>
<p id="section-4.2.6-2">To avoid key collisions between different domains, extension
key names <span class="bcp14">MUST</span> use reverse domain notation (e.g.,
"com.example.custom_field"). Implementations <span class="bcp14">MUST NOT</span> use
unqualified key names within the "ext" object. To prevent
abuse and excessive token size, the serialized JSON
representation of the "ext" object <span class="bcp14">SHOULD NOT</span> exceed 4096
bytes, and the JSON nesting depth within the "ext" object
<span class="bcp14">SHOULD NOT</span> exceed 5 levels. Implementations <span class="bcp14">SHOULD</span> reject
ECTs whose "ext" claim exceeds these limits.<a href="#section-4.2.6-2" class="pilcrow"></a></p>
<p id="section-4.2.6-3">The following extension keys are <span class="bcp14">RECOMMENDED</span> for common use
cases. These are not registered claims; they are carried
within the "ext" object:<a href="#section-4.2.6-3" class="pilcrow"></a></p>
key names <span class="bcp14">SHOULD</span> use reverse domain notation (e.g.,
"com.example.custom_field") to avoid collisions between
independently developed extensions. To prevent abuse and
excessive token size, the serialized JSON representation of
the "ext" object <span class="bcp14">SHOULD NOT</span> exceed 4096 bytes, and the JSON
nesting depth within the "ext" object <span class="bcp14">SHOULD NOT</span> exceed 5
levels. Implementations <span class="bcp14">SHOULD</span> reject ECTs whose "ext" claim
exceeds these limits.<a href="#section-4.2.6-2" class="pilcrow"></a></p>
<p id="section-4.2.6-3">The following extension keys are defined by this specification
for common use cases. Because these keys are documented here,
they use short names without reverse domain prefixes:<a href="#section-4.2.6-3" class="pilcrow"></a></p>
<ul class="normal">
<li class="normal" id="section-4.2.6-4.1">
<p id="section-4.2.6-4.1.1">"org.ietf.wimse.exec_time_ms": Integer. Execution duration in
milliseconds.<a href="#section-4.2.6-4.1.1" class="pilcrow"></a></p>
<p id="section-4.2.6-4.1.1">"exec_time_ms": Integer. Execution duration in milliseconds.<a href="#section-4.2.6-4.1.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-4.2.6-4.2">
<p id="section-4.2.6-4.2.1">"org.ietf.wimse.regulated_domain": String. Regulatory domain
(e.g., "medtech", "finance", "military").<a href="#section-4.2.6-4.2.1" class="pilcrow"></a></p>
<p id="section-4.2.6-4.2.1">"regulated_domain": String. Regulatory domain (e.g.,
"medtech", "finance", "military").<a href="#section-4.2.6-4.2.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-4.2.6-4.3">
<p id="section-4.2.6-4.3.1">"org.ietf.wimse.model_version": String. AI/ML model version.<a href="#section-4.2.6-4.3.1" class="pilcrow"></a></p>
<p id="section-4.2.6-4.3.1">"model_version": String. AI/ML model version.<a href="#section-4.2.6-4.3.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-4.2.6-4.4">
<p id="section-4.2.6-4.4.1">"org.ietf.wimse.witnessed_by": Array of StringOrURI. Identifiers
of third-party entities that the issuer claims observed the
<p id="section-4.2.6-4.4.1">"witnessed_by": Array of StringOrURI. Identifiers of
third-party entities that the issuer claims observed the
task. Note: this is self-asserted; for verifiable witness
attestation, witnesses should submit independent signed ECTs.<a href="#section-4.2.6-4.4.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-4.2.6-4.5">
<p id="section-4.2.6-4.5.1">"org.ietf.wimse.inp_classification": String. Data sensitivity
classification (e.g., "public", "confidential", "restricted").<a href="#section-4.2.6-4.5.1" class="pilcrow"></a></p>
<p id="section-4.2.6-4.5.1">"inp_classification": String. Data sensitivity classification
(e.g., "public", "confidential", "restricted").<a href="#section-4.2.6-4.5.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-4.2.6-4.6">
<p id="section-4.2.6-4.6.1">"org.ietf.wimse.pol_timestamp": NumericDate. Time at which the
policy decision was made, if distinct from "iat".<a href="#section-4.2.6-4.6.1" class="pilcrow"></a></p>
<p id="section-4.2.6-4.6.1">"pol_timestamp": NumericDate. Time at which the policy
decision was made, if distinct from "iat".<a href="#section-4.2.6-4.6.1" class="pilcrow"></a></p>
</li>
</ul>
</section>
@@ -2401,10 +2401,10 @@ policy decision was made, if distinct from "iat".<a href="#section-4.2.6-4.6.1"
"out_hash": "sha-256:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564",
"ext": {
"org.ietf.wimse.pol_timestamp": 1772064145,
"org.ietf.wimse.exec_time_ms": 245,
"org.ietf.wimse.regulated_domain": "medtech",
"org.ietf.wimse.model_version": "clinical-reasoning-v4.2"
"pol_timestamp": 1772064145,
"exec_time_ms": 245,
"regulated_domain": "medtech",
"model_version": "clinical-reasoning-v4.2"
}
}
</pre>
@@ -2479,8 +2479,8 @@ provides a cryptographically signed record of execution ordering,
enabling auditors to reconstruct the complete workflow and verify
that required predecessor tasks were recorded before dependent
tasks.<a href="#section-6.1-1" class="pilcrow"></a></p>
<p id="section-6.1-2">DAG validation is performed against the audit ledger, which
serves as the authoritative store of previously verified ECTs.<a href="#section-6.1-2" class="pilcrow"></a></p>
<p id="section-6.1-2">DAG validation is performed against the ECT store — either an
audit ledger or the set of parent ECTs received inline.<a href="#section-6.1-2" class="pilcrow"></a></p>
</section>
</div>
<div id="validation-rules">
@@ -2549,14 +2549,14 @@ relationship has been established.<a href="#section-6.2-2.6.1" class="pilcrow">
<figure id="figure-6">
<div class="lang-pseudocode sourcecode" id="section-6.3-2.1">
<pre>
function validate_dag(ect, ledger, clock_skew_tolerance):
function validate_dag(ect, ect_store, clock_skew_tolerance):
// Step 1: Uniqueness check
if ledger.contains(ect.jti, ect.wid):
if ect_store.contains(ect.jti, ect.wid):
return error("ECT ID already exists")
// Step 2: Parent existence and temporal ordering
for parent_id in ect.par:
parent = ledger.get(parent_id)
parent = ect_store.get(parent_id)
if parent is null:
return error("Parent task not found: " + parent_id)
if parent.iat &gt;= ect.iat + clock_skew_tolerance:
@@ -2564,14 +2564,14 @@ function validate_dag(ect, ledger, clock_skew_tolerance):
// Step 3: Cycle detection (with traversal limit)
visited = set()
result = has_cycle(ect.jti, ect.par, ledger, visited,
result = has_cycle(ect.jti, ect.par, ect_store, visited,
max_ancestor_limit)
if result is error or result is true:
return error("Circular dependency or depth limit exceeded")
return success
function has_cycle(target_jti, parent_ids, ledger,
function has_cycle(target_jti, parent_ids, ect_store,
visited, max_depth):
if visited.size() &gt;= max_depth:
return error("Maximum ancestor traversal limit exceeded")
@@ -2581,9 +2581,9 @@ function has_cycle(target_jti, parent_ids, ledger,
if parent_id in visited:
continue
visited.add(parent_id)
parent = ledger.get(parent_id)
parent = ect_store.get(parent_id)
if parent is not null:
result = has_cycle(target_jti, parent.par, ledger,
result = has_cycle(target_jti, parent.par, ect_store,
visited, max_depth)
if result is error or result is true:
return result
@@ -2714,7 +2714,7 @@ fails.<a href="#section-7.1-4" class="pilcrow">¶</a></p>
<div class="breakable lang-pseudocode sourcecode" id="section-7.2-1.1">
<pre>
function verify_ect(ect_jws, verifier_id,
trust_domain_keys, ledger):
trust_domain_keys, ect_store):
// Parse JWS
(header, payload, signature) = parse_jws(ect_jws)
@@ -2775,14 +2775,15 @@ function verify_ect(ect_jws, verifier_id,
["approved", "rejected", "pending_human_review"]:
return reject("Invalid pol_decision value")
// Validate DAG (against ledger or inline parent ECTs)
result = validate_dag(payload, ledger,
// Validate DAG (against ECT store or inline parent ECTs)
result = validate_dag(payload, ect_store,
clock_skew_tolerance)
if result is error:
return reject("DAG validation failed")
// All checks passed; append to ledger
ledger.append(payload)
// All checks passed; record if store is available
if ect_store is not null:
ect_store.append(payload)
return accept
</pre>
</div>
@@ -2799,14 +2800,14 @@ function verify_ect(ect_jws, verifier_id,
<h2 id="name-audit-ledger-interface">
<a href="#section-8" class="section-number selfRef">8. </a><a href="#name-audit-ledger-interface" class="section-name selfRef">Audit Ledger Interface</a>
</h2>
<p id="section-8-1">ECTs are designed to be recorded in an immutable audit ledger for
compliance verification and post-hoc analysis. This specification
defines required properties for the ledger but does not mandate
a specific storage technology. Implementations <span class="bcp14">MAY</span> use
append-only logs, databases with cryptographic commitment schemes,
distributed ledgers, or any storage mechanism that provides the
required properties.<a href="#section-8-1" class="pilcrow"></a></p>
<p id="section-8-2">An audit ledger implementation <span class="bcp14">MUST</span> provide:<a href="#section-8-2" class="pilcrow"></a></p>
<p id="section-8-1">ECTs <span class="bcp14">MAY</span> be recorded in an immutable audit ledger for compliance
verification and post-hoc analysis. A ledger is <span class="bcp14">RECOMMENDED</span> for
regulated environments but is not required for point-to-point
operation. This specification does not mandate a specific storage
technology. Implementations <span class="bcp14">MAY</span> use append-only logs, databases
with cryptographic commitment schemes, distributed ledgers, or
any storage mechanism that provides the required properties.<a href="#section-8-1" class="pilcrow"></a></p>
<p id="section-8-2">When an audit ledger is deployed, the implementation <span class="bcp14">MUST</span> provide:<a href="#section-8-2" class="pilcrow"></a></p>
<ol start="1" type="1" class="normal type-1" id="section-8-3">
<li id="section-8-3.1">
<p id="section-8-3.1.1">Append-only semantics: Once an ECT is recorded, it <span class="bcp14">MUST NOT</span> be
@@ -2883,7 +2884,7 @@ Human Release Manager:
exec_act: approve_release
pol: release_approval_policy pol_decision: approved
pol_enforcer: spiffe://meddev.example/human/release-mgr-42
ext: {org.ietf.wimse.witnessed_by: [...]} (extension metadata)
ext: {witnessed_by: [...]} (extension metadata)
</pre>
</div>
<figcaption><a href="#figure-8" class="selfRef">Figure 8</a>:
@@ -3167,7 +3168,7 @@ evaluating the policy).<a href="#section-10.2-1" class="pilcrow">¶</a></p>
</ul>
<p id="section-10.2-4">The trustworthiness of ECT claims depends on the trustworthiness
of the signing agent. To mitigate single-agent false claims,
regulated environments <span class="bcp14">SHOULD</span> use the "org.ietf.wimse.witnessed_by"
regulated environments <span class="bcp14">SHOULD</span> use the "witnessed_by"
extension key (carried in "ext") to include independent
third-party observers at critical decision points. However,
this value is self-asserted by the ECT issuer: the listed
@@ -3181,7 +3182,7 @@ did not participate.<a href="#section-10.2-4" class="pilcrow">¶</a></p>
<a href="#section-10.2.1" class="section-number selfRef">10.2.1. </a><a href="#name-witness-attestation-model" class="section-name selfRef">Witness Attestation Model</a>
</h4>
<p id="section-10.2.1-1">To address the self-assertion limitation of the
"org.ietf.wimse.witnessed_by" extension, witnesses <span class="bcp14">SHOULD</span> submit their
"witnessed_by" extension, witnesses <span class="bcp14">SHOULD</span> submit their
own independent signed ECTs to the audit ledger attesting to the
observed task. A witness attestation ECT:<a href="#section-10.2.1-1" class="pilcrow"></a></p>
<ul class="normal">
@@ -3201,7 +3202,7 @@ linking the attestation to the original task.<a href="#section-10.2.1-2.3.1" cla
confirms the observation.<a href="#section-10.2.1-2.4.1" class="pilcrow"></a></p>
</li>
</ul>
<p id="section-10.2.1-3">When a task's "org.ietf.wimse.witnessed_by" extension lists one or more
<p id="section-10.2.1-3">When a task's "witnessed_by" extension lists one or more
witnesses, auditors <span class="bcp14">SHOULD</span> verify that corresponding witness
attestation ECTs exist in the ledger for each listed witness. A
mismatch between the extension value and the set of independent
@@ -3353,7 +3354,7 @@ create a false execution history if they control the ledger.<a href="#section-10
by an entity independent of the workflow agents.<a href="#section-10.8-3.1.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-10.8-3.2">
<p id="section-10.8-3.2.1">Witness attestation: Using the "org.ietf.wimse.witnessed_by" extension
<p id="section-10.8-3.2.1">Witness attestation: Using the "witnessed_by" extension
key in "ext" to include independent third-party observers.<a href="#section-10.8-3.2.1" class="pilcrow"></a></p>
</li>
<li class="normal" id="section-10.8-3.3">
@@ -4412,7 +4413,7 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
"pol_decision": "approved",
"pol_enforcer": "spiffe://meddev.example/human/release-mgr-42",
"ext": {
"org.ietf.wimse.witnessed_by": [
"witnessed_by": [
"spiffe://meddev.example/audit/qa-observer-1"
]
}
@@ -4423,7 +4424,7 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
implementation, implementation preceded testing, testing preceded
build, and a human release manager approved the final release.
The "ext" object in task 5 carries witness metadata via
the "org.ietf.wimse.witnessed_by" extension key.<a href="#appendix-D.2-12" class="pilcrow"></a></p>
the "witnessed_by" extension key.<a href="#appendix-D.2-12" class="pilcrow"></a></p>
<div class="alignLeft art-text artwork" id="appendix-D.2-13">
<pre>
task-...-0001 (review_requirements_spec)