LM lmscript.io Host SDKs
Host Implementation Guide · 7 Language Runtimes

Building & Embedding an LMCode Host

Any CLI tool or application in C++17, Rust, Go, Java 21, C#/.NET 8, JavaScript (ESM), or Python 3 can become an _lmcode_ host by embedding its runtime SDK, registering its domain types/functions, and honoring three CLI flags.

1. The Three Mandatory Host CLI Flags

FlagRequired Behavior
HOST --lmcode-schema --json Print the exact raw bytes of the host's dsl.schema: 2 JSON document to stdout and exit 0. The compiler hashes these exact bytes into the DSLB v6 header.
HOST --lmcode-signatures Print the sorted canonical signatures registered in the host's runtime Registry (one per line: context ..., fn ..., method ...) and exit 0.
HOST --lmcode FILE.dslb Load, verify, and execute the compiled DSLB v6 bytecode file (may be repeated for multi-slot builders) and write one terminal LMCODE_RETURN envelope to stdout.

2. Registering Types & Running Bytecode Across All 7 Runtimes

host_main.cpp (lmcode-cpp/include/dslvm.hpp)
#include "dslvm.hpp"
#include "stdlib/std.hpp"

struct Money { int64_t cents; };

int main(int argc, char** argv) {
  dslvm::Registry reg;
  dslvm::stdlib::register_all(reg); // registers the 206-sig lmcode_std_v3 catalog

  reg.fn("money", {"int"}, "Money", [](dslvm::Args& a) {
    return dslvm::Value::obj("Money", std::make_shared<Money>(Money{a.i(0)}));
  });
  reg.method("Money", "cents", {}, "int", [](dslvm::Args& a) {
    return dslvm::Value::from_int(a.self<Money>()->cents);
  });
  reg.context_type("shop", "Shop");

  auto prog = dslvm::Program::load(bytecode_bytes, raw_schema_bytes, reg);
  dslvm::Context ctx;
  ctx.bind("shop", dslvm::Value::obj("Shop", std::make_shared<Shop>()));
  prog.run(ctx);
}
main.rs (lmcode-rust / dslvm)
use dslvm::{Context, Program, Registry, Value};

fn main() -> Result<(), dslvm::Error> {
    let mut reg = Registry::with_std(); // includes lmcode_std_v3
    reg.register_fn("money", &["int"], "Money", |args| {
        Ok(Value::obj("Money", Money { cents: args.int(0) }))
    });
    reg.register_method("Money", "cents", &[], "int", |recv, _args| {
        let m = recv.downcast_ref::<Money>().unwrap();
        Ok(Value::Int(m.cents))
    });

    let prog = Program::load(&bytecode, SCHEMA_BYTES, &reg)?;
    let mut ctx = Context::new();
    prog.run(&mut ctx)?;
    Ok(())
}
main.go (lmcode-go)
package main

import lmcode "github.com/lmctl-ai/lmcode-go"

func main() {
    reg := lmcode.NewRegistryWithStd()
    reg.Func("money", []string{"int"}, "Money", func(a *lmcode.Args) (lmcode.Value, error) {
        return lmcode.Obj("Money", &Money{Cents: a.Int(0)}), nil
    })
    prog, err := lmcode.Load(bytecode, schemaBytes, reg)
    if err != nil { panic(err) }
    ctx := lmcode.NewContext()
    _ = prog.Run(ctx)
}
HostMain.java (lmcode-java)
import ai.lmctl.lmcode.*;

Registry reg = Registry.withStd();
reg.fn("money", List.of("int"), "Money", args ->
    Value.obj("Money", new Money(args.getInt(0))));
reg.method("Money", "cents", List.of(), "int", args ->
    Value.ofInt(args.self(Money.class).cents()));

Program prog = Program.load(bytecodeBytes, schemaBytes, reg);
Context ctx = new Context();
prog.run(ctx);
Program.cs (lmcode-csharp)
using Lmcode.DslVm;

var reg = Registry.WithStd();
reg.Fn("money", new[] { "int" }, "Money", args =>
    DslValue.Obj("Money", new Money(args.Int(0))));

var prog = DslProgram.Load(bytecodeBytes, schemaBytes, reg);
var ctx  = new DslContext();
prog.Run(ctx);
host.mjs (lmcode-js)
import { Registry, Program, Context, InvocationResults } from "lmcode-js";

const reg = Registry.withStd();
reg.fn("money", ["int"], "Money", (args) =>
  ({ __type: "Money", cents: args.int(0) })); // BigInt int64 at boundary

const prog = Program.load(bytecodeBuffer, schemaBytes, reg);
const ctx  = new Context();
prog.run(ctx);
host.py (lmcode-python)
from lmcode import Registry, Program, Context, Value

reg = Registry.with_std()
reg.fn("money", ["int"], "Money", lambda a: Value.obj("Money", Money(a.int(0))))

prog = Program.load(bytecode_bytes, schema_bytes, reg)
ctx  = Context()
prog.run(ctx)

3. Private Invocation Metadata & Terminal LMCODE_RETURN Envelope

When run_lmcode or run_lmcode(b) spawns a host process, it passes the path to a private JSON manifest in LMCODE_INVOCATION_FILE:

LMCODE_INVOCATION_FILE JSON
{"version":1,"mode":"single","dry":false,"parts":[{"index":0,"file":"/tmp/lmcode-slot-0.dslb"}]}

After running all submitted slots (and any domain validation), the host writes exactly one terminal frame at the end of stdout:

Terminal Return Envelopes
# 1. Single-program success (exit 0)
LMCODE_RETURN_BEGIN
{"ok":true,"result":{"zone_count":1,"domain":"lmscript.io"}}
LMCODE_RETURN_END

# 2. Multi-slot builder success (exit 0, includes "parts": N)
LMCODE_RETURN_BEGIN
{"ok":true,"result":[{"step":"bucket"},{"step":"cloudfront"}],"parts":2}
LMCODE_RETURN_END

# 3. Controlled failure (exit 2=cli, 3=load/run, 4=app)
LMCODE_RETURN_BEGIN
{"ok":false,"phase":"run","code":"RUN_HOST","message":"bucket name cannot be empty","line":12,"column":3}
LMCODE_RETURN_END

4. Discovering & Invoking @Rule Entries from the Host

Compiled @annotation fn name(...) implements Interface rules live in Section 6 of the DSLB v6 file. After running the body region (`prog.run(ctx)`), the host can inspect prog.entries() and call any entry with typed arguments via prog.call(entry_name, ctx, args).

5. Conformance Suite & Signature Drift Verification

Verify Host Schema & Registry Alignment
# Diff the compiler's canonical signature list against the host's runtime registry:
diff -u <(lmscript --lmcode-signatures host.schema.json) \
        <(./my-host --lmcode-signatures)