Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Time-Travel Debugging

Time-travel debugging lets you inspect a workflow run’s state at any historical point in time. The system reconstructs snapshots on the fly by replaying audit events up to the requested timestamp — no pre-computed snapshots are stored.

Time-Travel Debugging — showing state reconstruction at any point and diff between two moments

Time-travel requires recording to be enabled on the function. Only runs from recording-enabled functions have the audit events needed for state reconstruction. The function must use the all recording profile, which is what recording: true selects when you set no recordingProfile. The run_lifecycle and steps profiles do not support time-travel; the server rejects the queries with FailedPrecondition.

import { ironflow } from "@ironflow/node";
const processOrder = ironflow.createFunction(
{
id: "process-order",
recording: true, // enable recording, it is disabled by default
triggers: [{ event: "order.placed" }],
},
async ({ event, step }) => {
const validated = await step.run("validate", async () => {
return validateOrder(event.data);
});
const charged = await step.run("charge", async () => {
return chargeCard(validated);
});
return { validated, charged };
},
);

Open a run’s detail page. When the function has recording enabled, a timeline scrubber appears at the top of the page.

  1. Scrub to a point in time — click or drag on the timeline to see the run’s state at that moment. The step list updates to show which steps had completed, their outputs, and the run’s status.
  2. Ghost steps — steps that had not yet executed at the selected time appear dimmed. This makes it easy to see what was still ahead.
  3. Diff view — select two timestamps on the timeline to compare the run’s state between them. The diff highlights which step outputs changed (useful for spotting hot-patches or injected values).
  4. Return to live — click the Live button to leave time-travel mode and return to the real-time view.

The ironflow inspect command supports time-travel via --at and --replay flags.

Inspect the run’s state at a specific timestamp:

Terminal window
ironflow inspect run_abc123 --at "2024-01-15T10:30:00Z"

The TUI renders the run as it existed at that moment. Steps completed after the timestamp appear as ghost steps (dimmed).

Step through the run’s audit events one at a time:

Terminal window
ironflow inspect run_abc123 --replay

Use --all-events to include step.started events (omitted by default to reduce noise):

Terminal window
ironflow inspect run_abc123 --replay --all-events

These are the --replay bindings. The frozen --at view has no frames, so it accepts only the step-navigation subset (j/k, Tab, q).

KeyAction
→ or lNext frame (next audit event)
← or hPrevious frame
j or ↓Next step in list / scroll detail
k or ↑Previous step in list / scroll detail
gGo to first frame
GGo to last frame
TabSwitch between steps and detail panels
q or Ctrl+cQuit

Reconstruct the full run state as it existed at a specific point in time.

import { createClient } from "@ironflow/node";
const client = createClient({ serverUrl: "http://localhost:9123" });
const state = await client.getRunStateAt("run_abc123", new Date("2024-01-15T10:30:00Z"));
console.log(state.runId); // "run_abc123"
console.log(state.status); // "running" | "completed" | "failed" | ...
console.log(state.steps); // Array<{ stepId, name, status, output, ... }> — steps at that moment
console.log(state.timestamp); // Date of the requested timestamp

Fetch the full list of audit events for a run. This is what powers the dashboard’s timeline scrubber.

const events = await client.getRunTimeline("run_abc123");
for (const event of events) {
console.log(`${event.timestamp} ${event.eventType} ${event.stepName}`);
}

Retrieve a specific step’s output as it existed at a point in time. Useful for checking what a step returned before a hot-patch was applied. Pass the step name your code used (charge) or the full step ID ({runId}:charge:0). A step that did not exist yet at that time returns not found.

const output = await client.getStepOutputAt(
"run_abc123",
"step_001",
new Date("2024-01-15T10:30:00Z"),
);
console.log(output.stepId); // "step_001"
console.log(output.status); // the step status at that time
console.log(output.output); // the step output at that time, decoded from JSON

  • Recording required — time-travel is only available for functions with recording: true and the all recording profile. Runs created before recording was enabled have no audit events to reconstruct from.
  • Query-time reconstruction — state snapshots are reconstructed on the fly by replaying audit events. There are no pre-computed snapshots. For runs with a very large number of audit events, reconstruction may take longer.
  • Audit rows are best effort — a row can drop under load. A snapshot built without it can show a finished step as still running.
  • Retention applies — audit events are subject to the global IRONFLOW_AUDIT_RETENTION_DAYS policy. Once events are cleaned up, time-travel for those runs is no longer possible.