@causal-order/transport
v0.1.2
Published
WebSocket + JSON transport layer for normalizing node traffic into the event contract expected by the causal-order stack.
Maintainers
Readme
@causal-order/transport
WebSocket + JSON transport layer for normalizing node traffic into the event contract expected by the causal-order stack.
This package is the deployable WebSocket + JSON transport layer for the current causal-order stack.
Scope
Supported ingress path:
- WebSocket + JSON
Other ingress shapes belong in separate adapters built against the same event contract and normalization boundary when needed.
Relationship to causal-order and @causal-order/dedupe
@causal-order/transport sits before @causal-order/dedupe and causal-order.
It is responsible for:
- receiving wire-level node traffic
- decoding JSON messages from WebSocket connections
- normalizing those messages into event objects
- surfacing peer lifecycle and transport errors
In other words, this package hides network and wire-format differences so the upper runtime layers can work with one consistent event contract.
Conceptually:
WebSocket + JSON -> @causal-order/transport -> @causal-order/dedupe -> causal-orderWhat It Does
This package provides:
- a minimal transport contract for start, stop, send, and receive
- peer state reporting for connect, disconnect, and errors
- a concrete WebSocket + JSON adapter
- a default normalizer that converts common node message fields into a
causal-orderevent envelope
Install
npm install @causal-order/transport @causal-order/dedupe causal-orderRuntime requirements:
- Node.js
20+ - ESM package usage
Quick Start
import {
WebSocketJsonTransport,
normalizeTransportEventMessage,
} from "@causal-order/transport";
const transport = new WebSocketJsonTransport({
mode: "server",
port: 8080,
normalizeMessage: normalizeTransportEventMessage,
});
transport.onEvent((event, context) => {
console.log("event", context.peerId, event.id);
});
transport.onPeerState((state) => {
console.log("peer", state.peerId, state.status);
});
await transport.start();Quick Start With @causal-order/testing
If you want the published /testing suite to exercise the real transport implementation directly, install @causal-order/[email protected] or newer alongside the latest published @causal-order/transport so the shared reporting logic and the @causal-order/transport/testing entrypoint are both available. Then install the stack and point the adapter runtime at the transport testing entrypoint:
npm install @causal-order/testing @causal-order/transport @causal-order/dedupe causal-order
causal-order-testing-adapter-runtime --adapter @causal-order/transport/testing --duration 20m --time-scale 60 --profile expected-production-3way-meshThen inspect the latest run with the /testing summary tools:
causal-order-testing-latest
causal-order-testing-summary artifacts/runs/<run-folder>@causal-order/[email protected]+ is the shared reporting layer for both short adapter-runtime validation runs and long wall-clock transport runs. The published summary and compare CLIs can read /testing artifacts under artifacts/runs/ and /transport wall-clock artifacts under artifacts/transport-runs/, then normalize both into the same dedupe-aware correctness model:
delivered -> accepted/dropped -> orderedRecommended validation ladder:
- Start with a clean sanity pass:
causal-order-testing-adapter-runtime --adapter @causal-order/transport/testing --duration 10m --time-scale 60 --node-ids edge-a,edge-b,edge-c,edge-d,edge-e,edge-f,edge-g,edge-h --profile-file /path/to/transport-sanity-mesh.json --run-name transport-sanity-8node-10m- Then move to a more realistic production-like mesh run:
causal-order-testing-adapter-runtime --adapter @causal-order/transport/testing --duration 10m --time-scale 60 --node-ids edge-a,edge-b,edge-c,edge-d,edge-e,edge-f,edge-g,edge-h --profile typical-real-world-mesh --run-name transport-typical-8node-10m- Use a harsher resilience run only after the first two are healthy:
causal-order-testing-adapter-runtime --adapter @causal-order/transport/testing --duration 10m --time-scale 60 --report-every 1m --node-ids edge-a,edge-b,edge-c,edge-d,edge-e,edge-f,edge-g,edge-h --profile expected-production-mesh-dark-jitter --dark-nodes edge-b,edge-e --jitter-nodes edge-c,edge-f --run-name transport-chaos-8node-10mUse the sanity profile to confirm the adapter path is clean with no intentional duplicate injection or sequence reordering. Use typical-real-world-mesh as the normal deployment baseline for the transport package. Treat expected-production-mesh-dark-jitter as a resilience or chaos profile rather than the default production expectation.
For long wall-clock transport runs produced by this repo, point the same published reporting tools at artifacts/transport-runs/:
causal-order-testing-summary artifacts/transport-runs/<run-folder>
causal-order-testing-compare artifacts/transport-runs/<older-run> artifacts/transport-runs/<newer-run>Normalized Event Shape
The default normalizer tries to produce a runtime event with these fields:
idnodeIdsequenceclock.physicalTimeMspayload- optional
traceId - optional
ingestedAt
That keeps the shape friendly for both @causal-order/dedupe and causal-order.
Default Wire Message Shape
The adapter assumes JSON messages over WebSocket.
The default normalizer accepts common fields like:
{
"type": "event",
"id": "edge-a-000000000042",
"nodeId": "edge-a",
"sequence": "42",
"clock": {
"physicalTimeMs": "1781000000000"
},
"payload": {
"temperature": 21
}
}It also tolerates practical aliases such as:
nodeseqtsbody
Library API
import {
WebSocketJsonTransport,
normalizeTransportEventMessage,
createEventId,
} from "@causal-order/transport";The public API is transport-first:
WebSocketJsonTransportnormalizeTransportEventMessage()createEventId()- transport and peer-state types
Validation
Validation details, including completed 2h, 4h, and 8h wall-clock runs, are documented separately in VALIDATION.md.
Conduct
Project conduct expectations are documented in CODE_OF_CONDUCT.md.
Notes
- This is the transport layer, not the dedupe layer and not the ordering core.
- This is an ESM-only package.
- The current package surface is intentionally centered on the WebSocket + JSON path.
- The package vendors the narrow
wsruntime it relies on so publish artifacts keep a stable WebSocket implementation boundary without an extra npm runtime dependency. SeeTHIRD_PARTY_NOTICES.mdfor provenance. - If another deployment needs Kafka, a broker bridge, or another wire protocol, that adapter belongs in a separate package rather than forcing every transport mode into this package.
- The normalizer is opinionated but replaceable. If your node wire format differs, provide your own
normalizeMessagefunction.
