> ## Documentation Index
> Fetch the complete documentation index at: https://warden-re.io/llms.txt
> Use this file to discover all available pages before exploring further.

# The reverse-engineering loop

> The practical guide to reverse-engineering an Emscripten WebAssembly module with WARDEN: ingest, identify, decompile, understand, review, and iterate as a human-plus-agent loop on one shared knowledge base.

This is the answer to "how do I actually reverse-engineer a WASM with WARDEN". It is not a
linear checklist. It is a loop that you, the agent crew, and any external model you bring over MCP
all run together against one versioned knowledge base, where every change is tracked and reversible.

## The loop in one picture

You ingest a module, the Oracle collapses the known runtime, the decompiler lifts the rest to C,
per-function agents propose understanding bottom-up, you review and correct in the UI, the
provenance economy propagates everyone's work without clobbering, you re-run the agents, and when a
new build ships the diff carries every annotation forward. Understanding converges over iterations.

<Steps>
  <Step title="Ingest" icon="download">
    Parse the `.wasm` and its JS glue, fingerprint every function, seed names for free, and see
    your starting coverage.
  </Step>

  <Step title="Identify" icon="telescope">
    Run the Emscripten Oracle to auto-identify known musl, libc++, dlmalloc, and runtime code with
    real upstream names, so effort concentrates on the application-specific remainder.
  </Step>

  <Step title="Decompile and understand" icon="braces">
    Lift the remaining functions to readable pseudo-C, then run the per-function deep engine
    bottom-up so each function gets a name, an understanding, variable renames, and cleaned C.
  </Step>

  <Step title="Review and correct" icon="user-check">
    Open the UI, read the inferred C, jump between functions, and fix names by hand. Human edits are
    sovereign and locked, and the economy means your corrections are never overwritten.
  </Step>

  <Step title="Bring your own model" icon="plug">
    Expose the same knowledge base over MCP so Claude or any MCP-capable model can drive it
    alongside you, economy-gated.
  </Step>

  <Step title="Iterate" icon="repeat">
    Re-run the agents (they respect your locked names) so understanding converges. When a new build
    ships, the diff carries every annotation forward by stable identity with zero rework.
  </Step>
</Steps>

<Note>
  Everything below uses real commands. Run `warden demo` first to watch the whole loop on generated
  samples with no network and no native toolchain, then point these commands at your own module.
</Note>

## One-time setup

Configure your model provider once, globally, so you never export an API key or pass it again:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden config set openrouter_api_key sk-or-...
warden config set openrouter_model xiaomi/mimo-v2.5-pro
```

Settings live in `~/.config/warden/config.json`. A project can override any of them in its own
`.warden/config.json` (`warden config set --project ...`), which is handy when one engagement needs
a different key or model.

## Start: ingest the module and see coverage

Create a project, then ingest the target. `warden init` makes a `.warden/` directory with the
database inside it, so every command you run from this directory tree uses it automatically with no
`--db` flag. Pass the Emscripten `.js` glue with `--glue` so WARDEN can read the export-index map
and `dynCall` signatures. Ingestion parses the binary in pure Python, fingerprints every function,
and seeds names for free from exports, imports, and the name section.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden init                                              # creates .warden/ (db inside; no --db needed)
warden ingest app_v1.wasm --glue app_v1.js --label v1
warden coverage v1                                       # how much is named already?
warden funcs v1 --unnamed                                # what still needs a name?
```

`warden coverage` is your scoreboard for the whole loop. Run it again after each stage to watch the
named fraction climb.

## Identify: collapse the known runtime with the Oracle

A real Emscripten module is mostly musl, libc++, dlmalloc, and Emscripten runtime code that already
has public source. The Oracle fingerprints those functions against a corpus of labeled, compiled
ground truth and writes the real upstream names straight into the knowledge base at high confidence.
You stop reversing code you could have read on GitHub.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Build a signature store from any module that still carries a name section
# (a debug or --profiling-funcs build), then identify the stripped target against it.
warden oracle build runtime_debug.wasm --out oracle.json --emver 3.1.55 --opt -O2
warden oracle identify v1 --store oracle.json
warden oracle inspect oracle.json                        # counts by library and Emscripten version
```

For larger corpora, the index makes matching sublinear:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden oracle identify v1 --store oracle.json --indexed   # MinHash-LSH candidate index
```

