ADocumentation Index
Fetch the complete documentation index at: https://edgepython.com/llms.txt
Use this file to discover all available pages before exploring further.
.wasm module that an Edge Python script imports via from "<url>" import <names> must export functions following the wire format below. The contract is small (3 scalar types, one calling convention) and language-agnostic — Rust, C, Zig, AssemblyScript, anything that targets wasm32 and exports C ABI functions can produce a compatible module.
The Edge Python project does not ship an SDK crate. This page is the spec; you write the boilerplate yourself or use any community-maintained wrapper.
Wire format
Every exported function visible to Edge Python has signature:u64 carries one NaN-boxed Val, the same 64-bit tagged value Edge Python’s VM uses internally. The host marshals scripts’ arguments into u64s, calls your function, and decodes the returned u64.
Tag bits
| Type | u64 encoding |
|---|---|
int (i48 inline) | 0xFFFC_0000_0000_0000 | (i & 0xFFFF_FFFF_FFFF) — 48 signed bits, sign-extended on decode |
float (f64) | f.to_bits() directly. Any NaN whose top bits collide with the 0x7FFC_* tag pattern must be canonicalized to 0x7FF8_0000_0000_0000 to avoid ambiguity with tagged values |
bool (True) | 0x7FFC_0000_0000_0002 |
bool (False) | 0x7FFC_0000_0000_0003 |
None | 0x7FFC_0000_0000_0001 |
Pack / unpack reference
Hand-rolled in any language. The Rust version:Minimal Rust example
Cargo.toml:
src/lib.rs:
How the host loads it
When the host (browser shim, WASI runtime, Rust embedder) seesfrom "<url>" import <names> and the URL ends in .wasm, it:
- Fetches the bytes.
- Instantiates the module (
WebAssembly.instantiatein the browser,wasmtime::Moduleserver-side). - Walks the module’s exports table and registers every function under the same name.
- When a script invokes a binding, the host packs the args into u64s, calls the export, and unpacks the return.
demo/edge.js (_registerNativeModule + _handleNativeCall). Other hosts mirror the same shape against their own runtime.
Constraints and gotchas
- Integers are i48, not i64. Values outside ±2⁴⁷ silently truncate on the wire. If you need full i64 or BigInt, that’s not representable here — work around it (split into limbs, etc.) or use the Rust embedder path.
- NaN payloads can collide with tagged values.
pack_floatmust canonicalize any NaN whose top mantissa bits look like the QNAN tag pattern. Most NaNs from arithmetic don’t collide; this only affects user-constructed NaN payloads. - No string / list / dict marshalling. A
Val::Strarrives as an opaque heap-index u64 your function can’t read. Either encode handles you exchange via host channels, or use the in-process Rust API. - No exceptions across the boundary. A binding returns one u64. To signal an error from a
.wasmmodule, reserve a sentinel return value and document it. (The Rust embedder API does have properVmErr; the WASM wire doesn’t.) - Memory ownership. The host doesn’t read your module’s linear memory; it just calls exports and reads/writes u64s. If your module allocates internally, those allocations are its private concern.
Author conveniences (community-maintained)
The Edge Python project ships only this spec. Authors who want sugar (a#[edge_export] macro, FromWire/IntoWire traits, etc.) have two options:
- Use a community SDK crate. If anyone publishes one to crates.io with the
edge-pythonkeyword, you can depend on it. - Hand-roll the boilerplate. It’s ~25 lines per module crate (the example above is the entire boilerplate; per-function it’s ~5 lines).
See also
- Imports — how
from "..." importresolves on the script side. - Writing modules — the in-process Rust embedder path (full type coverage, no wire format).