What Wax remembers.

How recording works

A Wax recording is really three things coming together: everything needed to understand and rebuild the program, zero or more snapshots of the Wax heap, and a tape of the data the program observed from outside the Wax world over some window of time.

Given all of that, plus Wax's determinism guarantees, we can reconstruct any retained frame. This is the important part: Wax does not remember every instruction or intermediate value. It remembers enough to rebuild them on demand. That is why a Wax recording can stay much smaller than a complete execution trace, with way lower performance and storage overhead.

Wax is intended to sit inside a larger application, and obviously it cannot rebuild state that was never part of Wax or exposed to it. File handles, network sockets, graphics resources, and host application state are not coming back. Generally, they do not need to. The basic rule of thumb is: record the outside observations that can affect Wax, reproduce the work that happened inside Wax, and reconstruct the results Wax produced by replaying the recording without the original host attached.

What and when Wax snapshots

A normal program is a terrible thing to freeze halfway through its work. It might be halfway through changing an object, holding temporary state on the stack, or sitting inside a call to the host. Then there are the mechanics: pausing a program at an arbitrary point is a super difficult problem, and the solution changes with the platform and hardware it is running on. You would need a consistent way to serialize CPU registers, stack frames, and heap state, then restore all of it correctly somewhere else. No thanks.

Wax sidesteps that problem entirely by dividing work into frames. The host begins a frame, calls into Wax as many times as it needs to, and then ends the frame. What a frame represents is entirely up to the application. It could be one rendered image, one request, one simulation step, anything else that makes sense for that particular application.

At the end of a frame, Wax has returned control to the host. The Wax stack is empty, the input tape has an exact position, and anything that needs to survive lives in memory the runtime understands. That gives Wax a clean moment to collect garbage, take a snapshot, restore an earlier state, or do any other work that depends on a stable view of the program.

Recordings are living things

Once a frame is over, Wax can choose to save its entire program world in a snapshot. The heap, object identities, allocator, runtime state, all of it. Restore that snapshot later and the program is back at that exact point, ready for the next frame with whatever new input we want to give it.

If all we wanted was a checkpoint, we would be done.

For a recording to be useful, it needs to be more than a collection of moments. At the bare minimum, it needs to tell us how the program got there and what happened next.

Wax could brute force the problem by taking a snapshot after every frame. It would work, but it would also mean repeatedly copying the entire live heap. That is horribly wasteful when the program's entire world can be reconstructed by running the code again.

Instead, Wax takes snapshots at occasional intervals and uses them as recording keyframes. Between those keyframes, it records the outside observations that moved the program forward. When we want a frame that does not have its own keyframe, Wax restores the newest one before it and replays those observations from there. Determinism fills in all the work we did not record and your hard drive can stay on a diet.

At a high level, that's how we can reconstruct every retained frame at a surprisingly low cost. A lot of other time travel systems basically stop there: record what happened and replay it later.

Wax goes quite a bit further than standard time travel replay.

For all the magic to work, a Wax recording also needs to contain all the machinery required to understand what happened and later change what happens next. A heap full of raw object bytes is useless if we no longer know what any of them are.

The recording carries the entire program source and all the metadata needed to connect the raw heap back into something we can make sense of. Rather than stuffing another 4 to 5 MB into every recording, it carries the cryptographic identity needed to fetch and verify the exact compiler and debugger toolset later. We never grab the latest compiler and hope everything still works the same way.

Before any of that can happen, Wax makes sure the program code and snapshot are 100% compatible. The compiler and runtime versions must match, the source fingerprints must match, and the saved program metadata must agree. If anything is wrong, Wax barfs loudly instead of trying to force it to work.

This means the Inspector can use that exact toolset to rebuild the program that produced the recording and compile completely new Wax code against it. We can then use that to ask questions that did not exist when the program ran, add instrumentation after the fact, change an input, or change the code and see what happens next.

What Wax forgets on purpose

We have said a few times now that Wax does not remember every instruction or intermediate value. The reason is simple: if Wax can calculate something again, putting it in the recording would just make the recording bigger without giving replay anything useful.

