Behind the button below is an ordinary Debian image — nginx, gunicorn and Django, five processes, nothing rebuilt — running as a WebAssembly module in this browser tab, on an x86-64 interpreter written in Rust and a small Linux-personality kernel compiled into the same module. It was booted once, in CI, and written to a file; the page continues it from that instant, already listening on port 80.
The page is the server's only client. It sends the container an HTTP
request, nginx proxies it to gunicorn, Django answers, and the response
comes back to the page. Then the slider goes backwards: every instruction
the request cost is an instant the machine can be stood on again — its
processes, their files and descriptors and sockets as they were, the
bytes that crossed between the container and the page, and underneath,
the registers, the instructions at rip, the stack, the memory
map — because a container's run is a pure function of what the host
answered it, and between two instructions the module's memory is the
whole machine.
The container is a StructFS store, and the page is a browser of it
with a time axis: every panel is a read of one of the store's paths at
the chosen instant, titled by the path, the raw value a click away, and
the panels themselves are what the store's meta lens
declares. The timeline is the store's traffic — the container's reads
and writes to the host, the clock, the request's bytes — beside its
syscalls. Pin an instant and every panel shows what changed since.
GET / HTTP/1.0; the answer arrives in a fraction of a second, and the page stands the container on the instant the request came in.accept4, recvfrom, sendto, the response going out — or drag the slider inside the request's span. The container restores the nearest checkpoint and re-executes to that exact instruction; the panels show the machine as it was. Paths and descriptors in a row are links to what they name./etc/nginx/nginx.conf, nginx's logs under /var/log — as they stood at that instant; click a process to view its paths; type any path of the store into the bar at the top and read it.| Django's boot, done once in CI | 3.29 G instructions, about half a minute |
| standing the container up from the snapshot, files decompressed again | about 1 s |
| a request through nginx, gunicorn and Django | about 4 M instructions, a fraction of a second |
| a seek into the middle of the request | a fraction of a second |
Source, design and measurements: github.com/mgyenik/zaqaru
— docs/time-travel-debugger.md for the design, web/ for the page and the JavaScript host.
The debugger asks the container questions through the isotope Server Protocol: the container is a StructFS store, and every panel is a read of one of its paths.