Inspecting Components with wasm-tools
This page answers one task: you have a .wasm file that is supposed to be a component — built by a teammate, downloaded from a registry, produced by a
toolchain you do not know — and you need to see what it is: which interfaces it imports and exports, whether it is valid, what core modules it contains,
and why a host refuses to instantiate it.
Prerequisites
- [ ]
wasm-toolsinstalled (cargo install wasm-toolsor a release binary), recent enough for the component features you use. - [ ] The component file, and ideally the WIT the host expects.
- [ ] Optionally a host (Wasmtime) to reproduce instantiation errors.
What a component file contains
A core module is a flat list of sections: types, imports, functions, memory, exports, code. A component is a different binary format layered on top: it
contains core modules (one or more), core instances that wire them together, component types describing the WIT-level interface, canonical
functions that lift core functions into component functions (and lower imports the other way), and component-level imports and exports named with WIT
interface names such as wasi:cli/run@0.2.0. It may also carry custom sections: producers (which toolchains built it), embedded WIT for documentation, and
names.
Because of this nesting, tools for core modules (wasm-objdump, older versions of browser DevTools) cannot read components, and the first question with
any unknown file is which kind it is.
Step 1 — identify the file and print its world
wasm-tools print app.wasm | head -3
# (component ← a component; a core module would start with (module
wasm-tools component wit app.wasm
# package root:component;
# world root {
# import wasi:cli/environment@0.2.0;
# import wasi:io/streams@0.2.0;
# import acme:storage/blobs@1.2.0;
# export wasi:cli/run@0.2.0;
# }
component wit reconstructs the component’s world from its type information: every import the host must satisfy and every export it offers, with full
versions. This single command answers most “will this run in my host?” questions.
Step 2 — validate with the right features
wasm-tools validate app.wasm
wasm-tools validate --features all app.wasm # if it uses newer proposals
Validation checks the component and every embedded core module. A failure names the section and offset. If validation fails only without --features,
the component uses proposals (for example async or newer types) that your host may not support either — a useful early warning.
Step 3 — look at structure and metadata
wasm-tools print app.wasm > app.wat # full text form, including core modules
wasm-tools objdump app.wasm # section-level overview with sizes
wasm-tools metadata show app.wasm # producers: language, toolchain versions
metadata show reports which toolchains produced the component and its modules (for example rustc 1.8x, wit-component 0.2xx), which matters when
a bug is suspected in a particular toolchain version. objdump shows sizes per section and per nested module, so you can see whether size comes from your
code, from an embedded interpreter, or from an adapter module.
Step 4 — extract the core modules
To use core-module tools — twiggy for size, wasm-opt for optimisation, wasm-objdump for disassembly — extract the modules inside:
wasm-tools component unbundle app.wasm --module-dir out/ # in versions that support it
# or print the component and inspect (core module ...) blocks directly
Most components built from one language contain one main core module plus, for components adapted from WASI Preview 1, a small adapter module. Size analysis on the main module then works as for any core module.
Step 5 — diagnose instantiation failures
When a host fails to instantiate a component, the error usually names an import: “component imports instance acme:storage/blobs@1.2.0, but a matching
implementation was not found”. Compare the component’s world (component wit) with what the host provides:
- Version mismatch. The component imports
@1.2.0, the host provides@1.1.0— the component uses something newer than the host implements. - Missing WASI interface. The component imports
wasi:sockets/tcp, the host linked only CLI interfaces — add it, or rebuild the component without that dependency. - Wrong world kind. A library component (exporting a custom interface) run with
wasmtime run, which expectswasi:cli/run— use a host that calls the custom export. - Unexpected imports from the toolchain. An interpreter-based component importing more WASI than expected — normal; link full WASI.
Inspecting composed components
Composed components contain other components. wasm-tools component wit shows only the outer world — what remains unsatisfied after composition — which is
exactly what the host must provide. wasm-tools print shows nested (component ...) blocks, letting you confirm which sub-components were included and how
their imports were wired. If a composed component still imports an interface you expected another component to satisfy, the composition did not connect
them — often a version mismatch between the exporting and importing sides.
Using inspection in CI
Inspection commands are cheap and deterministic, which makes them good CI checks. Store the expected world of each component (component wit output) in
the repository and fail the build when it changes unexpectedly — a new import may mean a dependency started using the network or file system. Check that
release components contain no debug custom sections and that metadata show reports the expected toolchain versions. These checks catch supply-chain
surprises and accidental capability creep before deployment.
Reviewing third-party components before use
Components downloaded from a registry or received from another team deserve the same review as any dependency, and inspection makes the review concrete.
Start with the world: a component that claims to transform images should not import wasi:sockets or wasi:http/outgoing-handler; unexpected imports
are the clearest signal that a component does more than advertised. Check metadata show for the producing toolchains and compare them with what the
publisher documents. Compare the published digest with the file you have, and if the publisher provides signatures or provenance attestations, verify them.
Finally, because the Component Model enforces capabilities at the boundary, the host decides what a component can actually reach: link only the
interfaces its stated purpose requires, so even a component with surprising imports fails to instantiate rather than quietly gaining access.
Comparing builds and spotting regressions
Two builds of the same component should differ only where the source changed. Diffing their component wit output catches interface changes; diffing
objdump section sizes catches size regressions and newly embedded modules; diffing metadata show catches toolchain changes that slipped in through a
lockfile update. For reproducible-build workflows, identical source and toolchains should produce byte-identical components, and cmp on the two files is
the simplest possible check; when they differ, wasm-tools print of both, diffed, shows where — often an embedded path or timestamp in a custom section.
Expected output
wasm-tools component wit app.wasm prints a world importing three WASI interfaces and acme:storage/blobs@1.2.0 and exporting wasi:cli/run@0.2.0;
validation passes; metadata show lists the Rust and wit-component versions; the extracted core module analyses cleanly with twiggy; and a host failure is
traced to the host implementing blobs@1.1.0.
Gotchas
- Using core-module tools on components. They fail or mislead. Check the file kind first.
- Old wasm-tools versions. Newer component features fail to parse. Keep wasm-tools current.
- Validating without features. Proposal use goes unnoticed. Try
--features alland compare. - Ignoring adapter modules. They add imports and size. Expect them in adapted components.
- Reading only the outer world of composed components. Check nested wiring with
print. - Linking every interface for third-party components. Unexpected imports then succeed. Grant only what the purpose requires.
Performance note
All inspection commands run in milliseconds even for large components; printing a 30 MB interpreter-based component to text takes a few seconds and
produces a very large file, so prefer component wit and objdump for routine checks.
Frequently Asked Questions
Can browser DevTools inspect components?
Browsers do not run components directly; inspect the transpiled core modules produced by jco transpile.
How do I see the exact WIT types of exports?
component wit prints full type definitions for interfaces the component defines or uses.
Does wasm-tools strip work on components?
Yes — it removes custom sections from the component and nested modules.
How do I compare two builds?
Diff the component wit and objdump outputs of both.
How should I review a third-party component before using it? Check its imports against its stated purpose, verify producers and digests, and link only the interfaces it needs.
Why do two builds from the same source differ? Usually embedded paths, timestamps or toolchain versions in custom sections; diff the printed text forms to find where.
Can I tell which language a component was written in?
Usually — metadata show lists producers such as rustc, TinyGo or componentize-py, and interpreter-based components are recognisable by their size.
Related
- Running components in Wasmtime — the host side.
- Versioning WIT packages — reading versioned imports.
- Validating binaries with wasm-validate — core-module validation.
- Composing two Wasm components — what composition produces.
← Back to Wasm Component Model & WIT Bindings