MESH ONLINECODENAME:
v0.34

Watch the Event Stream

Invoking gets you one result. Watching gets you the ongoing facts: the events the work emits while it happens. This is the observe half of the agent loop, and it is what lets you recover from a partial failure instead of trusting a single return value — see Submitted Is Not Completed.

Subscriptions are hot

You see events emitted after you subscribe, plus whatever is still in the ring buffer. Not the whole history.

There is no replay-from-the-beginning on the bus, and that is a design decision rather than a missing feature. Durability is a separate layer: a RedEX log or a retaining adapter, covered in Durable Logs. A consumer that needs to see everything since a known point needs one of those, not a longer timeout.

Two consumption models, split by binding

This is the sharpest asymmetry on the whole spine, and it is not cosmetic:

  • Rust, TypeScript and Python give you a stream. You subscribe and iterate; the runtime delivers.
  • Go and C give you a cursor. You call Poll(limit, cursor), get a batch and a next cursor, and call again.

Neither is a wrapper over the other at the API level, and code does not port between them by changing syntax. A Go consumer is a loop you drive; a Python consumer is a loop that drives you. Plan the shape of your consumer around the binding you are on rather than around the one the example was written in.

The bus is location-transparent

The same consumption code works whether the publisher is in-process or several hops away on the mesh. Between mesh nodes a subscriber joins a named channel by the publisher's node id and the publisher fans out to its roster; the reading side does not change.

What does change is what "still in the buffer" means. In-process, the ring buffer is the whole story. Across the mesh, an event has to arrive before it can be buffered, so a consumer that starts late misses more than it would locally.

Watching capabilities is a different thing

This page is about the event stream — the data flowing through a node. Watching the set of available tools change is on Discover, and it uses a different surface with a different cadence model. The two are easy to confuse by name and share nothing in implementation.

The concepts are in Channels and Events and Causality.

Watch it — TypeScript

Subscribe to typed events

typescript
import { NetNode } from '@net-mesh/sdk';
 
interface TemperatureReading { sensorId: string; celsius: number }
 
const node = await NetNode.create({ shards: 4 });
 
for await (const reading of node.subscribeTyped<TemperatureReading>({ limit: 100 })) {
  if (reading.celsius > 80) {
    console.log(`HOT: ${reading.sensorId} at ${reading.celsius}C`);
  }
}

subscribeTyped<T> returns a TypedEventStream<T> — an async iterable that deserializes each event for you. subscribe({ limit }) gives the raw EventStream instead.

T is a compile-time assertion, not a runtime check. The stream JSON.parses and casts; a payload that does not match your interface produces a wrong-shaped object rather than an error. Validate at the boundary if the producer is not yours.

One batch instead of a live loop

typescript
const batch = await node.poll({ limit: 100 });

poll returns what is currently available and returns. pollOne() takes a single event or null. Use these when you want a drain rather than a subscription — a for await loop over subscribeTyped does not end on its own.

Verify it worked

typescript
const stats = node.stats();
console.log(`consumed against ${stats.eventsIngested} ingested`);
if (stats.eventsIngested === 0) throw new Error('nothing was ever accepted to watch');

If the loop never yields, check the transport before the subscription: the default memory transport counts events and discards them, so emitting and then subscribing waits forever for something already gone. See Quickstart.

Next: Move artifacts.

§ parity · Distributed mesh channels — register / subscribe / publishfrom the capability record
Rust supportedNode / TS supportedPython supported · core-onlyGo supportedC supported

core-only means the operation exists on the low-level binding but not on the ergonomic SDK wrapper. Reach one layer down; it is not a gap.