The language, its library and its whole toolchain, in one download
One language, one binary, the entire development lifecycle.
Stop learning an entire constellation of third-party tools just to build on the web.
Zuri ships a standard library that covers the whole stack, a JIT compiler, and every tool from the first line to the shipped app: a formatter, a test runner, a language server, a package manager, a package registry and a bundler.
$ curl -fsSL https://zuri-lang.github.io/zuri/install.sh | sh
> powershell -c "irm https://zuri-lang.github.io/zuri/install.ps1 | iex"
$ git clone https://github.com/zuri-lang/zuri && cd zuri && cargo patch-crates && cargo build --release
Linux and macOS on x86-64 and ARM, and Windows on x86-64.
zuri upgrade keeps it current.
Installation guide
~ $ zuri init weather --yes
zuri init weather 0.1.0
added project.toml
added index.zu
added app/index.zu
added tests/app.test.zu
added README.md
added .gitignore
added .gitattributes
Your project is ready.~/weather $ zuri install thermo
Resolving dependencies
Installing thermo 0.1.0
+ thermo 0.1.0
Installed 1 package.~/weather $ zuri run
Hello, world!~/weather $ zuri test
zuri test 1 file in tests
PASS app.test.zu 41ms 2 tests
1 file
2 passed • 2 total
time 43ms~/weather $ zuri fmt
Nothing to do. All 3 files are already formatted.~/weather $ zuri bundle --format exe
Built /home/ada/weather/dist/weather-0.1.0-x86_64-unknown-linux-gnu~/weather $ ./dist/weather-0.1.0-x86_64-unknown-linux-gnu
Hello, world!- REPL
- Tiered JIT
- Standard library
- Test runner
- Formatter
- Language server
- Package manager
- Package registry
- Bundler
- Self-update
The ecosystem tax
When you learn Zuri, you already know the stack.
Software creation has become buried beneath endless layers of glue code, external configs and shifting package ecosystems. Zuri brings the focus back to writing software: one cohesive platform, one documentation site, and zero framework fatigue.
When you pick up a modern language, you do not just learn that language. To serve a single web page or persist a record, you must research, evaluate and configure dozens of disconnected third-party libraries. Every piece comes from a different author, follows conflicting conventions, and keeps its own documentation in its own shape.
You spend twenty percent of your time learning core concepts and eighty percent wrestling with arbitrary tool churn.
Zuri removes that tax. The runtime, the server, the data layer and the utility toolchain were designed together and speak the same conceptual dialect.
- Elsewhere, you assembleZuri ships
- a package manager and a lockfile
zuri install - an HTTP framework
import http - a database driver for each engine
import sql - a validation library
import validate - a template engine
import wire - a test runner
zuri test - a formatter
zuri fmt - a language server
zuri lsp - a bundler
zuri bundle - a package registry
zuri serve
The toolchain
Built in, not bolted on.
Every tool below ships inside the zuri binary,
was designed alongside the language it serves, and is
documented in the same book. Install once, and the whole
lifecycle is a subcommand away.
Run anything
Scripts, packages and whole directories, with a REPL one bare zuri away.
Test runner
Every test file in a process of its own, as many at once as --jobs allows, with filters, timeouts and a report that points at the failing line.
Formatter
One house style for every Zuri file. --dry-run turns it into the check a commit hook or CI job runs.
Language server
Completion, navigation, diagnostics, refactoring and test running in every editor that speaks LSP.
Editor Support zuri installPackage manager
Version ranges resolved into a lockfile with exactly one version of every package, zuri publish to share your own, and a --dry-run on every package command that changes a project.
Package registry
Nyssa, a complete package repository with its own website, for a team, a company or the public.
Running Nyssa zuri bundleBundler
Your program and the runtime as a directory, an archive, a single executable or a macOS app, for every platform, from one machine.
Bundles zuri upgradeSelf-update
Moves the installation to a newer release, verified against its published checksum before anything is replaced.
Upgrading zuri <yours>Your own commands
A project or a package adds commands to zuri, written in Zuri, listed in zuri --help beside the built-in ones.
Everything speaks the same dialect
This is a whole application.
Every import below is in the box: nothing was installed, nothing was resolved, nothing was configured.
import http
import validate
import log
var signups = validate.schema({
name: validate.required().string().max_length(60),
email: validate.required().string().email(),
})
var server = http.server(3000)
server.post('/signup', @(request, response) {
var body = request.json_body() or {}
var checked = signups.check(body)
if !checked.valid {
return response.json({ errors: checked.errors }, 422)
}
log.info('signed up ${body.email}')
response.json({ ok: true }, 201)
})
server.listen()
~/signup $ zuri run signup.zu
2026-10-09T21:43:40+01:00 INFO [signup]: signed up ada@example.com
$ curl -X POST localhost:3000/signup -d '{"name":"Ada","email":"ada@example.com"}'
{"ok":true}
$ curl -X POST localhost:3000/signup -d '{"name":"Ada","email":"nope"}'
{"errors":[{"field":"email","message":"The email field must be a valid email address."}]}
There is one binary and the library that ships beside it. Zuri has first-class language-level support for package management and vendoring, so the code you bring in from outside needs no third-party tooling either.
Every module was built by the same hands. The naming is the same. The error types are the same. The documentation is one book.
Batteries included
The standard library is the framework.
Each program here runs exactly as shown, on a fresh install, with nothing added. The output beside it is what it printed.
import sql
var db = sql.open('sqlite://shop.db')
db.exec('create table orders (id integer primary key, item text, total real)')
db.insert('orders', { item: 'Kettle', total: 34.5 })
db.insert('orders', { item: 'Teapot', total: 21 })
for order in db.query('select * from orders where total > ?', [25]) {
echo '${order.item}: ${order.total}'
}
Kettle: 34.5
SQLite, PostgreSQL, MySQL and MariaDB behind one interface, with pools, transactions, streaming and prepared statements. Changing engines means changing the connection string. Read more
import wire
var page = '
<h1>{{ title|upper }}</h1>
<ul>
<li x-for="tasks" x-value="task">{{ task }}</li>
</ul>'
echo wire.render_string(page, {
title: 'Today',
tasks: ['Ship the release', '<script>alert(1)</script>'],
})
<h1>TODAY</h1>
<ul>
<li>Ship the release</li><li><script>alert(1)</script></li>
</ul>
Wire escapes every value for exactly where it lands in the page, so the script in that list arrives as text. Read more
import isolate
import hash
def digest(text) {
return hash.sha256(text)
}
# Each call runs on its own OS thread, with its own heap.
var digests = isolate.map(digest, ['alpha', 'beta', 'gamma'])
echo digests.map(@(d) => d[,12])
[8ed3f6ad685b, f44e64e75f39, be9d587defa1]
Isolates are real OS threads with heaps of their own, talking through channels. Every core works, and no lock is ever yours to forget. Read more
import bcrypt
import jwt
var secret = 'keep-this-out-of-the-repository'
var stored = bcrypt.hash('correct horse battery staple')
if bcrypt.compare('correct horse battery staple', stored) {
var token = jwt.sign({ user: 'ada' }, secret, { expires_in: 3600 })
var claims = jwt.verify(token, secret)
echo 'signed in as ${claims.user}'
}
signed in as ada
Password hashing, signed tokens, ciphers, digests and UUIDs, all in the standard library and all documented in one place. Read more
import imagine { Image }
Image(480, 270, '#1e3a5f')
.fill_circle(340, 90, 48, '#f99320')
.fill_rect(0, 190, 480, 80, '#0c9d96')
.fill_polygon([[40, 190], [150, 70], [260, 190]], '#744baa')
.fill_polygon([[180, 190], [270, 110], [360, 190]], '#fd5625')
.save('poster.png')
echo Image.open('poster.png').size()
{width: 480, height: 270}

