Skip to content

Allow restricting serialization and transfer to the same agent cluster - #13055

Merged
annevk merged 3 commits into
mainfrom
annevk/agent-cluster-bound
Oct 14, 2026
Merged

annevk merged 3 commits into
mainfrom
annevk/agent-cluster-bound

Conversation

@annevk

@annevk annevk commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Allow restricting serialization and transfer to the same agent cluster

Some serializable and transferable objects, such as VideoFrame and MediaSourceHandle, cannot leave their agent cluster. Specifications have approximated this using [Exposed=(Window,DedicatedWorker)], but Window objects in different agent clusters can communicate with each other, and a dedicated worker created by a shared worker is in the agent cluster of that shared worker.

Serialization and transfer steps can now restrict their result to the same agent cluster, and [SameAgentCluster] does so for every object implementing an interface. StructuredDeserialize and StructuredDeserializeWithTransfer then throw in a different agent cluster, as they already did for SharedArrayBuffer, which now uses the same mechanism. StructuredDeserializeWithTransfer now does this, and checks that all serialized and transferred interfaces are exposed, before running any transfer-receiving steps. Serializing such objects for storage throws. A result restricted this way may additionally refer to data that is only accessible within that agent cluster.

This will help w3c/webcodecs#717

  • At least two implementers are interested (and none opposed):
    • N/A, this is needed by Web Codecs and possibly OffscreenCanvas. Adopting there requires interest and tests.
  • Tests are written and can be reviewed and commented upon at:
    • N/A, see above.
  • Implementation bugs are filed:
    • N/A, see above.
  • Corresponding HTML AAM & ARIA in HTML issues & PRs: N/A
  • MDN issue is filed: N/A
  • The top of this comment includes a clear commit message to use.

(See WHATWG Working Mode: Changes for more details.)


/index.html ( diff )
/structured-data.html ( diff )

Some serializable and transferable objects, such as VideoFrame and MediaSourceHandle, cannot leave their agent cluster. Specifications have approximated this using [Exposed=(Window,DedicatedWorker)], but Window objects in different agent clusters can communicate with each other, and a dedicated worker created by a shared worker is in the agent cluster of that shared worker.

Serialization and transfer steps can now restrict their result to the same agent cluster, and [SameAgentCluster] does so for every object implementing an interface. StructuredDeserialize and StructuredDeserializeWithTransfer then throw in a different agent cluster, as they already did for SharedArrayBuffer, which now uses the same mechanism. Serializing such objects for storage throws. A result restricted this way no longer needs to be realm-independent.

This will help w3c/webcodecs#717
@zcorpan

zcorpan commented Oct 12, 2026

Copy link
Copy Markdown
Member

Looks reasonable to me. Some suggestions from claude:

AI review

Reviewed at ceb3c16. The SharedArrayBuffer refactor looks like it behaves the same as before: the order of the cross-origin isolation, forStorage, and deserialization checks hasn't changed. Lint passes; I didn't run a full build.

