What is Vexil?
Vexil (Validated Exchange Language) is a schema definition language (SDL) in the tradition of Protocol Buffers and Cap'n Proto, distinguished by two properties:
Encoding semantics are part of the type system. The type u4 means exactly 4 bits on the wire — not "an integer that fits in 4 bits." The annotation @varint on a u64 field changes the wire encoding to unsigned LEB128. The schema is the wire contract, not just the shape contract.
The schema is the single source of truth. Each schema has a deterministic BLAKE3 hash. That hash is embedded in generated code as a compile-time constant. A mismatch between the schema a sender compiled against and the schema a receiver compiled against is detectable at runtime, before any data corruption occurs.
Quick Look
A sensor telemetry schema:
namespace sensor.packet
enum SensorKind : u8 {
Temperature @0
Humidity @1
Pressure @2
Light @3
}
message SensorReading {
channel @0 : u4 # 4 bits — values 0..15
kind @1 : SensorKind
value @2 : u16
sequence @3 : u32 @varint # variable-length encoding
delta_ts @4 : i32 @zigzag # signed, ZigZag-encoded
}
Generated Rust code encodes and decodes in a few lines:
use ;
let reading = SensorReading ;
let mut w = new;
reading.pack.unwrap;
let bytes = w.finish; // compact, bit-packed
let mut r = new;
let decoded = unpack.unwrap;
assert_eq!;
The same schema generates TypeScript with identical wire output:
import { BitWriter, BitReader } from '@vexil/runtime';
const w = new BitWriter();
encodeSensorReading({
channel: 0, kind: 'Temperature',
value: 2350, sequence: 1, delta_ts: -50,
}, w);
const bytes = w.finish(); // identical bytes as Rust
const r = new BitReader(bytes);
const decoded = decodeSensorReading(r);
// decoded.value === 2350
Features
- Sub-byte integer types —
u1..u7andi1..i7; each occupies exactly N bits on the wire with LSB-first bit packing - Encoding annotations —
@varint(unsigned LEB128),@zigzag(ZigZag + LEB128),@delta(delta from previous value) directly in the schema - Rich type vocabulary —
message,enum,flags,union,newtype, andconfigdeclarations - Schema versioning — BLAKE3 hash of the canonical schema form; mismatch is detectable at the protocol boundary
- Multi-language code generation — Rust and TypeScript backends from the same schema, with byte-identical wire output verified by compliance vectors
- Deterministic encoding — same data always produces identical bytes, enabling content addressing and replay detection
- Structured error model — every invalid input produces a distinct error class with file, line, column, and a human-readable description
- 82-file conformance corpus — 26 valid schemas and 56 invalid schemas; a conformant implementation must accept all valid and reject all invalid
Comparison
| Vexil | Protobuf | Cap'n Proto | FlatBuffers | |
|---|---|---|---|---|
Sub-byte types (u1..u63) |
Yes | — | — | — |
| Encoding annotations in schema | Yes | — | — | — |
| Schema hash (mismatch detection) | BLAKE3 | — | — | — |
| LSB-first bit packing | Yes | — | — | — |
| Self-describing wire format | No | Optional | No | Optional |
| Zero-copy decode | No | No | Yes | Yes |
| Deterministic encoding | Yes | No (maps) | No (padding) | No (vtables) |
| Schema evolution | Yes | Yes | Yes | Yes |
| Language targets | Rust, TypeScript | Many | Many | Many |
Installation
cargo install
Pre-built binaries
Pre-built binaries for Linux, Windows, and macOS are available on the Releases page.
From source
Requires Rust 1.94 or later (install via rustup).
# Binary is at target/release/vexilc
Usage
CLI
Check a schema for errors:
Generate code from a schema:
Compile a multi-file project:
Errors are rendered with source spans and structured diagnostics:
Error: duplicate field name
--> schema.vexil:8:5
|
8 | value: u32,
| ^^^^^ field "value" was already declared on line 5
Library
Add vexil-lang to your Cargo.toml:
[]
= "0.2"
Parse and compile a schema programmatically:
let result = compile;
if result.diagnostics.iter.any
if let Some = result.compiled
Repository Structure
spec/
vexil-spec.md # Language specification (normative, §1-§14)
vexil-grammar.peg # Formal PEG grammar derived from spec
corpus/
valid/ # 26 conformant schemas — all must be accepted
invalid/ # 56 invalid schemas — all must be rejected
projects/ # Multi-file project fixtures for integration tests
compliance/
vectors/ # Golden byte vectors (JSON) — cross-implementation contract
crates/
vexil-lang/ # Compiler library: lexer, parser, IR, type checker, canonical hash
vexil-codegen-rust/ # Rust code generation backend
vexil-codegen-ts/ # TypeScript code generation backend
vexil-runtime/ # Rust runtime: bit I/O, Pack/Unpack traits, LEB128, ZigZag
vexilc/ # CLI frontend with ariadne error rendering
vexil-store/ # .vx text format and .vxb binary format for schemas and data
vexil-bench/ # Encode/decode benchmarks (Criterion)
packages/
runtime-ts/ # @vexil/runtime npm package: TypeScript BitWriter/BitReader
examples/
cross-language/ # Rust <-> Node.js interop demo
system-monitor/ # Real-time dashboard: Rust -> browser via Vexil WebSocket
Documentation
- Language Specification
- FAQ
- Examples
- Limitations and Gaps
- API reference: vexil-lang · vexil-runtime · vexil-codegen-rust · vexil-codegen-ts · vexil-store
Contributing
Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request. For architectural decisions, language changes, and protocol modifications, see GOVERNANCE.md.
License
Licensed under either of
at your option.