Imagine decodes, draws, filters and encodes PNG, JPEG, WebP, AVIF, GIF and more. The picture beside the output is the file this program wrote. Read more
import ffi
var c = ffi.open(ffi.LIBC).declare('
typedef struct { int quot; int rem; } div_t;
div_t div(int numerator, int denominator);
size_t strlen(const char *text);
')
echo c.strlen('Hello from C')
echo c.div(17, 5)
12
{quot: 3, rem: 2}
Paste a C declaration and call it. Structs, callbacks across threads and libraries of your own all come through the same door. Read more
import test { * }
def subtotal(items) {
return items.reduce(@(total, item) => total + item.price * item.quantity, 0)
}
describe('subtotal', @{
it('is zero for an empty cart', @{
expect(subtotal([])).to_be(0)
})
it('adds up every line', @{
var cart = [{ price: 4, quantity: 2 }, { price: 10, quantity: 1 }]
expect(subtotal(cart)).to_be(18)
})
})
$ zuri test
zuri test 1 file in tests
PASS cart.test.zu 41ms 2 tests
1 file
2 passed • 2 total
time 44ms
import test gives you suites, expectations, spies and a report that names the line that failed. zuri test runs them. Read more
The standard library
Crafted by the same hands.
Every module is reachable with a bare import,
and every public name is documented in the
standard library reference,
generated from the library's own doc blocks.
- The web
httpserver, client, HTTP/2, WebSocketwiretemplateshtmlHTML5 parser, CSS selectorsurlmimemailSMTP, IMAP and POP3, clients and servers- Networking
netTCP, UDP, TLS, DTLS, pollingrpcJSON-RPC 2.0- Databases
sqlSQLite, PostgreSQL, MySQL, MariaDB, pools, transactions- Data formats
jsonyamltomlcsvstructbase64convert- Correctness
validatetypesenum- Security
cryptohashbcryptjwtuuid- The machine
osenvioargslogstatdatewith the IANA time zones- Concurrency
isolateOS threads, separate heaps, channels- Numbers and collections
matharraytyped arraysset- Archives
compressgzip, zlib, deflate, bzip2, brotli, zstd, lz4, tar, zip- Images and colour
imaginedecode, draw, filter, encodecolorscolour spaces, terminal colour- Interop
ffiC and Rust libraries, callbacks- Testing
testsuites, expectations, mocks and spies- Metaprogramming
zurilexer, parser, compiler, reflection
Because every one of them was designed together, moving between a database query, a hash and an HTTP response costs you no mental friction.
Ship it anywhere
Ship to machines that have never heard of Zuri.
zuri bundle packs your program, the packages it
uses, the standard library and the runtime into one thing
you can hand to anyone. It runs the same on every machine it
lands on, with nothing to install first.
--format exe--format app.tar.gz, or a .zip for Windows.--format dir- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- x86_64-apple-darwin
- aarch64-apple-darwin
- x86_64-pc-windows-msvc
Build for every one of them from the machine you are on with
zuri bundle --target all. Single-file macOS
bundles come out signed ad hoc, which is all an Apple silicon
Mac needs to run them, and the book walks through Developer
ID signing and notarization.
Bundles and upgrades
~/weather $ zuri bundle --format exe
Built /home/ada/weather/dist/weather-0.1.0-x86_64-unknown-linux-gnu
~/weather $ scp dist/weather-0.1.0-x86_64-unknown-linux-gnu server:weather
ada@server:~ $ zuri --version
bash: zuri: command not found
ada@server:~ $ ./weather
Lagos: 31°C, 87.8°F
Packages and Nyssa
Share code, and host it yourself.
When you share code, the package manager is already installed, and so is the registry it talks to.
~/thermo $ zuri publish
Publishing thermo 0.1.0 to https://pub.zurilang.org
Published thermo 0.1.0 (1.4 KiB, sha256:a682a47cda7a3a6bc9e4c2ee0ee5477b591d3030fa42d1cd305ff43d3b536e8d)
~/weather $ zuri install thermo
Resolving dependencies
Installing thermo 0.1.0
+ thermo 0.1.0
Installed 1 package.
- One version of every package, resolved from version ranges into a lockfile that
zuri restorereproduces exactly. - Every package command that changes a project shows exactly what it would do with
--dry-run. - Packages carry commands, and globally installed tools land on your PATH.
~ $ zuri serve
Nyssa is serving http://127.0.0.1:3000
listening on 127.0.0.1:3000, storage in /home/ada/.zuri/nyssa
- Nyssa is the registry in every installation: the API the package commands talk to, and a website for finding packages and reading their documentation.
- SQLite with nothing to set up, or PostgreSQL, MySQL and MariaDB through the same connection strings
sqlopens. - Ready for production: HTTPS, accounts and tokens, closed sign-up, a read-only mode, live backups and archive checks.
Language design
Dynamic, and it declines to be vague.
Zuri takes the expressive, familiar syntax of modern dynamic languages and adds deliberate structural controls.
Zuri features true access controls with sealed classes, function parameter type guards, private methods and modules, all enforced by the compiler. The point of each is the same. The things that are hard to see when reading code are the things the language refuses to let you get wrong.
Every error names what went wrong, where, and the line it happened on, whether it surfaces in your editor, at compile time or while the program runs.
class Account {
@new(owner: string) {
self.owner = owner
self._balance = 0
}
deposit(amount: number) {
self._balance += amount
return self._balance
}
}
var account = Account('ada')
Typed parameters are checked at the callaccount.deposit('50')
$ zuri run account.zu
Unhandled TypeError: deposit() expects parameter 'amount' (argument 1) to be a number, got string
--> /home/ada/account.zu:7
5 | }
6 |
> 7 | deposit(amount: number) {
8 | self._balance += amount
9 | return self._balance
Stack trace (most recent call last):
at deposit() /home/ada/account.zu:7
at @.script() /home/ada/account.zu:15
Classes are sealedaccount.nickname = 'countess'
$ zuri run account.zu
Unhandled PropertyError: undefined field 'nickname' on instance of 'Account'
--> /home/ada/account.zu:15
13 | var account = Account('ada')
14 |
> 15 | account.nickname = 'countess'
Stack trace (most recent call last):
at @.script() /home/ada/account.zu:15
Private stays private, before a line runsecho account._balance
$ zuri run account.zu
SyntaxError: '_balance' is private and can only be accessed via 'self' or 'parent'
--> /home/ada/account.zu:15:14
|
15 | echo account._balance
| ^
class Account { @new(owner: string) { self.owner = owner self._balance = 0 } deposit(amount: number) { self._balance += amount return self._balance }} var account = Account('ada')account.deposit('50')TypeError deposit() expects parameter 'amount' (argument 1) to be a number, got string account.mdeposit(amount: number)fownermto_string()
Editor support
Your editor already speaks Zuri.
zuri lsp is a full language server, written in
Zuri on the compiler's own parser, so what it reports is
what the runtime would do with the same code.
- CompletionMembers, names in scope and module exports, with the import added for you.
- Diagnostics as you typeWith the runtime's own messages, and quick fixes beside them.
- NavigationDefinitions, references, implementations, call and type hierarchies.
- RefactoringRename across the workspace, extract and inline variables and functions.
- Hover and signaturesDocumentation laid out the way the reference lays it out.
- Tests and formattingRun a test from the line it is on, and format a file or a selection.
Performance
Fast, without being asked.
A fast Just-In-Time compiler driven underneath by Cranelift,
the same engine that drives wasmtime, with
cutting edge specialization and optimized to production
workloads. The tiering happens on its own, on background
threads, with no annotations and no flags.
-
The interpreter
Your program starts at once, as bytecode, and every function begins here while the runtime watches what is hot.
-
Kebbi
The quick compiler turns a hot function into machine code that keeps values in registers, proving what it can about types before it compiles.
-
Bayelsa
The optimizing compiler builds the function as a graph of typed values, builds small functions into their callers, moves checks out of loops and bets on what the code has done so far.
Compiles never block
Both compilers run on background threads. A program carries on in the code it already has until the faster code is ready.
Every core, safely
Isolates run on real OS threads with heaps of their own, so parallel work needs no locks and shares no state by accident.
Native when it matters
Call straight into C and Rust libraries through ffi when a hot path already exists in native code.
Documentation
One book for the whole platform.
The Zuri Programming Language
The main text covers everything from installing to publishing, plus appendices covering the keywords, the operators and every method on every built-in type.
Read the bookThe Zuri Standard Library
The reference: every module, every public name, generated from the library's own doc blocks so a page and the code it describes can never disagree.
Look something upRoadmap
Already here, and what comes next.
Already here
- The language, complete and self-hosting enough to parse, compile and reflect on itself
- A tiered Just-In-Time compiler
- A standard library covering the whole stack
- SQLite, PostgreSQL, MySQL and MariaDB behind one interface
- A test runner and a formatter
- A language server for every editor
- Package management built into the runtime
- Nyssa, a self-hosted package registry in every installation
- Bundles that run where Zuri is not installed
- C and Rust interop
- Two books, one of them generated from the library itself
On the way, in the order it matters
- An ORM, so that persisting a model never means reaching outside the language.
- HTTP/3. The
httpmodule speaks HTTP/1.1 and HTTP/2 today.
AI involvement, on the record
AI wrote code in this repository, and how much, where, and how well it went are documented rather than glossed over. AI.md is that account, and it ends in a rule that contributors are held to.
The whole platform, one command away.
One command installs the runtime, the standard library and the whole toolchain. The book takes you from the first line to a full-stack application.
$ curl -fsSL https://zuri-lang.github.io/zuri/install.sh | sh
> powershell -c "irm https://zuri-lang.github.io/zuri/install.ps1 | iex"
$ git clone https://github.com/zuri-lang/zuri && cd zuri && cargo patch-crates && cargo build --release
Linux and macOS on x86-64 and ARM, and Windows on x86-64.
zuri upgrade keeps it current.
Installation guide