Docs · Wasm bindings
Describe the meaning that raw Wasm cannot.
A Wasm binary exposes machine signatures. A restricted .hal interface supplies Hara names, semantic types, ownership, lifecycle and host authority. Bindgen verifies the two and emits a deterministic package; it does not require per-library generated JavaScript, Rust, Java, or C glue.
Model
Binary facts plus semantic intent.
Inspection discovers exports, imports, memories and raw signatures without instantiating the module. The interface then states what those values mean. Hara compiles that declaration into one canonical binding plan shared by the native Rust runtime, the JVM/Truffle runtime and the browser runtime.
01library.wasm + interface.halRaw ABI and semantic contract
→ 02canonical binding IRVerified names, types, ownership and target
→ 03portable packageDigests, bindings, conformance and optional adapter
Target selection
Three execution routes.
core.v1
Direct scalar calls.
Use for synchronous, import-free functions whose arguments and result are direct i32, i64, f32, f64, boolean-as-i32, or void. Native package consumers use the direct import route.
(wasm/interface
{:schema "hara.wasm-interface/0-alpha"
:namespace example.math
:module "add.wasm"
:exports
{add {:wasm/export "add"
:arguments [{:name left :hara/type :i64 :wasm/type :i64}
{:name right :hara/type :i64 :wasm/type :i64}]
:returns {:hara/type :i64 :wasm/type :i64}}}})
memory.v1
Strings and exact bytes.
Use for synchronous, import-free libraries that expose one bounded linear memory and explicit allocation/release conventions. Hara lowers pointer/length inputs and lifts packed pointer/length results through a generic host executor.
(wasm/interface
{:schema "hara.wasm-interface/0-alpha"
:namespace example.codec
:module "echo.wasm"
:memory {:export "memory" :allocate "alloc" :release "free"}
:exports
{echo {:wasm/export "echo_bytes"
:arguments [{:name input
:hara/type :bytes
:wasm/type :i32
:lower [:pointer :length]
:ownership :borrowed}]
:returns {:hara/type :bytes
:wasm/type :i64
:lift :packed-i64
:ownership :caller}}}})
hta.v1
Async and host interaction.
Use HTA when the semantic interface needs asynchronous completion, cancellation, host calls, callbacks, handles, resources, or richer lifecycle rules. Generated HTA packages are loaded through :require; eligible scalar interfaces can receive a deterministic adapter.wasm.
(wasm/interface
{:schema "hara.wasm-interface/0-alpha"
:namespace example.math.async
:module "add.wasm"
:exports
{add {:wasm/export "add"
:arguments [{:name left :hara/type :i64 :wasm/type :i64}
{:name right :hara/type :i64 :wasm/type :i64}]
:returns {:hara/type :i64 :wasm/type :i64}
:async true
:operation "add"}}})
No silent downgrade
The interface chooses the boundary.
Bindgen never treats a pointer-bearing or asynchronous declaration as a scalar call merely because the raw Wasm uses integers. Unsupported combinations fail before publication instead of falling back to a weaker ABI.
Authoring workflow
Inspect, describe, bind, verify.
1. Inspect the module
The inspector reads Wasm sections but does not instantiate the module. It deliberately emits unresolved Hara types where the binary cannot reveal whether an integer is a number, pointer, enum, handle, length, or error code.
hara extension inspect library.wasm \
--out library.interface.hal \
--namespace example.library
2. Complete the semantic interface
Replace unresolved types with the Hara-visible contract. For non-scalar values, state lowering/lifting and ownership explicitly. The source is parsed as restricted data and is not evaluated during generation.
3. Bind and validate
bind verifies every declared raw export and helper against the exact module bytes, selects the target, computes digests and writes the package atomically.
hara extension bind interface.hal \
--module library.wasm \
--out dist
hara extension check dist
hara extension test dist
Generated package
The output is data plus Wasm.
dist/
├── package.edn
├── project.edn
├── interface.hal
├── bindings.edn
├── hara.build-product.edn
├── conformance/
│ └── bindings.edn
├── library.wasm
├── adapter.wasm # eligible HTA interfaces only
└── adapter.edn # when adapter.wasm is emitted
interface.hal- canonical semantic interface
bindings.edn- target-specific execution plan
project.edn- Hara extension declaration
package.edn- artifact identity, digests and lifecycle
hara.build-product.edn- tool and input provenance
conformance/- portable claimed-export evidence
memory.v1
Ownership is part of the ABI.
| Position | Ownership | Host action |
| input | borrowed | copy for the call; do not call free |
| input | transferred | release only when invocation does not complete |
| result | caller | copy, then release exactly once |
| result | callee | copy; do not release from the host |
Limits
Copying is bounded.
The current alpha contract bounds linear memory to 64 MiB, one transferred value to 16 MiB, input copies to 32 MiB, and aggregate input/output copying to 48 MiB. Pointer arithmetic is checked before access.
Current boundary
reallocate is reserved.
The current memory.v1 implementation supports explicit alloc(i32) -> i32 and free(i32) -> void. A declared reallocator is rejected until a later ABI revision.
Cross-runtime execution
One plan, host-specific executors.
Rust / Wasmtime
The native runtime verifies the package and canonical plan, then uses a generic bounded Wasmtime executor. Direct scalar calls stay on the scalar fast path.
JVM / Truffle
The JVM reads the same bindings.edn plan and mirrors pointer checks, UTF-8 validation, copy limits, ownership cleanup and public-to-raw export aliases without generating Java for the library.
Browser
The browser SDK exposes installMemoryWasmBinding(manifest, interface, bindings, bytes), verifies the canonical plan and module, then exposes the installed namespace through the ordinary Hara runtime.
HTA providers
Rust, browser worker/shared-worker and JVM HTA providers share operation, cancellation, capability and handle semantics. Rich bindings use HTA rather than inventing host-language wrappers.
WIT interoperability
Use WIT as an input, not as Hara's semantic authority.
Supported WIT packages, worlds, interfaces, functions, records, variants, lists, options, results, tuples and resources can seed a deterministic Hara interface. Hara-specific ownership, capabilities, errors, callbacks, handles and HTA lifecycle remain authoritative in .hal. Strict mode fails before writing a lossy projection.
hara extension wit-import api.wit \
--out api.hal \
--ir api.edn \
--module api.wasm \
--strict
hara extension wit-project api.hal \
--out api.wit \
--strict
Sample projects
Start from a complete fixture.
core.v1scalar
example.math
Direct add
A tiny import-free i64 + i64 -> i64 module with the authored interface and inspect/bind/check commands.
memory.v1bytes
example.codec
Echo bytes
A bounded linear-memory module with alloc, free, pointer/length input lowering, packed result lifting and caller-owned cleanup.
hta.v1adapter
example.math.async
Async scalar adapter
The same scalar library described as an asynchronous operation, causing bindgen to select HTA and emit an eligible deterministic adapter package.
Implementation references
Follow the versioned contract.
The public schemas are still alpha. This guide describes the current implementation; exact ABI rules remain with the runtime specification and source.