| name | alef |
| description | Generate fully-typed polyglot language bindings for Rust libraries using Alef. Use when configuring alef.toml, running alef CLI commands, writing e2e test fixtures, debugging binding generation, or setting up CI/CD for multi-language Rust libraries. Covers 16 language backends (Python, TypeScript, WASM, Ruby, PHP, Go, Java, C#, Kotlin, Elixir, Gleam, R, Swift, Dart, Zig, C), DTO styles, trait bridges, adapter patterns, version sync, and pre-commit hooks. |
| license | MIT |
| metadata | {"author":"kreuzberg-dev","version":"1.0","repository":"https://github.com/kreuzberg-dev/alef"} |
Alef Polyglot Binding Generator
Alef generates fully-typed, lint-clean language bindings for Rust libraries across 16 languages from a single TOML config file. It handles the entire pipeline: API extraction, code generation, type stubs, package scaffolding, build orchestration, version sync, and e2e test generation. Trait bridges (foreign-language objects implementing Rust traits) are emitted for all 16 backends.
Use this skill when:
- Configuring
alef.toml for a new or existing Rust library
- Running alef CLI commands (generate, build, test, verify, e2e)
- Writing or debugging e2e test fixtures (JSON fixtures -> multi-language test suites)
- Adding a new language backend to a project
- Setting up CI/CD pipelines for polyglot Rust libraries
- Debugging binding generation issues (stale bindings, type mismatches, missing types)
- Configuring DTO styles, adapter patterns, or custom FFI bridges
- Deciding what to include/exclude in bindings for a Rust library
Installation
cargo binstall alef-cli
cargo install alef-cli
brew install kreuzberg-dev/tap/alef
git clone https://github.com/kreuzberg-dev/alef.git
cd alef && cargo install --path crates/alef-cli
Quick Start
1. Initialize
cd your-rust-crate
alef init --lang python,node,ruby,go
This creates alef.toml with your crate's configuration.
2. Generate Bindings
alef generate
alef generate --lang node
alef generate --crate my-library
alef generate --clean
When your workspace has multiple crates, use --crate <name> (repeatable) to restrict operations to a subset. If omitted, all crates are processed.
3. Build
alef build
alef build --lang python
alef build --release
4. Test
alef test
alef test --e2e
alef test --lang python,go
5. Verify (CI)
alef verify --exit-code
alef diff
6. Publish (release)
alef publish prepare --target x86_64-unknown-linux-gnu
alef publish build --target x86_64-unknown-linux-gnu --use-cross
alef publish package --output dist
alef publish validate
Minimal Configuration
Alef now uses a multi-crate schema. A [workspace] section defines shared defaults; each [[crates]] entry describes one independently published binding package:
[workspace]
languages = ["python", "node", "go", "java"]
[[crates]]
name = "my-library"
sources = ["src/lib.rs", "src/types.rs"]
[crates.output]
python = "crates/my-library-py/src/"
node = "crates/my-library-node/src/"
ffi = "crates/my-library-ffi/src/"
[workspace.tools]
python_package_manager = "uv"
node_package_manager = "pnpm"
[crates.python]
module_name = "_my_library"
[crates.node]
package_name = "@myorg/my-library"
[workspace.dto]
python = "dataclass"
node = "interface"
Legacy alef.toml Migration
If you have an existing alef.toml in the old single-crate schema (with top-level [crate], languages, etc.), run this migration command:
alef migrate --write
This rewrites your config to the new [workspace] + [[crates]] layout atomically. The migration handles naming, path updates, and other structural changes automatically. Review the output and fix any path issues if needed, then continue as normal.
alef.toml is validated at load time. Custom [lint|test|build_commands|setup|update|clean].<lang> tables that override a main command must declare a precondition; redundant fields (value identical to the built-in default) emit a tracing::warn! so the file stays minimal.
Supported Languages
| Language | Framework | DTO Styles |
|---|
| Python | PyO3 | dataclass, typed-dict, pydantic, msgspec |
| TypeScript/Node.js | NAPI-RS | interface, zod |
| WebAssembly | wasm-bindgen | -- |
| Ruby | Magnus | struct, dry-struct, data |
| PHP | ext-php-rs | readonly-class, array |
| Go | cgo + C FFI | struct |
| Java | Panama FFM | record |
| C# | P/Invoke | record |
| Elixir | Rustler | struct, typed-struct |
| R | extendr | list, r6 |
| C | cbindgen | -- |
Common Workflows
Add a New Language
- Add the language to
languages array in alef.toml
- Add output directory in
[output]
- Add language-specific config section (e.g.,
[python])
- Run
alef generate && alef scaffold
Update After Changing Rust API
alef all
alef verify --exit-code
Run E2E Tests
alef e2e generate
alef test --e2e
Version Bump
alef sync-versions --bump patch
alef sync-versions --set 1.2.3
alef sync-versions
Pre-commit Hooks
Alef provides pre-commit hooks for consumer repos:
repos:
- repo: https://github.com/kreuzberg-dev/alef
rev: v1.1.11
hooks:
- id: alef-verify
- id: alef-generate
- id: alef-readme
Local-install Workflow
When iterating on alef's code generation logic (changes to alef-codegen, alef-backend-*, or alef-cli), install the local binary instead of using the published crates.io version:
cargo install --path crates/alef-cli --force
The resulting ~/.cargo/bin/alef shadows any crates.io install until replaced. After every meaningful change to the codegen pipeline, reinstall with the same command so the binary picks up your latest changes. Only reinstall from crates.io (via cargo binstall alef-cli or cargo install alef-cli) after a published release.
Caching
Alef uses blake3-based content hashing to skip regeneration when inputs haven't changed. The cache lives in .alef/ (gitignored).
alef cache status
alef cache clear
Verify (per-file source+output, idempotent across alef versions)
alef verify is a pure read+strip+rehash+compare. The alef:hash:<hex> line in every generated file is a per-file fingerprint of the rust sources and the on-disk byte content:
alef:hash:<hex> = blake3( sources_hash || file_content_without_hash_line )
sources_hash = blake3( sorted(rust_source_files) )
alef generate writes whitespace-normalised codegen output and finalises the hash after the optional formatter pass (--format) has run, so the on-disk hash always describes the on-disk byte content. alef verify reads each alef-headered file, strips the alef:hash: line, recomputes the same hash, and compares — no regeneration, no writes. Without --format, alef generate does not invoke any formatter; if you keep formatters in pre-commit hooks, run alef fmt (or alef generate --format) before committing so the hash matches the formatted bytes.
The hash deliberately does not include the alef CLI version or alef.toml. Bumping the alef CLI on a tagged repo does not by itself flag any file as stale; verify only goes red when (a) a crate's sources rust file changed, (b) an alef-generated file was edited or mutated by something post-format, or (c) alef generate would now produce a different file body. The IR cache (.alef/<crate>/ir.json) keys on sources_hash alone — pass --clean to bust it when the alef extractor itself has changed.
--lang, --compile, --lint flags on verify are accepted for backwards compatibility but ignored — verify is a per-file hash compare. Use alef build / alef lint / alef test for the per-language checks those flags used to imply.
See references/cli-reference.md#alef-verify for the full mental model.
Common Pitfalls
- Missing
ffi language: Go, Java, and C# require the C FFI layer. Add ffi to languages or it's implicitly included.
- Stale bindings after Rust changes: Run
alef generate or alef all after modifying your Rust source files.
- Wrong DTO style: Check
[workspace.dto] or [crates.dto]. Python typed-dict is read-only, dataclass is mutable. Choose based on usage.
- Types not appearing: Check
[exclude]/[include] filters. Use alef extract -o /dev/stdout | jq to inspect the IR.
- Version mismatch: Always use
alef sync-versions instead of manually editing package manifests.
- Opaque vs transparent types: Types with private fields or complex generics need
[crates.opaque_types] config.
Additional References