Vodozemac

Elixir bindings to Matrix's Olm and Megolm cryptographic primitives, backed by Element's vodozemac (Rust) — the modern successor to the deprecated libolm.

def deps do
  [{:vodozemac, "~> 0.1"}]
end

⚠️ Pre-1.0. The surface is documented and tested, but signatures may shift before 1.0 — see the stability table below for the rows that are explicitly unstable.

Scope

Olm and Megolm are the pairwise and group ratchets defined by the Matrix end-to-end encryption spec. This library wraps only those primitives. It does not speak HTTP, parse Matrix events, or talk to Synapse. Callers are responsible for:

That boundary is deliberate so the same library can power a thin client SDK, a server-side chat backend, a CLI testing tool, or an appservice — none of which agree on the rest of the stack.

Quick start

# Long-term identity for a device.
account = Vodozemac.account_create()

# Identity keys to publish to the server.
{curve25519, ed25519} = Vodozemac.account_identity_keys(account)

# Generate ten unpublished one-time keys for the pre-key bundle.
{account, otks} = Vodozemac.account_generate_one_time_keys(account, 10)

# After `/keys/upload` succeeds, mark them published so future
# generate calls return only NEW ones.
account = Vodozemac.account_mark_published(account)

# Establish an outbound Megolm session for a room.
{session_id, session_key, outbound} = Vodozemac.outbound_group_session_create()
{ciphertext, outbound} = Vodozemac.outbound_group_session_encrypt(outbound, "hello")

# The corresponding inbound session is built from the session_key the
# sender shares (typically wrapped in an Olm m.room_key to-device event).
{^session_id, inbound} = Vodozemac.inbound_group_session_create(session_key)
{:ok, "hello", _index, _inbound} =
  Vodozemac.inbound_group_session_decrypt(inbound, ciphertext)

Every function that mutates state returns a fresh pickle. Callers must persist the latest pickle — once you advance the ratchet, the previous pickle can no longer decrypt the next message.

Status of the surface

Area API Stability
Account (identity + OTKs) account_* stable
Olm pairwise sessions olm_* stable
Inbound Megolm group session inbound_group_session_* stable
Outbound Megolm group session outbound_group_session_* stable
Raw Ed25519 / Curve25519 ed25519_* / curve25519_* stable
SAS (emoji verification) sas_* stable
Pickle encryption key (not exposed) unstable

The last row is the headline pre-1.0 gap: pickle bytes are wrapped with vodozemac's default zero-key (no confidentiality at rest). Callers must apply their own at-rest encryption layer until a pickle_key parameter lands in a later release.

Precompiled binaries

0.1.0 is source-only. Consumers need a Rust toolchain (rustup install stable) on mix deps.get; the NIF compiles from source via Rustler. Precompiled tarballs for the targets below are scheduled for 0.1.1:

Target Status
aarch64-apple-darwin planned for 0.1.1
x86_64-apple-darwin planned for 0.1.1
aarch64-unknown-linux-gnu planned for 0.1.1
x86_64-unknown-linux-gnu planned for 0.1.1
FreeBSD planned

Once 0.1.1 ships, the precompiled artifact for your target is fetched automatically from the sr.ht release ref. Force a local cargo build at any time with RUSTLER_PRECOMPILATION_FORCE_BUILD=1 before mix deps.get.

Hacking

The repo lives at https://git.sr.ht/~sbr/vodozemac. Patches welcome — send via git send-email to the ~sbr/public-inbox list, or open a ticket on the tracker.

git clone https://git.sr.ht/~sbr/vodozemac
cd vodozemac
mix deps.get
RUSTLER_PRECOMPILATION_FORCE_BUILD=1 mix compile
mix test

License

The Elixir + Rust glue in this repository is licensed under BSD-3-Clause — see LICENSE.

The precompiled NIF binaries (the .tar.gz shipped to the sr.ht release ref for each tagged version) statically link Element's vodozemac (Apache-2.0) and a tree of transitive Rust crates. Each tarball includes:

The source-only Hex package does not bundle vodozemac — Cargo resolves it from crates.io at build time on the consumer's machine — so the Apache-2.0 redistribution obligation only applies to the precompiled artifacts.