If a frame takes one input and calculates ten thousand values from it, the tape only needs the input. Replay can do the same work again. The result comes back because the program reruns, not because Wax wrote all ten thousand values into the recording.

The compiler can take that idea even further. If it proves that an API only looks at the program and cannot change anything that survives the call, the call and its arguments do not need to go onto the tape at all. We can run the same inspection again later. Immutable program data only needs to be stored once, and input that has not been consumed stays outside the persistent heap instead of accidentally becoming part of a snapshot. The compiler decides what replay needs before the data is written. The rest is simply not recorded.

Sometimes we want Wax to forget something even though the running program really did use it.

The secret type exists for exactly that case. A secret behaves like normal text while the application is running, and the program sees the real characters. When Wax takes a snapshot, it zeros those characters in the saved copy without touching the live application. Secret input records preserve the edits that happened without preserving the characters that were entered.

Replay still knows that a secret existed, how its length changed, which edits happened, and which frame they happened on. It sees zeros where the original characters used to be.

This is not encryption. The host still handled the original characters, and the program can still copy or derive information from a secret into ordinary state. That ordinary state can end up in the recording. The promise is much narrower: the characters stored in a secret never become part of the .wxs artifact. Wax makes this easier to manage because its text input protocol has a secret entry mode. The host marks each protected text event as secret as it passes through the standard channels for text input and clipboard data.

Overhead and cost

A recording has two main costs: the input tape and the keyframes. They behave very differently.

The tape is usually small because it stores what Wax observed, not what Wax computed. If the host sends a few values into each frame, there is not much to record. If it shoves a massive object into Wax every frame, that data has to go somewhere. Wax cannot make a hundred megabytes of input magically free, although repeated data generally compresses very well.

Keyframes are different. Before compression, each one is roughly the size of the persistent live heap. It does not really matter how busy the previous frame was. A quiet frame and a frame that performed millions of calculations cost roughly the same to snapshot if they leave behind the same amount of state.

That is the uncompressed number. Wax's memory layout is extremely friendly to zstd: free regions are full of zeros, type information repeats, and nearby keyframes tend to look very similar. In practice, heap images often compress by 5 to 10 times, so the keyframe that reaches disk can be dramatically smaller than the heap it came from.

Temporary objects are already gone by then. If a frame creates a million objects and none of them survive, those objects do not become part of the keyframe. What matters is what remains alive when the frame ends.

This gives us a useful choice. A production recording can keep one keyframe followed by a huge input tape. That is enough to reconstruct every frame after it, and production does not have to keep copying the entire heap. The trade is that reaching a late frame means replaying everything that happened since that keyframe.

Luckily, we can pay that cost later. Take the recording to a development machine, replay it once, and materialize new keyframes throughout the history offline. Because Wax execution is deterministic, we can just create new keyframes at will. Nothing about the recorded history changes. We are just adding more places to jump in. The next time we seek to a late frame, Wax can start nearby instead of replaying the entire run. This is a super useful lever to have. It means you can leave your production overhead as low as possible and only spend the CPU cycles on seek optimization when you actually need to seek.

Applications that need fast live rewind can take more keyframes while they run. Applications that only need investigation after the fact can keep production light and make the recording fast to navigate later.

Live recording still needs a limit, though. We generally wouldn't want to keep hours of playback data unless you happen to have enormous storage capacity. Wax keeps a rolling window measured in frames and bytes, then discards history that falls outside it. An eight hour session does not automatically mean eight hours of retained history. The application chooses the keyframe cadence, how many keyframes to retain, and how many input frames or bytes can accumulate between them.

Recording is designed to stay enabled in production, but it is not free. A large persistent heap creates large keyframes, and a large amount of host input creates a large tape. The useful rule is simple: Wax pays for what enters the program and what remains alive, not for every calculation the program performs. Taking a snapshot is generally fast and scales with the size of the live heap. Supported native applications can move keyframe processing onto a background thread.