Suggestions

  • source:10501-10512: in restrict to the same agent cluster, forStorage is optional and defaults to false. If another spec's serialization steps restrict serialized but forget to pass it, the restricted data can be stored (IndexedDB, history state) without an error. Consider making forStorage required. Alternatively, drop the argument and have StructuredSerializeInternal throw itself when forStorage is true and serialized.[[AgentCluster]] is non-null.
  • StructuredDeserializeWithTransfer, step 3.1 (source:11481): the check runs inside the loop, so earlier holders' transfer-receiving steps have already run when it throws. For example, a MessagePort listed before a [SameAgentCluster] object gets entangled in targetRealm and then dropped. Consider checking every holder in a separate loop first. The existing "not exposed" check has the same problem, so maybe this is acceptable.
  • PR description: the checklist is empty. Nothing in HTML uses [SameAgentCluster] yet, so I think tests and implementer interest would come with WebCodecs (Specify cross-agent-cluster structured cloning for various Web Codecs API objects w3c/webcodecs#717). Maybe say that in the PR.

Nits

  • source:10532-10535: the StructuredSerializeInternal intro still says the result is always "realm-independent". It now needs the same "unless restricted" qualification the PR added to serialization steps.
  • source:10515-10517: says Records created by StructuredSerializeWithTransfer have an [[AgentCluster]] field. Its return value ({ [[Serialized]], [[TransferDataHolders]] }) doesn't, only its transfer data holders do.

@annevk

annevk commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

Thanks, reworked it a bit.

@annevk
annevk requested review from noamr and zcorpan October 12, 2026 14:41
annevk added a commit to annevk/WebKit that referenced this pull request Oct 13, 2026
https://bugs.webkit.org/show_bug.cgi?id=327156
rdar://189747796

Reviewed by NOBODY (OOPS!).

whatwg/html#13055 adds the [SameAgentCluster]
extended attribute: serializing or transferring an object implementing
such an interface restricts the result to the agent cluster it came from,
deserializing it in another agent cluster throws a DataCloneError, and
serializing it for storage throws as well. This is what
w3c/webcodecs#717 needs for EncodedVideoChunk,
VideoFrame, EncodedAudioChunk, and AudioData, and what the WebCodecs
crossAgentCluster tests already expect. MediaStreamTrack and
MediaStreamTrackHandle are transferred the same way, so restrict them
too.

The bindings generator now emits isRestrictedToSameAgentCluster for
[Serializable] and [Transferable] interfaces. SerializedScriptValue maps
each serialization tag to the PlatformObjectSerializationTraits of its
interface in one place, platformObjectSerializationTraitsForTag(), which
both the exposure check and the new isTagRestrictedToSameAgentCluster()
use. When CloneSerializer writes a restricted tag it writes the agent
cluster ID after it, as JSC already does for SharedArrayBuffer, and
CloneDeserializer checks that ID before reading the object. All of these
objects already refuse to be serialized for storage, before their tag is
written.

The existing tests only cover the video types, so add tests for
AudioData, EncodedAudioChunk, MediaStreamTrack, and
MediaStreamTrackHandle.

Tests: imported/w3c/web-platform-tests/mediacapture-insertable-streams/MediaStreamTrackHandle-transfer-cross-agent-cluster.https.html
       imported/w3c/web-platform-tests/mediacapture-streams/MediaStreamTrack-transfer-cross-agent-cluster.https.html
       imported/w3c/web-platform-tests/webcodecs/audio-serialization.crossAgentCluster.https.html
annevk added a commit to annevk/wpt that referenced this pull request Oct 13, 2026
whatwg/html#13055 adds the [SameAgentCluster]
extended attribute: serializing or transferring an object implementing
such an interface restricts the result to the agent cluster it came from,
deserializing it in another agent cluster throws a DataCloneError, and
serializing it for storage throws as well. This is what
w3c/webcodecs#717 needs for EncodedVideoChunk,
VideoFrame, EncodedAudioChunk, and AudioData, and what the WebCodecs
crossAgentCluster tests already expect. MediaStreamTrack and
MediaStreamTrackHandle are transferred the same way, so restrict them
too.

The bindings generator now emits isRestrictedToSameAgentCluster for
[Serializable] and [Transferable] interfaces. SerializedScriptValue maps
each serialization tag to the PlatformObjectSerializationTraits of its
interface in one place, platformObjectSerializationTraitsForTag(), which
both the exposure check and the new isTagRestrictedToSameAgentCluster()
use. When CloneSerializer writes a restricted tag it writes the agent
cluster ID after it, as JSC already does for SharedArrayBuffer, and
CloneDeserializer checks that ID before reading the object. All of these
objects already refuse to be serialized for storage, before their tag is
written.

The existing tests only cover the video types, so add tests for
AudioData, EncodedAudioChunk, MediaStreamTrack, and
MediaStreamTrackHandle.

WebKit-Bug: https://bugs.webkit.org/show_bug.cgi?id=327156
webkit-commit-queue pushed a commit to annevk/WebKit that referenced this pull request Oct 13, 2026
https://bugs.webkit.org/show_bug.cgi?id=327156
rdar://189747796

Reviewed by Youenn Fablet.

whatwg/html#13055 adds the [SameAgentCluster]
extended attribute: serializing or transferring an object implementing
such an interface restricts the result to the agent cluster it came from,
deserializing it in another agent cluster throws a DataCloneError, and
serializing it for storage throws as well. This is what
w3c/webcodecs#717 needs for EncodedVideoChunk,
VideoFrame, EncodedAudioChunk, and AudioData, and what the WebCodecs
crossAgentCluster tests already expect. MediaStreamTrack and
MediaStreamTrackHandle are transferred the same way, so restrict them
too.

The bindings generator now emits isRestrictedToSameAgentCluster for
[Serializable] and [Transferable] interfaces. SerializedScriptValue maps
each serialization tag to the PlatformObjectSerializationTraits of its
interface in one place, platformObjectSerializationTraitsForTag(), which
both the exposure check and the new isTagRestrictedToSameAgentCluster()
use. When CloneSerializer writes a restricted tag it writes the agent
cluster ID after it, as JSC already does for SharedArrayBuffer, and
CloneDeserializer checks that ID before reading the object. All of these
objects already refuse to be serialized for storage, before their tag is
written.

The existing tests only cover the video types, so add tests for
AudioData, EncodedAudioChunk, MediaStreamTrack, and
MediaStreamTrackHandle.

Tests: imported/w3c/web-platform-tests/mediacapture-insertable-streams/MediaStreamTrackHandle-transfer-cross-agent-cluster.https.html
       imported/w3c/web-platform-tests/mediacapture-streams/MediaStreamTrack-transfer-cross-agent-cluster.https.html
       imported/w3c/web-platform-tests/webcodecs/audio-serialization.crossAgentCluster.https.html

Canonical link: https://commits.webkit.org/323558@main
@noamr

noamr commented Oct 13, 2026

Copy link
Copy Markdown
Contributor
AI review ## Review of #13055 — "Allow restricting serialization and transfer to the same agent cluster" (head `2113e92`)
  1. [Substantive] The pre-pass in StructuredDeserializeWithTransfer only covers transfer data holders, not restricted records inside [[Serialized]].
    postMessage(videoFrame, [port]) where VideoFrame is [Serializable, SameAgentCluster] (cloned, not transferred): the new loop passes (the port's holder has [[AgentCluster]]: null), the port is entangled in targetRealm via transfer-receiving steps, and only then does ? StructuredDeserialize([[Serialized]], …) throw at the nested record — the port is lost exactly as before. This is pre-existing for the "not exposed" check, and the pre-pass is still an improvement for transferred [SameAgentCluster] objects, but as written it implies an atomicity it doesn't deliver. Either (a) add a note stating that restricted/unexposed serialized (non-transferred) values are still only detected during deserialization, or (b) go further and have StructuredSerializeWithTransfer record whether any record in the graph was restricted (e.g. a [[AgentCluster]] on the result that is the union, or a simple boolean), so the pre-pass can reject up front. (a) seems fine for this PR. ALG-VALIDATE-BEFORE-SIDE-EFFECTS

  2. [Editorial] Commit message vs. spec text on realm-independence. The description says "A result restricted this way no longer needs to be realm-independent", but the normative text (correctly) keeps "must be independent of any realm" and adds "may also refer to data that is only accessible within that agent cluster". A restricted record still has to be deserializable in any realm of the cluster, so suggest the message say "…may additionally refer to data that is only accessible within that agent cluster."

  3. [Editorial] "Some serializable objects and transferable objects, such as SharedArrayBuffer objects…" — per the dfn, serializable objects are platform objects with [Serializable]; SAB is a JS object "handled by StructuredSerialize directly". Suggest: "Some objects, such as SharedArrayBuffer objects, and some serializable objects and transferable objects, are restricted…" Relatedly, the Serializable objects intro ("…cloned across agent and even agent cluster boundaries", line 10274) is no longer universally true; a "generally" or a forward reference to the new section would square it.

  4. [Editorial] Declare the field where authors of serialization/transfer steps will look. Nothing states that serialized/dataHolder carry an [[AgentCluster]] field (initially null) — the restrict hook just assigns to it. One sentence in the <dt>/<dd> for serialization steps and transfer steps (or in the new section) would make the contract explicit for other specs. STATE-DECLARE

  5. [Nit] Redundant two-step init. "Set serialized.[[AgentCluster]] to null." immediately followed by "If sameAgentCluster is true, then restrict…" could be a single step: "If sameAgentCluster is true, then restrict serialized…; otherwise, set serialized.[[AgentCluster]] to null." Purely optional.

annevk added a commit to web-platform-tests/wpt that referenced this pull request Oct 13, 2026
whatwg/html#13055 adds the [SameAgentCluster]
extended attribute: serializing or transferring an object implementing
such an interface restricts the result to the agent cluster it came from,
deserializing it in another agent cluster throws a DataCloneError, and
serializing it for storage throws as well. This is what
w3c/webcodecs#717 needs for EncodedVideoChunk,
VideoFrame, EncodedAudioChunk, and AudioData, and what the WebCodecs
crossAgentCluster tests already expect. MediaStreamTrack and
MediaStreamTrackHandle are transferred the same way, so restrict them
too.

The bindings generator now emits isRestrictedToSameAgentCluster for
[Serializable] and [Transferable] interfaces. SerializedScriptValue maps
each serialization tag to the PlatformObjectSerializationTraits of its
interface in one place, platformObjectSerializationTraitsForTag(), which
both the exposure check and the new isTagRestrictedToSameAgentCluster()
use. When CloneSerializer writes a restricted tag it writes the agent
cluster ID after it, as JSC already does for SharedArrayBuffer, and
CloneDeserializer checks that ID before reading the object. All of these
objects already refuse to be serialized for storage, before their tag is
written.

The existing tests only cover the video types, so add tests for
AudioData, EncodedAudioChunk, MediaStreamTrack, and
MediaStreamTrackHandle.

WebKit-Bug: https://bugs.webkit.org/show_bug.cgi?id=327156
@zcorpan

zcorpan commented Oct 13, 2026

Copy link
Copy Markdown
Member
AI review

Nits

  • source:10515-10522 (base of the PR): the removed sentence saying StructuredDeserialize and StructuredDeserializeWithTransfer enforce the restriction would help specs migrate. mediacapture-extensions' own check (which uses [[agentCluster]], lowercase, not HTML's field) is the kind of code it would remove. Consider restoring it in the new section.

When MediaStreamTrack adopts [SameAgentCluster], the transfer steps should drop setting dataHolder.[[agentCluster]] and the transfer-receiving steps should drop the agent cluster check. Otherwise there would be two checks.

@annevk

annevk commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

Simon, I think that’s obvious and does not need specification language.

@annevk
annevk merged commit 790ce89 into main Oct 14, 2026
2 checks passed
@annevk
annevk deleted the annevk/agent-cluster-bound branch October 14, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic: agent The interaction with JavaScript's agent and agent cluster concepts topic: serialize and transfer

Development

Successfully merging this pull request may close these issues.

3 participants