--- title: Message transport with @jeeboard/protocol description: Explains the endpoint abstraction and the in-process endpoint pair from the @jeeboard/protocol package. --- # Message transport with @jeeboard/protocol The `@jeeboard/protocol` package defines a small message-transport abstraction. A message transport is a channel that moves messages between two parts of an application. The replication layer in [@jeeboard/automerge](../automerge/index.md) uses this abstraction. It sends document changes and presence data through an endpoint. The package does not contain a network implementation. You supply the transport, or you use the in-process pair that the package provides. ## Key concepts - A **message** is a value that an endpoint delivers to listeners. You select the message type with a generic type parameter. - A **message listener** is a function that receives a message. The type is `MessageListener`. - An **endpoint** is one side of a message transport. It has a `send` method and a `subscribe` method. The type is `Endpoint`. - An **endpoint pair** is a set of two connected endpoints, `a` and `b`. The type is `EndpointPair`. An endpoint has two operations: - `send(message)` delivers a message to the listeners of the opposite endpoint. - `subscribe(listener)` registers a message listener. It returns a function that removes the listener. NOTE: An endpoint does not receive its own messages. A call to `a.send` notifies only the listeners of `b`. ## Entry points The package exports three types and one function: - `MessageListener` — the message listener type. - `Endpoint` — the endpoint interface. - `EndpointPair` — the endpoint pair interface with fields `a` and `b`. - `createInProcessEndpointPair()` — creates a connected endpoint pair in the current process. ## Typical usage This example creates an endpoint pair and sends messages in both directions. It matches the tests in `packages/protocol/test/protocol.test.mjs`. ```ts import { createInProcessEndpointPair } from "@jeeboard/protocol"; const { a, b } = createInProcessEndpointPair<{ type: string; revision: number }>(); a.subscribe((message) => { console.log("a received", message); }); b.subscribe((message) => { console.log("b received", message); }); a.send({ type: "patch", revision: 1 }); // delivered to b b.send({ type: "ack", revision: 1 }); // delivered to a ``` ## Remove a listener To remove a listener, call the function that `subscribe` returns. 1. Call `subscribe` and keep the returned function. 2. Call the returned function to remove the listener. 3. Send again to confirm that the listener does not run. ```ts const { a, b } = createInProcessEndpointPair<{ type: string }>(); const unsubscribe = a.subscribe((message) => { console.log(message); }); b.send({ type: "first" }); // the listener runs unsubscribe(); b.send({ type: "second" }); // the listener does not run ``` ## Custom transports The in-process pair delivers messages synchronously in the same process. For network communication, you implement the `Endpoint` interface yourself. For example, you can wrap a `WebSocket` or a `MessagePort` in an endpoint. The `createBoardRealtimeSession` function in [@jeeboard/automerge](../automerge/index.md) accepts any `Endpoint`. This design keeps the replication logic independent from the transport. WARNING: The in-process pair does not copy messages. The listener receives the same object reference. Do not mutate a message after you send it. ## API reference See the generated API documentation at [API reference](../api/modules/protocol_src.html).