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.encryptedevents - 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
| 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.2 |
x86_64-apple-darwin | planned for 0.1.2 |
aarch64-unknown-linux-gnu | planned for 0.1.2 |
x86_64-unknown-linux-gnu | planned for 0.1.2 |
x86_64-unknown-freebsd | planned 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 textLICENSE-APACHE— full Apache-2.0 text covering vodozemacTHIRD-PARTY-NOTICES— auto-generated inventory of every linked crate with its license text (built viacargo-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.