Alef: generating native bindings for a Rust library across 16 languages
Generate fully-typed, lint-clean language bindings for Rust libraries across 16 languages
At a glance
- What is it?
- Alef is a binding generator that reads one alef.toml in a Rust workspace and emits language-native packages, type stubs, docs and e2e fixtures for targets from PyO3 to NAPI-RS and cgo. Here is what the configuration actually controls, and where the approach stops fitting.
- Who is it for?
- Adopt Alef if your library lives in a Rust workspace and you need several language packages kept in step from one configuration; the multi-crate schema, the per-language override sections and the staleness hashes are the parts that earn their keep. Do not adopt it if you only ship one binding and hand-tune it, or if the language you need is not among the canonical slugs.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Alef solves for Rust library authors
Writing one binding is a weekend. Writing and keeping six is a maintenance tax that never stops. Each language has its own bridge crate, its own package manifest, its own type stub format, and its own idea of how an error should cross the boundary. Alef's answer is to extract the Rust API surface once into an intermediate representation and then drive every enabled target from that single extraction.
The README describes it as the polyglot binding generator behind the xberg.io ecosystem, and the framing matters: this is a tool built to serve a set of libraries that ship across many languages, not a general-purpose code generator. The unit of configuration is the Rust workspace, and the unit of output is a published package per language.
Who is it for? Maintainers of a Rust core who already know they will publish to more than two ecosystems. If you are shipping a single Python extension and nothing else, the configuration surface here is larger than the problem.
The extraction and emission pipeline
The pipeline has a visible seam. `alef extract` turns Rust source into Alef IR JSON; `alef generate` consumes that and writes bindings, service API wrappers, public API wrappers and type stubs. Everything downstream, including scaffolding, READMEs, docs and release metadata, reads from the same configured crate list.
Configuration lives in `alef.toml` and uses a multi-crate schema. `[workspace]` holds shared target languages, tool preferences and pipeline defaults. Each `[[crates]]` entry describes one Rust API surface that becomes one or more published packages, with `sources` pointing at the Rust files and `version_from` pointing at a manifest. Per-language behaviour goes in `[crates.<language>]` sections, which is where module names, package names, feature flags, output paths, field naming and dependency extras are customized.
Two ownership rules are stated plainly and are worth internalizing. Generated binding files carry Alef hashes and are overwritten by generation commands. Scaffolded package files are generated once unless a command explicitly opts into overwrite. Generated README and API doc files belong to `alef readme` and `alef docs`. That split is the reason the staleness checks exist: Alef caches inputs, embeds generation hashes, and can tell you whether generated files are current.
Installing Alef and generating a first binding set
Alef requires Rust 1.88 or newer according to the README, while the crate manifest declares `rust-version = "1.89"`. Treat the manifest as the stricter of the two. Installation is a normal cargo install, with a binary-install path for cargo-binstall users.
cargo install alef --lockedIf you already use cargo-binstall, the crate publishes binary metadata, so this pulls a prebuilt binary instead of compiling:
cargo binstall alefFor a new project, `alef init` writes the initial config and first generated files. The README gives this example, restricting generation to three targets:
alef init --lang python,node,ffiIf you would rather write the config yourself, the README's quick start uses a workspace block plus one crate entry. Note that the example pins `alef_version` to `0.24.12`, which is far behind the `0.88.0` in the repository manifest; set the version you actually run.
[workspace]
languages = ["python", "node", "ffi", "go"]
alef_version = "0.24.12"
[[crates]]
name = "sample_core"
sources = ["src/lib.rs"]
version_from = "Cargo.toml"With the config in place, the README's sequence runs generation, scaffolding, documentation and a verification pass. `alef verify` is the step that checks whether the generated output matches the current source.
alef generate --format
alef scaffold
alef readme
alef docs --output docs/reference
alef verifyFor a full local pass in one command, `alef all --format` runs the whole chain. Both `--lang python,node` and `--crate <name>` narrow any command to selected targets or a single configured crate, which is what you want while iterating on one language instead of regenerating everything.
Where the generated bindings stop being yours
The overwrite rule is the sharpest constraint in the design. Binding files are regenerated, so any hand edit is lost on the next `alef generate`. That is a defensible choice for a generator, and it is also the point where teams get burned: a small fix applied directly to a PyO3 wrapper survives until someone runs the pipeline.
Scaffolded package files behave differently and are generated once, which means the manifest for a language package is yours to edit after the first pass. The two rules together create an asymmetry you have to hold in your head: the binding layer is generated, the packaging layer is semi-manual.
The extension system exists for the gap in between. Domain-specific generation logic can be shipped three ways. A linked extension implements `alef::Extension` in a consumer crate and exposes a thin CLI binary that calls `alef::run_with_extensions(vec![Box::new(MyDomainExtension)])`. A dynamic extension loads a compiled `.so`, `.dylib` or `.dll` that declares a C-ABI factory, configured with a `[[extensions.dylib]]` block and a `path` key. A template-only extension declares `[[extensions.template]]` blocks pointing at Jinja templates and needs no Rust at all. The README calls the linked form the recommended one for frameworks that generate an HTTP service API, and that ranking is sensible: it is the only variant with full type safety.
If your customization does not fit any of those three shapes, Alef is the wrong tool. There is no documented escape hatch for editing generated output in place.
Supported targets and the cost of breadth
The target list is long: Python via PyO3 with type stubs, TypeScript and Node.js via NAPI-RS with `.d.ts` output, WebAssembly via wasm-bindgen, Ruby via Magnus, a native PHP extension, Elixir via Rustler, R via extendr, Go via cgo over the generated C FFI layer, Java and Kotlin over the generated native library, Kotlin Android with JNI shims, C# via P/Invoke, Dart and Flutter via flutter_rust_bridge, Swift, Zig over the C ABI, Gleam backed by Rustler, plus raw C FFI and a JNI shim crate.
Breadth here is not free. Each target pulls in a different build toolchain, and the README documents the generation side rather than the environment setup for each language. Nothing in the repository's README describes what a machine needs installed to build the Kotlin Android AAR or the PHP extension. That is the practical cost: Alef standardizes how bindings are produced, not what your CI image must contain.
The canonical slugs are the vocabulary you configure against: `python`, `node`, `wasm`, `ruby`, `php`, `elixir`, `r`, `go`, `java`, `csharp`, `kotlin`, `kotlin_android`, `swift`, `dart`, `gleam`, `zig`, `ffi`, `jni`. If your language is not on that list, the extension surface is the only route, and a template-only extension writing a new target from scratch is a substantial project.
Alternatives and how their approach differs
The closest comparison is UniFFI, which also generates bindings from a Rust API definition, but it starts from a separate interface definition file you maintain by hand rather than extracting the surface from your Rust source. That difference shows up in daily work: with UniFFI the interface file is a second source of truth you keep in sync; with Alef the Rust source is the input and `alef verify` exists to tell you when the generated output has drifted. UniFFI's target set is narrower and its per-target customization is expressed differently.
A second alternative is writing each binding by hand with the bridge crate directly, PyO3 for Python, NAPI-RS for Node, and so on. You get complete control over the wrapper layer and no configuration schema to learn. You also own every packaging manifest, every type stub, and every documentation file for every language, which is exactly the work Alef is trying to remove.
A third option is cbindgen plus hand-written FFI wrappers in each host language. That produces a C header from Rust and leaves the ergonomic layer to you. It is lighter than Alef and it does not attempt type stubs, e2e fixtures or release metadata. If your needs are a C ABI and nothing more, `ffi` as a single Alef target covers that without the rest.
Maintenance, versioning and licence
The repository is not archived and the last push was on 2026-08-29, the same day as the v0.76.0 release. Three releases landed that day: v0.74.0, v0.75.0 and v0.76.0. That cadence, combined with a crate version of `0.88.0` in the manifest, tells you the project moves quickly and that the version you pin matters. The README's own quick-start example pins `alef_version` to `0.24.12`, so a config copied from the documentation will reference a version far behind current. Check that key against the binary you installed.
Upgrade cost has two parts. The config schema is the first: the README refers to "the current multi-crate schema", which implies earlier schemas existed, so a major bump may require editing `alef.toml`. The second is the generated output itself. Because bindings are overwritten and carry generation hashes, an upgrade followed by `alef generate` can produce a large diff across every language package at once. Running `alef verify` before and after an upgrade is the cheap way to see the scope.
The licence is MIT. That permits commercial use and redistribution, and it requires that the copyright notice and permission notice travel with copies or substantial portions of the software. This is a summary of the identifier in the repository, not legal advice; if you redistribute generated bindings inside a proprietary product, have your own counsel read the LICENSE file rather than this paragraph.
Editorial conclusion
Adopt Alef if your library lives in a Rust workspace and you need several language packages kept in step from one configuration; the multi-crate schema, the per-language override sections and the staleness hashes are the parts that earn their keep. Do not adopt it if you only ship one binding and hand-tune it, or if the language you need is not among the canonical slugs. Before committing, verify three things against your own workspace: that the language you care about is listed under Supported Targets, that the pinned Rust toolchain in rust-toolchain.toml satisfies the rust-version in Cargo.toml, and that you are willing to treat generated binding files as disposable, because generation commands overwrite them.
Frequently asked questions
What is Alef and which languages does it support?
Alef is a binding generator that extracts a Rust API surface and emits language-native bindings, package scaffolding, type stubs, READMEs, API docs, e2e tests and release metadata from one alef.toml. The canonical language slugs are python, node, wasm, ruby, php, elixir, r, go, java, csharp, kotlin, kotlin_android, swift, dart, gleam, zig, ffi and jni.
How do I install Alef?
The README gives two options: cargo install alef --locked, or cargo binstall alef if you use cargo-binstall and want the published binary-install metadata. The README states that Alef requires Rust 1.88 or newer, while the crate manifest declares rust-version 1.89.
Does Alef work with a multi-crate Rust workspace?
Yes. The configuration uses a multi-crate schema where [workspace] holds shared target languages and pipeline defaults, and each [[crates]] entry describes one Rust API surface that becomes one or more published packages. Commands accept --crate <name> to restrict them to a single configured crate.
Can I edit the generated binding files by hand?
The README states that generated binding files carry Alef hashes and are overwritten by generation commands. Scaffolded package files are generated once unless a command explicitly opts into overwrite behavior, and generated README and API doc files are owned by alef readme and alef docs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/xberg-io-alef)