The autopsy — what the analysis does and how to read it
Autopsy takes an artwork bundle and produces a conservation-grade description: what the work is, what it needs to run, and everything it reaches on the network. It is deliberately split into two layers, and understanding that split is the key to trusting the output.
Two layers: extract vs. judge
Deterministic layer — extracts, never decides. Unpacking, format identification, hashing, entry-point resolution, binary/import parsing, and a fair sweep for every network-protocol indicator in the code. This layer does no validation, no scheme-matching, no set-fitting, and no starvation — it surfaces generic signals fairly and hands them up. It never classifies a URL or guesses what a work “is”; that would bias the evidence.
Agentic layer — the analyst — judges. A tool-using agent reads the flagged evidence (list the tree, grep sources, read slices, unpack/deep-read on demand, walk the run graph) and reconstructs the work: what it is, its crucial components, every network endpoint judged in context, its unknowns, and what it deliberately left unexplored. It runs with no step or time caps — until it reaches a coherent picture or you cancel.
This separation is doctrine: deterministic tools flag files fairly; the AI reconstructs and judges. It’s why a report about your work isn’t shaped to fit some other work.
The network mandate
For a broken artwork, its network reach is the thing you most need and most easily lose. So it’s a hard requirement:
- Every distinct network indicator is put in front of the analyst as a one-line lead with a pointer to the code, and the analyst must look at all of them — even if that means sifting many false positives.
- The analyst may not state where an endpoint comes from without citing the code slice it read — so it classifies each as a real endpoint, a constructed one (built up over several steps), or a field default, on evidence rather than a regex.
- A safety net records URLs the analyst read even if it forgot to formally note them, so nothing slips out of the final report.
The result: for a work dying because a host is gone, you get the host — to restore it or serve it from an archive.
From “endpoint gone” to a revival contract — wayback-mcp
Naming a dead host is only half the answer; the other half is what it served. The analyst settles
that during the analysis, using a shared wayback-mcp MCP server over the Internet Archive’s
Wayback Machine. It exposes four read-only tools:
wayback_snapshots— the CDX index: every capture of a URL, with timestamp, status and content-type, scoped to the work’s own era.wayback_available— the single capture closest to a chosen moment.wayback_fetch— the ground truth: the archived capture’s actual bytes and content-type.wayback_prognosis— a cheap triage that sizes up a whole cluster of a work’s dead URLs at once.
Resolving a dead endpoint is a search-and-judge loop, not a lookup, so a wayback specialist sub-agent drives these tools while the server stays dumb and factual. The analyst spawns it for a dead endpoint, it picks the era- and request-matched capture and fetches the real archived bytes, and the finding turns from “this URL is gone” into a concrete revival contract: the actual format a revival must reproduce.
This is distinct from the guest-side wayback-cache-proxy that replays archived responses into the
running VM (see Working in the VM). wayback-mcp
answers what the dead server served; the cache proxy serves it back to the artwork. rvmc points
the proxy at the same archive the analyst reasoned from.
The run-graph cartographer — a grounded flow over the file skeleton
A pile of files is not a work; the flow between them is. Before the analyst starts, the deterministic layer builds a thin run graph — the entry point, the imports it declares, the files it references, and the containment of archives. That skeleton is honest but shallow: for anything whose real flow only appears after decompilation — an embedded Director movie carved out of a projector, a decompiled Java tree, an exported ActionScript bundle — “the archive contains these members” is provenance, not execution flow.
So at the end of the investigation a dedicated cartographer pass hands the analyst — which has just read the decompiled code and traced the behaviour — a set of graph-authoring tools and asks it to lay the real execution and data flow over the skeleton: what launches what, loads what, reads what, reaches which endpoint. Two rules keep it honest:
- Grounded. Every node the model maps is resolved against a real file in the workspace (path- jailed) before it enters the graph. A node or edge that doesn’t ground is dropped and counted, never trusted. The model judges the flow; the jail keeps it from inventing one.
- No caps, no truncation. The graph accretes one node and one edge at a time, so a large, tangled work maps all of its flow rather than losing detail to a truncated JSON blob.
The payoff shows up when runtimes nest: a classic-Mac Director piece wrapped in a compatibility layer reads as a stack (wrapper → interpreter → the movie), and the cartographer unwinds it so the graph shows the artwork, not the launcher it came wrapped in.
Orientation — finding the front door first
Before anything is subtracted, the analysis settles the two questions everything else depends on: where does the work start, and which files are the work at all. The deterministic layer draws a bundle portrait — the file tree plus what it already knows about every executable — and puts it in front of an LLM judge that names the entry point and partitions work from commodity. When the entry is already unambiguous (one clear executable, a header that names it), no model is spent; the judge is called only on genuine ambiguity — several executables, or an artwork interleaved with the runtime it ships.
Two rules keep this honest. The judged entry never outranks a deterministic or human one — it fills a gap, it doesn’t override evidence. And the judgment happens before the runtime subtraction below, so when a bundled runtime is collapsed out of view, the work’s own anchors — the entry point and the members the judge named as the work — stay visible through it. A Unity piece is the clean example: the launcher and the artist’s compiled code live inside the engine-dense tree, and without this step they would be subtracted along with the engine and the work would read as nothing.
Commodity vs. authored — bounding the work
A born-digital work rarely ships alone. It carries its runtime, its libraries, sometimes an entire bundled interpreter. If the report treats all of that as “the artwork”, the conservator drowns: the authored part — the thing that must be understood and kept — is buried under thousands of files of boilerplate that any copy of that runtime would also contain.
So the analysis bounds the work and subtracts the commodity, and it does so from generic signals only — never a hardcoded library name and never a per-format rule (that would be set-fitting to one work). Strongest evidence first:
- Content prevalence. An exact whole-file hash that a set of known-good runtimes and other analysed works already contain. If a thousand other bundles ship this exact byte-for-byte file, it is not this artist’s authorship.
- Vendored runtimes. A subtree recognised as a bundled interpreter or library tree from its
structure alone — a JRE/JDK tree, a
node_modules, a Pythonsite-packages, a Processing or .NET runtime — collapsed to a single opaque node. - Self-declared libraries. A component that declares itself a published third-party library (a package coordinate, a vendor manifest) rather than the work. Content beats filename here: an artist’s jar and a library jar can be named alike, but the library announces its own coordinate and the artwork does not.
- Generated files. Machine-generated, minified, or lockfile content.
Nothing is deleted — the runtime still ships into the VM; this is a reading decision, not a provisioning one. And it is deliberately conservative: an unnamed, hand-authored file trips nothing, and the genuinely undecidable “artist’s own jar vs. third-party library” case is surfaced to you rather than guessed. What the conservator sees is the authored part, with the boilerplate marked as what it is.
The scoping specialist — artwork vs. runtime inside the graph
Preflight (below) is a whole-bundle question asked once, up front. A distinct scoping specialist works inside the analysis graph: a work often ships its whole runtime beside itself, and graphed member by member that buries the artwork in thousands of nodes and mis-resolves the entry point into the runtime.
A deterministic detector recognises the unambiguous vendored runtimes from structure alone and collapses each to a single opaque node — in the analysis graph only. Nothing is deleted; the runtime still ships into the VM. The collapse is anchor-aware: it keeps the entry point and the members the orientation judge (above) named as the work, even when they sit inside the collapsed runtime tree. For the genuinely ambiguous subtrees, a scoping specialist sub-agent judges from the evidence — which subtree is the work and which is dependency it merely carries — across ecosystems (JVM / Processing, .NET, JS, Python). It prefers to keep when unsure: excluding the artwork is worse than graphing a few extra dependency files. This is why the run-graph can show the work rather than the library it came wrapped in.
The verification spine — grounded, falsifiable claims
The analyst is an LLM, so the analysis refuses to let it assert on faith. Every claim is held to evidence by a spine of checks that runs alongside the reasoning:
- Citations are validated. A claim about where an endpoint comes from is only kept if the code slice it cites resolves to a real path in the workspace. Hallucinated citations are dropped and counted, and the report’s confidence is downgraded accordingly.
- An independent grader re-checks the hard claims. When the analyst characterises an actionable unknown — a form backend, a plugin, an external target — and writes a revival contract for it, a separate grading pass re-reads just that contract and its source and reports where the source backs the claim, where it contradicts it (over-reach), and what the source shows that the claim never accounts for. It never marks the analyst’s own homework; it is a fresh look. On a weak grade it recommends the cheapest next step — re-read the source, re-reason, or escalate to needing a running VM or a human — rather than failing loudly.
- Claims are falsified against an oracle. A revival plan that assumes an archive capture is tested against the Internet Archive, which is ground truth for that assumption. A plan that relies on a capture the archive never took is refuted — the over-trust failure, where the analyst banked on an archive it could not have seen. A live capture the analyst dismissed is a revision: recovery is actually possible after all. (A second oracle — booting the work under rvmc and observing the claimed behaviour — is the seam autopsy is built toward but hands to the research stage.)
All three degrade rather than crash: if a grader or oracle is unavailable, the claim simply stays unverified and is marked as such, and you decide whether to trust it.
The per-language deep-read
Under the analyst sits a family of format-specific deep-readers. Each turns a runtime’s binary into readable evidence — never by running it, always by parsing or decompiling — and each writes its recovered source into the durable analysis record so the conservation file keeps it. The consistent shape is a deterministic pass that always runs (so you get some structural fact even with no decompiler present) plus a decompiler pass that degrades to a flagged note when its tool is absent.
- Director / Lingo. Decompiles the movie’s Lingo bytecode back to source, harvests the script
and cast inventory and the authoring version (a hard era fact), and surfaces the network handlers
(
gotoNetPage/getNetText…). For a classic projector that ships the movie inside its executable, a carving pass first extracts the embedded movie so the decompiler works on the artwork, not the projector bloat. - Flash / ActionScript. A pure-Python header/tag pass (version, stage size, frame rate, AVM1 vs
AVM2) always runs; the decompiler recovers the ActionScript and its network calls (
getURL/navigateToURL/URLLoader/XMLSocket…) and what the timeline does. - Java. The manifest,
.classversion (the JRE era), and dependency closure are read from bytes with no JVM; the decompiler recovers Java source and its URLs and referenced files. It knows the QuickTime-for-Java surface — the Java+QuickTime works of that era. - .NET / CLR. The managed PE header, CLR version, and referenced assemblies (WinForms / WPF /
XNA / networking) are read deterministically; the decompiler recovers C# and its
System.Netcalls and spawned processes. - Unity. Located by its data layout: the Unity version (the era and player VM), the scripting backend (Mono — decompilable C#; or IL2CPP — a much harder metadata dump), and the platform, with the managed assembly routed to the .NET reader.
- HyperCard. A pure-Python walk of the stack’s binary blocks recovers its HyperTalk scripts and their navigation and network handlers — there is no external decompiler to shell into, so the format is parsed directly.
- Max/MSP. A saved patch is plain JSON, so it is read directly: the Max major version (the
runtime anchor) and the network-capable objects (HTTP, OSC/UDP, serial to an Arduino, shell
externals) with every literal URL and
host portmined into the network layer. - QuickTime. An atom walk answers both the codec-runtime question (which legacy decoder a
.movpins) and the behaviour question — wired sprite actions, clickable HREF tracks, navigable QTVR panoramas, and external media references that reach the net. - Mac resource forks. Classic-Mac carriers (AppleDouble, AppleSingle, MacBinary, raw
.rsrc) are parsed to a verdict — 68k vs. PowerPC, application vs. HyperCard stack, type/creator codes — where PRONOM is largely blind. - Native binaries. PE/ELF import parsing names what a compiled executable does from its import table (3D via OpenGL, network via the socket stack, audio, process spawning), and where a Ghidra side-car is available, a deeper decompile.
- Interpreted scripts. Bare Perl / shell / PHP / Python / JS members are read by a code-semantics layer that traces dispatchers, command sinks, remote-include risks, and the network mechanism.
- Modern web art. The same machinery reaches contemporary web pieces:
- Electron applications — a Chromium + Node runtime; unpacked, pinned to its bundled version, then read the same way as any other bundle.
- Browser extensions — read across their format eras, including what hosts a WebExtension is permitted to reach and whether it intercepts or blocks traffic. The preservation problem is identical: a runtime frozen at one moment, servers that may already be gone.
- Installers — detected and surfaced to preflight, never silently treated as the art.
Whatever the format, an artifact with no matching decompiler still gets a generic “what’s inside” pass — strings, endpoints, a toolchain fingerprint, referenced files — so nothing drops to a bare “needs tool X” note.
Preflight (step 1.5)
Before the expensive analysis, preflight checks whether the whole upload bundles a runtime installer or install tree alongside the art and, if so, asks you rather than analyzing the runtime by mistake. It never rejects. See Bring your artwork.
Human in the loop
The analysis is not a black box you get back at the end. The analyst runs cooperatively with you while it works:
- It can ask you a question mid-run and resume. When the analyst hits something only you can settle — intent, a dead-server address, whether a subtree is the work — it blocks on that question, you answer in the web UI, and it continues with your answer folded into its evidence. The job pauses rather than guessing.
- You can steer it without being asked. You can drop unsolicited guidance at any time; it is picked up on the next turn and surfaced to the analyst, because you may know something the deterministic leads can’t carry.
- You can cancel, and still get a report. There are no step or time caps, but the stop is cooperative: a cancel composes a partial report from everything accumulated so far rather than throwing the work away. A per-turn heartbeat means you see churn and the cancel is honoured promptly.
Some decisions are made durable per job — a scoping decision you confirm sticks across attempts and sessions, so you are not re-asked the same question every run.
What’s in a report
- Verdict & work kind — what the work is, in a sentence.
- Entry point — the file that starts it, with authority (deterministic beats AI-guess).
- Layers — host (machine/OS/display), guest (runtime, dependencies), network (endpoints, each with an origin: artwork / constructed / field-default).
- Components — the crucial parts, each with cited evidence.
- Fidelity risks — identity-defining properties at risk (timing, resolution, input…).
- Suggested base VM + tools — the era-correct machine and what to stock.
- Revival runbook — the ordered plan to reconstruct the environment.
- Unknowns & unexplored — what the analyst couldn’t resolve or chose not to chase, stated openly.
From the same evidence the analysis composes three audience-specific views:
- Dependency manifest — for the conservator: what must be provisioned to run the work.
- Asset catalogue — for the researcher: what the work is actually made of.
- The artist’s hand — for the art historian: the artist’s own words harvested from the source and the decompiled code — comments, authorship and copyright lines, contact URLs, the voice inside the work. The harvest runs across every language the code-semantics layer reads (Lingo, ActionScript, Java, C#, the scripting languages, …) plus the format sidecars (a binary’s version-info block, a patch’s metadata), and each note lands in the behaviour map as an annotation attached to the component it describes. Deterministic and interpretive in equal measure: the pass extracts the words, the historian reads them. The same map also carries an interaction map: which visitor inputs — mouse, keyboard, a form — are wired to which effects, so what the work responds to is recorded alongside what it does.
Reading it critically
The report states its own uncertainty — trust the cited components, read the unknowns, and treat
observed-unjudged endpoints as leads, not conclusions. The deterministic evidence stands on its
own even if the AI layer is degraded; the two are kept separable on purpose.
You bring the AI
The analyst is an LLM you supply — see Bring your own AI. Under the hood, the analysis does not treat “the model” as one thing. It reasons in capabilities, and each capability routes to a tier you configure (one config line per tier), so you spend big-model tokens only where they earn it:
- explore — the analyst’s tool loop, the long agentic pass that navigates the evidence. Wants a strong tool-calling model.
- finalize — the one-shot synthesis of the final verdict from gathered evidence, no tools. Wants a big thinking model, dialled independently from explore.
- navigate / classify — the many small, cheap graph and per-file labelling calls. A fast, cheap model is the right choice here.
- reflect — the narration and synthesis that turn the map into prose.
- vision / code — on-screen text and decompiled-code reasoning, where a specialised model helps.
The pipeline never hardcodes a model id; it names a capability, and your configuration decides which model backs each tier. That is what lets a careful mix — a strong explorer, a big finaliser, cheap models for the volume work — keep a serious analysis affordable.