<Tip>
  The Oracle is honest about its corpus. A small seed store ships with the repo for the offline
  demo and tests. The multi-thousand-signature corpus that gives the high identification rate is
  produced by the containerized farm in `scripts/corpus/`, which needs the emsdk toolchain. Once a
  matrix is built, `warden oracle harvest <dir>` reads its `manifest.json` and builds the store in
  one call. See [the Oracle guide](/pipeline/oracle).
</Tip>

## Decompile and understand: run the per-function deep engine

Now lift the application-specific remainder to C and let the agents understand it. The decompiler is
a pure-Python lifter that renders readable pseudo-C (real `if`/`else`, `while` loops with
`break`/`continue`, and `switch`), with no native tooling.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden lift v1 --index 412        # decompile one function to pseudo-C
warden lift v1 --out v1.c         # or lift the whole module to a file
```

`warden deep` is the centerpiece of understanding. It runs **one agent per function**, walked
bottom-up over the call graph so leaves are analyzed first and a caller is only seen after its
callees are understood. Each agent gets the function's disassembly facts, its decompiled C, and the
recovered understanding of the functions it calls. That callee context is read from the knowledge
base, not from raw bytes, so a parent's context stays bounded even in a module with thousands of
functions. Each agent returns a name, an understanding, a variable rename map, cleaned C, and
whether the function is now closed.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden deep v1 --backend kimi --parent-model moonshotai/kimi-k2.6 --watch
```

With `--watch` you see each agent live, one line per function:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
analyzing func[412]
 proposed func[412] parse_packet_header
   closed func[412] parse_packet_header
   reused func[418]
```

<AccordionGroup>
  <Accordion title="Leaf vs parent tiering" icon="git-branch">
    Leaf functions are the bulk of any module, so name them with a cheap leaf `--backend`. Escalate
    the harder, high-fan-in parents with a stronger `--parent-model`. The leaf and parent backends
    are configured independently, so you pay frontier prices only where they earn their keep.
  </Accordion>

  <Accordion title="Cheap by default" icon="badge-dollar-sign">
    Naming from grounded facts is bulk work, not frontier reasoning, and it is gated by the verifier
    and the economy, so the default backend is the cheapest capable one. The auto-detect order is
    OpenRouter (Kimi K2.6) first, then OpenAI, then Anthropic, then the zero-dependency offline
    heuristic. The whole loop runs offline with no API key as a dry run of the bottom-up flow,
    context drop, events, and storage; once you have run `warden config set openrouter_api_key ...`
    the agents are real (the one-time setup above).
  </Accordion>

  <Accordion title="Dedup by stable identity" icon="copy">
    Identical functions (same stable identity) are analyzed once and reused. That is the lever that
    makes a module with thousands of functions tractable; you can see it in the `reused` events.
  </Accordion>
</AccordionGroup>

There is also a lighter pass, `warden agent`, when you only want names (not full per-function
understanding). It sweeps unnamed functions bottom-up, proposes names grounded in hard facts, gates
each through a verifier, and writes back under the economy. See [the agent crew](/pipeline/agents)
for the full backend and routing detail.

## Review and correct: the human turn

This is where the loop earns its keep. Start the read-only dashboard and read what the agents
produced.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden serve                       # local dashboard at http://127.0.0.1:8787
```

The dashboard renders a confidence heatmap (every function tinted by who claimed its name and how
sure they are), the raw and cleaned C side by side, and panels that let you search for a symbol,
jump to a single function, and walk its history. When you find a wrong or thin name, fix it from the
CLI:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden show v1 412                            # everything known about a function
warden set-name v1 412 parse_packet_header    # provenance=human, locked by default
```

A human-set name is **sovereign**. It is written at the top authority tier and locked by default, so
no automated source can overwrite it. Because every name and variable rename is logged, every change
is reversible. You are not editing a fragile file; you are committing to a tracked, versioned
knowledge base.

<Note>
  The dashboard is structurally read-only: the HTTP connection runs in query-only mode for the whole
  request, so no route can write. Writes go through the CLI, the library, or MCP, all of which pass
  through the provenance economy. See [the UI reference](/reference/ui).
</Note>

## Bring your own model over MCP

You are not limited to WARDEN's own agents. `warden mcp` serves the knowledge base over the Model
Context Protocol, so Claude or any MCP-capable model can drive the same loop alongside you.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
pip install -e '.[mcp]'
warden mcp                          # serves the KB over MCP (stdio)
```

