A private pipe is two ends that already know each other. You do not address a window. You do not join a named bus. You hold a port.
new MessageChannel() returns two MessagePorts, port1 and port2.
The constructor entangles them: a message posted on one is delivered at the other, and vice versa, as a DOM message event.
That is the whole topology — a two-way pipe, not a room.
(HTML §9.4.1)
There is no targetOrigin on a port. There is no origin to check on the MessageEvent it fires.
The permission is the port itself: whoever holds an end can talk on that channel.
The spec calls this an object-capability model.
(HTML §9.4.1.2)
| Tool | You already have | Who hears |
|---|---|---|
window.postMessage |
A window handle (parent, opener, iframe.contentWindow) |
That window — after you pick targetOrigin and they check event.origin |
BroadcastChannel |
A string name | Every same-origin context that joined that name |
| A function / React callback | The same JS world | Whoever you called. No channel. |
MessageChannel |
Nothing but the pair you just minted | Only the other port |
This lab is a real MessageChannel in this page. Send from port1; it must show up only on port2. Then the other way.
Same-page use is a teaching trick — two React components in one document should just call a function.
The lab needs JavaScript in this page.
Read the non-normative intro: HTML Living Standard §9.4.1 Channel messaging. Stop before the contacts/game capability story if you want — we will use that mental model when we transfer a port.
If a distinction here is mushy — especially “I already have a window, why would I mint a pair?” — ask.
Next: the port API (postMessage, onmessage vs start(), close()).
Pocket card: entangled ports.