Small and fast
Few keywords, one obvious way, one formatter, fast builds, and static binaries for Linux, Mac, Windows, and WASI.
Xo (pronounced ex-oh) is a small, statically typed language for backends, tools, and web services. A human project owner oversees AI that builds and maintains the language. Things like ignored errors, nil, missed cases, and hidden side effects are compile errors in Xo. The compiler is optimized to return helpful information back to the agent (or human).

fn greet(name: Str) -> Str {
"Hello, ${name}!"
}
fn main(os: Os) {
os.stdio.println(greet("world"))
}
test "greets by name" {
expect(greet("Xo")) == "Hello, Xo!"
}$ xo run . Hello, world! $ xo test . ok: 1 passed, 0 failed, 0 skipped
Xo borrows what has proven itself in Go, Rust, Swift, and Kotlin, and turns the bugs tests usually miss into compile errors. In a study of 30 planted safety bugs, Xo caught or prevented 23 to 29, against 13 for Go with strict analyzers and 9 for plain Go.
Few keywords, one obvious way, one formatter, fast builds, and static binaries for Linux, Mac, Windows, and WASI.
Result and ?; an unhandled error is a compile error.
Every case handled, and every match target.os arm type checked for every target.
Optionals are T? and checked at every use.
No task outlives its scope; value semantics mean no data races.
uses net, fs.read in the signature, with capabilities passed from main.
To help with adoption, Go libraries can be used with Xo until its own library support grows. Bindings are generated on the fly. (T, error) becomes T ! go.Error, and a nil pointer becomes T?.
use go "regexp" use go "github.com/google/uuid" as uuid
Use C libraries the way Go programs do with cgo: the LLVM and arm64 backends bind C headers directly, with effects declared at the boundary.
use c "zlib.h" link "z" uses none // zlib.crc32(0, b"hello")
Every program in the tour checks, runs, and passes its tests with the current toolchain, and every error shown is the compiler's real output.
Supply chain attacks are on the rise, so Xo's effects give you control over what each dependency can do. Put a ceiling on a dependency in xo.mod. If an upgrade starts doing more, the build fails at your call, before the new code ever runs. No mainstream language checks limits per dependency in the compiler: Go and Rust rely on audits and analysis tools, Deno and Node grant permissions per process, and Java's SecurityManager is disabled as of JDK 24.
$ cat xo.mod module example.com/shop edition preview require github.com/acme/billing v1.4.1 uses none $ xo check . main/main.xo:4:6: error XO1309: `charge` of github.com/acme/billing v1.4.1 performs `go`, above the effect ceiling `uses none` of that dependency in xo.mod note: xo.mod:4:1: the ceiling is declared here note: review why github.com/acme/billing needs `go`; then raise the ceiling, or require a version within it (decision 0086)
pub function declares its effects, checked against its body; logging is the log effect.xo.mod (XO1309).vouch (XO1316).xo.lock records each dependency's effects; an update that reaches further fails until you accept it (XO1317).xo get prints every API and effect change.xo publish rejects a minor or patch release that adds an effect (XO1310).os.net.only(["api.example.com"]) reaches only those hosts; anything else is Denied before it happens.Env.only, Proc.only, and Fs.sub narrow variables, commands, and directories.task.with_budget caps the steps and running time of a call, so a dependency that loops forever is stopped.xo audit reports every dependency's effects, escape hatches, log call sites, and the capabilities you pass it.Measured: of 17 attack scenarios from the Backstabber's Knife Collection taxonomy, rebuilt as malicious Xo dependencies, Xo blocks 12, four of them with no setup. Hardening is opt-in, a vouch is your own review, and a wrong answer performs no effect, so tests remain the defense there; an endless loop is stopped only under a budget. How to use each one, and the full results.
Xo has five backends. Its own arm64 and x86-64 backends, assemblers, and linkers are written in Xo and need no C toolchain. The native runtime is written in Xo, with no C, and frees memory by reference counting, with no garbage collector; a function marked uses realtime gets bounded loops, no recursion, and no allocation. Debuggers see your variables, structs, lists, and maps, and a profiler and an execution trace are built in. The Go backend is the other way to build the same program, and the WebAssembly backend runs it over WASI. Interop differs by backend: native builds can call C with use c, and the Go backend can call Go. On the network side Xo's servers match or beat Go's on the rows measured: HTTP/2 with 100 streams runs 2.1 times Go's rate, bulk TLS 1.17 to 1.37 times, and the HTTP/1.1 servers use 0.39 to 0.63 of Go's CPU per request (medians, a loaded machine). Against Go on the benchmarks measured for each release, 0.32.0 runs 14 of 20 workloads faster and 4 within noise, uses less peak memory on all 20, and builds smaller binaries (1.7 MB against 2.4 MB); the 0.32 notes list the two it is still slower on.
Fast builds on Apple silicon Macs and arm64 Linux, static Linux binaries from a Mac, and binaries smaller than LLVM's.
xo buildFast builds on x86-64 Linux, Intel Macs, and Windows, built the same way as arm64's, with no C toolchain. Within about 6 percent of LLVM's instructions on our benchmarks, and closing (how).
xo buildBuilds optimized native code with clang, for every native target, Windows included, when you want clang's optimizer.
xo build --backend=llvmWrites Go source that points back to your .xo lines, for every Go target, with Go's garbage collector, and can use go any Go package.
xo build --backend=goWebAssembly that runs over WASI with its own runtime. It builds the compiler that runs the playground; a program it cannot build yet stops with a clear error, never with wrong output.
xo build --backend=wasmEvery backend is tested against the others: the test programs and randomly generated ones must print the same output on each, and every change to the compiler must rebuild it byte for byte.
The latest release has archives for macOS and Linux (arm64 and x86-64) and Windows (x86-64): unpack one and put its bin on your PATH. Native programs build with no C toolchain on arm64 and x86-64 hosts. To build from source instead: the compiler is written in Xo, so any xo builds the next one, and with none at all the first build starts from Xo's last Go version and walks forward to today's; that needs git and Go 1.26 or later, once (and clang on x86-64), and took 25 to 37 minutes on a 16-core machine.
$ curl -LO https://github.com/xo-lang/xo/releases/download/v0.32.0/xo-v0.32.0-darwin-arm64.tar.gz $ tar -xzf xo-v0.32.0-darwin-arm64.tar.gz $ export PATH="$PWD/xo-v0.32.0-darwin-arm64/bin:$PATH" $ xo init example.com/hello
Checks, builds, and queries answer in JSON. Lookups and edits work on resolved symbols, so an agent never guesses from text, and every edit is rechecked before it is written. dbg(x) prints a value from any function, pure ones included, on every backend, and a release build strips it. And when the check is clean, Finan sleeps soundly.
xo check --jsonType, effect, and concurrency checksxo def NAMEWhere a symbol is definedxo refs NAMEEvery use of a symbolxo doc NAMESignature, docs, and methodsxo edit renameEdits rechecked before writingxo test --explore=NSearches task interleavingsxo vet --costCosts the source does not showxo explain XO0401Every code, explained$ xo check ./... 1 module(s), 0 error(s), 0 warning(s) $ xo build --targets=all . # static binaries for Linux, Mac, Windows, WASI