The MCP surface exposes project reads, function facts, backend discovery, server-side agent runs,
and symbol proposals. Every write through MCP is economy-gated at the KB layer, exactly like the
agent crew and exactly like an import. An external model cannot clobber your locked names or a
high-confidence Oracle match. Humans, the agent crew, and external models all collaborate on one
knowledge base, and every change is tracked and reversible. See [the MCP reference](/reference/mcp).

## Iterate: converge, then carry forward

Re-running the agents is always safe. Already-confident and locked entries are skipped before a
backend is even called, and the economy rejects any proposal that would overwrite stronger work. So
re-run them after you lock a batch of names: a caller named with thin context in one round can be
re-proposed once its callees carry your real names, and understanding converges.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden deep v1                      # re-run; locked human names are respected
warden coverage v1                  # watch the named fraction climb
```

The payoff arrives on the **next build**. Ingest the new version and diff it. WARDEN classifies
every function as unchanged, moved, modified, new, or deleted, carries all annotations forward by
stable identity for unchanged and moved functions, applies a confidence penalty to fuzzy matches,
and prints a changelog that separates genuine application deltas from runtime churn caused by an
Emscripten version bump.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden ingest app_v2.wasm --glue app_v2.js --label v2
warden diff v1 v2                   # carry-over plus a semantic changelog
warden history parse_packet_header  # when it first appeared, how it evolved, who named it
```

Every name, type, and note you and the agents produced for `v1` is already attached to the matching
functions in `v2`. You review only the handful that genuinely changed. That is the whole point: the
second through hundredth decompile cost almost nothing.

## Export the deliverable

At any point, emit a deliverable for an external tool or a teammate:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
warden export v1 --format pseudo    # readable per-function pseudo-C
warden export v1 --format headers   # a C header
warden export v1 --format ghidra    # a runnable Ghidra rename script
warden export v1 --format csv        # neutral symbols for round-trip
warden import v1 names.csv --provenance human --lock   # bring edits back, economy-gated
```

Names you edit in Ghidra, IDA, or by hand come back through `warden import`, keyed on stable identity
first so a name recovered against one build lands on the same logical function in another, and routed
through the economy so an import never clobbers higher-authority work.

## Best practices

<CardGroup cols={2}>
  <Card title="Correct high-fan-in functions first" icon="git-branch">
    A name you lock on a widely-called function propagates context to every caller on the next
    bottom-up round. Fixing one high-fan-in parent is worth more than fixing many leaves.
  </Card>

  <Card title="Lock names you trust" icon="lock">
    `warden set-name` locks by default. Lock a name the moment you are confident in it so no
    automated pass can ever touch it, and so it carries forward cleanly on the next diff.
  </Card>

  <Card title="Measure two models on a slice" icon="scale">
    Before committing to one backend for a whole module, run two on a representative slice and
    compare coverage and quality. Naming is cheap, gated work; pick the model that earns its cost.
  </Card>

  <Card title="Use history to see evolution" icon="clock">
    `warden history <name-or-id>` shows when a function first appeared, when its body actually
    changed, and who named it. Use it to decide whether a "modified" function needs a real re-review.
  </Card>
</CardGroup>

## Where to go next

<CardGroup cols={2}>
  <Card title="CLI reference" icon="terminal" href="/reference/cli">
    Every command, flag, and default in one place.
  </Card>

  <Card title="MCP reference" icon="plug" href="/reference/mcp">
    Drive the knowledge base from Claude or any MCP-capable model.
  </Card>

  <Card title="UI reference" icon="layout-dashboard" href="/reference/ui">
    The terminal diff view and the read-only HTTP dashboard.
  </Card>

  <Card title="Diff and carry-over" icon="git-compare-arrows" href="/pipeline/diff">
    How annotations survive a rebuild by stable identity.
  </Card>
</CardGroup>
