Architecture
A map for contributors. Two services, each with a clean seam between deterministic machinery and the LLM layer.
artwork-autopsy
A pipeline that turns a bundle into a conservation report. Shape:
intake → static core (deterministic) → preflight → orient → scope collapse → run-graph build + deep-read (fixpoint) → ANALYST SESSION LOOP (agentic) → synthesis- Static core — the deterministic extractors in
static/tools/(self-registering, run over the unpacked members), withdomain/pure logic sequencing them: unpack, format-ID, hash, entry-point resolution, binary/import parsing, and the network-indicator sweep. No framework, no LLM. This layer extracts and never decides (no validation, no set-fitting, no starvation). - Preflight (step 1.5) —
domain/preflight.py: detects a possible bundled runtime installer and asks the researcher (never rejects). On the async path this is a two-phase pause: the pipeline returns early at anawaiting-confirmationstatus, and a confirm resumes it over the already- unpacked workspace. - Orient —
domain/phases/orient.py, run after preflight and before the scope collapse: a deterministic bundle portrait is judged by the LLM for the entry point + the work-vs-commodity partition, so the collapse below can keep the artwork’s anchors instead of subtracting them blind. The judged entry fills in at LLM-fallback authority only — a deterministic or human entry always wins. - Scope collapse — opaque vendored-runtime subtrees collapse to single nodes, anchor-aware: the entry point and the orient-judged work members stay visible through the collapsed tree.
- Run graph + deep-read — builds a graph of the work (entry point, imports, references, containment) and deep-reads every important unread node, relooping to a fixpoint until the read-list is drained and the KB stops changing (no top-N coverage gate; only a defensive runaway backstop).
- Analyst session loop —
ai/investigate/: a tool-using pydantic-ai agent with navigation (list/grep/read-slice), graph, and extraction tools. It runs with no caps; the bound is agent-complete OR researcher cancel (a cooperative flag → graceful partial). Its findings merge into the KB, which rides into the next session so it extends rather than restarts. - Synthesis — projects the accumulated KB into the layered manifest, behavior map, runbook, and suggestions.
The post-preflight sequence is not a hand-wired call chain: it is a declared phase plan
(domain/orchestration/) whose read/write dependencies are validated before it runs, so a reorder
that breaks a data dependency fails at startup, not mid-analysis.
The seam rule: pydantic-ai is import-banned outside ai/ (enforced by a lint rule + a check
script). The LLM lives behind adapters; the deterministic evidence stands even if the AI layer is
degraded.
Jobs. Analysis runs as jobs on a PostgreSQL store (SELECT … FOR UPDATE SKIP LOCKED claim
loop). A dumb runner supervises one child process per job (autopsy run-job), heart-beats, enforces
a timeout, and reconciles the exit. Statuses: queued → running → done | done-partial | failed, plus
awaiting-confirmation (the preflight pause) and staging (drive-path bundle staging).
rvmc (research-vm-controller)
The workbench: clone era-correct VMs, drive the guest, capture the network, save state.
- Controller — libvirt/KVM lifecycle: clone a golden into a project, boot/power, snapshot.
- Golden catalog — the era-correct base images; each ships a guest agent.
- Guest agent — a per-OS in-VM helper (Windows 2000 … 10 and Mac OS 9.2.2 today; 11, 98 SE and Mac OS X Tiger in progress) exposing run-command, file I/O, keys, screenshots, event log — the controller’s hands inside the guest.
- Drive lifecycle — one project = one artwork; a durable drive carries the stage (analysis → workbench → …) and the deliverables, independent of the services.
- Analysis bridge —
rvmc/analysis.py: dispatches autopsy on the project’s drive, reads the report back, surfaces the preflight pause, and confirms/cancels it. - Agent surface (MCP) —
rvmc/mcp_tools.py: the workbench capabilities as tools for an LLM agent (see MCP tools), exposed via Open WebUI, scoped per project.
The pipeline: autopsy → rvmc → evmc
The core idea — three stages, one artwork moving left to right. Each stage has exactly one job; the boundary between them is a durable handoff, not a live coupling. Get the stage boundaries wrong and everything downstream rots — so this is the canonical statement.
The whole platform is one method, first reported in the paper that grew out of the Choose Your Filter! exhibition [1]: do the research for as long as it takes to get the artwork into the three-layer model, then bake Layer 1 into a verified snapshot and never touch it again. The three stages below are just that method made operational — autopsy and rvmc are the “for as long as it takes” research; the handoff to evmc is the bake-and-freeze. The three-layer model is the target the research aims at; the snapshot is what makes Layer 1 permanent.
-
autopsy — understand the artwork. Turn the bundle into a conservation report: what it is, what it depends on, how to bring it up. Deterministic extraction + an agentic analyst. No VM, no repair.
-
rvmc — restore, and build the conservation unit. Bring the work back to life and build everything it needs to run unattended for months: the era-correct VM, the fixes/shims/mirrors, the auto-heal + self-repair infrastructure, the conservation plugin(s), the guest monitoring. rvmc proves the work runs and defines what “healthy” means. This is where the AI + human are; it is expensive and one-time-per-artwork. rvmc does not bake the final image and does not stamp the exhibition snapshot.
-
handoff → evmc — bake, prove, snapshot, ship. The handoff turns rvmc’s proven project into a field-ready conservation unit, strictly in this order:
- bake the image — flatten the overlay chain onto its base into one standalone disk;
- boot it on/toward the dedicated machine and let it warm up to its verified-good state;
- only once it proves to run (healthy / verified live) stamp the
readysnapshot — a live memory + disk snapshot, so a revert resumes the exact warmed state in 3–5 s with no boot (the iPRES paper’s guarantee); - hand the finished unit to the dedicated exhibition machine, where evmc is just the
player: on any fault it reverts to
ready, forever, deterministically, with no AI.
The ready-snapshot timing rule — do not mess this up
The ready snapshot is NOT made in rvmc and NOT baked into the bundle. It captures memory +
disk — the paper’s guarantee is that a revert “resumes execution at the snapshot point without boot
sequences” in three to five seconds [1] — so it can only be taken while the VM is
running and warmed to the verified-good moment, which is at the handoff, after the baked image proves
it runs on the target. Baking (qemu-img convert)
flattens disk only and drops internal snapshots; an import-time, VM-shut-off snapshot is disk-only
(a cold baseline, not the real ready). The live ready is stamped at the last point where the running
state exists on the machine that will play it. evmc-the-player only ever reverts to ready; it never
creates it. The bundle carries the ready state (the disk) and the ready name; the handoff
materialises the ready snapshot.
This maps onto the paper’s three-layer runtime model [1]: Layer 1 — the VM guest (OS, browser, plugins, artwork), “never modified after initial setup” — is the conservation object rvmc authors and the handoff freezes as the snapshot; Layer 2 — the host controller + the Wayback Cache Proxy — is the preservation infrastructure evmc plays; Layer 3 is the managed external dependencies, isolated in replaceable host services so preserving a work never means freezing the live internet. The paper’s finding is the whole reason for the stage split: every error inside the encapsulated environment was recovered automatically; every failure outside it required a human — so Layer 1 is baked and inviolable, and all the fragility is pushed out to Layer 2/3 where it can be serviced without touching the conservation object.
How they connect
rvmc dispatches an autopsy job for a project’s artwork (POST /jobs/drive), polls status (including
the preflight pause), and reads the deliverables off the shared drive. The report’s suggested base VM
pre-fills the golden picker. The hand-off onward to the exhibition controller (evmc) is a promotion
bundle — golden/baked image + boot config (snapshot name + plugins) + the conservation plugin +
provenance, checksum-signed — which evmctl import unpacks on the field host; the live ready
snapshot is stamped there per the rule above, then evmc plays it.
Contributor entry points
- Teach autopsy a new file type → Runtime modules.
- Add or fix a guest agent for an OS → same doc, guest-agent section.
- Extend the agent’s abilities → MCP tools.
- Config surface → Configuration reference.
References
The pipeline and the three-layer model come directly from the Choose Your Filter! exhibition (ZKM, 2025) and its reported practice:
- M. Schütze, “Keeping Net Art Alive: Self-Healing VMs and a Caching Wayback Proxy from Six Months of Exhibition Operation,” iPRES 2026 (in press). — Introduces the three-layer model (conservation object / preservation infrastructure / managed external dependencies), the memory+disk “ready” snapshot with 3–5 s revert, and the self-healing boundary between internal errors (recovered automatically) and external failures (isolated to the host layer).
The tools this platform builds on are released under the MIT license:
- exhibition-vm-controller (evmc’s ancestor) — github.com/zkmkarlsruhe/exhibition-vm-controller, archived at DOI 10.5281/zenodo.18652760.
- wayback-cache-proxy — github.com/zkmkarlsruhe/wayback-cache-proxy.