Mako is a compiled language for backend and systems work. You write .mko
files; Mako turns them into standalone native binaries — no garbage collector,
no VM, nothing extra to install next to them at runtime.
Memory is managed by ownership (hold), shared references (share), and
arenas, so it's freed deterministically instead of by a tracing GC that stalls
your service at the worst moment. Concurrency is built into the language rather
than bolted on: structured crew / kick / fan, channels, and actors that
tidy up after themselves. The standard library covers the everyday backend
surface — HTTP, TLS, JSON, databases, networking — and is candid about which
corners are battle-tested and which are still shallow.
Status: experimental/alpha (v0.4.13 tip; last tag v0.4.5). The language works and compiles real programs, but the surface is young. Expect breaking changes, missing features, and bugs. This is not yet suitable for production use without careful evaluation.
Mako provides compiler-enforced ownership, bounds checks (including in release
builds), escape checks, and deterministic cleanup without a tracing garbage
collector. The safety model is still experimental and is being validated through
sanitizer testing — the full test suite is exercised under ASan (with leak
detection disabled — validating invalid accesses, use-after-free, and
double-free) and UBSan, while a focused concurrency suite is exercised under
TSan. This does not prove complete memory safety. unsafe blocks, the C
runtime, and FFI are outside the safety model.
What's new in 0.4.5+: a direct native backend that skips C for many builds —
ownership-explicit IR to Cranelift (debug) or LLVM (release) plus a bundled
linker. The full examples/testing suite passes on --backend native (367/367).
Release artifacts and install scripts ship for Linux, macOS, and Windows. Runtime
is competitive with hand-written C and Rust on measured compute workloads (see
changelog); binary-size and some residual benches remain open
for 0.5.0. Full history: changelog · roadmap ·
soundness.
mako-lang.com · Status · Roadmap · Guide · Book · Soundness · Memory model
Mako is built with cargo on our CI. You download the finished binary bundle (CLI + runtime + std). You never install Rust or run cargo yourself.
Linux
curl -fsSL https://github.com/loreste/mako/releases/latest/download/install-linux.sh | bash
source "$HOME/.local/share/mako/env.sh"
mako versionmacOS
curl -fsSL https://github.com/loreste/mako/releases/latest/download/install-release.sh | bash
source "$HOME/.local/share/mako/env.sh"That single command:
- Installs clang if missing (so
.mkofiles can compile) - Downloads the cargo-built
makobinary package for your CPU - Installs runtime headers, native-link support sources, and the standard library
- Verifies SHA-256, writes
env.sh, updates shell RC when possible
mako init hello && cd hello && mako run main.mkoOr grab the archive directly (the bare binary alone cannot compile programs without the runtime files shipped in the archive):
https://github.com/loreste/mako/releases/latest/download/mako-x86_64-unknown-linux-gnu.tar.gz
Options:
curl -fsSL …/install-linux.sh | bash -s -- --prefix /opt/mako --yes
curl -fsSL …/install-linux.sh | bash -s -- --no-deps # skip clang install
curl -fsSL …/install-linux.sh | bash -s -- --version v0.4.5You do not need Rust or cargo on the machine that runs Mako.
make install
mako versionPrefer the .zip from Releases, or build from source with LLVM clang on PATH.
mako init hello && cd hello
mako run main.mkofn main() {
print("hello from mako")
print(fib(10))
}
fn fib(n: int) -> int {
if n <= 1 { return n }
return fib(n - 1) + fib(n - 2)
}
Real work, low ceremony: infer locals, one print, ? for errors, match for
routes, power (hold / crew / arena) only when you need it.
Ergonomics.
Two paths, one language. The mature path lowers your .mko to C and lets clang
optimize it (-O3 -flto in release) — that's what compiles the full language
today. Alongside it, a newer direct backend skips C altogether: it turns
your program into an ownership-explicit IR and emits machine code through LLVM
(release) or Cranelift (fast debug builds), links it with a bundled linker, and
ships a single standalone binary — no clang, no system linker, nothing to
install. It doesn't cover the whole language yet, but where it does, the
binaries come out small and fast.
Either way there's no interpreter, no JIT, no runtime VM, and no tracing
collector. Memory is freed deterministically — ownership, scope exits, arenas —
and concurrency (crew, fan, channels) is part of the language, not a library
bolt-on.
The compiler tracks who owns what. hold bindings move when you use them —
try to use a value after it's moved and the compiler stops you. For
request-scoped work, arenas let you allocate a bunch of things and free them
all at once when the scope ends:
arena a {
let msg = arena_text(a, "hello arena")
let xs = arena_ints(a, 1000)
}
// everything in `a` is gone — one free, zero bookkeeping
crew blocks own jobs started with kick. When the block ends, those jobs are
cancelled and joined. Explicit detach is a process-scoped escape and must be
followed by detached_join_all():
fn main() {
let ch = chan_new(4)
crew t {
let p = t.kick(producer(ch, 5))
let c = t.kick(consumer(ch))
let _ = p.join()
print_int(c.join())
}
// everything joined and done here
}
If a function returns a Result, you have to deal with it. The compiler
won't let you ignore one:
fn load() -> Result[int, string] {
let fd = open_cfg("config.toml")?
Ok(fd)
}
The stdlib provides APIs for common backend tasks: HTTP server/client, TLS, WebSocket, JSON, SQLite, regex, file I/O, channels, binary buffers, and more. Some are fully tested integrations; others are early API surfaces that verify shape but lack deep integration testing. External dependencies (OpenSSL, SQLite, etc.) are optional — the stdlib soft-skips when they're absent. See STDLIB for what's implemented vs. planned.
fn main() {
let fd = http_bind(8080)
while true {
let c = http_accept(fd)
let path = http_path(c)
if str_eq(path, "/health") {
let _ = http_respond_json(c, 200, "{\"ok\":true}")
}
let _ = http_close(c)
}
}
Incremental by default. Release builds use -O3 -flto.
Benchmarks.
// Defer — runs on exit, last-in first-out
defer print("cleanup")
// Enums with methods
enum Shape {
Circle(int),
Rect(int, int),
}
fn Shape_area(self: Shape) -> int {
match self {
Circle(r) => r * r,
Rect(w, h) => w * h,
}
}
// Interfaces
interface Writer {
fn write(string) -> int
}
// Derive macros
#[derive(json)]
struct Person {
name: string
age: int
}
// Actors
actor Session {
receive Invite { print("invite") }
receive Bye { print("bye") }
}
// Memory-mapped files
let m = mmap_create("data.bin", 4096)
let _ = mmap_write(m, 0, "hello")
// Binary protocols
let b = buf_pack_new(64)
buf_write_u32be(b, 1024)
// Concurrent hashmaps
let m = cmap_new()
cmap_set(m, "key", "value")
// FFI
extern "C" fn my_c_function(n: int) -> int
Putting it all together — struct, database, error handling, arenas, and concurrency in one program (examples/showcase.mko):
#[derive(json)]
struct Task {
id: int
title: string
done: int
}
fn insert_task(db: SqlDB, title: string) -> Result[int, string] {
let rc = sql_exec_str4(db, "INSERT INTO tasks (title, done) VALUES ($1, $2)", title, "0", "", "")
if rc != 0 { return error("insert failed") }
Ok(0)
}
fn worker(ch: chan[int], id: int, results: CMap) -> int {
arena a {
let label = arena_text(a, format_int(id))
while true {
let task_id = ch.recv()
if task_id == 0 { break }
cmap_set(results, format_int(task_id), "worker " + label + " done")
}
}
return id
}
fn main() {
let db = sql_open_sqlite("/tmp/showcase.db")
let _ = sql_exec_plain(db, "CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY, title TEXT, done INTEGER)")
match insert_task(db, "write tests") {
Ok(_) => print("inserted"),
Err(e) => print("error: " + e),
}
let results = cmap_new()
let ch = chan_new(10)
crew t {
let w1 = t.kick(worker(ch, 1, results))
let w2 = t.kick(worker(ch, 2, results))
for i in 5 { let _ = ch.send(i + 1) }
let _ = ch.send(0)
let _ = ch.send(0)
let _ = w1.join()
let _ = w2.join()
}
for i in 5 { print(cmap_get(results, format_int(i + 1))) }
let _ = sql_close(db)
}
More in examples/ and The Mako Book.
mako init myapp # new project
mako run main.mko # compile and run
mako build main.mko # compile
mako build --release main.mko # optimized
mako test examples/testing # test suite
mako fmt -w # format
mako lint # lint
mako check main.mko # type-check only
mako build --target wasm32-wasip1 main.mko # WebAssemblySplit your code across files, import what you need:
// main.mko
import "./db.mko"
import "./routes.mko"
fn main() {
let _ = db_init()
let fd = http_bind(8080)
while true {
let c = http_accept(fd)
handle(c)
let _ = http_close(c)
}
}
Grouped and aliased imports work too:
import (
"./db.mko"
"./routes.mko"
"strings"
)
import "./helpers.mko" as h
For separate packages, use mako.toml:
[dependencies]
helper = { path = "../helper" }| The Mako Book | Start here |
| Identity | Our syntax + identity checklist |
| Dual-form inventory | Optional sugar (not preferred) |
| How-to Guides | Practical walkthroughs |
| Language Guide | Current syntax reference (Mako-native) |
| Compat | Dual forms / what won't break |
| Standard Library | What's included |
| Built-in Functions | Documented built-ins and signatures |
| Language Spec | Formal specification |
| CLI Reference | Current commands, flags, and workflows |
| Tutorials | Backend, game networking, databases, FFI, concurrency, cloud |
| Examples | Runnable programs |
| Debugging | dbg(), lldb, sanitizers, error messages |
| Security | Safety model |
| Soundness | SAFE/RT program (bounds, drops, ownership) |
| Memory model | Concurrency, crew lifecycle, channel ownership |
| Performance | Benchmarks |
| Status | What works, what doesn't |
| Roadmap | What's next |
| Changelog | What changed |
VS Code extension with syntax highlighting, LSP, format-on-save, launch configurations, and a custom dark theme. Debugging uses host adapters where available. See editors/vscode/.
The language server (mako lsp) speaks stdio JSON-RPC and supports the
implemented features in LSP-compatible editors.
mako test examples/testing
mako test -r TestAdd -v
mako test examples/testing --json # stable CI report
mako test --sanitize address examples/testing # ASan
mako test --race examples/testing/crew_fan_test.mko # TSanJSON test reports remain parseable on failure and the command exits nonzero when any test run fails.
360 test programs. The suite runs under AddressSanitizer and ThreadSanitizer in CI. Tests that require optional external libraries (SQLite, QUIC) soft-skip when the dep is absent — they verify API shape but not full integration. See TEST_CATEGORIES.md for what each test actually proves.
Mako is its own language with its own syntax. We take simplicity and control as goals, and we design the surface around the problems backend engineers actually hit — GC pauses, null, error-handling noise, leaked tasks, and lifetime ceremony — solving them with Mako's own tools rather than borrowing another look.
Keywords: fn, on, pack, pull, hold, share, arena, crew, kick,
match, export.
Identity · Pain points.
export struct Point {
x: int
y: int
}
on Point {
fn distance(self) -> int {
return self.x + self.y
}
}
fn divmod(a: int, b: int) -> (int, int) {
return (a / b, a % b)
}
fn main() {
let p = Point { x: 3, y: 4 }
print(p.distance())
let q, r = divmod(17, 5)
print(q)
print(r)
// Structured concurrency (ordinary kicked tasks are joined)
crew t {
let j = t.kick(work())
print(j.join())
}
}
Identity: docs/IDENTITY.md.
Sample: examples/mako_style.mko.
Dual spellings (func, :=, …) still parse for compatibility.
- Full Unicode property database for regex
- JPEG readable by standard viewers
- Struct field reflection beyond schema registry
- SMTP AUTH over TLS
- HTTP engine is POSIX-only for now
Full list in STATUS.md.
See CONTRIBUTING.md.
MIT