Metadata-Version: 2.4
Name: meld-cli
Version: 0.65.1
Summary: Static WebAssembly component fusion (the `meld` command-line tool)
Keywords: webassembly,component-model,fusion,cli
Author: PulseEngine
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Compilers
Project-URL: Homepage, https://github.com/pulseengine/meld
Project-URL: Repository, https://github.com/pulseengine/meld
Project-URL: Changelog, https://github.com/pulseengine/meld/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/pulseengine/meld/issues
Requires-Python: >=3.8
Description-Content-Type: text/markdown

<div align="center">

# Meld

<sup>Static WebAssembly component fusion</sup>

&nbsp;

[![CI](https://github.com/pulseengine/meld/actions/workflows/ci.yml/badge.svg)](https://github.com/pulseengine/meld/actions/workflows/ci.yml)
[![Bench](https://github.com/pulseengine/meld/actions/workflows/bench.yml/badge.svg)](https://github.com/pulseengine/meld/actions/workflows/bench.yml)
[![codecov](https://codecov.io/gh/pulseengine/meld/graph/badge.svg)](https://codecov.io/gh/pulseengine/meld)
![Rust](https://img.shields.io/badge/Rust-CE422B?style=flat-square&logo=rust&logoColor=white&labelColor=1a1b27)
![WebAssembly](https://img.shields.io/badge/WebAssembly-654FF0?style=flat-square&logo=webassembly&logoColor=white&labelColor=1a1b27)
![Component Model](https://img.shields.io/badge/Component_Model-654FF0?style=flat-square&logoColor=white&labelColor=1a1b27)
[![Formal Methods](https://img.shields.io/badge/Formal_Methods-Rocq_%2B_Kani_(partial)-2962FF?style=flat-square&logoColor=white&labelColor=1a1b27)](#formal-verification)
![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue?style=flat-square&labelColor=1a1b27)

&nbsp;

<h6>
  <a href="https://github.com/pulseengine/meld">Meld</a>
  &middot;
  <a href="https://github.com/pulseengine/loom">Loom</a>
  &middot;
  <a href="https://github.com/pulseengine/synth">Synth</a>
  &middot;
  <a href="https://github.com/pulseengine/kiln">Kiln</a>
  &middot;
  <a href="https://github.com/pulseengine/sigil">Sigil</a>
</h6>

</div>

&nbsp;

Meld fuses. Loom weaves. Synth transpiles. Kiln fires. Sigil seals.

Meld statically fuses multiple WebAssembly components into a single core module, eliminating the need for runtime linking. Import resolution, index-space merging, and canonical ABI adapter generation happen at build time. The core transformations — resolution, index-space merging, and adapter generation — carry mechanized Rocq proofs, plus local Kani bounded-model-checking harnesses on the Canonical-ABI layout invariants; see [Formal Verification](#formal-verification) for the per-stage coverage (some stages are specified but not yet proved, and the Kani harnesses are run locally, not gated in CI).

Unlike composition tools that produce linked-but-separate component graphs, Meld produces a single monolithic module suitable for whole-program optimization by Loom and native transpilation by Synth.

## Quick Start

### Prebuilt binaries

Every release ships archives for six targets. On Linux, pick by what the
machine has rather than by architecture alone:

| | use |
|---|---|
| glibc 2.34 or newer (Ubuntu 22.04+, Debian 12+, Fedora) | `*-unknown-linux-gnu` |
| **anything older, or no glibc at all** — Alpine, distroless-static, RHEL 8, Ubuntu 20.04 | `*-unknown-linux-musl` |
| macOS | `*-apple-darwin` |

The musl archives are statically linked: no dynamic loader, no glibc symbol
versions, nothing to install alongside them. If you are not sure which your
target has, the musl one runs in both places.

```bash
TAG=v0.62.0
TARGET=x86_64-unknown-linux-musl
gh release download "$TAG" --repo pulseengine/meld --pattern "meld-${TAG}-${TARGET}.tar.gz"
tar xzf "meld-${TAG}-${TARGET}.tar.gz"
./meld-${TAG}-${TARGET}/meld --version
```

Verify what you downloaded before running it — see
[Verifying a Release](#verifying-a-release).

### From source

```bash
# From source (Cargo) — the only build for meld itself
cargo install --path meld-cli

# Fuse two components
meld fuse component_a.wasm component_b.wasm -o fused.wasm
```

### Full Pipeline

```bash
# 1. Build components
cargo component build --release

# 2. Fuse into single module
meld fuse composed.wasm -o fused.wasm

# 3. Optimize with Loom
loom optimize fused.wasm -o optimized.wasm

# 4. Run
wasmtime run optimized.wasm
```

### Bazel Integration

```starlark
load("@meld//rules:meld.bzl", "meld_fuse")

meld_fuse(
    name = "my_app",
    components = [
        ":component_a",
        ":component_b",
    ],
    # Mandatory: the meld binary that will be recorded as the producer in the
    # fused artifact's attestation. There is deliberately no default — the old
    # one pointed at a Bazel self-build that stamped a version matching nothing
    # (#406).
    meld = "@meld_release//:meld",
)
```

`meld_fuse` is for projects that build *their* components with Bazel. It is not
exercised by meld's own CI, so treat it as community-supported.

## How It Works

1. **Parse** — Extract core modules and type information from components
2. **Resolve** — Build import/export graph, identify cross-component calls
3. **Merge** — Combine function/memory/table/global index spaces
4. **Adapt** — Generate Canonical ABI trampolines for cross-component calls
5. **Encode** — Output single core WebAssembly module

### Adapter Generation

Cross-component calls may require adapters that handle string transcoding (UTF-8, UTF-16, Latin-1), memory copying between component memories, list/array serialization, and resource handle transfer. Meld generates these adapters using techniques inspired by Wasmtime's FACT (Fused Adapter Compiler of Trampolines).

## Memory Strategies

### Auto (Default)

Always selects multi-memory, the strategy that is sound for every input. Auto never selects shared memory or address rebasing on its own (#326, #409); both remain available explicitly via `--memory shared --address-rebase`. The fused module therefore keeps one linear memory per input component, needs `wasm-opt --enable-multimemory` downstream, and has no single-address-space (MCU) lowering (issue #172).

```bash
meld fuse a.wasm b.wasm -o fused.wasm
wasm-opt -Os --enable-multimemory fused.wasm -o fused.opt.wasm
```

### Multi-Memory

Each component retains its own linear memory. Cross-component calls use adapters that allocate in the callee's memory via `cabi_realloc` and copy data with `memory.copy`. Requires multi-memory support (WebAssembly Core Spec 3.0) — downstream tools need `wasm-opt --enable-multimemory`, and single-address-space (MCU) targets have no lowering for it.

```bash
meld fuse --memory multi a.wasm b.wasm -o fused.wasm
```

### Shared Memory

All components share a single linear memory; pair with `--address-rebase`. Unsound if any component uses `memory.grow` — one component's growth corrupts every other component's address space, which is why `auto` never selects this form on its own.

```bash
meld fuse --memory shared --address-rebase a.wasm b.wasm -o fused.wasm
```

### Further `fuse` Options

Options for single-address-space targets and for auditing a fusion; the first three require `--memory shared`. Run `meld fuse --help` for the full text of each.

| Flag | Effect |
|------|--------|
| `--pack-rebase` | Place each component at its used data extent (16-byte aligned) instead of its declared page count, and size the merged memory to the packed total. Implies `--address-rebase`. Opt-in; sound only when a component references no address above its last data segment. |
| `--share-stack` | Collapse the per-provider shadow stacks into one shared region. Implies `--pack-rebase`. Requires every provider to carry a `__heap_base` marker and be stack-first, and fails loudly otherwise. Opt-in; sound only for non-reentrant, single-threaded, mutually non-calling providers. |
| `--domain NAME=INPUT[,INPUT...]` | Group inputs into memory domains: fuse within a domain, keep the Canonical ABI between domains. Repeat once per domain; every input must be named in exactly one. A domain is a trust boundary and is recorded in the attestation. |
| `--profile safety` | Require safety-relevant properties to be stated rather than inferred. Today this means `--memory` must be given: `auto` is refused. Output is byte-identical to the same explicit invocation under the default `--profile ecosystem`. |
| `--explain` | Report, for every fused cross-component call, the call-lowering class chosen (direct, memory-copy, transcode, async-lift) and how it was wired. The same records are embedded in the attestation. |
| `--emit-manifest` | Emit a `meld.signature-manifest` section giving, per export, the WIT and core signatures, the flattened parameter count, and the memory, allocator and post-return a host must use to invoke it. Opt-in. |
| `--exports <all\|world>` | Which exports the fused core module keeps. `all` (default) keeps every export of every inner module. `world` keeps only the functions that implement the outer world of the input(s), traced through their `canon lift`s (never by name) and exported under their world name `<interface>#<function>`, plus what the kept surface and the imports the module still has need: the memory and realloc of the kept lifts and of every `canon lower` behind a remaining import (a realloc nothing exports is exported as `meld:realloc/<component>`), `cabi_post_*`, the `[dtor]` exports of resources still imported, and the start function. Drops the internal interfaces of a composition, `__data_end`, `__heap_base` and every other global or table export. Only the export list changes; function bodies stay. A world function that cannot be traced to an exported fused function is an error. Not valid with `--component`. Recorded in the attestation. |
| `--reproducible` | Produce a byte-reproducible artifact: the attestation id is derived from the output content and the timestamp from `SOURCE_DATE_EPOCH` (default 0). Attested input names are replaced by positional identifiers (`component-0`, ...). |
| `--opaque-rep IFACE.RESOURCE` | Mark a resource as opaque-rep, for re-exporters whose representation is treated as a `u32` rather than a boxed pointer. Such resources skip the per-resource handle table layer. Repeatable. |

## Supply Chain Security

Meld integrates with [Sigil](https://github.com/pulseengine/sigil) for supply chain attestation. Each fusion operation records input component hashes, tool version and configuration, and transformation metadata. The attestation is embedded in the output module's custom section.

### Verifying a Release

Every tagged release ships with a CycloneDX SBOM, a cosign-signed `SHA256SUMS.txt`, and a SLSA v1 build-provenance attestation. To verify an installed binary against what GitHub Actions actually built:

```bash
# 1. Download an archive plus the signed checksum manifest from the release page.
TAG=v0.62.0
TARGET=x86_64-unknown-linux-gnu
gh release download "$TAG" --repo pulseengine/meld \
  --pattern "meld-${TAG}-${TARGET}.tar.gz" \
  --pattern 'SHA256SUMS.txt' \
  --pattern 'SHA256SUMS.txt.cosign.bundle'

# 2. Verify SHA256SUMS.txt was signed by meld's release workflow (Sigstore keyless).
cosign verify-blob \
  --certificate-identity-regexp \
    'https://github.com/pulseengine/meld/.github/workflows/release.yml@.*' \
  --certificate-oidc-issuer \
    'https://token.actions.githubusercontent.com' \
  --bundle SHA256SUMS.txt.cosign.bundle \
  SHA256SUMS.txt

# 3. Verify the archive's digest is in the signed manifest.
sha256sum --ignore-missing -c SHA256SUMS.txt

# 4. (Optional) Verify the SLSA v1 build provenance attestation.
gh attestation verify "meld-${TAG}-${TARGET}.tar.gz" --repo pulseengine/meld
```

Step 2 proves the checksum file came from this repo's `release.yml`; step 3 ties the archive on disk to that signed manifest; step 4 independently confirms the archive was built by the same workflow run via GitHub's attestation API. Each release also includes a `meld-<version>.cdx.json` (CycloneDX SBOM, transitively covered by the signature on `SHA256SUMS.txt`) and `build-env.txt` capturing the runner toolchain versions.

## Formal Verification

Meld's **core** transformations carry mechanized Rocq proofs — 357 closed proofs (`Qed`) across the resolver, merger, adapter, and specification layers (see [`proofs/STATUS.md`](proofs/STATUS.md) for the per-stage coverage matrix). This is real, substantial verification on the core; it is **not** yet a whole-pipeline proof: the parser, rewriter, segments, and attestation stages are specified but their proofs are placeholders, orchestration (`lib.rs`) is unproven, and the semantic-preservation result is a **forward** simulation (the fused module simulates the original graph step-by-step; the reverse direction and the backward half of trap-equivalence are not yet proved).

Proven properties (Rocq):
- **Merge correctness** — Index remapping preserves function/memory/table references (injectivity, completeness, boundedness); memory-layout disjointness.
- **Resolve correctness** — Topological sort produces a valid instantiation order; cycle detection terminates.
- **Adapter correctness** — Canonical-ABI lift/lower roundtrip and crossing-adapter semantics (no `Admitted` lemmas remain in `proofs/`).
- **Forward simulation** — The fused module simulates the original component graph step-by-step (forward direction; fully proved).

In addition, the Canonical-ABI layout invariants (size/alignment saturating arithmetic, the #141 stream ring model) carry **Kani** bounded-model-checking harnesses (`abi_proofs.rs`, `resolver.rs`, `merger/`) that were `VERIFICATION SUCCESSFUL` locally (SR-40); these are **not gated in CI** (the shared runners do not ship the Kani toolchain), and the ABI size/alignment and aggregate-padding contracts that did not converge in CBMC fall back to unit tests.

Proofs are built via Bazel using [`rules_rocq_rust`](https://github.com/pulseengine/rules_rocq_rust):

```bash
bazel build //proofs/transformations/merge:merge_spec
bazel build //proofs/spec:fusion_spec
```

See [`proofs/`](proofs/) for the full proof tree and [`PROOF_GUIDE.md`](proofs/PROOF_GUIDE.md) for an introduction.

> [!NOTE]
> **Cross-cutting verification** &mdash; Rocq mechanized proofs, Kani bounded model checking, Z3 SMT verification, and Verus Rust verification are used across the PulseEngine toolchain. Sigil attestation chains bind it all together.

## Limitations

- **Resources** — Resource handle transfer across components is limited
- **Async** — P3 async lowering (`stream<T>`, `future<T>`, async lift/lower) is partial; async cross-component calls are covered only for the shapes exercised by the test suite, and the full async surface is not yet supported
- **String transcoding** — all pairs of UTF-8, UTF-16 and `latin1+utf16` are implemented for synchronous calls; async cross-encoding transcoding covers a bounded set of types (nested `list<list<string>>` to depth 2)

## Development

```bash
cargo build                # Build
cargo test                 # Test
bazel build //proofs/...   # Rocq proofs only (there is no Bazel build of meld)
RUST_LOG=debug cargo run -- fuse a.wasm b.wasm -o out.wasm
```

### Benchmarks

Criterion benchmarks for the fusion pipeline live in
`meld-core/benches/fusion_benchmarks.rs` and exercise four groups:

- `parser` — `ComponentParser::parse` throughput per input byte.
- `merger` — `Merger::merge` throughput per component count.
- `resolver` — `Resolver::resolve_with_hints` throughput per resolved-symbol count.
- `end_to_end` — `Fuser::fuse_with_stats` over small / medium / large
  graphs.

```bash
cargo bench -p meld-core                          # full run
cargo bench -p meld-core --bench fusion_benchmarks -- parser   # one group
cargo bench -p meld-core --no-run                 # compile-only sanity (CI smoke)
```

CI runs `cargo bench --no-run` on every PR (`.github/workflows/bench.yml`)
to prevent the bench harness from rotting. Full benches are not run in CI
to keep PRs fast and avoid noisy regressions on shared runners.

## License

Apache-2.0

---

<div align="center">

<sub>Part of <a href="https://github.com/pulseengine">PulseEngine</a> &mdash; formally verified WebAssembly toolchain for safety-critical systems</sub>

</div>
