Workflow History propogation & signing#102
Conversation
(this proposal has not been one-shot). Signed-off-by: joshvanl <me@joshvanl.dev>
There was a problem hiding this comment.
Pull request overview
Adds a new design proposal describing opt-in workflow history propagation and chain-of-custody signing for Dapr Workflows (Durable Task Framework), aimed at enabling downstream authorization/audit scenarios (including MCP-style workflows).
Changes:
- Introduces a detailed spec for propagating workflow execution history to child workflows/activities with configurable scopes.
- Specifies a history signing model (chunked signatures chained via digests) and a deterministic canonical digest algorithm for cross-language verification.
- Outlines intended protobuf, runtime, and SDK surface changes plus lifecycle/acceptance criteria.
Comments suppressed due to low confidence (4)
20260304-RBS-workflow-history-propagation-signing.md:612
- In the sequence diagram, the propagated history signatures list shows
signatures=[sig1,sig2], but earlier in the same diagram the persisted signatures aresig0andsig1. This looks like an off-by-one/naming typo that could confuse implementers; update the diagram to reference the correct signature identifiers/order (e.g.,[sig0,sig1]).
| |-- TaskScheduled -------------------------------->| |
| | (PropagatedHistory: | |
| | events=[e0..e4] | |
| | signatures=[sig1,sig2]) | |
20260304-RBS-workflow-history-propagation-signing.md:566
- The numbered list under In
dapr/dapr: has two items labeled3.(the orchestrator actor section and the history verification utility). Renumber the second one (likely4.) to avoid ambiguity when referencing these steps later in reviews/implementation tracking.
3. **`pkg/actors/targets/workflow/orchestrator/`**:
- **State persistence**: After each orchestrator execution completes, in `saveInternalState()`, the new `HistorySignature` entries are included in the transactional state operation alongside history and inbox entries. Each signature is persisted as an individual actor state key (`signature-000000`, etc.).
- **Propagation**: When building child workflow start events and activity task events, read signatures from the loaded state and copy the relevant events to assemble the `PropagatedHistory`.
3. **History Verification Utility** (`pkg/runtime/wfengine/historyverify/`):
- `VerifySignatures(signatures []*HistorySignature, events []*HistoryEvent) error` - walks the signature chain, recomputes the canonical digest of each referenced event range, verifies it against the stored `events_digest`, then checks the cryptographic signature against the certificate and verifies certificate validity against the timestamp of the last event in each signed range.
20260304-RBS-workflow-history-propagation-signing.md:66
## Related Itemsis currently empty. Since this repo’s proposal template expects links to related proposals/issues (or the section should be removed if none), please either add relevant references (issues/PRs/specs) or drop the heading to avoid an unfinished-looking section.
## Related Items
## Expectations and Alternatives
20260304-RBS-workflow-history-propagation-signing.md:406
- In this section,
WorkflowStateMetadatais described as addingsignatureLength“matchinginboxLengthandhistoryLength”, but the protobuf example usessignature_length/history_signing_enabled(snake_case). To avoid implementer confusion, consider using the protobuf field names consistently here (or explicitly call out when you’re referring to generated-language property names).
The `WorkflowStateMetadata` protobuf is extended with a `signatureLength` field to track the number of stored signatures, matching the existing `inboxLength` and `historyLength` pattern.
```protobuf
// Extend existing WorkflowStateMetadata
message WorkflowStateMetadata {
// ... existing fields (inboxLength, historyLength, generation) ...
// Number of HistorySignature entries stored.
uint64 signature_length = 4;
// Whether history signing is enabled for this workflow instance.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
I have concerns about this proposal, I think making history propagation available on any CallActivity/CallSubOrchestrator opens the door to developers using propagated history as a substitute for explicit inputs. Using the same scenario in the proposal: Lets imagine now we remove the fraud step, the child workflow will break an none of the payments will move forward. This is a big risk, it opens the door to this checks without explicit contracts AlternativeMaybe an alternative is having the MCP as a First-Class Primitive, so in this case it makes sense to propagate the history as the MCP expects it. So I would prefer:
|
|
I very much approve of this proposal. Even outside of MCP, having some provenance that this activity/child-workflow was called under the right circumstances (that can't be forged) I think is very valuable. I would be in favour of having the concept of simple first-class Something Like this. ... in the middle of workflow code ... somewhere in |
Signed-off-by: joshvanl <me@joshvanl.dev>
Signed-off-by: joshvanl <me@joshvanl.dev>
Extends the backend service proto with messages needed for chain-of-custody signing of workflow history events. Each orchestrator execution signs the new event range and chains to the previous signature. Certificates are deduplicated in a separate table and referenced by index. - SigningCertificate: stores DER-encoded X.509 certificate per identity - HistorySignature: signing metadata covering a contiguous event range, linked via previousSignatureDigest to form a verifiable chain - WorkflowStateMetadata: extended with signatureLength and signingCertificateLength fields Proposal: dapr/proposals#102 Signed-off-by: joshvanl <me@joshvanl.dev>
Introduce chain-of-custody signing for workflow history events, allowing each orchestrator execution to produce a cryptographic signature over newly appended events. Signatures are chained via previousSignatureDigest to form a tamper-evident log. - Add historysigning package with deterministic event marshaling, raw-bytes digest computation, and sign/verify logic supporting Ed25519, ECDSA P-256, and RSA PKCS#1 v1.5 - Verify certificate validity (NotBefore/NotAfter) against the timestamp of the last event in each signed range - Export SigningCertificate and HistorySignature type aliases from backend package - Add comprehensive tests covering all key types, chain verification, certificate rotation, tamper detection, and certificate validity Proposal: dapr/proposals#102 Signed-off-by: joshvanl <me@joshvanl.dev>
Introduce chain-of-custody signing for workflow history events, allowing each orchestrator execution to produce a cryptographic signature over newly appended events. Signatures are chained via previousSignatureDigest to form a tamper-evident log. - Add historysigning package with deterministic event marshaling, raw-bytes digest computation, and sign/verify logic supporting Ed25519, ECDSA P-256, and RSA PKCS#1 v1.5 - Verify certificate validity (NotBefore/NotAfter) against the timestamp of the last event in each signed range - Export SigningCertificate and HistorySignature type aliases from backend package - Add comprehensive tests covering all key types, chain verification, certificate rotation, tamper detection, and certificate validity Proposal: dapr/proposals#102 Signed-off-by: joshvanl <me@joshvanl.dev>
Introduce chain-of-custody signing for workflow history events, allowing each orchestrator execution to produce a cryptographic signature over newly appended events. Signatures are chained via previousSignatureDigest to form a tamper-evident log. - Add historysigning package with deterministic event marshaling, raw-bytes digest computation, and sign/verify logic supporting Ed25519, ECDSA P-256, and RSA PKCS#1 v1.5 - Verify certificate validity (NotBefore/NotAfter) against the timestamp of the last event in each signed range - Export SigningCertificate and HistorySignature type aliases from backend package - Regenerate protobuf outputs for renamed proto types - Add comprehensive tests covering all key types, chain verification, certificate rotation, tamper detection, and certificate validity Proposal: dapr/proposals#102' Signed-off-by: joshvanl <me@joshvanl.dev>
cicoyle
left a comment
There was a problem hiding this comment.
+1 binding - I am good with the proposal as-is. There is alignment that the sdk perspective will be:
workflow.WithHistoryPropagation(workflow.PropagateLineage()),
OR
workflow.WithHistoryPropagation(workflow.PropagateOwnHistory()),
|
I think I'd want to spend more time thinking about what the SDK shape looks like myself as it seems like a better fit to drop into a workflow option instead of adopting a fluent interface just for this. Is there any expectation that that SDK will be able to read any of the signed history out or is it only accessible through tooling that can access the events? |
All sdks should have nice helpers for end users to consume. This is what I was thinking for the go-sdk.. We should try to align for consistency across asks, and account for future expansion with the options - like the redaction and depth in the description from the PR :) |
(this proposal has not been one-shot).