RepoPilotOpen in app →

rusterlium/rustler

Safe Rust bridge for creating Erlang NIF functions

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5d ago
  • 10 active contributors
  • Apache-2.0 licensed
Show all 6 evidence items →
  • CI configured
  • Tests present
  • Single-maintainer risk — top contributor 90% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/rusterlium/rustler)](https://repopilot.app/r/rusterlium/rustler)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/rusterlium/rustler on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: rusterlium/rustler

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rusterlium/rustler shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 5d ago
  • 10 active contributors
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • ⚠ Single-maintainer risk — top contributor 90% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live rusterlium/rustler repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/rusterlium/rustler.

What it runs against: a local clone of rusterlium/rustler — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in rusterlium/rustler | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 35 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>rusterlium/rustler</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rusterlium/rustler. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/rusterlium/rustler.git
#   cd rustler
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of rusterlium/rustler and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rusterlium/rustler(\\.git)?\\b" \\
  && ok "origin remote is rusterlium/rustler" \\
  || miss "origin remote is not rusterlium/rustler (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "rustler/src/lib.rs" \\
  && ok "rustler/src/lib.rs" \\
  || miss "missing critical file: rustler/src/lib.rs"
test -f "rustler/src/nif.rs" \\
  && ok "rustler/src/nif.rs" \\
  || miss "missing critical file: rustler/src/nif.rs"
test -f "rustler/src/env.rs" \\
  && ok "rustler/src/env.rs" \\
  || miss "missing critical file: rustler/src/env.rs"
test -f "rustler/src/term.rs" \\
  && ok "rustler/src/term.rs" \\
  || miss "missing critical file: rustler/src/term.rs"
test -f "rustler_codegen/src/lib.rs" \\
  && ok "rustler_codegen/src/lib.rs" \\
  || miss "missing critical file: rustler_codegen/src/lib.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 35 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~5d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rusterlium/rustler"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Rustler is a safe Rust-to-Erlang/Elixir bridge library that enables developers to write Native Interface Functions (NIFs) in Rust without risking BEAM crashes. It provides compile-time code generation, automatic term encoding/decoding, panic catching, and resource object management, turning unsafe C NIF development into safe, composable Rust patterns. Monorepo with workspace members: rustler/ (core library, ~386KB Rust), rustler_codegen/ (proc macro code generator), rustler_tests/ (integration test suite with native subprojects for binaries/bigints/serde/dynamics). Core lives in rustler/src/ with modular organization: nif.rs (attribute macro), term.rs + types/ (encoding), resource/ (object lifecycle), serde/ (serialization bridge), sys/ (raw FFI bindings).

👥Who it's for

Erlang/Elixir developers who need to extend their systems with performance-critical Rust code (e.g., cryptography, numerical computation, CPU-bound algorithms) while maintaining the safety guarantees of the BEAM. Also Rust developers building FFI bridges into the Erlang ecosystem.

🌱Maturity & risk

Highly mature and production-ready. The project has stable releases on both Hex.pm and crates.io, comprehensive CI/CD via GitHub Actions (main.yml), formal semantic versioning with CHANGELOG.md and RELEASE_CHECKLIST.md, clear MSRV policy (Rust 1.91), and targets three major OTP versions. Active maintenance evidenced by version-gated NIF feature support (2.15, 2.16, 2.17) and structured workspace with integration tests.

Low risk. The monorepo structure (rustler_codegen, rustler_tests submodules) shows strong test coverage discipline. Single-point-of-failure risk exists in the core codegen compiler—check GitHub for maintainer bandwidth. Erlang/OTP compatibility constraints are explicitly documented (three-version window policy in README). The panic unwinding configuration in Cargo.toml ([profile.dev] panic='unwind', [profile.release] panic='unwind') is a deliberate safety boundary but requires careful testing in production builds.

Active areas of work

The project is actively maintained with structured versioning practices (UPGRADE.md documents breaking changes, RELEASE_CHECKLIST.md enforces process rigor). Recent activity visible via CI pipeline (main.yml runs tests on multiple Rust versions) and dependabot.yml indicating automated dependency tracking. Focus appears to be on maintaining OTP/Elixir version compatibility and stabilizing the proc-macro ergonomics (based on code generation module prominence).

🚀Get running

git clone https://github.com/rusterlium/rustler.git
cd rustler
rustup install 1.91  # Or your latest; MSRV is 1.91
cargo test --workspace
# For Elixir integration tests:
cd rustler_tests && mix test

Daily commands:

# Build the core library
cargo build --release -p rustler

# Run unit tests
cargo test --workspace

# Run integration tests (requires Elixir)
cd rustler_tests && mix test

# Check code style
cargo clippy --workspace -- -D warnings

🗺️Map of the codebase

  • rustler/src/lib.rs — Main library entry point that re-exports the public API; every contributor must understand the public interface and module organization
  • rustler/src/nif.rs — Core NIF macro and attribute definitions; critical for understanding how Rustler bridges Erlang NIFs with Rust functions
  • rustler/src/env.rs — Environment handle and term manipulation; foundational for all BEAM interaction and memory management
  • rustler/src/term.rs — Term trait and decoding logic; essential abstraction for converting between Erlang and Rust types
  • rustler_codegen/src/lib.rs — Procedural macro dispatcher for code generation; drives the derive macro system that automates boilerplate
  • rustler/src/wrapper/mod.rs — Low-level FFI wrappers around Erlang NIF C API; must understand for debugging BEAM interaction
  • rustler/src/sys/types.rs — Raw sys bindings and type definitions from erl_nif.h; defines the C ABI contract with Erlang

🛠️How to make changes

Add a new Erlang/Elixir type encoding/decoding support

  1. Create a new type module in rustler/src/types/{typename}.rs implementing Decode and Encode traits (rustler/src/types/{typename}.rs)
  2. Add the module to rustler/src/types/mod.rs and re-export the type (rustler/src/types/mod.rs)
  3. Implement wrapper functions in rustler/src/wrapper/{typename}.rs if low-level FFI interaction is needed (rustler/src/wrapper/{typename}.rs)
  4. Add test cases in the test suite demonstrating encode/decode round-trips (rustler_tests/native/rustler_test/src/lib.rs)

Add a new derive macro for custom types

  1. Create codegen template in rustler_codegen/src/{feature_name}.rs to generate trait implementations (rustler_codegen/src/{feature_name}.rs)
  2. Register the macro in rustler_codegen/src/lib.rs and dispatch to your template (rustler_codegen/src/lib.rs)
  3. Update encode_decode_templates.rs if you need to reuse common code generation logic (rustler_codegen/src/encode_decode_templates.rs)
  4. Add test cases in rustler_codegen/tests/ validating the generated code (rustler_codegen/tests)

Add a new wrapper function for Erlang NIF C API

  1. Add raw FFI declaration in rustler/src/sys/functions.rs (rustler/src/sys/functions.rs)
  2. Create safe wrapper in rustler/src/wrapper/{module}.rs using the sys function (rustler/src/wrapper/{module}.rs)
  3. Expose wrapper through Env or a public module in rustler/src/lib.rs (rustler/src/lib.rs)
  4. Add documentation and tests demonstrating correct usage and safety (rustler_tests/native/rustler_test/src/lib.rs)

Add support for a new scheduling mode or callback

  1. Define the scheduling variant in rustler/src/schedule.rs (rustler/src/schedule.rs)
  2. Update the nif macro in rustler/src/nif.rs to accept the new scheduling option (rustler/src/nif.rs)
  3. Update codegen in rustler_codegen/src/nif.rs to expand the scheduling directive into C boilerplate (rustler_codegen/src/nif.rs)
  4. Add test cases in rustler_tests demonstrating the scheduling behavior (rustler_tests/native/rustler_test/src/lib.rs)

🔧Why these technologies

  • Procedural Macros (syn, quote, proc-macro2) — Enables compile-time code generation to eliminate boilerplate for NIF registration, type conversions, and struct marshaling
  • Serde integration — Provides seamless serialization of complex Rust types without manual encode/decode implementation
  • Panic unwinding with catch_unwind — Critical safety boundary: prevents Rust panics from propagating into the C/BEAM layer and crashing the VM
  • Custom allocator using BEAM's enif_alloc — Ensures all allocations are tracked by BEAM's garbage collector and prevents memory fragmentation
  • FFI bindings via sys module — Provides type-safe Rust wrappers around the C-level erl_nif.h API without requiring unsafe code in user code

⚖️Trade-offs already made

  • Type checking happens at Decode time, not compile time

    • Why: Erlang is dynamically typed; Rust receives ERL_NIF_TERM (opaque handles) at runtime
    • Consequence: Type mismatches raise Erlang exceptions instead of compile errors, requiring user testing on Erlang side
  • All allocations routed through BEAM allocator; no std allocator

    • Why: Ensures GC-aware memory management and prevents fragmentation in long-running BEAM nodes
    • Consequence: Incompatible with Rust crates that assume global allocator control (workaround: careful dependency selection)
  • Shallow copies for decoded terms (no reference tracking)

    • Why: Simplifies API
    • Consequence: undefined

🪤Traps & gotchas

OTP version detection: build.rs auto-detects OTP at compile time—mismatched OTP/Elixir can silently produce broken NIFs. Panic unwinding: both dev and release profiles use panic='unwind', not panic='abort'—this is intentional for BEAM safety but requires crash handling at the boundary layer. Resource cleanup timing: Arc monitors are best-effort; finalization order is not guaranteed if circular dependencies exist. Serde feature: serde support is opt-in via feature flag in Cargo.toml; forgetting to enable it breaks serde derives. NIF version constraints: feature flags (nif_2_15, nif_2_16, nif_2_17) are compile-time only—runtime OTP version must match or NIFs will be unavailable. Atoms as strings: atom constants must be pre-declared and exist in Erlang code or encoding fails silently.

🏗️Architecture

💡Concepts to learn

  • Native Interface Functions (NIFs) — NIFs are the official Erlang/OTP extension mechanism for calling compiled code; understanding NIF calling conventions, term layout, and env cleanup is core to what Rustler abstracts
  • Erlang Term Format (External Term Format) — Rustler's entire term.rs and types/ modules exist to encode/decode Rust values into Erlang's binary term representation; this is the on-wire protocol
  • Proc Macros (Procedural Macros) — Rustler uses proc macros (#[rustler::nif], #[derive(NifStruct)]) to generate boilerplate; understanding syn, quote, and proc-macro crate is essential for extending code generation
  • Reference-Counted Resource Handles (Arc + Monitors) — The resource/ module uses Arc<T> + monitor callbacks to safely pass Rust struct pointers into Erlang; this is Rustler's solution to rust-side cleanup when Erlang GC runs
  • Panic Catching and Unwinding Boundaries — error.rs catches Rust panics before they unwind into the C NIF boundary, preventing BEAM crashes; this requires panic='unwind' and catch_unwind in error handling
  • Erlang Environment (enif_env) — The env.rs module wraps the Erlang NIF environment handle; all term creation, atom lookups, and exception raising flow through it
  • Lifetime-Bound Resource Contexts — Rustler types like Env<'a> and Binary<'a> are lifetime-bound to prevent use-after-free of Erlang terms; understanding Rust lifetimes is critical to type safety guarantees
  • elixir-nx/Polaris — Similar Erlang/Elixir-to-compiled-code bridge for high-performance compute; alternative approach using C++ instead of Rust
  • PhilippHeuer/erlang-nif-sys — Lower-level unsafe NIF FFI bindings; Rustler builds safety layers atop patterns from this project
  • rusterlium/NifIo — Official Rustler example project demonstrating async I/O patterns and real-world NIF usage (referenced in main README)
  • serde-rs/serde — Serialization framework Rustler optionally uses for term encoding; understanding serde traits is required for the serde feature
  • erlang/otp — The Erlang/OTP repository; Rustler must track NIF API changes across OTP versions (build.rs and sys/types.rs depend on OTP headers)

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for rustler/src/serde module

The serde module (rustler/src/serde/) handles serialization/deserialization between Rust and Erlang terms. Currently there's a rustler_tests/native/rustler_serde_test crate, but integration tests at the serde submodule level (atoms.rs, de.rs, ser.rs, error.rs, util.rs) are likely incomplete. Adding unit tests directly in these files would improve reliability for type conversions and error handling, critical for data marshalling.

  • [ ] Add #[cfg(test)] modules to rustler/src/serde/atoms.rs with tests for atom encoding/decoding edge cases
  • [ ] Add unit tests to rustler/src/serde/de.rs covering deserialization of complex nested structures and error paths
  • [ ] Add unit tests to rustler/src/serde/ser.rs covering serialization of all supported Rust types
  • [ ] Add unit tests to rustler/src/serde/error.rs for error variant construction and display formatting
  • [ ] Verify tests run with cargo test -p rustler and update CHANGELOG.md

Add resource lifecycle and safety tests in rustler/src/resource/

The resource subsystem (rustler/src/resource/) is critical for managing stateful data across NIF calls. Files like arc.rs, monitor.rs, and registration.rs handle memory safety and cleanup. There are likely gaps in unit tests for edge cases like resource cleanup on NIF errors, double-free prevention, and monitor callback invocation. Adding focused tests would strengthen safety guarantees.

  • [ ] Add unit tests to rustler/src/resource/arc.rs verifying reference counting and thread-safety with Arc cloning
  • [ ] Add unit tests to rustler/src/resource/monitor.rs testing monitor registration, firing, and cleanup sequences
  • [ ] Add unit tests to rustler/src/resource/registration.rs for resource type registration and lookup
  • [ ] Add integration test case in rustler_tests demonstrating resource cleanup on NIF panic/error
  • [ ] Document resource lifecycle guarantees in rustler/src/resource/mod.rs with examples

Implement wrapper function tests for rustler/src/wrapper/ submodules

The wrapper directory (rustler/src/wrapper/) contains low-level FFI bindings to Erlang NIF functions (atom.rs, binary.rs, list.rs, map.rs, term.rs, etc.). These are thin abstractions over unsafe C interop. Unit tests for safe wrapper boundaries (bounds checking, null handling, type validation) would catch regressions early and document safe usage patterns.

  • [ ] Add unit tests to rustler/src/wrapper/binary.rs testing binary creation/reading with edge cases (empty, max size, misaligned)
  • [ ] Add unit tests to rustler/src/wrapper/list.rs for list head/tail operations and iteration safety
  • [ ] Add unit tests to rustler/src/wrapper/map.rs for key lookup, insertion, and collision handling
  • [ ] Add unit tests to rustler/src/wrapper/tuple.rs for tuple bounds checking and element access
  • [ ] Add documentation comments explaining safety invariants for each wrapper module

🌿Good first issues

  • Add missing Encode/Decode trait documentation with concrete examples in rustler/src/term.rs comments. The traits lack usage examples that would help newcomers understand trait bounds in macro-generated code.
  • Expand rustler_tests/native/rustler_test/src/lib.rs with test cases for error handling edge cases (e.g., encoding a malformed tuple, decoding with wrong term type). Current tests don't cover all error paths in term.rs and types/.
  • Create a standalone guide document (e.g., ARCHITECTURE.md) explaining the three-layer model: rustler (safe Rust API) → sys/functions.rs (unsafe FFI wrapper) → Erlang C NIF ABI. New contributors struggle with this abstraction stack.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 0370e41 — (release) 0.37.4 (filmor)
  • 6cfaede — Fix compile warning and clarify comment (filmor)
  • 7c81903 — fix: gate ErlNifResourceTypeInit members/dyncall on nif_version_2_16 (#725) (lmth)
  • 49de29b — (release) 0.37.3 (filmor)
  • 58cc808 — Update changelog (filmor)
  • 30272d9 — Add no_artifacts compiler message (#721) (chgeuer)
  • b539c3b — Add missing inline and From<Binary> for Term (#719) (filmor)
  • a0c574d — (release) 0.37.2 (filmor)
  • 0ff9ab1 — Fix iolist inspect (#715) (filmor)
  • e594828 — Update CI to use current versions of OTP and Elixir (#713) (filmor)

🔒Security observations

Rustler demonstrates a generally sound security posture as a safety-focused FFI bridge library. The primary concern is the panic configuration in release builds, which could compromise the safety guarantees between Rust and the Erlang VM. No obvious injection vulnerabilities, hardcoded secrets, or critical misconfigurations were identified in the provided file structure. The codebase appears well-organized with proper separation of concerns. Recommendations focus on strengthening operational security practices (vulnerability disclosure policy, automated dependency scanning) and fixing the panic configuration to align with NIF safety best practices.

  • Medium · Panic Configuration in Release Profile — Cargo.toml (root workspace, profiles.dev and profiles.release). The Cargo.toml workspace configuration sets 'panic = "unwind"' for both dev and release profiles. For a safety-critical FFI library like Rustler that bridges Rust and Erlang NIFs, unwinding panics can lead to undefined behavior in the BEAM runtime. The NIF specification typically requires 'panic = "abort"' to prevent stack unwinding across FFI boundaries, which could corrupt the Erlang VM state. Fix: Change panic configuration to 'panic = "abort"' in the release profile, and consider using abort for dev as well to maintain consistency and safety: [profile.release] panic = "abort" [profile.dev] panic = "abort"
  • Low · Missing SECURITY.md Policy — Repository root. No security disclosure policy or SECURITY.md file is present in the repository. For a library used in production systems (especially one dealing with FFI safety), a clear security reporting mechanism is important for responsible disclosure of vulnerabilities. Fix: Create a SECURITY.md file with a clear vulnerability disclosure policy, including contact information and timeline expectations for security issues.
  • Low · No Dependency Verification Mechanism — .github/workflows/main.yml. While the Cargo.lock file typically locks dependencies, there is no evidence of additional dependency verification mechanisms like cargo-deny, cargo-audit, or supply chain security tooling mentioned in CI/CD configuration. Fix: Add automated dependency vulnerability scanning in CI/CD: use 'cargo-audit' or 'cargo-deny' in GitHub Actions to detect known vulnerabilities in dependencies.
  • Low · Limited Visibility into CI/CD Security Practices — .github/workflows/main.yml. The GitHub Actions workflow configuration file exists but its contents are not provided in the analysis. Without reviewing the actual CI/CD pipeline, potential security issues like unsigned artifacts, missing build verification, or insecure secret handling cannot be assessed. Fix: Review the CI/CD configuration to ensure: proper secret management, signed releases, reproducible builds, and artifact verification.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · rusterlium/rustler — RepoPilot