README.md

# elixir-mst

An Elixir implementation of [AT Protocol-flavoured Merkle Search Trees (MST)][spec].

## Overview

A Merkle Search Tree is a content-addressed, ordered key/value structure.
Every unique set of key/value pairs produces a unique root CID, making it
suitable for Merkle proofs, efficient diffs, and repository synchronisation.
This library implements the AT Protocol flavour: nodes are encoded as
DRISL, keys are arbitrary byte strings, values are `DASL.CID` links, and
tree depth is derived from the SHA-256 hash of each key.

[spec]: https://atproto.com/specs/repository#mst-structure

## Installation

Get elixir-mst from [hex.pm](https://hex.pm) by adding it to your `mix.exs`:

```elixir
# mix.exs
def deps do
  [
    {:mst, "~> 0.1"}
  ]
end
```

Documentation can be found on HexDocs at https://hexdocs.pm/mst.

## Quick start

```elixir
# Build a tree
tree = MST.new()

val = DASL.CID.compute("my record data")
{:ok, tree} = MST.put(tree, "app.bsky.feed.post/3jqfcqzm3ft2j", val)
{:ok, ^val} = MST.get(tree, "app.bsky.feed.post/3jqfcqzm3ft2j")

# Mutate (persistent — the original tree is unchanged)
{:ok, tree2} = MST.put(tree, "app.bsky.feed.post/3jqfcqzm3fz2j", val)
{:ok, tree2} = MST.delete(tree2, "app.bsky.feed.post/3jqfcqzm3ft2j")

# Enumerate in sorted order
{:ok, pairs} = MST.to_list(tree2)
# => [{"app.bsky.feed.post/3jqfcqzm3fz2j", val}]

# Or stream lazily
tree2 |> MST.stream() |> Enum.each(fn {key, cid} -> ... end)
```

## CAR import / export

```elixir
# Export to a CARv1 binary
{:ok, car_bytes} = MST.to_car(tree)
File.write!("repo.car", car_bytes)

# Import from a CARv1 binary
{:ok, tree} = MST.from_car(File.read!("repo.car"))
```

## Diffing two trees

```elixir
{:ok, diff} = MST.diff(tree_a, tree_b)

diff.created_nodes  # MapSet of node CIDs added in tree_b
diff.deleted_nodes  # MapSet of node CIDs removed from tree_a

for %MST.Diff.Op{key: key, old_value: old, new_value: new} <- diff.record_ops do
  case {old, new} do
    {nil, cid} -> IO.puts("create  #{key} → #{cid}")
    {cid, nil} -> IO.puts("delete  #{key} (was #{cid})")
    {old, new} -> IO.puts("update  #{key}: #{old} → #{new}")
  end
end
```

---

This project is licensed under the [MIT License](./LICENSE)