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:

  • talking to the homeserver (/keys/upload, /keys/query, /keys/claim, /sendToDevice, /sync)
  • routing m.room_key / m.room.encrypted events
  • persisting the serialized session state this library returns
  • key backup transport

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

AreaAPIStability
Account (identity + OTKs)account_*stable
Olm pairwise sessionsolm_*stable
Inbound Megolm group sessioninbound_group_session_*stable
Outbound Megolm group sessionoutbound_group_session_*stable
Raw Ed25519 / Curve25519ed25519_* / 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:

TargetStatus
aarch64-apple-darwinplanned for 0.1.2
x86_64-apple-darwinplanned for 0.1.2
aarch64-unknown-linux-gnuplanned for 0.1.2
x86_64-unknown-linux-gnuplanned for 0.1.2
x86_64-unknown-freebsdplanned for 0.1.2

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:

  • LICENSE — this project's BSD-3-Clause text
  • LICENSE-APACHE — full Apache-2.0 text covering vodozemac
  • THIRD-PARTY-NOTICES — auto-generated inventory of every linked crate with its license text (built via cargo-about)

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.