A programming language for AI-written business systems. Write logic in ZX (TypeScript-like), wire it together in RX (XML), and compile to a native executable.
CLI · RX Reference · Compiler · Feature Reference · Design
A checkout flow. RX is the map — every step, and what flows between them, in one file:
<!-- checkout.rx -->
<Module>
<Call fn="subtotal" in={$in.items} />
<Call fn="discount" in={{subtotal: $ctx.subtotal, coupon: $in.coupon}} />
<Call fn="shipping" in={{amount: $ctx.discount, region: $in.region}} />
<Call fn="total" in={{amount: $ctx.discount, shipping: $ctx.shipping}} />
<Return value={$ctx.total} />
</Module>ZX holds the logic — each step is one typed function in its own file:
// shipping.zx
export type Input = {
amount: u64
region: string
}
export type Output = u64
export default function (in: Input): Output {
return match {
in.amount >= 10000 => 0,
in.region == "remote" => 1500,
_ => 600
}
}Call no longer accepts out. Each non-void call automatically exposes its result as $ctx.<filename>: discount.zx becomes $ctx.discount. The name comes from the target filename without its suffix; Call has no name attribute. RX attributes assemble data and simple expressions; function calls and collection logic belong in ZX.
The complete checkout example includes every referenced function. From that directory, build and run:
zxc build checkout.rx --out build/checkout
./build/checkout '{"items":[{"price":4500,"quantity":2},{"price":3000,"quantity":1}],"coupon":"SAVE10","region":"remote"}'
# {"payable":10800,"shipping":0}To change the shipping rule, an AI only needs shipping.zx — checkout.rx already tells it what goes in and what comes out. The compiler infers types across every Call, so a mismatched edit fails at build time, not in production.
- Structure you can read — RX files are the call graph and data flow. No digging through function bodies.
- Boundaries the compiler enforces — typed
Input/Outputper unit, ownership checks, and a dependency graph that must be acyclic. - Small, local changes — change a rule in one
.zxfile; the RX wiring stays untouched. - Native output — ZX compiles to Zig; release packages embed the Zig toolchain, and
-no-zigpackages use the Zig named byZXC_ZIG.
flowchart LR
RX[".rx modules"] --> Graph["Dependency graph<br/>acyclic check"]
ZX[".zx functions"] --> Check["Type & ownership<br/>check"]
Graph --> Infer["Type inference<br/>across Call"]
Check --> Infer
Infer --> IR["IR"] --> Zig["Zig"] --> Bin["Native executable"]
Every .rx and .zx file in the entry's closure is loaded, checked and type-inferred together, then lowered to Zig. zxc embeds the official Zig toolchain, so ordinary native builds need no separate Zig installation. Contract verification requires an external SMT solver.
A module is just another unit. Call.module replaces the former Call.service attribute. Call it with module and branch on its result:
<!-- order.rx -->
<Module>
<Call module="checkout" in={$in} />
<Switch on={$ctx.checkout.shipping}>
<Case value={0}>
<Return value={{payable: $ctx.checkout.payable, free_shipping: true}} />
</Case>
</Switch>
<Return value={{payable: $ctx.checkout.payable, free_shipping: false}} />
</Module>Functions form modules, modules form larger modules, and every level follows the same rules. Cycles are rejected anywhere in the graph.
Add requires / ensures to a ZX function and prove it with an SMT solver:
// bounded_discount.zx
export type Input = {
price: u64
discount: u64
}
export type Output = u64
export default function (in: Input): Output
requires (in.discount <= in.price)
ensures (out <= in.price)
{
return in.price - in.discount
}zxc verify bounded_discount.zx --solver z3 --out bounded_discount.smt2
# verified: entry paths satisfy supported safety obligations and reached contractsThe command assumes Z3 is on PATH; otherwise pass its executable path to --solver.
Remove the requires and verification fails with a concrete counterexample: the subtraction can underflow. Verification covers fixed-width integers, booleans, static objects and branches.
| Tag | Purpose |
|---|---|
<Module> |
Root of an .rx file; the module is identified by its file path |
<Call> |
Call a ZX function (fn) or another RX module (module) |
<Return> |
Return a value from the module |
<Task> |
Group steps; optional out={...} exposes $ctx.task.<name> |
<Switch> |
Branch on a value |
<Case> |
A branch of Switch matching one value |
<Default> |
The fallback branch of Switch |
<Import> |
Declare a module dependency without calling it |
<Parallel> |
Run supported pure Call/Task branches concurrently |
<Emit> |
Reserved event syntax; execution is not implemented |
<Store> |
Define application-lifetime shared memory in *.store.rx |
<Object> |
A state object inside Store |
<Field> |
A typed field with an initial value inside Object |
<Gateway> |
Expose modules over HTTP in *.gateway.rx |
<Group> |
A route prefix inside Gateway |
<Route> |
Map a path and method to a module |
See the RX Reference for attributes and nesting rules.
| Area | What ZX offers |
|---|---|
| Units | One default-exported function per file, with typed Input and Output |
| Types | u8–u64, i32, i64, f32, f64, bool, string, objects, enums, T? optionals, T[] lists, [A, B] tuples |
| Bindings | const only, destructuring and spread; no semicolons |
| Control flow | if, switch, match and loop expressions |
| Collections | Capture-free map, filter, reduce |
| Errors | const [err, res] = try expression; finite error sets and checked success narrowing |
| Concurrency | async expression, await task, cancel task and parallel({name: () => expression}) |
| Ownership | No pointers and no clone; the compiler decides how values move |
| Imports | Relative paths, @/ project paths and packages; imports must be acyclic |
| Standard lib | Encoding, crypto, paths, URLs, compression, target info, files, processes and HTTP; see the module index |
| Native code | Explicitly declared zig: and c: interfaces |
| Contracts | requires / ensures, proven with an SMT solver |
See typed errors and ZX async and parallel for examples. A task handle can be consumed at most once by await or cancel. cancel requests cooperative cancellation and waits for the task to finish. Task execution requires an I/O-capable host.
| Command | Description |
|---|---|
zxc build <entry> --out <path> |
Build a native executable from an .rx or .zx entry |
zxc build <entry> --watch |
Rebuild whenever sources or dependencies change |
zxc build <entry> --watch --run |
Restart the native app after successful rebuilds |
zxc build <entry> --mode lib --out <dir> |
Publish a library for zxc and Zig consumers |
zxc build ... --target --cpu --optimize |
Cross-compile and tune the output |
zxc build ... --asm <file> |
Also write the generated assembly |
zxc <file.zx> --out <file.zig> |
Emit Zig source only |
zxc fmt <source> |
Format ZX, RX or package configuration |
zxc verify <file.zx> --solver <z3> |
Prove contracts and safety obligations |
zxc fpga <file.zx> --out <file.sv> |
Generate a SystemVerilog kernel |
zxc check-rx --entry <file.rx> |
Check RX structure and call targets |
zxc pkg install |
Resolve and install dependencies from pkg.yaml |
See the CLI docs for every option.