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
Rename the durable publication-identity surface introduced in #415 to JournalProvenance, and make provenance preservation across a trusted journal
wrapper explicit at the wrapping site.
The exact-backend mechanism remains load-bearing. The vocabulary and wrapping
seam change so the code says what the mechanism proves and makes every transfer
of that proof visible.
This is a security-boundary refactor, not only a symbol rename. It must preserve
the existing authority and transaction behavior exactly.
What JournalProvenance proves
When a live Workspace effect publishes, its filtered Yield must land in the
authoritative journal of the specific WorkflowRun whose mutation is being
transacted. Otherwise the filesystem mutation could commit to run A while the
journal result is sent to an InMemoryStream, another run's journal, or a
look-alike wrapper on another connection. That would break:
one expansion → one effect → one caller-owned transaction
JournalProvenance is a non-operational, equality-only witness that a live
publication stream descends from the exact journal backend selected by the
provider. It grants no append or read capability, does not mean that journal
contents are immutable, and is meaningful only because the provider retains
the witness it established and later requires exact equality.
The current implementation associates a fresh opaque witness with the
authoritative stream. The Deno provider retains it for the WorkflowRun journal
and refuses a Workspace publication whose provenance differs before opening the
transaction. WAC11 proves that an in-memory stream, another run's journal, a
copied property, and an unproven wrapper cannot reach mutation or publication.
Why change it
The current names describe the mechanism as a generic publication identity:
claim…Identity does not say that the call creates and associates a fresh
provenance witness.
publicationIdentity does not say which concrete boundary the value proves.
guardDurableStream() currently preserves provenance for every canonical
guard through an implicit inherit(source, target) side effect.
JournalProvenance names the narrower fact being proven: lineage from the
provider-selected journal backend. establish… describes minting and
associating the witness. preserve… makes propagation through the trusted
secret-filter wrapper visible without making the generic guard responsible for
provenance policy.
The refusal should describe the failed fact rather than a forged capability:
the live Workspace journal does not have the provenance of the selected WorkflowRun.
These exported symbol renames are source-level breaking changes for direct
consumers of @executablemd/durable-streams. That is acceptable before release,
but it must be recorded accurately. Stable context API names such as executablemd.workflow.workspace.coordination* do not change, so loaded-copy
composition keys remain compatible.
Explicit preservation at the secret boundary
guardDurableStream() remains the generic, policy-neutral pre-persistence
guard. An ordinary guard does not automatically preserve JournalProvenance.
The trusted secret-filter wrapping site explicitly preserves it:
Preservation is visible composition, not new authority. It can preserve only
the provenance already associated with the exact source object. An unproven
source produces no proven target, and a separately loaded package copy cannot
read or transfer the canonical copy's association.
Nested trusted wrappers preserve the same exact witness. A generic wrapper,
custom look-alike, copied property, wrapper from another loaded copy, or wrapper
whose source has no established provenance remains unproven.
Architecture decision: retain a narrow weak-provenance exception
The association remains a canonical-module-local WeakMap keyed by exact
stream object identity. This is an approved, narrowly scoped exception to the
ordinary rule that execution state is operation-owned and module-scoped
registries are forbidden.
JournalProvenance is security metadata about an exact stream, not operational or
lifecycle state. The exception applies only when all of these constraints hold:
the association carries a non-operational, equality-only provenance witness
and grants no append, read, execution, publication, or reconciliation
capability;
keys are exact objects held weakly, the association cannot enumerate them,
and it cannot keep a stream alive or outlive that stream;
public structural metadata would be forgeable or copyable and therefore
cannot preserve the security property;
a separately loaded package copy must neither read nor transfer the canonical
copy's association;
establishment and transfer occur only through the canonical module's explicit establishJournalProvenance() and preserveJournalProvenance() functions;
and
the weak association retains no unrelated execution, lifecycle, journal
content, or provider state.
This exception is not a general allowance for module-scoped caches or
registries. It exists because the exact-object, anti-forgery and loaded-copy
boundary is the provenance contract itself, while weak keys make the
association's lifetime subordinate to the stream objects that own it.
A scope-owned alternative would have to thread new authority through core,
durable execution, secret wrapping and Workspace coordination. A wrapper-private
alternative cannot also refuse duplicate establishment on an arbitrary stream
without retaining an association elsewhere or changing the stream contract.
Neither adds a security property the weak association lacks, and both enlarge
the trusted surface.
The implementation therefore preserves the current exact-object mechanism,
renames it to JournalProvenance, removes implicit propagation from guardDurableStream(), and makes the trusted secret-filter boundary call preserveJournalProvenance() explicitly. The Planner and Implementor inherit
this decision; they do not reopen it. The same PR must record the exception in architecture.md before describing the implementation as complete.
Scope and blast radius
packages/durable-streams/guard.ts
packages/durable-streams/effect.ts
packages/durable-streams/live-coordinator.ts
packages/durable-streams/mod.ts
packages/core/src/secrets/policy.ts
packages/workflow/src/deno/connections.ts
packages/workflow/src/deno/workspace/effect.ts
packages/workflow/src/workspace/effect.ts
focused durable-stream, secret-policy, Workspace coordination and
physical-loaded-copy tests
architecture.md, specs/workflow-spec.md, specs/workflow-workspace-spec.md, and the affected DLC/WAC conformance rows
This does not change the journal representation, filtering policy, transaction
protocol, replay protocol, stable context names, or public workflow behavior.
Sequencing
Implement as its own focused PR after #419 lands. Do not grow the #365 crash
proof slice.
Acceptance criteria
JournalProvenance is defined precisely in architecture terminology and
used consistently in code, types, diagnostics and specifications.
Symbols are renamed according to the table, with no compatibility aliases
unless explicitly approved for the pre-release API.
The architecture decision is settled here: retain the
canonical-module-local weak provenance association under the narrow
exception above.
architecture.md records that exception and its constraints in the
implementation PR.
Establishing provenance creates one fresh witness, associates it with one
exact authoritative stream, and refuses duplicate establishment.
guardDurableStream() no longer preserves provenance implicitly.
The trusted secret-filter wrapper explicitly preserves provenance.
An ordinary guard, arbitrary wrapper, copied field, foreign journal,
in-memory stream and separately loaded package copy remain unproven.
Nested trusted wrappers preserve the same exact provenance.
An unproven source cannot make a proven target through the preservation
seam.
Provider validation happens before transaction, savepoint, mutation or
publication work.
Secret rejection and cancellation retain their existing pre-persistence
behavior.
Complete replay remains independent of live provenance and provider
coordination.
WAC1, WAC5, WAC11, DLC8 and the physical loaded-copy guard regressions
pass without behavioral weakening.
deno task lint, deno task check, deno task check:jsr, affected Deno
tests, and fresh Deno/Node/Bun CI pass.
Summary
Rename the durable publication-identity surface introduced in #415 to
JournalProvenance, and make provenance preservation across a trusted journal
wrapper explicit at the wrapping site.
The exact-backend mechanism remains load-bearing. The vocabulary and wrapping
seam change so the code says what the mechanism proves and makes every transfer
of that proof visible.
This is a security-boundary refactor, not only a symbol rename. It must preserve
the existing authority and transaction behavior exactly.
What JournalProvenance proves
When a live Workspace effect publishes, its filtered
Yieldmust land in theauthoritative journal of the specific WorkflowRun whose mutation is being
transacted. Otherwise the filesystem mutation could commit to run A while the
journal result is sent to an
InMemoryStream, another run's journal, or alook-alike wrapper on another connection. That would break:
JournalProvenance is a non-operational, equality-only witness that a live
publication stream descends from the exact journal backend selected by the
provider. It grants no append or read capability, does not mean that journal
contents are immutable, and is meaningful only because the provider retains
the witness it established and later requires exact equality.
The current implementation associates a fresh opaque witness with the
authoritative stream. The Deno provider retains it for the WorkflowRun journal
and refuses a Workspace publication whose provenance differs before opening the
transaction. WAC11 proves that an in-memory stream, another run's journal, a
copied property, and an unproven wrapper cannot reach mutation or publication.
Why change it
The current names describe the mechanism as a generic publication identity:
claim…Identitydoes not say that the call creates and associates a freshprovenance witness.
publicationIdentitydoes not say which concrete boundary the value proves.guardDurableStream()currently preserves provenance for every canonicalguard through an implicit
inherit(source, target)side effect.JournalProvenancenames the narrower fact being proven: lineage from theprovider-selected journal backend.
establish…describes minting andassociating the witness.
preserve…makes propagation through the trustedsecret-filter wrapper visible without making the generic guard responsible for
provenance policy.
Vocabulary and API
class PublicationIdentity { #opaque }class JournalProvenance { #opaque }type DurablePublicationIdentitytype JournalProvenanceclaimDurablePublicationIdentity(stream)establishJournalProvenance(stream)durablePublicationIdentity(stream)getJournalProvenance(stream)inherit(source, target)preserveJournalProvenance(source, target)RunConnectionLease.journalIdentityjournalProvenanceWorkspaceCoordinationAuthority.publicationIdentityjournalProvenancevalidateJournal(database, identity)validateJournalProvenance(database, provenance)Establishing the authoritative journal reads:
The coordination boundary reads:
The refusal should describe the failed fact rather than a forged capability:
These exported symbol renames are source-level breaking changes for direct
consumers of
@executablemd/durable-streams. That is acceptable before release,but it must be recorded accurately. Stable context API names such as
executablemd.workflow.workspace.coordination*do not change, so loaded-copycomposition keys remain compatible.
Explicit preservation at the secret boundary
guardDurableStream()remains the generic, policy-neutral pre-persistenceguard. An ordinary guard does not automatically preserve JournalProvenance.
The trusted secret-filter wrapping site explicitly preserves it:
Equivalently, it may be composed directly:
Preservation is visible composition, not new authority. It can preserve only
the provenance already associated with the exact source object. An unproven
source produces no proven target, and a separately loaded package copy cannot
read or transfer the canonical copy's association.
Nested trusted wrappers preserve the same exact witness. A generic wrapper,
custom look-alike, copied property, wrapper from another loaded copy, or wrapper
whose source has no established provenance remains unproven.
Architecture decision: retain a narrow weak-provenance exception
The association remains a canonical-module-local
WeakMapkeyed by exactstream object identity. This is an approved, narrowly scoped exception to the
ordinary rule that execution state is operation-owned and module-scoped
registries are forbidden.
JournalProvenance is security metadata about an exact stream, not operational or
lifecycle state. The exception applies only when all of these constraints hold:
and grants no append, read, execution, publication, or reconciliation
capability;
and it cannot keep a stream alive or outlive that stream;
cannot preserve the security property;
copy's association;
establishJournalProvenance()andpreserveJournalProvenance()functions;and
content, or provider state.
This exception is not a general allowance for module-scoped caches or
registries. It exists because the exact-object, anti-forgery and loaded-copy
boundary is the provenance contract itself, while weak keys make the
association's lifetime subordinate to the stream objects that own it.
A scope-owned alternative would have to thread new authority through core,
durable execution, secret wrapping and Workspace coordination. A wrapper-private
alternative cannot also refuse duplicate establishment on an arbitrary stream
without retaining an association elsewhere or changing the stream contract.
Neither adds a security property the weak association lacks, and both enlarge
the trusted surface.
The implementation therefore preserves the current exact-object mechanism,
renames it to JournalProvenance, removes implicit propagation from
guardDurableStream(), and makes the trusted secret-filter boundary callpreserveJournalProvenance()explicitly. The Planner and Implementor inheritthis decision; they do not reopen it. The same PR must record the exception in
architecture.mdbefore describing the implementation as complete.Scope and blast radius
packages/durable-streams/guard.tspackages/durable-streams/effect.tspackages/durable-streams/live-coordinator.tspackages/durable-streams/mod.tspackages/core/src/secrets/policy.tspackages/workflow/src/deno/connections.tspackages/workflow/src/deno/workspace/effect.tspackages/workflow/src/workspace/effect.tsphysical-loaded-copy tests
architecture.md,specs/workflow-spec.md,specs/workflow-workspace-spec.md, and the affected DLC/WAC conformance rowsThis does not change the journal representation, filtering policy, transaction
protocol, replay protocol, stable context names, or public workflow behavior.
Sequencing
Implement as its own focused PR after #419 lands. Do not grow the #365 crash
proof slice.
Acceptance criteria
JournalProvenanceis defined precisely in architecture terminology andused consistently in code, types, diagnostics and specifications.
unless explicitly approved for the pre-release API.
canonical-module-local weak provenance association under the narrow
exception above.
architecture.mdrecords that exception and its constraints in theimplementation PR.
exact authoritative stream, and refuses duplicate establishment.
guardDurableStream()no longer preserves provenance implicitly.in-memory stream and separately loaded package copy remain unproven.
seam.
publication work.
behavior.
coordination.
pass without behavioral weakening.
deno task lint,deno task check,deno task check:jsr, affected Denotests, and fresh Deno/Node/Bun CI pass.
Follow-up to #415. Related to the #365 stack.