Runtime modules & guest agents
Two extension points that most contributions touch: teaching autopsy to understand a new kind of file, and giving rvmc a guest agent for another operating system.
Autopsy: teaching it a new runtime is a data edit
The primary extension path writes no code at all. Runtime detection is data-driven: a single
dispatcher walks runtime/runtime-catalog.yaml, where each entry is a self-contained runtime module —
detection signals (extension / magic bytes / container structure / embedded strings), the analysis
plan, the ordered environment recipe, fidelity notes, and the runtime’s known endpoints. Teaching
autopsy a new runtime means adding one catalog entry; the dispatcher loads every module whose
matches fires (multi-runtime is normal), and unmatched formats fall through to the AI fallback
module rather than a forced wrong guess.
Write a new extractor only when a new format tool is actually needed — a parser for a container no existing tool reads. For that case, the doctrine below applies.
Autopsy: per-kind extractors
Autopsy handles each kind of code with its own module, and every module obeys the same doctrine:
A module EXTRACTS generic signals. It never JUDGES, never validates, never fits one artwork.
That is the load-bearing rule. A module surfaces facts (formats, strings, references, network-protocol indicators) fairly and hands them up; the analyst reconstructs and decides. Concretely:
- No validation / no scheme-matching. Don’t “clean” or regex-filter URLs — a work may construct a URL over several steps, and a scheme filter would drop it. Surface the protocol indicator and let the analyst read the code.
- No set-fitting. A rule that happens to be true for one artwork (drop this extension, prefer that file) must not be hardcoded — build something that can’t starve one source in favor of another (fair, round-robin allocation; normalized dedup).
- No starvation. Every source gets a fair share of attention even in a huge tree; if you must cap, log what was dropped — never silent truncation.
Practical shape of a new extractor:
- It registers itself (side-effect import) and runs over the unpacked members it recognizes.
- It emits findings (kind/layer/summary/evidence) — generic signals, deduped.
- If it can go deeper on demand, it exposes a deep-read the analyst can call as a tool.
- Reuse the shared network probe rather than writing a parallel URL extractor.
Existing modules (Director/Lingo, PE/ELF, installers, media, etc.) are the templates. Add tests that assert extraction, not classification — the AI does classification, so don’t bake a verdict into a deterministic test.
Installers are not automatically “not art”
An installer can be the artwork (a self-installing or packed work). Detection feeds preflight, which asks; a module must not decide an installer is runtime bloat and drop it.
Scoping: artwork vs. bundled runtime (distinct from preflight)
Preflight is a whole-bundle question asked once, up front. Scope — which subtree is the work and which is the runtime it merely carries — is settled in three steps, in order:
- The orient judge, first. Right after preflight, a deterministic bundle portrait (the tree + per-executable metadata) is put in front of an LLM judge that names the entry point(s) and the work-vs-commodity partition — before anything is collapsed. The judged entry fills in at LLM-fallback authority only; a deterministic or human entry always wins.
- The anchor-aware collapse. The deterministic detector recognises the unambiguous vendored
runtimes from structural signals alone (a JRE/JDK tree, a
node_modules, a Pythonsite-packages, a Processing or .NET runtime) 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 keeps the artwork’s own anchors — the entry point and the orient-judged work members — visible through the collapsed tree (a Unity build’s launcher andAssembly-CSharp.dllsurvive while the engine’sBuild/tree becomes one node). - The in-graph scoping specialist, for the hard cases. A sub-agent judges the genuinely ambiguous subtrees across ecosystems (JVM / Processing, .NET, JS, Python), and prefers to keep when unsure — excluding the artwork is worse than graphing a few extra dependency files.
A new extractor must respect this: don’t drop or hide files a scoping decision owns. Surface them; the orient judge, the collapse, and the specialist decide what is opaque runtime and what is the work.
rvmc: guest agents
Every golden image ships a guest agent — an in-VM helper the controller talks to, so it can act inside the guest (run commands, read/write files, send keys, screenshot, read the event log). A golden without a working, reachable agent is not a usable golden.
Contributing a guest agent for an OS:
- Match the era. Old guests need era-appropriate runtimes (e.g. an older Python, or a native helper) — don’t assume a modern toolchain is available in the guest.
- Provide the same capability surface the controller expects, so per-OS quirks stay behind the agent (cursor/mouse handling, event-log access, networking transport).
- Networking into old guests is often the hard part (NIC driver availability); document the working NIC and transport for the tier.
The per-OS agent builds live alongside the controller; use an existing tier (e.g. the Windows agents) as the template and follow its capability contract.
Testing
- Autopsy: deterministic tests over real fixtures; assert extracted signals. LLM-touching paths must degrade to a trace line and never crash the pipeline.
- rvmc: exercise the controller/bridge against fakes; guest-agent changes want an actual boot on the target tier before calling them done.