MatchBox makes BoxLang practical for a classic deployment target: the single-file command-line application.
The MatchBox open beta is available at https://github.com/ortus-boxlang/matchbox.
With the MatchBox native target, you can compile a .bxs script into a standalone executable for macOS, Linux, or Windows. The generated binary includes the MatchBox VM core and your compiled BoxLang bytecode. It does not require a JVM, a separate MatchBox install, or any runtime on the target machine.
That alone is useful for internal tools, release helpers, data transforms, developer utilities, and small automation scripts. But the more interesting path is Native Fusion: writing most of the tool in BoxLang, then dropping into Rust for the pieces that need native speed or operating-system access.
ποΈ The Native Build Model
MatchBox native builds use a runner-stub architecture that keeps the final binary lean:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Native Build Pipeline β
β β
β app.bxs β
β β β
β β matchbox --target native app.bxs β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Step 1: Compile .bxs β bytecode β β
β βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ β
β β Step 2: Append bytecode to runner stub (~500 KB) β β
β β (pre-compiled, stripped, arch-specific VM core) β β
β βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ β
β β Step 3: Output single executable β β
β β app / app.exe β β
β β No JVM. No runtime. No installer. β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β At startup: binary reads its own trailing bytes, β
β finds embedded bytecode, executes it. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Build with:
matchbox --target native app.bxs
That produces an executable named after your input file - app on macOS/Linux, app.exe on Windows. You distribute that file directly. No installer, no dependency notes, no runtime prerequisites.
Binaries are aggressively size-optimized at compile time (opt-level = "z", LTO, dead-code elimination, symbol stripping) and typically land around ~500 KB.
There is also a starter template to scaffold a new project:
https://github.com/ortus-boxlang/matchbox-cli-template
Cross-Compilation
Native binaries are platform-specific. The easiest path for shipping multi-platform releases is the included GitHub Actions workflow, which builds for all five targets on every tagged release:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GitHub Actions Release Targets β
βββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ€
β x86_64-unknown-linux-gnu β Linux (x64) β
β aarch64-unknown-linux-gnu β Linux (ARM64) β
β x86_64-apple-darwin β macOS (Intel) β
β aarch64-apple-darwin β macOS (Apple Silicon)β
β x86_64-pc-windows-msvc β Windows (x64) β
βββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
For manual cross-compilation, the cross tool handles target-specific linkers via Docker:
cargo install cross
cross build --release --target x86_64-unknown-linux-gnu
β‘ Native Fusion: Dropping Into Rust
Native Fusion is the MatchBox Rust interop layer. It lets you expose Rust functions as BoxLang BIFs and Rust structs as BoxLang native objects. Everything is statically linked into the final binary - no shared libraries, no FFI boilerplate at the BoxLang call site.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Native Fusion Project Layout β
β β
β my-tool/ β
β βββ app.bxs β BoxLang entry point β
β βββ native/ β
β βββ math.rs β exports register_bifs() β
β βββ counter.rs β exports register_bifs() + register_classes() β
β βββ Cargo.toml β optional external crate dependencies β
β β
β matchbox detects native/, compiles Rust with the VM, β
β merges registrations, produces one binary. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Note: Native Fusion is available in
--target nativebuilds only. It is not available in WASM or browser targets.
Exposing a Rust Function as a BoxLang BIF
Use the #[matchbox_fn] macro to annotate a plain Rust function. The macro generates a wrapper that handles argument-count validation and type coercion automatically:
// native/math.rs
use matchbox_vm::{ matchbox_fn, types::{ BxNativeFunction } };
use std::collections: :HashMap;
#[matchbox_fn]
pub fn fast_factorial( n: f64 ) -> f64 {
( 1..=n as u64 ).product::<u64>() as f64
}
pub fn register_bifs() -> HashMap<String, BxNativeFunction> {
let mut map = HashMap::new();
// Register the generated _wrapper, not the original function
map.insert( "fast_factorial".to_string(), fast_factorial_wrapper as BxNativeFunction );
map
}
Call it from BoxLang exactly like any built-in function:
println( fast_factorial( 10 ) ) // 3628800
The macro supports these parameter type coercions automatically:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β #[matchbox_fn] Supported Parameter Types β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ€
β Rust type β VM coercion β
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β f64 β args[i].as_number() β
β i32 β args[i].as_int() β
β bool β args[i].as_bool() β
β String β vm.to_string(args[i]) β
β BxValue β passed through unchanged β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
For BIFs that need to return strings, null, or object pointers, skip the macro and write a manual BIF signature directly:
pub fn greet( vm: &mut dyn BxVM, args: &[BxValue] ) -> Result<BxValue, String> {
if args.is_empty() {
return Err( "greet: expected one argument".to_string() );
}
let name = vm.to_string( args[0] );
Ok( BxValue::new_string( format!( "Hello, {}!", name ) ) )
}
Exposing a Rust Struct as a BoxLang Native Object
Use #[matchbox_class] and #[matchbox_methods] together to expose a stateful Rust struct as a BoxLang object:
// native/counter.rs
use matchbox_vm::{ matchbox_class, matchbox_methods, types::{ BxValue, BxVM, BxNativeFunction, BxNativeObject } };
use std::collections: :HashMap;
use std::rc: :Rc;
use std::cell: :RefCell;
#[matchbox_class]
#[derive( Debug )]
pub struct Counter {
pub value: f64,
}
#[matchbox_methods]
impl Counter {
pub fn increment( &mut self ) -> f64 {
self.value += 1.0;
self.value
}
pub fn add( &mut self, n: f64 ) -> f64 {
self.value += n;
self.value
}
pub fn get( &self ) -> f64 {
self.value
}
}
// Constructor registered separately via register_classes()
pub fn create_counter( vm: &mut dyn BxVM, args: &[BxValue] ) -> Result<BxValue, String> {
let initial = args.first().map( |v| v.as_number() ).unwrap_or( 0.0 );
let obj = Counter { value: initial };
let id = vm.native_object_new( Rc::new( RefCell::new( obj ) ) );
Ok( BxValue::new_ptr( id ) )
}
pub fn register_classes() -> HashMap<String, BxNativeFunction> {
let mut map = HashMap::new();
// Key format: "<module_filename>.<ClassName>"
map.insert( "counter.Counter".to_string(), create_counter as BxNativeFunction );
map
}
Instantiate and use from BoxLang with new rust:<module>.<ClassName>(...):
c = new rust:counter.Counter( 0 )
c.increment()
c.add( 9 )
println( c.get() ) // 10
Using External Rust Crates
Add a Cargo.toml to the native/ directory to pull in any Rust crate:
[package]
name = "native"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
base64 = "0.22"
MatchBox compiles this as a standard Cargo project and links the output into the final binary. That means you can reach any crate in the Rust ecosystem - serde, reqwest, image, ring, compression libraries - and surface it cleanly through a BoxLang BIF.
π― Why This Is Useful
Most CLI tools do not need every line to be native Rust. They need easy argument handling, readable business logic, clean output, and a fast path for the expensive work.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β The MatchBox CLI Sweet Spot β
β β
β Write in BoxLang: Move to Rust when needed: β
β βββββββββββββββββ ββββββββββββββββββββββββββ β
β Argument handling Parsing large files β
β Application flow Hashing and crypto β
β Business logic Image operations β
β Output formatting Compression β
β Structs, arrays, closures Custom binary formats β
β Error messages Hardware or OS access β
β Config file reading Hot loops over large data β
β Any Rust crate you need β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Write orchestration and flow in BoxLang. Move measured hot paths into Rust only when needed. Ship one binary when the tool is ready.
Experimental Java Interop (JNI)
In native builds only, MatchBox also includes an experimental JNI bridge that lets you instantiate Java classes and call methods - provided a compatible JVM is installed on the host machine at runtime:
// native builds only - experimental
sb = java.new( "java.lang.StringBuilder", "Hello" )
sb.append( ", World!" )
println( sb.toString() ) // Hello, World!
This is not available in WASM builds and the API may change.
β οΈ Beta Boundaries
Native Fusion limitations worth knowing upfront:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Native Fusion Capability Matrix β
ββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββ€
β Native builds (--target native) β β
β
β WASM builds (--target js / --target wasm) β β Not supported β
β Multiple .rs files in native/ β β
β
β External Rust crates via Cargo.toml β β
β
β #[matchbox_fn] return types other than f64 β β οΈ Use manual BIF β
β Mutable self in #[matchbox_methods] β β
β
β Property access on native objects β β οΈ Override manually β
β Java interop (JNI) β β
Experimental β
ββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββ
The macro path is intentionally simple. It handles common parameter coercions well. For richer return types or careful validation, write a manual BIF and allocate values through the VM directly. That is the right tradeoff for beta: easy things should be easy, and advanced interop should remain explicit.
π Where to Start
Start with the CLI template, build a small tool in plain BoxLang, and only then add a native/ directory.
# Step 1: Scaffold from the starter template
git clone https://github.com/ortus-boxlang/matchbox-cli-template my-tool
cd my-tool
# Step 2: Build a native binary
matchbox --target native app.bxs
# Step 3: Run it
./app
A good first Native Fusion experiment: a BoxLang command that reads input, passes the expensive operation to Rust, and formats the result in BoxLang.
// app.bxs - BoxLang handles the interface, Rust handles the math
name = server.cli.args[1] ?: "World"
input = server.cli.args[2] ?: 10
println( "Computing factorial of " & input & "..." )
println( "Result: " & fast_factorial( input ) )
// native/math.rs - Rust handles the hot path
#[matchbox_fn]
pub fn fast_factorial( n: f64 ) -> f64 {
( 1..=n as u64 ).product::<u64>() as f64
}
matchbox --target native app.bxs
./app World 20
# Computing factorial of 20...
# Result: 2432902008176640000
π The Bigger Picture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BoxLang Runtime Landscape β
β β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β BoxLang JVM Runtime β β MatchBox (Rust VM) β β
β β β β β β
β β - Web Servers β β - Native CLI βββ Here β β
β β - AWS Lambda β β - Native Web Server β β
β β - Google Cloud Fns β β - Browser (WASM/JS) β β
β β - Desktop (Electron) β β - WASI Containers β β
β β - Full Java interop β β - Edge Runtimes β β
β β - All BL modules β β - ESP32 / IoT β β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β
β Same BoxLang language. Different runtimes. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MatchBox is still beta, but this workflow is already the clearest example of the project philosophy: keep BoxLang productive, keep deployment small, and let Rust handle the native edge when you need it.
Explore the project and open issues at https://github.com/ortus-boxlang/matchbox.
Read the full Native Fusion docs at https://boxlang.ortusbooks.com/boxlang-framework/matchbox/native-fusion-builds.
Join the Ortus Community
Be part of the movement shaping the future of web development. Stay connected and receive the latest updates on product launches, tool updates, promo services and much more.
Subscribe to our newsletter for exclusive content.
Follow Us on Social media and don't miss any news and updates:
Add Your Comment