---
title: "Live attach"
url: https://recursiveai.org/onescope/docs/live-attach/
description: "A viewer connects to a probe (see probe-wire.md) and turns its stream into a source that the history re-simulates locally."
version: "1.0.0"
source: https://github.com/recursive-ai-oss/onescope-csharp/blob/d681be56420836ea0d8874416367bc8181235813/docs/live-attach.md
---

# Live attach

A viewer connects to a probe (see `probe-wire.md`) and turns its stream into a source that the history re-simulates locally.

1. `ProbeFinder.List` lists the running probes: socket path, module id, library version, format version and process id.
2. `LiveSession.Connect` connects, reads the handshake, asks for the stream and reads the snapshot. The host takes the snapshot at its next recorded step, so it blocks for as long as that takes (up to `timeoutMilliseconds`, default 5000); the shell runs it on a worker thread so its window keeps drawing (`attach` returns `connecting` and `sources.attaching` shows it). `maxRecordBytes` (default 256 MiB, each record counted with `LiveSession.RecordOverheadBytes`) bounds what the viewer keeps: past it `Pump` stops, `Capped` is true and the connection closes. It finds the loaded module whose id the handshake names; that module must implement `ILiveModule` (in the SDK, `OneScope.Sdk`, so one module serves files and live streams).
3. `ILiveModule.OpenLive` gets the library version, the format version and the snapshot, and returns an `ILiveSource`: a source whose world starts at the snapshot and whose recording is writable, with `Recording.StartStep` set to the host's step at the snapshot.
4. A reader thread receives frames into a bounded queue. `LiveSession.Pump`, on the thread that owns the history, hands each record to `ILiveSource.AppendRecord`, which appends to the recording. The history sees new steps as they arrive. If the viewer stops pumping the queue fills, the reader waits, and the host's probe drops the viewer.

A live history starts at the host's step at the attach (`History.StartStep`) and its step numbers are the host's, so the viewer's hash at step s equals the host's hash at step s bit for bit. Seeking, forking and diffing work as for a file. Saving a live source writes a replay that starts from the snapshot, so a session reopens to the same hashes.

In the app, `attach` connects (by pid or socket) and makes the stream a source. The shell moves arrived records into the history on its own thread (every operation, and every frame of a window), so the history grows while the user scrubs. A live source has a follow flag: on, the shared cursor moves to the host's newest step as records arrive; seeking off the newest step turns it off, seeking to it (or `follow` with `on`) turns it on. `sources` shows `live {state, follow, firstStep, newestStep}`; when the stream ends (the host closed its probe, dropped a slow viewer, exited or sent bad bytes) the source stays, with what was received, and `state` says why.

When the host disconnects the viewer, closes its probe, or dies, `LiveSession.State` says which (`Disconnected`, `Ended`) and everything already received stays usable. Attaching again makes a new session from a fresh snapshot.

## A OnePhysics host

`samples/PhysicsHost` is a small program that hosts a OnePhysics world (2D or 3D) behind a probe: `physicshost <2d|3d> <steps> <delay-ms> <hash-file>`. The world records itself with `world.StartRecording(feed)`, where `feed` is the OnePhysics module's `LiveFeedStream`; `feed.Record` is set to the probe's `Record` and the probe's snapshot source calls `feed.WriteSnapshot`. The host also calls `probe.Poll()` after each step so a viewer that attaches while nothing is being recorded still gets its snapshot. It writes `step hash` lines when it ends; a viewer's hashes at the same step numbers are equal, whenever it attached.
