Retrace

Record selected application state and messages, deliver them durably, and reconstruct a session for inspection. Retrace includes a portable typed core, Lustre capture and replay, an optional viewer, and a server package for storage and retrieval.

The packages are experimental; APIs may change before 1.0. Browser and Tauri examples use the same application model; native integration has its own checks and runbook.

Capabilities

When to use Retrace

Retrace records explicitly selected state and messages. It does not record pixels, arbitrary DOM changes, native screens or OS dialogs. The server package is a library to host, not a managed collection service.

Packages

PackageOwnsYour application supplies
retraceTyped recording, checkpoints, replay and wire validationSafe model/events, codecs and deterministic reducer
retrace_frontendLustre capture, durable delivery, replay frame and optional viewerView, adapters, build artifacts and explicit lifetimes
retrace_serverAdmission, indexing, archive and retrievalRouting, authentication, pool, object store and worker lifetime

Install

Use Gleam 1.16 or later and add only the packages you import from Hex. A browser or Tauri application typically starts with:

gleam add retrace retrace_frontend

A backend uses the core and server packages:

gleam add retrace retrace_server

Commit gleam.toml and manifest.toml to retain the resolved versions. The core-only example shows recording without Lustre, storage or a network.

Capture a Lustre session

The basic inventory application supplies the domain codecs, update function and view. Import retrace_frontend/capture and retrace_frontend/client, then connect capture once:

let session =
  capture.session("Warehouse", domain.build, domain.contract, "web")
let assert Ok(#(recording, connect)) =
  capture.start(
    session,
    client.Config("http://127.0.0.1:4417", "basic-outbox", 100, 1_048_576),
    client.browser_storage(),
    client.http_transport("http://127.0.0.1:4417"),
  )

After accepting a domain command, observe it using the previous model. Feed the returned delivery effect back through the application’s capture updates:

let #(recording, save) =
  capture.observe(
    model.capture,
    model.stock,
    command,
    domain.encode,
    domain.encode_command,
  )

For replay, keep the build’s codecs, pure reducer and full view together in its replay module. Mount that module in a separate historical entrypoint:

import retrace_frontend/replay
import retrace_inventory/domain

pub fn mount() -> fn() -> Nil {
  let assert Ok(dispose) =
    replay.mount(domain.replay_module(), domain.build, "#app", 5000)
  dispose
}

The returned disposer owns the frame’s lifetime. The viewer example selects a recording and drives the frame. Usage topics link each integration step to its source and existing check.

How it works

flowchart LR
  App["Live application"] --> Capture["Frontend capture and typed core"]
  Capture --> Host["Host collector and server library"]
  Host --> Store["Database and object store"]
  Host -->|recording batches| Viewer["Viewer"]
  Viewer -->|seek and replay| Frame["Inert historical application frame"]
  Build["Build-specific codecs, reducer and view"] --> Frame

The portable core owns recording and replay rules. The frontend owns local delivery and viewer integration. The host owns server routing and borrowed resources; importing a package starts no listener, pool or background worker.

Try the browser example

From the repository root, start the local services:

nix develop
bash dev/services.sh

In a second terminal inside Nix:

cd examples/basic
npm ci
npm run dev

Open http://127.0.0.1:5193/, change inventory and choose Inspect this recording. The local PostgreSQL and S3-compatible data remains in ignored .local/. Use the advanced example for Tauri, immutable replay artifacts and custom adapters.

Operational notes

See the design for the recording, archive and replay contracts. AGENTS.md separates core, browser, service and native checks; CI timing checks are not a benchmark suite. THIRD_PARTY.md records attribution.

✨ Search Document