You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Execution lineage is hash-linked but unsigned, so a different history can be presented around a valid chain #168
Stated on #150 while answering @ngallo and not tracked anywhere since, so it exists only inside a discussion thread.
At d3b7eb618084c4dc2f1270c2050d443f5bab5a3d:
DelegationRecord (src/ca2a_runtime/provenance.py:36) carries ten fields and no signature. The word does not appear in the class.
verify_dag accepts one ordered root-to-leaf path, checking that a child's parent_record_hash equals the recomputed hash of the immediately preceding record.
cross_check_chain(records, chain) reconciles that path against the credential chain.
So execution lineage is a hash-linked path cross-checked against the authenticated object rather than part of it. The credential chain is signed; the history around it is not.
The consequence, in the words I used on #150: an attacker who cannot forge a credential can still present a different execution history around the same valid chain, and nothing in the signed material contradicts them.
@ngallo confirmed independently that this substitution is accepted by cA2A today and refused by PIC Profile 0.2, where the workload-signed Transition COSE carries predecessor.hash over the predecessor's exact signed bytes, making the binding state-to-state with no separate history object to cross-check.
The remedy is not decided here. This issue records the defect and its exact extent. Anything that closes it touches the signed material, so it is a wire-format decision rather than a check to add.
One constraint on any conformance vector for this. The substitution case must be carried as an expected refusal referencing this issue, never as an accepted result. A fixture that records current behaviour as expected defines the defect as correct, and the next implementer builds to the fixture.
One thing from related work I have been doing in Agent Memory may be useful here.
We ended up making an explicit distinction between proving a local predecessor relation and proving a complete or canonical history. In our temporal commitment work, a predecessor chain can establish the declared relationship between two exact objects, but that does not by itself establish that the history is complete, unique, or canonical. Two sibling successors can both be cryptographically valid without either one automatically becoming the authoritative branch.
That seems relevant to the eventual fix here. Authenticating the lineage or transition closes the substitution problem, but I would be careful not to let that stronger binding imply more than it proves.
A few cases that were useful for us to keep separate were: valid predecessor binding, substituted predecessor, reordered history, missing predecessor evidence, and two independently valid children of the same predecessor. The last case is especially useful because it tests whether the verifier accidentally equates "valid chain" with "only possible chain."
Freely offered as a design data point. The issue already has the right core diagnosis, this is just one boundary that may be worth preserving when the wire format decision is made.
@Knapp-Kevin agreed, and #198 already draws that line: verify-lineage authenticates the signed declarations and says nothing about completeness or uniqueness, which is why this stays open. Two independently valid children of one predecessor is the case #198's suite does not have; it goes into the vector set for the wire-format decision, as an expected finding that authenticity alone cannot pick a branch.
Closing. #198 shipped verify-lineage, which authenticates the signed lineage and refuses substituted histories. Binding authority to a live request continues in #222, and the two-valid-children vector from @Knapp-Kevin's comment moves there.
Activity
Knapp-Kevin commentedon Sep 28, 2026
One thing from related work I have been doing in Agent Memory may be useful here.
We ended up making an explicit distinction between proving a local predecessor relation and proving a complete or canonical history. In our temporal commitment work, a predecessor chain can establish the declared relationship between two exact objects, but that does not by itself establish that the history is complete, unique, or canonical. Two sibling successors can both be cryptographically valid without either one automatically becoming the authoritative branch.
That seems relevant to the eventual fix here. Authenticating the lineage or transition closes the substitution problem, but I would be careful not to let that stronger binding imply more than it proves.
A few cases that were useful for us to keep separate were: valid predecessor binding, substituted predecessor, reordered history, missing predecessor evidence, and two independently valid children of the same predecessor. The last case is especially useful because it tests whether the verifier accidentally equates "valid chain" with "only possible chain."
For reference, the open source Agent Memory ADR that formalized this separation is here:
https://github.com/MythologIQ-Labs-LLC/agent-memory/blob/main/docs/adr/ADR-031-temporal-claims-require-deterministic-content-commitments.md
Freely offered as a design data point. The issue already has the right core diagnosis, this is just one boundary that may be worth preserving when the wire format decision is made.
imran-siddique commentedon Sep 29, 2026
@Knapp-Kevin agreed, and #198 already draws that line:
verify-lineageauthenticates the signed declarations and says nothing about completeness or uniqueness, which is why this stays open. Two independently valid children of one predecessor is the case #198's suite does not have; it goes into the vector set for the wire-format decision, as an expected finding that authenticity alone cannot pick a branch.imran-siddique commentedon Oct 15, 2026
Closing. #198 shipped
verify-lineage, which authenticates the signed lineage and refuses substituted histories. Binding authority to a live request continues in #222, and the two-valid-children vector from @Knapp-Kevin's comment moves there.