Merge tid into jti and make policy claims optional
- Eliminate the "tid" claim; "jti" now serves as both token ID (for replay detection) and task ID (for DAG parent references in "par") - Make "pol" and "pol_decision" OPTIONAL (must be paired when present) - Regulated deployments SHOULD still include policy claims - Reduces required ECT-specific claims to just "exec_act" and "par" - Remove "tid" from IANA JWT Claims registration - Update all examples, pseudocode, and DAG validation rules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2180,11 +2180,15 @@ upon issuance.<a href="#section-4.2.1-3" class="pilcrow">¶</a></p>
|
||||
<span class="break"></span><dl class="dlParallel" id="section-4.2.1-4">
|
||||
<dt id="section-4.2.1-4.1">jti:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.1-4.2">
|
||||
<p id="section-4.2.1-4.2.1"><span class="bcp14">REQUIRED</span>. String. A unique identifier for the ECT in UUID
|
||||
format <span>[<a href="#RFC9562" class="cite xref">RFC9562</a>]</span>. Used for replay detection: receivers <span class="bcp14">MUST</span>
|
||||
reject ECTs whose "jti" has already been seen within the
|
||||
expiration window. The "jti" value <span class="bcp14">MUST</span> be unique across all
|
||||
ECTs issued by the same agent.<a href="#section-4.2.1-4.2.1" class="pilcrow">¶</a></p>
|
||||
<p id="section-4.2.1-4.2.1"><span class="bcp14">REQUIRED</span>. String. A globally unique identifier for both the
|
||||
ECT and the task it records, in UUID format <span>[<a href="#RFC9562" class="cite xref">RFC9562</a>]</span>. Since
|
||||
each ECT represents exactly one task, "jti" serves as both the
|
||||
token identifier (for replay detection) and the task identifier
|
||||
(for DAG parent references in "par"). Receivers <span class="bcp14">MUST</span> reject
|
||||
ECTs whose "jti" has already been seen within the expiration
|
||||
window. When "wid" is present, uniqueness is scoped to the
|
||||
workflow; when "wid" is absent, uniqueness <span class="bcp14">MUST</span> be enforced
|
||||
globally across the ECT store.<a href="#section-4.2.1-4.2.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
</dl>
|
||||
@@ -2201,36 +2205,26 @@ ECTs issued by the same agent.<a href="#section-4.2.1-4.2.1" class="pilcrow">¶<
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.2-2.2">
|
||||
<p id="section-4.2.2-2.2.1"><span class="bcp14">OPTIONAL</span>. String. A workflow identifier that groups related
|
||||
ECTs into a single workflow. When present, <span class="bcp14">MUST</span> be a UUID
|
||||
<span>[<a href="#RFC9562" class="cite xref">RFC9562</a>]</span>. When absent, the "tid" uniqueness requirement
|
||||
applies globally across the entire ledger.<a href="#section-4.2.2-2.2.1" class="pilcrow">¶</a></p>
|
||||
<span>[<a href="#RFC9562" class="cite xref">RFC9562</a>]</span>.<a href="#section-4.2.2-2.2.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
<dt id="section-4.2.2-2.3">tid:</dt>
|
||||
<dt id="section-4.2.2-2.3">exec_act:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.2-2.4">
|
||||
<p id="section-4.2.2-2.4.1"><span class="bcp14">REQUIRED</span>. String. A globally unique task identifier in UUID
|
||||
format <span>[<a href="#RFC9562" class="cite xref">RFC9562</a>]</span>. Each task <span class="bcp14">MUST</span> have a unique "tid" value.
|
||||
When "wid" is present, uniqueness is scoped to the workflow;
|
||||
when "wid" is absent, uniqueness <span class="bcp14">MUST</span> be enforced globally
|
||||
across the ledger.<a href="#section-4.2.2-2.4.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
<dt id="section-4.2.2-2.5">exec_act:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.2-2.6">
|
||||
<p id="section-4.2.2-2.6.1"><span class="bcp14">REQUIRED</span>. String. The action or task type identifier describing
|
||||
<p id="section-4.2.2-2.4.1"><span class="bcp14">REQUIRED</span>. String. The action or task type identifier describing
|
||||
what the agent performed (e.g., "process_payment",
|
||||
"validate_safety", "calculate_dosage"). Note: this claim is
|
||||
intentionally named "exec_act" rather than "act" to avoid
|
||||
collision with the "act" (Actor) claim registered by
|
||||
<span>[<a href="#RFC8693" class="cite xref">RFC8693</a>]</span>.<a href="#section-4.2.2-2.6.1" class="pilcrow">¶</a></p>
|
||||
<span>[<a href="#RFC8693" class="cite xref">RFC8693</a>]</span>.<a href="#section-4.2.2-2.4.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
<dt id="section-4.2.2-2.7">par:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.2-2.8">
|
||||
<p id="section-4.2.2-2.8.1"><span class="bcp14">REQUIRED</span>. Array of strings. Parent task identifiers
|
||||
representing DAG dependencies. Each element <span class="bcp14">MUST</span> be a valid
|
||||
"tid" from a previously executed task. An empty array indicates
|
||||
<dt id="section-4.2.2-2.5">par:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.2-2.6">
|
||||
<p id="section-4.2.2-2.6.1"><span class="bcp14">REQUIRED</span>. Array of strings. Parent task identifiers
|
||||
representing DAG dependencies. Each element <span class="bcp14">MUST</span> be the "jti"
|
||||
value of a previously verified ECT. An empty array indicates
|
||||
a root task with no dependencies. A workflow <span class="bcp14">MAY</span> contain
|
||||
multiple root tasks.<a href="#section-4.2.2-2.8.1" class="pilcrow">¶</a></p>
|
||||
multiple root tasks.<a href="#section-4.2.2-2.6.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
</dl>
|
||||
@@ -2245,17 +2239,18 @@ multiple root tasks.<a href="#section-4.2.2-2.8.1" class="pilcrow">¶</a></p>
|
||||
<span class="break"></span><dl class="dlParallel" id="section-4.2.3-2">
|
||||
<dt id="section-4.2.3-2.1">pol:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.3-2.2">
|
||||
<p id="section-4.2.3-2.2.1"><span class="bcp14">REQUIRED</span>. String. The identifier of the policy rule that was
|
||||
<p id="section-4.2.3-2.2.1"><span class="bcp14">OPTIONAL</span>. String. The identifier of the policy rule that was
|
||||
evaluated for this task (e.g.,
|
||||
"clinical_data_access_policy_v1").<a href="#section-4.2.3-2.2.1" class="pilcrow">¶</a></p>
|
||||
"clinical_data_access_policy_v1"). <span class="bcp14">MUST</span> be present when
|
||||
"pol_decision" is present.<a href="#section-4.2.3-2.2.1" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
<dt id="section-4.2.3-2.3">pol_decision:</dt>
|
||||
<dd style="margin-left: 1.5em" id="section-4.2.3-2.4">
|
||||
<p id="section-4.2.3-2.4.1"><span class="bcp14">REQUIRED</span>. String. The result of the policy evaluation. <span class="bcp14">MUST</span>
|
||||
be one of the values registered in the ECT Policy Decision
|
||||
Values registry (<a href="#pol-decision-registry" class="auto internal xref">Section 13.4</a>). Initial values
|
||||
are:<a href="#section-4.2.3-2.4.1" class="pilcrow">¶</a></p>
|
||||
<p id="section-4.2.3-2.4.1"><span class="bcp14">OPTIONAL</span>. String. The result of the policy evaluation. When
|
||||
present, <span class="bcp14">MUST</span> be one of the values registered in the ECT Policy
|
||||
Decision Values registry (<a href="#pol-decision-registry" class="auto internal xref">Section 13.4</a>). <span class="bcp14">MUST</span> be
|
||||
present when "pol" is present. Initial values are:<a href="#section-4.2.3-2.4.1" class="pilcrow">¶</a></p>
|
||||
<ul class="normal">
|
||||
<li class="normal" id="section-4.2.3-2.4.2.1">
|
||||
<p id="section-4.2.3-2.4.2.1.1">"approved": The policy evaluation succeeded and the task
|
||||
@@ -2263,11 +2258,12 @@ was authorized to proceed.<a href="#section-4.2.3-2.4.2.1.1" class="pilcrow">¶<
|
||||
</li>
|
||||
<li class="normal" id="section-4.2.3-2.4.2.2">
|
||||
<p id="section-4.2.3-2.4.2.2.1">"rejected": The policy evaluation failed. A "rejected" ECT
|
||||
<span class="bcp14">MUST</span> still be appended to the audit ledger for accountability.
|
||||
An ECT with "pol_decision" of "rejected" <span class="bcp14">MAY</span> appear as a
|
||||
parent in the "par" array of a subsequent ECT, but only for
|
||||
compensation, rollback, or remediation tasks. Agents <span class="bcp14">MUST NOT</span> proceed with normal workflow execution based on a parent
|
||||
ECT whose "pol_decision" is "rejected".<a href="#section-4.2.3-2.4.2.2.1" class="pilcrow">¶</a></p>
|
||||
<span class="bcp14">MUST</span> still be recorded for accountability. An ECT with
|
||||
"pol_decision" of "rejected" <span class="bcp14">MAY</span> appear as a parent in the
|
||||
"par" array of a subsequent ECT, but only for compensation,
|
||||
rollback, or remediation tasks. Agents <span class="bcp14">MUST NOT</span> proceed
|
||||
with normal workflow execution based on a parent ECT whose
|
||||
"pol_decision" is "rejected".<a href="#section-4.2.3-2.4.2.2.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li class="normal" id="section-4.2.3-2.4.2.3">
|
||||
<p id="section-4.2.3-2.4.2.3.1">"pending_human_review": The policy evaluation requires human
|
||||
@@ -2277,6 +2273,10 @@ records an "approved" decision referencing this task as a
|
||||
parent.<a href="#section-4.2.3-2.4.2.3.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p id="section-4.2.3-2.4.3">When "pol" and "pol_decision" are absent, the ECT records task
|
||||
execution without a policy checkpoint. Regulated deployments
|
||||
<span class="bcp14">SHOULD</span> include policy claims on all ECTs to maintain complete
|
||||
audit trails.<a href="#section-4.2.3-2.4.3" class="pilcrow">¶</a></p>
|
||||
</dd>
|
||||
<dd class="break"></dd>
|
||||
<dt id="section-4.2.3-2.5">pol_enforcer:</dt>
|
||||
@@ -2463,10 +2463,9 @@ ECTs whose "ext" claim exceeds these limits.<a href="#section-4.2.7-2" class="pi
|
||||
"aud": "spiffe://example.com/agent/safety",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "7f3a8b2c-d1e4-4f56-9a0b-c3d4e5f6a7b8",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
|
||||
"wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"exec_act": "recommend_treatment",
|
||||
"par": [],
|
||||
|
||||
@@ -2576,10 +2575,10 @@ parent ECTs available to the verifier.<a href="#section-6.1-2" class="pilcrow">
|
||||
the following DAG validation steps:<a href="#section-6.2-1" class="pilcrow">¶</a></p>
|
||||
<ol start="1" type="1" class="normal type-1" id="section-6.2-2">
|
||||
<li id="section-6.2-2.1">
|
||||
<p id="section-6.2-2.1.1">Task ID Uniqueness: The "tid" claim <span class="bcp14">MUST</span> be unique within the
|
||||
<p id="section-6.2-2.1.1">Task ID Uniqueness: The "jti" claim <span class="bcp14">MUST</span> be unique within the
|
||||
applicable scope (the workflow identified by "wid", or the
|
||||
entire ECT store if "wid" is absent). If a task with the same
|
||||
"tid" already exists, the ECT <span class="bcp14">MUST</span> be rejected.<a href="#section-6.2-2.1.1" class="pilcrow">¶</a></p>
|
||||
entire ECT store if "wid" is absent). If an ECT with the same
|
||||
"jti" already exists, the ECT <span class="bcp14">MUST</span> be rejected.<a href="#section-6.2-2.1.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-6.2-2.2">
|
||||
<p id="section-6.2-2.2.1">Parent Existence: Every task identifier listed in the "par"
|
||||
@@ -2601,17 +2600,19 @@ ECT <span class="bcp14">MUST</span> be rejected.<a href="#section-6.2-2.3.1" cla
|
||||
</li>
|
||||
<li id="section-6.2-2.4">
|
||||
<p id="section-6.2-2.4.1">Acyclicity: Following the chain of parent references <span class="bcp14">MUST NOT</span>
|
||||
lead back to the current task's "tid". If a cycle is detected,
|
||||
lead back to the current ECT's "jti". If a cycle is detected,
|
||||
the ECT <span class="bcp14">MUST</span> be rejected.<a href="#section-6.2-2.4.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-6.2-2.5">
|
||||
<p id="section-6.2-2.5.1">Parent Policy Decision: If any parent task has a "pol_decision"
|
||||
of "rejected" or "pending_human_review", the current task's
|
||||
"exec_act" <span class="bcp14">MUST</span> indicate a compensation, rollback, remediation,
|
||||
or human review action. Implementations <span class="bcp14">MUST NOT</span> accept an ECT
|
||||
representing normal workflow continuation when a parent's
|
||||
"pol_decision" is not "approved", unless the current ECT has
|
||||
"compensation_required" set to true.<a href="#section-6.2-2.5.1" class="pilcrow">¶</a></p>
|
||||
<p id="section-6.2-2.5.1">Parent Policy Decision: If any parent ECT contains a
|
||||
"pol_decision" of "rejected" or "pending_human_review", the
|
||||
current ECT's "exec_act" <span class="bcp14">MUST</span> indicate a compensation,
|
||||
rollback, remediation, or human review action.
|
||||
Implementations <span class="bcp14">MUST NOT</span> accept an ECT representing normal
|
||||
workflow continuation when a parent's "pol_decision" is not
|
||||
"approved", unless the current ECT has "compensation_required"
|
||||
set to true. This rule only applies when the parent ECT
|
||||
contains policy claims.<a href="#section-6.2-2.5.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-6.2-2.6">
|
||||
<p id="section-6.2-2.6.1">Trust Domain Consistency: Parent tasks <span class="bcp14">SHOULD</span> belong to the
|
||||
@@ -2634,8 +2635,8 @@ relationship has been established.<a href="#section-6.2-2.6.1" class="pilcrow">
|
||||
function validate_dag(ect, ect_store, clock_skew_tolerance):
|
||||
// ect_store: ledger or local cache of verified ECTs
|
||||
// Step 1: Uniqueness check
|
||||
if ect_store.contains(ect.tid, ect.wid):
|
||||
return error("Task ID already exists")
|
||||
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:
|
||||
@@ -2647,26 +2648,26 @@ function validate_dag(ect, ect_store, clock_skew_tolerance):
|
||||
|
||||
// Step 3: Cycle detection (with traversal limit)
|
||||
visited = set()
|
||||
result = has_cycle(ect.tid, ect.par, ect_store, 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_tid, parent_ids, ect_store,
|
||||
function has_cycle(target_jti, parent_ids, ect_store,
|
||||
visited, max_depth):
|
||||
if visited.size() >= max_depth:
|
||||
return error("Maximum ancestor traversal limit exceeded")
|
||||
for parent_id in parent_ids:
|
||||
if parent_id == target_tid:
|
||||
if parent_id == target_jti:
|
||||
return true
|
||||
if parent_id in visited:
|
||||
continue
|
||||
visited.add(parent_id)
|
||||
parent = ect_store.get(parent_id)
|
||||
if parent is not null:
|
||||
result = has_cycle(target_tid, parent.par, ect_store,
|
||||
result = has_cycle(target_jti, parent.par, ect_store,
|
||||
visited, max_depth)
|
||||
if result is error or result is true:
|
||||
return result
|
||||
@@ -2757,12 +2758,13 @@ identity <span class="bcp14">MUST</span> appear in "aud".<a href="#section-7.1-2
|
||||
verifier's current time, to account for clock skew).<a href="#section-7.1-2.11.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-7.1-2.12">
|
||||
<p id="section-7.1-2.12.1">Verify all required claims ("jti", "tid", "exec_act", "par",
|
||||
"pol", "pol_decision") are present and well-formed.<a href="#section-7.1-2.12.1" class="pilcrow">¶</a></p>
|
||||
<p id="section-7.1-2.12.1">Verify all required claims ("jti", "exec_act", "par") are
|
||||
present and well-formed.<a href="#section-7.1-2.12.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-7.1-2.13">
|
||||
<p id="section-7.1-2.13.1">Verify "pol_decision" is one of "approved", "rejected", or
|
||||
"pending_human_review".<a href="#section-7.1-2.13.1" class="pilcrow">¶</a></p>
|
||||
<p id="section-7.1-2.13.1">If "pol" or "pol_decision" is present, verify that both are
|
||||
present and that "pol_decision" is one of "approved",
|
||||
"rejected", or "pending_human_review".<a href="#section-7.1-2.13.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-7.1-2.14">
|
||||
<p id="section-7.1-2.14.1">Perform DAG validation per <a href="#dag-validation" class="auto internal xref">Section 6</a>.<a href="#section-7.1-2.14.1" class="pilcrow">¶</a></p>
|
||||
@@ -2847,12 +2849,14 @@ function verify_ect(ect_jws, verifier_id,
|
||||
return reject("ECT issued in the future")
|
||||
|
||||
// Verify required claims
|
||||
for claim in ["jti", "tid", "exec_act", "par",
|
||||
"pol", "pol_decision"]:
|
||||
for claim in ["jti", "exec_act", "par"]:
|
||||
if claim not in payload:
|
||||
return reject("Missing required claim: " + claim)
|
||||
|
||||
// Validate pol_decision value
|
||||
// Validate policy claims (optional, but must be paired)
|
||||
if "pol" in payload or "pol_decision" in payload:
|
||||
if "pol" not in payload or "pol_decision" not in payload:
|
||||
return reject("pol and pol_decision must both be present")
|
||||
if payload.pol_decision not in
|
||||
["approved", "rejected", "pending_human_review"]:
|
||||
return reject("Invalid pol_decision value")
|
||||
@@ -2921,7 +2925,7 @@ ECTs<a href="#section-8.1-5.1.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li class="normal" id="section-8.1-5.2">
|
||||
<p id="section-8.1-5.2.1">Global replay detection relies solely on "jti" caches at each
|
||||
agent; there is no centralized "tid" uniqueness check<a href="#section-8.1-5.2.1" class="pilcrow">¶</a></p>
|
||||
agent; there is no centralized "jti" uniqueness check<a href="#section-8.1-5.2.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li class="normal" id="section-8.1-5.3">
|
||||
<p id="section-8.1-5.3.1">The parent ECT chain grows with each hop, increasing HTTP
|
||||
@@ -3008,7 +3012,7 @@ entries via a monotonically increasing sequence number.<a href="#section-9.2-2.2
|
||||
</li>
|
||||
<li id="section-9.2-2.3">
|
||||
<p id="section-9.2-2.3.1">Lookup by task ID: The ledger <span class="bcp14">MUST</span> support efficient retrieval
|
||||
of ECT entries by "tid" value.<a href="#section-9.2-2.3.1" class="pilcrow">¶</a></p>
|
||||
of ECT entries by "jti" value.<a href="#section-9.2-2.3.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="section-9.2-2.4">
|
||||
<p id="section-9.2-2.4.1">Integrity verification: The ledger <span class="bcp14">SHOULD</span> provide a mechanism
|
||||
@@ -3032,7 +3036,7 @@ workflow agents to reduce the risk of collusion.<a href="#section-9.2-3" class="
|
||||
<pre>
|
||||
{
|
||||
"ledger_sequence": 42,
|
||||
"task_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"ect_jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"agent_id": "spiffe://example.com/agent/clinical",
|
||||
"action": "recommend_treatment",
|
||||
"parents": [],
|
||||
@@ -3070,7 +3074,7 @@ examples demonstrate ECT mechanics; production deployments would
|
||||
include additional domain-specific requirements beyond the scope
|
||||
of this specification.<a href="#section-10-1" class="pilcrow">¶</a></p>
|
||||
<p id="section-10-2">Note: task identifiers in this section are abbreviated for
|
||||
readability. In production, all "tid" values are required to be
|
||||
readability. In production, all "jti" values are required to be
|
||||
UUIDs per <a href="#exec-claims" class="auto internal xref">Section 4.2.2</a>.<a href="#section-10-2" class="pilcrow">¶</a></p>
|
||||
<div id="medical-device-sdlc-workflow">
|
||||
<section id="section-10.1">
|
||||
@@ -3088,27 +3092,27 @@ software used in medical devices.<a href="#section-10.1-1" class="pilcrow">¶</a
|
||||
<div class="alignLeft art-text artwork" id="section-10.1-2.1">
|
||||
<pre>
|
||||
Agent A (Spec Reviewer):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: review_requirements_spec
|
||||
pol: spec_review_policy_v2 pol_decision: approved
|
||||
|
||||
Agent B (Code Generator):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: implement_module
|
||||
pol: coding_standards_v3 pol_decision: approved
|
||||
|
||||
Agent C (Test Agent):
|
||||
tid: task-003 par: [task-002]
|
||||
jti: task-003 par: [task-002]
|
||||
exec_act: execute_test_suite
|
||||
pol: test_coverage_policy_v1 pol_decision: approved
|
||||
|
||||
Agent D (Build Agent):
|
||||
tid: task-004 par: [task-003]
|
||||
jti: task-004 par: [task-003]
|
||||
exec_act: build_release_artifact
|
||||
pol: build_validation_v2 pol_decision: approved
|
||||
|
||||
Human Release Manager:
|
||||
tid: task-005 par: [task-004]
|
||||
jti: task-005 par: [task-004]
|
||||
exec_act: approve_release
|
||||
pol: release_approval_policy pol_decision: approved
|
||||
pol_enforcer: spiffe://meddev.example/human/release-mgr-42
|
||||
@@ -3213,17 +3217,17 @@ execution.<a href="#section-10.2-1" class="pilcrow">¶</a></p>
|
||||
<div class="alignLeft art-text artwork" id="section-10.2-2.1">
|
||||
<pre>
|
||||
Agent A (Risk Assessment):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: calculate_risk_exposure
|
||||
pol: risk_limits_policy_v2 pol_decision: approved
|
||||
|
||||
Agent B (Compliance):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: verify_compliance
|
||||
pol: compliance_check_v1 pol_decision: approved
|
||||
|
||||
Agent C (Execution):
|
||||
tid: task-003 par: [task-002]
|
||||
jti: task-003 par: [task-002]
|
||||
exec_act: execute_trade
|
||||
pol: execution_policy_v3 pol_decision: approved
|
||||
</pre>
|
||||
@@ -3266,9 +3270,8 @@ a cryptographic link to the original task:<a href="#section-10.3-1" class="pilcr
|
||||
"aud": "spiffe://bank.example/system/ledger",
|
||||
"iat": 1772150550,
|
||||
"exp": 1772151150,
|
||||
"jti": "e4f5a6b7-c8d9-0123-ef01-234567890abc",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440099",
|
||||
"wid": "d3e4f5a6-b7c8-9012-def0-123456789012",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440099",
|
||||
"exec_act": "initiate_trade_rollback",
|
||||
"par": ["550e8400-e29b-41d4-a716-446655440003"],
|
||||
"pol": "compensation_policy_v1",
|
||||
@@ -3301,27 +3304,27 @@ required checks were completed:<a href="#section-10.4-1" class="pilcrow">¶</a><
|
||||
<div class="alignLeft art-text artwork" id="section-10.4-2.1">
|
||||
<pre>
|
||||
Agent A (Route Planning):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: plan_route
|
||||
pol: route_policy_v1 pol_decision: approved
|
||||
|
||||
Agent B (Customs):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: validate_customs
|
||||
pol: customs_policy_v2 pol_decision: approved
|
||||
|
||||
Agent C (Safety):
|
||||
tid: task-003 par: [task-001]
|
||||
jti: task-003 par: [task-001]
|
||||
exec_act: verify_cargo_safety
|
||||
pol: safety_policy_v1 pol_decision: approved
|
||||
|
||||
Agent D (Payment):
|
||||
tid: task-004 par: [task-002, task-003]
|
||||
jti: task-004 par: [task-002, task-003]
|
||||
exec_act: authorize_payment
|
||||
pol: payment_policy_v3 pol_decision: approved
|
||||
|
||||
System (Commitment):
|
||||
tid: task-005 par: [task-004]
|
||||
jti: task-005 par: [task-004]
|
||||
exec_act: commit_shipment
|
||||
pol: commitment_policy_v1 pol_decision: approved
|
||||
</pre>
|
||||
@@ -3422,7 +3425,7 @@ attestation ECT:<a href="#section-11.2.1-1" class="pilcrow">¶</a></p>
|
||||
specific equivalent).<a href="#section-11.2.1-2.2.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li class="normal" id="section-11.2.1-2.3">
|
||||
<p id="section-11.2.1-2.3.1"><span class="bcp14">MUST</span> include the observed task's "tid" in the "par" array,
|
||||
<p id="section-11.2.1-2.3.1"><span class="bcp14">MUST</span> include the observed task's "jti" in the "par" array,
|
||||
linking the attestation to the original task.<a href="#section-11.2.1-2.3.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li class="normal" id="section-11.2.1-2.4">
|
||||
@@ -3892,14 +3895,6 @@ the "JSON Web Token Claims" registry maintained by IANA:<a href="#section-13.3-1
|
||||
<td class="text-center" rowspan="1" colspan="1">IETF</td>
|
||||
<td class="text-center" rowspan="1" colspan="1">
|
||||
<a href="#exec-claims" class="auto internal xref">Section 4.2.2</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center" rowspan="1" colspan="1">tid</td>
|
||||
<td class="text-left" rowspan="1" colspan="1">Task Identifier</td>
|
||||
<td class="text-center" rowspan="1" colspan="1">IETF</td>
|
||||
<td class="text-center" rowspan="1" colspan="1">
|
||||
<a href="#exec-claims" class="auto internal xref">Section 4.2.2</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -4438,8 +4433,8 @@ use cases are distinct.<a href="#appendix-A.7-1" class="pilcrow">¶</a></p>
|
||||
<ol start="1" type="1" class="normal type-1" id="appendix-B.1-2">
|
||||
<li id="appendix-B.1-2.1">
|
||||
<p id="appendix-B.1-2.1.1">Create JWTs with all required claims ("iss", "aud", "iat",
|
||||
"exp", "jti", "tid", "exec_act", "par", "pol",
|
||||
"pol_decision").<a href="#appendix-B.1-2.1.1" class="pilcrow">¶</a></p>
|
||||
"exp", "jti", "exec_act", "par") and policy claims ("pol",
|
||||
"pol_decision") when policy evaluation was performed.<a href="#appendix-B.1-2.1.1" class="pilcrow">¶</a></p>
|
||||
</li>
|
||||
<li id="appendix-B.1-2.2">
|
||||
<p id="appendix-B.1-2.2.1">Sign ECTs with the agent's private key using an algorithm
|
||||
@@ -4612,9 +4607,8 @@ Agent B:<a href="#appendix-D.1-1" class="pilcrow">¶</a></p>
|
||||
"aud": "spiffe://example.com/agent/validator",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "1a2b3c4d-e5f6-7890-abcd-ef0123456701",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"wid": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"exec_act": "fetch_patient_data",
|
||||
"par": [],
|
||||
"pol": "clinical_data_access_policy_v1",
|
||||
@@ -4636,9 +4630,8 @@ task, and creates its own ECT:<a href="#appendix-D.1-6" class="pilcrow">¶</a></
|
||||
"aud": "spiffe://example.com/system/ledger",
|
||||
"iat": 1772064160,
|
||||
"exp": 1772064760,
|
||||
"jti": "2b3c4d5e-f6a7-8901-bcde-f01234567802",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"wid": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"exec_act": "validate_safety",
|
||||
"par": ["550e8400-e29b-41d4-a716-446655440001"],
|
||||
"pol": "safety_validation_policy_v2",
|
||||
@@ -4675,9 +4668,8 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
|
||||
"aud": "spiffe://meddev.example/agent/code-gen",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "3c4d5e6f-a7b8-9012-cdef-012345678903",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000001",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000001",
|
||||
"exec_act": "review_requirements_spec",
|
||||
"par": [],
|
||||
"pol": "spec_review_policy_v2",
|
||||
@@ -4698,9 +4690,8 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
|
||||
"aud": "spiffe://meddev.example/agent/test-runner",
|
||||
"iat": 1772064200,
|
||||
"exp": 1772064800,
|
||||
"jti": "4d5e6f7a-b8c9-0123-def0-123456789004",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000002",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000002",
|
||||
"exec_act": "implement_module",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000001"],
|
||||
"pol": "coding_standards_v3",
|
||||
@@ -4719,9 +4710,8 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
|
||||
"aud": "spiffe://meddev.example/agent/build",
|
||||
"iat": 1772064260,
|
||||
"exp": 1772064860,
|
||||
"jti": "5e6f7a8b-c9d0-1234-ef01-234567890005",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000003",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000003",
|
||||
"exec_act": "execute_test_suite",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000002"],
|
||||
"pol": "test_coverage_policy_v1",
|
||||
@@ -4740,9 +4730,8 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
|
||||
"aud": "spiffe://meddev.example/human/release-mgr-42",
|
||||
"iat": 1772064310,
|
||||
"exp": 1772064910,
|
||||
"jti": "6f7a8b9c-d0e1-2345-f012-345678900006",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000004",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000004",
|
||||
"exec_act": "build_release_artifact",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000003"],
|
||||
"pol": "build_validation_v2",
|
||||
@@ -4761,9 +4750,8 @@ autonomous agents and human release approval:<a href="#appendix-D.2-1" class="pi
|
||||
"aud": "spiffe://meddev.example/system/ledger",
|
||||
"iat": 1772064510,
|
||||
"exp": 1772065110,
|
||||
"jti": "7a8b9c0d-e1f2-3456-0123-456789000007",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000005",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000005",
|
||||
"exec_act": "approve_release",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000004"],
|
||||
"pol": "release_approval_policy",
|
||||
@@ -4833,9 +4821,8 @@ task-...-0004 (execute_trade)
|
||||
"aud": "spiffe://bank.example/system/ledger",
|
||||
"iat": 1772064250,
|
||||
"exp": 1772064850,
|
||||
"jti": "8b9c0d1e-f2a3-4567-1234-567890000008",
|
||||
"jti": "f1e2d3c4-0004-0000-0000-000000000004",
|
||||
"wid": "d3e4f5a6-b7c8-9012-def0-123456789012",
|
||||
"tid": "f1e2d3c4-0004-0000-0000-000000000004",
|
||||
"exec_act": "execute_trade",
|
||||
"par": [
|
||||
"f1e2d3c4-0002-0000-0000-000000000002",
|
||||
|
||||
@@ -462,11 +462,15 @@ because ECTs record completed actions and are valid immediately
|
||||
upon issuance.
|
||||
|
||||
jti:
|
||||
: REQUIRED. String. A unique identifier for the ECT in UUID
|
||||
format {{RFC9562}}. Used for replay detection: receivers MUST
|
||||
reject ECTs whose "jti" has already been seen within the
|
||||
expiration window. The "jti" value MUST be unique across all
|
||||
ECTs issued by the same agent.
|
||||
: REQUIRED. String. A globally unique identifier for both the
|
||||
ECT and the task it records, in UUID format {{RFC9562}}. Since
|
||||
each ECT represents exactly one task, "jti" serves as both the
|
||||
token identifier (for replay detection) and the task identifier
|
||||
(for DAG parent references in "par"). Receivers MUST reject
|
||||
ECTs whose "jti" has already been seen within the expiration
|
||||
window. When "wid" is present, uniqueness is scoped to the
|
||||
workflow; when "wid" is absent, uniqueness MUST be enforced
|
||||
globally across the ECT store.
|
||||
|
||||
### Execution Context {#exec-claims}
|
||||
|
||||
@@ -475,15 +479,7 @@ The following claims are defined by this specification:
|
||||
wid:
|
||||
: OPTIONAL. String. A workflow identifier that groups related
|
||||
ECTs into a single workflow. When present, MUST be a UUID
|
||||
{{RFC9562}}. When absent, the "tid" uniqueness requirement
|
||||
applies globally across the entire ledger.
|
||||
|
||||
tid:
|
||||
: REQUIRED. String. A globally unique task identifier in UUID
|
||||
format {{RFC9562}}. Each task MUST have a unique "tid" value.
|
||||
When "wid" is present, uniqueness is scoped to the workflow;
|
||||
when "wid" is absent, uniqueness MUST be enforced globally
|
||||
across the ledger.
|
||||
{{RFC9562}}.
|
||||
|
||||
exec_act:
|
||||
: REQUIRED. String. The action or task type identifier describing
|
||||
@@ -495,8 +491,8 @@ exec_act:
|
||||
|
||||
par:
|
||||
: REQUIRED. Array of strings. Parent task identifiers
|
||||
representing DAG dependencies. Each element MUST be a valid
|
||||
"tid" from a previously executed task. An empty array indicates
|
||||
representing DAG dependencies. Each element MUST be the "jti"
|
||||
value of a previously verified ECT. An empty array indicates
|
||||
a root task with no dependencies. A workflow MAY contain
|
||||
multiple root tasks.
|
||||
|
||||
@@ -505,26 +501,27 @@ par:
|
||||
The following claims record policy evaluation outcomes:
|
||||
|
||||
pol:
|
||||
: REQUIRED. String. The identifier of the policy rule that was
|
||||
: OPTIONAL. String. The identifier of the policy rule that was
|
||||
evaluated for this task (e.g.,
|
||||
"clinical_data_access_policy_v1").
|
||||
"clinical_data_access_policy_v1"). MUST be present when
|
||||
"pol_decision" is present.
|
||||
|
||||
pol_decision:
|
||||
: REQUIRED. String. The result of the policy evaluation. MUST
|
||||
be one of the values registered in the ECT Policy Decision
|
||||
Values registry ({{pol-decision-registry}}). Initial values
|
||||
are:
|
||||
: OPTIONAL. String. The result of the policy evaluation. When
|
||||
present, MUST be one of the values registered in the ECT Policy
|
||||
Decision Values registry ({{pol-decision-registry}}). MUST be
|
||||
present when "pol" is present. Initial values are:
|
||||
|
||||
* "approved": The policy evaluation succeeded and the task
|
||||
was authorized to proceed.
|
||||
|
||||
* "rejected": The policy evaluation failed. A "rejected" ECT
|
||||
MUST still be appended to the audit ledger for accountability.
|
||||
An ECT with "pol_decision" of "rejected" MAY appear as a
|
||||
parent in the "par" array of a subsequent ECT, but only for
|
||||
compensation, rollback, or remediation tasks. Agents MUST
|
||||
NOT proceed with normal workflow execution based on a parent
|
||||
ECT whose "pol_decision" is "rejected".
|
||||
MUST still be recorded for accountability. An ECT with
|
||||
"pol_decision" of "rejected" MAY appear as a parent in the
|
||||
"par" array of a subsequent ECT, but only for compensation,
|
||||
rollback, or remediation tasks. Agents MUST NOT proceed
|
||||
with normal workflow execution based on a parent ECT whose
|
||||
"pol_decision" is "rejected".
|
||||
|
||||
* "pending_human_review": The policy evaluation requires human
|
||||
judgment before proceeding. Agents MUST NOT proceed with
|
||||
@@ -532,6 +529,11 @@ pol_decision:
|
||||
records an "approved" decision referencing this task as a
|
||||
parent.
|
||||
|
||||
When "pol" and "pol_decision" are absent, the ECT records task
|
||||
execution without a policy checkpoint. Regulated deployments
|
||||
SHOULD include policy claims on all ECTs to maintain complete
|
||||
audit trails.
|
||||
|
||||
pol_enforcer:
|
||||
: OPTIONAL. StringOrURI. The identity of the entity (system or
|
||||
person) that evaluated the policy decision. When present,
|
||||
@@ -659,10 +661,9 @@ The following is a complete ECT payload example:
|
||||
"aud": "spiffe://example.com/agent/safety",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "7f3a8b2c-d1e4-4f56-9a0b-c3d4e5f6a7b8",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
|
||||
"wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"exec_act": "recommend_treatment",
|
||||
"par": [],
|
||||
|
||||
@@ -744,10 +745,10 @@ parent ECTs available to the verifier.
|
||||
When receiving and verifying an ECT, implementations MUST perform
|
||||
the following DAG validation steps:
|
||||
|
||||
1. Task ID Uniqueness: The "tid" claim MUST be unique within the
|
||||
1. Task ID Uniqueness: The "jti" claim MUST be unique within the
|
||||
applicable scope (the workflow identified by "wid", or the
|
||||
entire ECT store if "wid" is absent). If a task with the same
|
||||
"tid" already exists, the ECT MUST be rejected.
|
||||
entire ECT store if "wid" is absent). If an ECT with the same
|
||||
"jti" already exists, the ECT MUST be rejected.
|
||||
|
||||
2. Parent Existence: Every task identifier listed in the "par"
|
||||
array MUST correspond to a task that is available in the ECT
|
||||
@@ -767,16 +768,18 @@ the following DAG validation steps:
|
||||
ECT MUST be rejected.
|
||||
|
||||
4. Acyclicity: Following the chain of parent references MUST NOT
|
||||
lead back to the current task's "tid". If a cycle is detected,
|
||||
lead back to the current ECT's "jti". If a cycle is detected,
|
||||
the ECT MUST be rejected.
|
||||
|
||||
5. Parent Policy Decision: If any parent task has a "pol_decision"
|
||||
of "rejected" or "pending_human_review", the current task's
|
||||
"exec_act" MUST indicate a compensation, rollback, remediation,
|
||||
or human review action. Implementations MUST NOT accept an ECT
|
||||
representing normal workflow continuation when a parent's
|
||||
"pol_decision" is not "approved", unless the current ECT has
|
||||
"compensation_required" set to true.
|
||||
5. Parent Policy Decision: If any parent ECT contains a
|
||||
"pol_decision" of "rejected" or "pending_human_review", the
|
||||
current ECT's "exec_act" MUST indicate a compensation,
|
||||
rollback, remediation, or human review action.
|
||||
Implementations MUST NOT accept an ECT representing normal
|
||||
workflow continuation when a parent's "pol_decision" is not
|
||||
"approved", unless the current ECT has "compensation_required"
|
||||
set to true. This rule only applies when the parent ECT
|
||||
contains policy claims.
|
||||
|
||||
6. Trust Domain Consistency: Parent tasks SHOULD belong to the
|
||||
same trust domain or to a trust domain with which a federation
|
||||
@@ -790,8 +793,8 @@ The following pseudocode describes the DAG validation procedure:
|
||||
function validate_dag(ect, ect_store, clock_skew_tolerance):
|
||||
// ect_store: ledger or local cache of verified ECTs
|
||||
// Step 1: Uniqueness check
|
||||
if ect_store.contains(ect.tid, ect.wid):
|
||||
return error("Task ID already exists")
|
||||
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:
|
||||
@@ -803,26 +806,26 @@ function validate_dag(ect, ect_store, clock_skew_tolerance):
|
||||
|
||||
// Step 3: Cycle detection (with traversal limit)
|
||||
visited = set()
|
||||
result = has_cycle(ect.tid, ect.par, ect_store, 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_tid, parent_ids, ect_store,
|
||||
function has_cycle(target_jti, parent_ids, ect_store,
|
||||
visited, max_depth):
|
||||
if visited.size() >= max_depth:
|
||||
return error("Maximum ancestor traversal limit exceeded")
|
||||
for parent_id in parent_ids:
|
||||
if parent_id == target_tid:
|
||||
if parent_id == target_jti:
|
||||
return true
|
||||
if parent_id in visited:
|
||||
continue
|
||||
visited.add(parent_id)
|
||||
parent = ect_store.get(parent_id)
|
||||
if parent is not null:
|
||||
result = has_cycle(target_tid, parent.par, ect_store,
|
||||
result = has_cycle(target_jti, parent.par, ect_store,
|
||||
visited, max_depth)
|
||||
if result is error or result is true:
|
||||
return result
|
||||
@@ -889,11 +892,12 @@ verification steps in order:
|
||||
(RECOMMENDED: no more than 30 seconds ahead of the
|
||||
verifier's current time, to account for clock skew).
|
||||
|
||||
12. Verify all required claims ("jti", "tid", "exec_act", "par",
|
||||
"pol", "pol_decision") are present and well-formed.
|
||||
12. Verify all required claims ("jti", "exec_act", "par") are
|
||||
present and well-formed.
|
||||
|
||||
13. Verify "pol_decision" is one of "approved", "rejected", or
|
||||
"pending_human_review".
|
||||
13. If "pol" or "pol_decision" is present, verify that both are
|
||||
present and that "pol_decision" is one of "approved",
|
||||
"rejected", or "pending_human_review".
|
||||
|
||||
14. Perform DAG validation per {{dag-validation}}.
|
||||
|
||||
@@ -969,12 +973,14 @@ function verify_ect(ect_jws, verifier_id,
|
||||
return reject("ECT issued in the future")
|
||||
|
||||
// Verify required claims
|
||||
for claim in ["jti", "tid", "exec_act", "par",
|
||||
"pol", "pol_decision"]:
|
||||
for claim in ["jti", "exec_act", "par"]:
|
||||
if claim not in payload:
|
||||
return reject("Missing required claim: " + claim)
|
||||
|
||||
// Validate pol_decision value
|
||||
// Validate policy claims (optional, but must be paired)
|
||||
if "pol" in payload or "pol_decision" in payload:
|
||||
if "pol" not in payload or "pol_decision" not in payload:
|
||||
return reject("pol and pol_decision must both be present")
|
||||
if payload.pol_decision not in
|
||||
["approved", "rejected", "pending_human_review"]:
|
||||
return reject("Invalid pol_decision value")
|
||||
@@ -1023,7 +1029,7 @@ Limitations of point-to-point mode:
|
||||
- No persistent audit trail unless agents independently retain
|
||||
ECTs
|
||||
- Global replay detection relies solely on "jti" caches at each
|
||||
agent; there is no centralized "tid" uniqueness check
|
||||
agent; there is no centralized "jti" uniqueness check
|
||||
- The parent ECT chain grows with each hop, increasing HTTP
|
||||
header size
|
||||
- Post-hoc audit reconstruction requires collecting ECTs from
|
||||
@@ -1083,7 +1089,7 @@ An audit ledger implementation MUST provide:
|
||||
entries via a monotonically increasing sequence number.
|
||||
|
||||
3. Lookup by task ID: The ledger MUST support efficient retrieval
|
||||
of ECT entries by "tid" value.
|
||||
of ECT entries by "jti" value.
|
||||
|
||||
4. Integrity verification: The ledger SHOULD provide a mechanism
|
||||
to verify that no entries have been tampered with (e.g.,
|
||||
@@ -1099,7 +1105,7 @@ Each ledger entry is a logical record containing:
|
||||
~~~json
|
||||
{
|
||||
"ledger_sequence": 42,
|
||||
"task_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"ect_jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"agent_id": "spiffe://example.com/agent/clinical",
|
||||
"action": "recommend_treatment",
|
||||
"parents": [],
|
||||
@@ -1129,7 +1135,7 @@ include additional domain-specific requirements beyond the scope
|
||||
of this specification.
|
||||
|
||||
Note: task identifiers in this section are abbreviated for
|
||||
readability. In production, all "tid" values are required to be
|
||||
readability. In production, all "jti" values are required to be
|
||||
UUIDs per {{exec-claims}}.
|
||||
|
||||
## Medical Device SDLC Workflow
|
||||
@@ -1143,27 +1149,27 @@ software used in medical devices.
|
||||
|
||||
~~~
|
||||
Agent A (Spec Reviewer):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: review_requirements_spec
|
||||
pol: spec_review_policy_v2 pol_decision: approved
|
||||
|
||||
Agent B (Code Generator):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: implement_module
|
||||
pol: coding_standards_v3 pol_decision: approved
|
||||
|
||||
Agent C (Test Agent):
|
||||
tid: task-003 par: [task-002]
|
||||
jti: task-003 par: [task-002]
|
||||
exec_act: execute_test_suite
|
||||
pol: test_coverage_policy_v1 pol_decision: approved
|
||||
|
||||
Agent D (Build Agent):
|
||||
tid: task-004 par: [task-003]
|
||||
jti: task-004 par: [task-003]
|
||||
exec_act: build_release_artifact
|
||||
pol: build_validation_v2 pol_decision: approved
|
||||
|
||||
Human Release Manager:
|
||||
tid: task-005 par: [task-004]
|
||||
jti: task-005 par: [task-004]
|
||||
exec_act: approve_release
|
||||
pol: release_approval_policy pol_decision: approved
|
||||
pol_enforcer: spiffe://meddev.example/human/release-mgr-42
|
||||
@@ -1231,17 +1237,17 @@ execution.
|
||||
|
||||
~~~
|
||||
Agent A (Risk Assessment):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: calculate_risk_exposure
|
||||
pol: risk_limits_policy_v2 pol_decision: approved
|
||||
|
||||
Agent B (Compliance):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: verify_compliance
|
||||
pol: compliance_check_v1 pol_decision: approved
|
||||
|
||||
Agent C (Execution):
|
||||
tid: task-003 par: [task-002]
|
||||
jti: task-003 par: [task-002]
|
||||
exec_act: execute_trade
|
||||
pol: execution_policy_v3 pol_decision: approved
|
||||
~~~
|
||||
@@ -1268,9 +1274,8 @@ a cryptographic link to the original task:
|
||||
"aud": "spiffe://bank.example/system/ledger",
|
||||
"iat": 1772150550,
|
||||
"exp": 1772151150,
|
||||
"jti": "e4f5a6b7-c8d9-0123-ef01-234567890abc",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440099",
|
||||
"wid": "d3e4f5a6-b7c8-9012-def0-123456789012",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440099",
|
||||
"exec_act": "initiate_trade_rollback",
|
||||
"par": ["550e8400-e29b-41d4-a716-446655440003"],
|
||||
"pol": "compensation_policy_v1",
|
||||
@@ -1294,27 +1299,27 @@ required checks were completed:
|
||||
|
||||
~~~
|
||||
Agent A (Route Planning):
|
||||
tid: task-001 par: []
|
||||
jti: task-001 par: []
|
||||
exec_act: plan_route
|
||||
pol: route_policy_v1 pol_decision: approved
|
||||
|
||||
Agent B (Customs):
|
||||
tid: task-002 par: [task-001]
|
||||
jti: task-002 par: [task-001]
|
||||
exec_act: validate_customs
|
||||
pol: customs_policy_v2 pol_decision: approved
|
||||
|
||||
Agent C (Safety):
|
||||
tid: task-003 par: [task-001]
|
||||
jti: task-003 par: [task-001]
|
||||
exec_act: verify_cargo_safety
|
||||
pol: safety_policy_v1 pol_decision: approved
|
||||
|
||||
Agent D (Payment):
|
||||
tid: task-004 par: [task-002, task-003]
|
||||
jti: task-004 par: [task-002, task-003]
|
||||
exec_act: authorize_payment
|
||||
pol: payment_policy_v3 pol_decision: approved
|
||||
|
||||
System (Commitment):
|
||||
tid: task-005 par: [task-004]
|
||||
jti: task-005 par: [task-004]
|
||||
exec_act: commit_shipment
|
||||
pol: commitment_policy_v1 pol_decision: approved
|
||||
~~~
|
||||
@@ -1377,7 +1382,7 @@ attestation ECT:
|
||||
- MUST set "iss" to the witness's own workload identity.
|
||||
- MUST set "exec_act" to "witness_attestation" (or a domain-
|
||||
specific equivalent).
|
||||
- MUST include the observed task's "tid" in the "par" array,
|
||||
- MUST include the observed task's "jti" in the "par" array,
|
||||
linking the attestation to the original task.
|
||||
- MUST set "pol_decision" to "approved" to indicate the witness
|
||||
confirms the observation.
|
||||
@@ -1673,7 +1678,6 @@ the "JSON Web Token Claims" registry maintained by IANA:
|
||||
| Claim Name | Claim Description | Change Controller | Reference |
|
||||
|:---:|:---|:---:|:---:|
|
||||
| wid | Workflow Identifier | IETF | {{exec-claims}} |
|
||||
| tid | Task Identifier | IETF | {{exec-claims}} |
|
||||
| exec_act | Action/Task Type | IETF | {{exec-claims}} |
|
||||
| par | Parent Task Identifiers | IETF | {{exec-claims}} |
|
||||
| pol | Policy Rule Identifier | IETF | {{policy-claims}} |
|
||||
@@ -1861,8 +1865,8 @@ use cases are distinct.
|
||||
A minimal conforming implementation needs to:
|
||||
|
||||
1. Create JWTs with all required claims ("iss", "aud", "iat",
|
||||
"exp", "jti", "tid", "exec_act", "par", "pol",
|
||||
"pol_decision").
|
||||
"exp", "jti", "exec_act", "par") and policy claims ("pol",
|
||||
"pol_decision") when policy evaluation was performed.
|
||||
2. Sign ECTs with the agent's private key using an algorithm
|
||||
matching the WIT (ES256 recommended).
|
||||
3. Verify ECT signatures against WIT public keys.
|
||||
@@ -1950,9 +1954,8 @@ ECT Payload:
|
||||
"aud": "spiffe://example.com/agent/validator",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "1a2b3c4d-e5f6-7890-abcd-ef0123456701",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"wid": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"exec_act": "fetch_patient_data",
|
||||
"par": [],
|
||||
"pol": "clinical_data_access_policy_v1",
|
||||
@@ -1974,9 +1977,8 @@ task, and creates its own ECT:
|
||||
"aud": "spiffe://example.com/system/ledger",
|
||||
"iat": 1772064160,
|
||||
"exp": 1772064760,
|
||||
"jti": "2b3c4d5e-f6a7-8901-bcde-f01234567802",
|
||||
"jti": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"wid": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
|
||||
"tid": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"exec_act": "validate_safety",
|
||||
"par": ["550e8400-e29b-41d4-a716-446655440001"],
|
||||
"pol": "safety_validation_policy_v2",
|
||||
@@ -2010,9 +2012,8 @@ Task 1 (Spec Review Agent):
|
||||
"aud": "spiffe://meddev.example/agent/code-gen",
|
||||
"iat": 1772064150,
|
||||
"exp": 1772064750,
|
||||
"jti": "3c4d5e6f-a7b8-9012-cdef-012345678903",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000001",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000001",
|
||||
"exec_act": "review_requirements_spec",
|
||||
"par": [],
|
||||
"pol": "spec_review_policy_v2",
|
||||
@@ -2033,9 +2034,8 @@ Task 2 (Code Generation Agent):
|
||||
"aud": "spiffe://meddev.example/agent/test-runner",
|
||||
"iat": 1772064200,
|
||||
"exp": 1772064800,
|
||||
"jti": "4d5e6f7a-b8c9-0123-def0-123456789004",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000002",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000002",
|
||||
"exec_act": "implement_module",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000001"],
|
||||
"pol": "coding_standards_v3",
|
||||
@@ -2054,9 +2054,8 @@ Task 3 (Autonomous Test Agent):
|
||||
"aud": "spiffe://meddev.example/agent/build",
|
||||
"iat": 1772064260,
|
||||
"exp": 1772064860,
|
||||
"jti": "5e6f7a8b-c9d0-1234-ef01-234567890005",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000003",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000003",
|
||||
"exec_act": "execute_test_suite",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000002"],
|
||||
"pol": "test_coverage_policy_v1",
|
||||
@@ -2075,9 +2074,8 @@ Task 4 (Build Agent):
|
||||
"aud": "spiffe://meddev.example/human/release-mgr-42",
|
||||
"iat": 1772064310,
|
||||
"exp": 1772064910,
|
||||
"jti": "6f7a8b9c-d0e1-2345-f012-345678900006",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000004",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000004",
|
||||
"exec_act": "build_release_artifact",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000003"],
|
||||
"pol": "build_validation_v2",
|
||||
@@ -2096,9 +2094,8 @@ Task 5 (Human Release Manager Approval):
|
||||
"aud": "spiffe://meddev.example/system/ledger",
|
||||
"iat": 1772064510,
|
||||
"exp": 1772065110,
|
||||
"jti": "7a8b9c0d-e1f2-3456-0123-456789000007",
|
||||
"jti": "a1b2c3d4-0001-0000-0000-000000000005",
|
||||
"wid": "c2d3e4f5-a6b7-8901-cdef-012345678901",
|
||||
"tid": "a1b2c3d4-0001-0000-0000-000000000005",
|
||||
"exec_act": "approve_release",
|
||||
"par": ["a1b2c3d4-0001-0000-0000-000000000004"],
|
||||
"pol": "release_approval_policy",
|
||||
@@ -2165,9 +2162,8 @@ Task 004 ECT payload:
|
||||
"aud": "spiffe://bank.example/system/ledger",
|
||||
"iat": 1772064250,
|
||||
"exp": 1772064850,
|
||||
"jti": "8b9c0d1e-f2a3-4567-1234-567890000008",
|
||||
"jti": "f1e2d3c4-0004-0000-0000-000000000004",
|
||||
"wid": "d3e4f5a6-b7c8-9012-def0-123456789012",
|
||||
"tid": "f1e2d3c4-0004-0000-0000-000000000004",
|
||||
"exec_act": "execute_trade",
|
||||
"par": [
|
||||
"f1e2d3c4-0002-0000-0000-000000000002",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user