Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
libtracer
libtracer

Getting started

  • Getting started
  • Examples
    • In-process pub/sub
    • Pub/sub fan-out & dispatch cost
    • Wire codec round-trip
    • Wire codec deep-dive & throughput
    • Rope scatter-gather
    • Two nodes over a wire — FWD delivery
    • Composition axes
    • Register a vertex, and address it
    • Read and write
    • await
    • Write-creates
    • :children[]
    • Retirement
    • A HANDLER vertex
    • A STREAM vertex
    • Subscribe to one vertex
    • One edge, a whole subtree
    • Delivery terminates at the target
    • The delivery policy is per subscription
    • Unsubscribe & the release hook
    • Unsubscribing from inside a delivery
    • Retire drops a producer's subscriptions
    • The TLV header and the opt byte
    • Structured or opaque — one bit decides
    • A PATH body is packed segment records
    • The escape record
    • The trailer: CRC and timestamp
    • What decode refuses
    • decode_into: a flat arena
    • tlv_view_t: a scattered frame
    • The segment, and its refcount
    • subview
    • borrow: the app's own bytes
    • The rope
    • subrope and the iovec egress
    • A bounded backend
    • A DEVICE link
    • A shared seam needs a thread-safe backend
    • The failable block seam
    • Two L0 seams, and the question that picks
    • A bump source
    • The upstream decides what the buffer means
    • A long-lived seam has to recycle
    • Classes do not share
    • A container that fails by value
    • The std::pmr adapter
    • Open by default, and the first ACE is the lock
    • The caller context is not the subject
    • access_mask is a bitfield
    • EVERYONE@ is reserved both ways
    • Effective ACL = own + inherited
    • expires_ns is checked against your clock
    • Two evaluators, and DENY
    • An ACL is a security document
    • The dst is a source route
    • Terminus or forward — one test decides
    • One NAME, one slot
    • A mount run is consumed whole
    • Three nodes, and a forwarder that stores nothing
    • The src you accumulated is the way home
    • A repeating flow buys its route back
    • A stale label is dropped and NACK'd
    • One seam, every wire technology
    • A kind is a NAME, resolved twice
    • DIAL and LISTEN are two constructors
    • A datagram already has boundaries
    • A stream has none, so the kind supplies them
    • No frame crosses until the Upgrade completes
    • The one BUS kind
    • One listener, many slots
    • Rust: a VALUE frame and its CRC trailer
    • Rust: an address is packed segment records
    • Rust: a remote write is one FWD frame
    • Rust: subscribing is a write
    • Rust: reading a node's :stats
    • TypeScript: a VALUE frame and its CRC trailer
    • TypeScript: write a remote vertex, then read it
    • TypeScript: subscribe to a remote producer
    • TypeScript: a remote failure is a typed error
    • TypeScript: dial a WebSocket link

Specification

  • The specification
    • Protocol v1 — the wire format

Reference

  • Reference (descriptive)
    • Overview — the six-layer model
    • Module catalog & composition
    • Deployment profiles
    • Concurrency & scaling
    • Reclamation policy
    • Memory substrate
    • Views & ownership
    • Data format
    • Protocol-defined TLVs
    • Graph model
    • Addressing
    • Communication flows
    • User data packing
    • Vertex roles & aggregation
    • Host embedding
    • Network formation
    • CAN transport
    • WebSocket session authentication
    • Composition over the network
    • Transports are vertices
    • Bindings map
    • ROS 2 integration (rmw_tracer)
    • Backpressure & sizing
  • Design notes
    • Concurrency & scaling
      • Scaling and serialization
      • Write and delivery path
    • Zero-copy and flatten
    • Build configuration
      • The configuration space
    • Failable allocation and backpressure

C++ API reference

  • C++ API reference
    • Interface map
    • File map — header to page
    • status & errors — result taxonomy
    • config — the build's traits type
    • instrumentation — reachability counters
    • segment — refcounted bytes
    • backends — allocators
    • views — view_t / rope_t / cast
    • frame-codec — TLV codec + CRC
    • Wire format, bit by bit
    • path — addressing
    • graph — vertices & dispatch
    • security & ACL — access control
    • fwd-router — FWD routing and the /net plane
    • transport — the wire
    • connection config — the SPEC config keys
    • can — the header-elided CAN stack

Interoperate

  • Interoperability
  • Build a custom device
  • A production ESP32 node
  • Capability matrix
  • Implementation registry

Evidence

  • Performance & conformance
  • Test report

Glossary

  • Context glossary

Start here

  • Route by intent
Back to top

Dial a WebSocket link (TypeScript)¶

TransportWs dials a WebSocket listener, and each libtracer frame crosses as one BINARY message. The listener here is a loopback ws server on port 0 in front of the stand-in node.

The node on the other end is stand-in-node.mjs, an in-memory stand-in. The TypeScript packages have no node of their own, and the stand-in lets the example run with no C++ build. Against a real node the client code is unchanged.

What to notice¶

  • connect() resolves when the socket is open. Only then can frames cross. The client code is the same as in the in-memory examples.

  • Port 0, read back. The kernel picks the port and the example reads it from the listener, so nothing collides with whatever else runs on the machine.

  • A browser dials the same way. On a runtime with a global WebSocket (a browser, or Node 22 and later) the { WebSocket } option can be left out.

Source¶

 1// SPDX-License-Identifier: Apache-2.0
 2// SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
 3
 4/**
 5 * @file
 6 * @brief One concept: dial a link. `TransportWs` dials a WebSocket listener and carries one
 7 * libtracer frame per BINARY message; the client on top is the same one the in-memory
 8 * examples use.
 9 *
10 * Run: `node examples/ws-dial.mjs` (from `bindings/typescript/`, after `npm run build`).
11 */
12
13import assert from 'node:assert/strict';
14import { WebSocketServer, WebSocket } from 'ws';
15import { TransportWs } from '@avatarsd-llc/libtracer-ws';
16import { LibtracerClient, encodeValue } from '@avatarsd-llc/libtracer-client';
17import { standInNode } from './stand-in-node.mjs';
18
19// LISTEN: a loopback listener on port 0 (the kernel picks), fronting the stand-in node.
20const node = standInNode();
21const wss = new WebSocketServer({ host: '127.0.0.1', port: 0 });
22await new Promise((resolve) => wss.once('listening', resolve));
23wss.on('connection', (sock) =>
24  sock.on('message', (data) => node.handle(new Uint8Array(data), (b) => sock.send(b, { binary: true }))),
25);
26const url = `ws://127.0.0.1:${wss.address().port}`;
27
28// DIAL: connect() resolves once the WebSocket is open; only then can frames cross.
29const transport = new TransportWs(url, { WebSocket });
30await transport.connect();
31console.log(`dialled ${url}`);
32
33const client = new LibtracerClient(transport);
34await client.write('/sensor/temp', encodeValue(Uint8Array.of(42)));
35assert.equal((await client.read('/sensor/temp')).payload[0], 42);
36console.log('write + read crossed the socket');
37
38await transport.close();
39await new Promise((resolve) => wss.close(resolve));

Run it from bindings/typescript/, after npm ci && npm run build:

$ node examples/ws-dial.mjs

See also: DIAL and LISTEN (C++) · the WebSocket upgrade (C++).

Next
The libtracer specification
Previous
A remote failure is a typed error (TypeScript)
Copyright © 2026, avatarsd LLC
Made with Sphinx and @pradyunsg's Furo
On this page
  • Dial a WebSocket link (TypeScript)
    • What to notice
    • Source